20210101のRubyに関する記事は19件です。

記事の並べ方(昇順・降順)について

自己紹介

こんにちは!
今回初めて投稿をします。
同じことで何度も悩んでしまわない為の、自分用のメモとして、またアウトプットする事によって記憶の定着に繋がればと思い始めました。

それでも、もしかするとどなたかに拝見していただける機会があるかもしれませんので、少しだけ自己紹介をさせていただきます。
2020年10月中旬より本格的にプログラミングの勉強をスタートさせ、現在はプログラミングスクールも活用し、未経験の中途という形でエンジニア転職を目指している者です。

さて今回の本題に!

投稿された記事をIDの若い順に並べてください。

この言葉にまず「若い順」ってどういう意味だろうと疑問に感じました。
結果から言うと、答えはそのままの意味で1.2.3.4....となるような数字の小さい順という事でした。
この若い順が一般的な昇順(asc)、その反対が降順(desc)になります。

今回のIDの話で例えると、
・昇順:IDが小さい順。古い記事が上にくる。
・降順:IDが大きい順。新しい記事が先頭にくる。

今回は、コントローラに記述する方法を使用したので残しておく事にします。

1.昇順、降順どちらも指定しない場合。
デフォルト値は降順になります。
スクリーンショット 2020-12-09 18.38.38.png

2.昇順、降順指定をする場合。
掲載画像のコメントにも載せましたが、orderを記述する場合は手前のallは省略可能。
スクリーンショット 2020-12-09 18.56.56.png

何かを投稿するようなSNSなどには必ず必要な表記になりそうですね。
忘れないようにここに記して、今回の投稿は以上で終了とさせていただきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】slick(スライダー機能)を簡単に導入する方法

はじめに

簡単に実装できると思います。

slickとは画像等をスライドショーのように表示させる機能を付けられるjQueryプラグインです。

本記事は導入方法簡単な実装のみを紹介します。

「サイトをおしゃれに仕上げたいから、もっと発展的なことを教えてくれ!」という方。
ごめんなさい。

hamlslimではなく普通のhtmlで記述していますので、初学者の人にとっても分かりやすいはず。。
haml.slimで書かれてる記事多すぎだぜ(つらい)

また私が最初に導入してエラーを吐いたところも解決方法とともに紹介していきます。
誰かが躓くところは皆転ぶ!

こんな人に向けて

1.haml.slimがよくわからない初学者
2.rails に slickを導入したいけど方法が分からない人
3.他の記事を見ながら実装し、エラーを吐かれて詰んだ人 (この記事で詰まったらごめんなさい)

今回はrails 5.2.4を使っています。
またOSはWindowsですが、macでも同じだと思います。(見た目は少し違うかも。。。)

1.導入準備

1.rails newコマンドでアプリケーションを既に作成していること
2.rails sでサーバーを起動したとき、ページが表示されるようになっていること(ルート・コントローラ・ビューが正しく記載されている)

上記の2点を前提として続けていきます。

まずslickはjQueryを使っていますので、

Gemfile
gem "jquery-rails"

を記述し、インストールします。

ターミナル
bundle install

インストールしたら別の場所にも以下を記述します。

app/assets/javascripts/application.js
下2行を追加(日本語の文字はいらないです)
//= require jquery
//= require jquery_ujs

//= require rails-ujs
//= require activestorage

//= require_tree .

以下の文は一応消しておきます。

//= require turbolinks

これで準備は完了です。
さっそく導入していきます。

2.slickを導入する

1.公式からファイルをダウンロード
2.自分のアプリケーションの特定のフォルダにファイルを入れる
3.htmlに特定の記述をする
4.scssに特定の記述をする
の3段階で導入できるので分けて説明します。

2-1.公式からダウンロード

slickからファイルをダウンロードします。
スクリーンショット (16)_LI.jpg
このボタンでダウンロードを開始します。(下の方にスクロールすると上記のような画面があります)

2-2.特定のフォルダに入れる

slick-1.8.1.zip
というフォルダがダウンロードされます。

そこから slick-1.8.1 → slick と進んでいくとファイルがいくつかあります。
その中から
slick.min.js
slick.scss
slick-theme.scss
の3つを使います。(最低限必要なものだけを)

ファイル 格納場所
slick.min.js app/assets/javascripts/
slick.scss app/assets/stylesheets/
slick-theme.scss app/assets/stylesheets/

上記の場所に入れてください。

2-3.htmlに記述をする

htmlに記述します。

app/views/layouts/application.html.erb
  <head>
    <title>Title</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>

    <!--この下2行を追加-->
    <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.css"/>
    <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick-theme.css"/>

    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>

  </head>

2-4.scssに記述をする

scssをインポートするために、以下の記述をします。

app/assets/stylesheets/application.scss
 *= require_tree .
 *= require_self
 */

*以下の2行を追加
@import "slick-theme"; 
@import "slick";

これで導入はできました。

3.slickを使用する

導入はできたのでこれから実際に使ってみます。

今回は基本的な以下の機能を実装します。(アレンジする場合は他の人の記事を見てくださいね)
・下にドットを表示する
・ドットを押せば写真がスライドする
・画像をフリックするとスライドする

html.erb
<div class="hoge">
    <div class="nest-hoge">aaa</div>
    <div class="nest-hoge">bbb</div>
    <div class="nest-hoge">ccc</div>
</div>

hogenest-hogeの部分は何でもいいです。(cssの命名規則は分からない。。)

app/assets/javascripts/application.js
$(function() {
    $('.hoge').slick({
        dots: true,
    });
});

親クラスを指定してあげれば動きます。

これで実装できました。(簡単なのか?)

4.エラー集(私のミスを含む)

最後に私が初めてslickを導入したときに起きたエラーを紹介します。
笑える人は笑ってください。

4-1.bundle install 忘れ

動かない

gemを追加しているので、bundle installをしましょう。

4-2.ファイル格納場所間違え

正しい位置に入れましょう。

間違ってもjsのファイルをstylesheets下に入れないように

4-3.htmlに未記述

ファイルをダウンロードしているし、記述はいらないと思ってました。
スクリーンショット (18).png
デベロッパーモードでエラーがこんにちは
記述は必要みたいです。

4-4.application.jsで注意マークが

スクリーンショット (23).png

もう無理。。。どうすればいいのだろう。。。
スクリーンショット (25).png

18行目に注目。

/*global $*/

を追加すればOKみたいです。

さいごに

以上で終わりです。
スライダープラグインはslickだけでなくswiperなどいろいろなものがあります。
まとめサイトもありますので参考にしてみてください。

ではでは。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails奮闘記/Railsチュートリアル編】Railsチュートリアル3章で学んだこと

はじめに

Railsチュートリアル3章を終えました。
実務に入った上で、ルーティング・コントローラー・ビュー周りとテストを学習しました。
学習した中で、実務に使える内容をまとめました。

コントローラー生成のコマンド

コントローラーを生成するときのコマンドは以下のようになります。

$ rails g controller (コントローラー名) (アクション名)

generateスクリプトではアクション名をまとめて指定することができる。
Railsチュートリアルでは、アクション名がhome、help、aboutに当たる。

もし、例えばコントローラー名のスペルを間違えた時など、コントローラーをコマンドですでに生成してまったら普通だったら1つ1つのファイルを削除することを考えるかと思います。
では、どうすれば良いか?
そんなときに便利なコマンドが以下のコマンドで、生成したコントローラーのファイルを一挙に削除することができます。

$ rails destroy controller (コントローラー名) (アクション名)

これだけで生成したコントローラーのファイルを削除して元に戻すことができます。

モデルの生成のコマンド

コントローラー生成と同じ要領で、モデル生成するときは以下のコマンドを叩きます。

$ rails g model (モデル名) (カラム名: データ型)

すでに生成してしまったモデルを生成する前に戻したいときは、以下のコマンドを叩きます。

$ rails destroy model (モデル名)

モデルを削除したいときはモデル名のあとの引数は不要です。

テスト

Railsチュートリアルでは、コントローラーのテスト(3章)、モデルのテスト(6章)、統合テスト(7章)の3つ。
テストは以下の順番で行う。
1.失敗するテストを最初に書く
2.アプリケーションのコードを書いて成功させる(テストをパスさせる)
3.必要ならリファクタリングを行う
これがテスト駆動サイクルと呼ばれるものになる。

minitest reporters

minitest-reportersはgemの1つであり、これを使えるようにすることで、テスト成功と失敗が見やすくなります。
RailsのテストはRSpecが主流ですが、Railsチュートリアルではミニテストで最後までやっていこうと思います。

Guard

Guardの設定を終えたら、以下のコマンドを叩きます。

$ bundle exec guard

コマンドを実行することで、コントローラーのファイルを変更すると、そのコントローラーのテストを自動で実行してくれます。
コマンドを終了したいときは、Ctrl + Dで終了することができます。
テスト自動化は本当に便利なので、初期設定を早めに終わらせておいたほうが開発に専念できそうだなとQiitaで書いていて思いました。

終わりに

以上がRailsチュートリアル3章のまとめになります。
自分の中で実務に活かせそうなことをQiitaにまとめした。
こんな感じで各章を終えたら、どこが実務で生きるかをまとめていこうと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Simple Calendar gemの導入・基本的な使い方

RailsのgemであるSimple Calendarを使ったアプリ開発を行っているので、後学のために、Simple Calendarの導入と基本的な使い方を説明します。

内容的にはsimple_calendarの公式GitHubの日本語訳になります。

Simple Calendarとは

カレンダー表示機能を簡単に実装できるようにしてくれるgemです。

実行環境

  • Ruby 2.6.5
  • Rails 6.0.3.4

Simple Calenderのインストール

Gemfileに以下のように記述し、ターミナルでbundle installを入力します。

Gemfile
gem "simple_calendar", "~> 2.0"
ターミナル
bundle install

application.cssファイルにデフォルトのスタイルシートを読み込みます。

app/assets/stylesheets/application.css
*= require simple_calendar

もしくはapplication.scssファイルに下記の記述をします。

app/assets/stylesheets/application.scss
@import "simple_calendar";

メソッドを使ってビューファイルにカレンダーを生成

Simple Calendarのメソッドを使ってビューファイルにカレンダーを生成する方法を説明します。

month_calendarメソッド

month_calendarメソッドを使えば月間カレンダーを生成できます。

month.html.erb
<%= month_calendar do |date| %>
  <%= date %>
<% end %>

スクリーンショット 2021-01-01 21.52.59.png

week_calendarメソッド

week_calendarメソッドを使えば週間カレンダーが生成できます。
オプションのnumber_of_weeksで何週分表示するかを設定できます。number_of_weeksを記述しない場合は、デフォルトで1週分表示します。

week.html.erb
<%= week_calendar(number_of_weeks: 2) do |date| %>
  <%= date %>
<% end %>

スクリーンショット 2021-01-01 21.55.36.png

calendarメソッド(表示日数をカスタムする)

calendarメソッドを使えばカレンダーの表示日数をカスタムすることができます。
オプションのnumber_of_daysで何日分表示するかを設定できます。number_of_daysを記述しない場合は、デフォルトで4日分表示します。

custom.html.erb
<%= calendar(number_of_days: 4) do |date| %>
  <%= date %>
<% end %>

スクリーンショット 2021-01-01 22.01.07.png

Simple Calendarの導入から基本的な使い方まではここまでとなります。

Simple Calendarの見た目を自分で編集する

各カレンダーの見た目を編集したい場合は下記のコマンドを入力して、ビューファイルを自身のアプリにコピーします。

ターミナル
rails g simple_calendar:views

#以下の通りsimple_calendarフォルダと各ビューファイルが自身のアプリに生成されます
create  app/views/simple_calendar
create  app/views/simple_calendar/_calendar.html.erb
create  app/views/simple_calendar/_month_calendar.html.erb
create  app/views/simple_calendar/_week_calendar.html.erb

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby】10の位と1の位を取得する方法

概要

二桁の整数から10の位と1の位を取得して、計算結果を出力するまでを書いていきます。

目次

  • 実践

    • 問題
    • 解答(解説)
    • 別解(digitsメソッド)
    • 別解(divmodメソッド)
  • まとめ

  • 参考文献

実践

問題

二桁の整数から10の位と1の位の数字の足し算と掛け算の結果を足し合わせて出力するプログラムを書いてください

解答(解説)

解答
def addition(a, b)
  a + b
end

# 10の位と1の位の掛け算
def multiplication(a, b)
  a * b
end

def check_num(num)
  tens_place = (num / 10) % 10   # 10の位を10で割ることで1桁にしてから余りを計算している
  ones_place = num % 10          # 10で割ったときの余りが1の位になる

  addition_result       = addition(tens_place, ones_place)        # additionメソッドで足し算
  multiplication_result = multiplication(tens_place, ones_place)  # multiplicationメソッドで掛け算

  p "足し算の結果と掛け算の結果の合計は#{ addition_result +  multiplication_result }です。"
end

# ターミナルで入力する
p '二桁の整数を入力してください'
num = gets.to_i

# メソッド呼び出し
check_num(num)

別解(digitsメソッド)

書き換えた箇所は10の位と1の位を計算しているところです。 digits.take の引数に 2 を指定しているのは1の位と10の位の2つの値を取得しているからです。注意点としては digits.take で取得する際 1の位 から順番に取得する点です。

digits
def addition(a, b)
  a + b
end

def multiplication(a, b)
  a * b
end

def check_num(num)
  ones_place, tens_place = num.digits.take(2) # 1の位と10の位を取得

  addition_result       = addition(tens_place, ones_place)       
  multiplication_result = multiplication(tens_place, ones_place) 

  p "足し算の結果と掛け算の結果の合計は#{ addition_result +  multiplication_result }です。"
end

p '二桁の整数を入力してください'
num = gets.to_i

check_num(num)

別解(divmodメソッド)

書き換えた箇所は10の位と1の位を計算しているところです。 divmod の引数(今回の場合は10)に数値を指定することで割ったときの商と余り取得することができます。

divmod
def addition(a, b)
  a + b
end

def multiplication(a, b)
  a * b
end

def check_num(num)
  tens_place, ones_place = num.divmod(10) # 1の位と10の位を取得

  addition_result       = addition(tens_place, ones_place)        
  multiplication_result = multiplication(tens_place, ones_place) 

  p "足し算の結果と掛け算の結果の合計は#{ addition_result +  multiplication_result }です。"
end

p '二桁の整数を入力してください'
num = gets.to_i

check_num(num)

まとめ

  • 10の位は二桁の整数を10で割り、さらに10で割った余り
  • 1の位は二桁の整数を10で割った余り
  • digitsメソッドを使用することで1の位から順番に数値を取得することができる

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スクレイピングでアクセスブロックを回避するために注意すること

普段、クローラー開発をメインに仕事をしている。
業務上、データを収集するために様々なサイトにアクセスする機会があり、日々アクセスブロックと格闘している。
今回は自分の備忘録も兼ねて、スクレイピングでアクセスブロックに合わないための技術や対処法をまとめておく。
扱うスクレイピング技術はRubyのopen-uri、curlコマンド、selenium。
改めて言うまでもないが、クロール先サイトの規約や関連法律の遵守はしよう。

アクセスブロックされたときにチェックする項目

アクセスブロックには様々な種類があり、適切な手段をもって回避する

IP

1:アクセス頻度
頻繁にサイト側にリスエストを送信すると、ロボット判定される可能性がある。
解決策:sleep時間を増やす。クロール回数*sleep=所要時間<許容所要時間に収まる範囲で、sleepは長く取る。

2:同じペースでリスエストを送信すると、ロボット判定される可能性がある。
解決策:ランダムなsleep時間を設定する。

3:同一IPから同一サイトにリクエストを頻繁に送信した場合、IP単位でブロックされる可能性がある。
解決策:1IPあたりの連続接続可能回数・秒数を調べて、必要ならSSHサーバーを踏み台にしてIPローテーションする。
別々のサイトであってもCDNがAkamai共通で、AkamaiにIPブロックされた場合はどちらのサイトもブロックされる可能性がある。

# rubyのopen-uri
socksify gem
Socksify::proxy("127.0.0.1", port) { open(url).read }
# curl
curl -x socks5h://loalhost:port url
# selenium
selenium_option = [ --proxy-server=socks5://127.0.0.1:port ]

4:特定のIPからのアクセスをブロックするサイトがある。
解決策:サーバーとしてデータセンター(IDCFやAWS)を使っている場合、SSHサーバーを踏み台にしてIPをプロバイダ等を変えてみる。

HTTP Header

1:デフォルトHTTP Header
リクエストを受け取るサーバー側は、不自然なHTTP Headerのリクエストがきたら怪しいリクエストだと認識して必要に応じてブロックするなどの処理をする。open-uriやcurl等のデフォルトHTTP Headerは明らかにBOTだとわかる。
解決策:User-AgentやAccept-Language等をブラウザでアクセスしたときと同じものを指定する。
ブラウザでアクセスしたときのHeaderは、Chrome→Developer tool→Networkタブ→Copy→Copy as cURLからコピーして確認する。

2:同一ユーザからのアクセス頻度
HTTP Header(やIP)から、同一ユーザーが過剰なアクセスをしているとサーバー側が判断した場合、ブロックされる可能性がある。
解決策:User-Agent等をローテーションしてみる。

Captcha

BOTの動きを判別して、人間が簡単に解決できる問題を解かせるサイトがある。
解決策1:Captchaテストをトリガーしない。
sleepを長くする、IPを変える、seleniumを使っているならwebdriverプロパティを削除するheadlessのオプションを外す等、様々な条件を試す。

解決策2:自動的にテストを解く。
Captha突破API、もしくは機械学習またはディープラーニングスキルを使用してこのチェックに合格する画像認識技術を使う。

補足

webdriverプロパティを削除する

seleniumを使っている場合、次のコードで簡単に自動操作していることがわかってしまう。

var isAutomated = navigator.webdriver;

if(isAutomated){
    blockAccess();
}

webdriverプロパティを削除するには次のコードを実行する。

delete Object.getPrototypeOf(navigator).webdriver;

headlessのオプションを外す

seleniumを使っている場合、headlessで実行するとサイト側にブロックされることがある。
headlessは人間でない証であり、headlessかどうかは次のコードで簡単にわかるからだ。
ditsilやgeetest系のボット防止技術を使っているサイトだと、headlessでは無理で、サーバーで実行する場合はGUI付きが必要。

navigator.permissions.query({name:'notifications'}).then(function(permissionStatus) {
    if(Notification.permission === 'denied' && permissionStatus.state === 'prompt') {
        console.log("Headless Chrome");
    } else {
        console.log("Not Headless Chrome");
    }
});

CDNがAkamaiか確認する

digコマンドで、ドメイン名からIPアドレスを調べる
dig ドメイン名
例:dig www.armaniexchange.com
CDNがAkamaiなら、Answerセクションにakamaiedgeの記載がある。

IPアドレスからドメイン名を調べる

digコマンドで、IPアドレスから逆引きする

dig -x IPアドレス

ポートフォワーディングができているかを確認する

digコマンドで自分のグローバルIPアドレスを確認して、digコマンドの-xオプションでドメインを逆引きする。
Answerセクションのドメイン名でポートフォワーディングができているか判断する。
IDCF:idcfcloud.net.
AWS EC2:amazonaws.com.
フレッツ:mesh.ad.jp.

SSHサーバーを踏み台として使う

SSHサーバーを踏み台にすれば、接続先のWebサーバーにSSHサーバーのIPアドレスから接続してきたと思わせることができる。SSHサーバーの「ダイナミックフォワード」という機能を使ってSSHサーバーまでの「トンネル」を作り、「SOCKSプロキシ」として利用する。

ssh -D ポート番号 ユーザー名@ホスト名

ランダムにSOCKSプロキシのポートを取り出す

IPローテーションする際、複数のSOCKSプロキシを立ち上げて、ランダムにポートを取り出す必要がある。
dynamic_portsメソッドを実行すると、Linuxコマンドpgrep -fal sshでsshサーバーを抽出し、SOCKSプロキシのポートを配列で返すため、sampleメソッドでランダムに一つだけ取り出せば良い。

dynamic_ports.rb
def dynamic_ports
  lines =  `pgrep -fal ssh`.split("\n")
  ports = []
  lines.each do |line|
    opts = line.split
    d_options_index = opts.find_index("-D")
    if d_options_index.present? && d_options_index > 0
      next if opts[d_options_index + 1].to_i <= 0
      ports << opts[d_options_index + 1].to_i
    end
  end
  ports
end

SOCKSプロキシの種類

ローカルでの名前解決
curl --socks5 localhost:port
サーバーでの名前解決
curl --socks5-hostname localhost:port
curl -x socks5h://localhost:post
chrome_option = [ --proxy-server="socks5://127.0.0.1:port" ]

参考文献

10 Ways to hide your Bot Automation from Detection | How to make Selenium undetectable and stealth
どこへ行っても安心!SSHサーバーを踏み台にしてWebアクセスする方法

さいごに

アクセスブロックはサイトによって様々であり、その対策も千差万別である。
スクレイピングできないサイトはない!と言える技術力を身に着けたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

cloud9でインスタンスを作成し直したい場合

初めて記事を書きます。
これは自分用のメモです

cloud9の設定をミスってしまった。
勢い余ってEC2のストレージ容量を無料枠以上にアゲてしまった。
その他よくわからなくなってまた最初から始めたい。
そのようばあい

githubにpushしていることが前提です。
そのpushをcloud9にcloneします。

loud9に新しいインスタンスを作成します。
作成時の設定はrails チュートリアルに従いました。

gitが入っているかを確認(私の環境では既に入っていました)します。

次に新しく作成したインスタンスに自分のgithubの情報を教えてあげます。

git config --global user.name USER_NAME

git config --global user.email EMAIL_ADDRESS

USER_NAME,EMAIL_ADDRESSには自分のユーザーネームとemailアドレスを代入します。私の場合は、

git config --global user.name irojiroserika

となります。

特にレスポンスがあるわけではありませんが、大丈夫です。もしここでユーザーネーム等間違えてしまった場合はもう一度入力しなおせば良いです。

次にcloneしたいcommitのURLを見つけます。
画像参照(緑色のところをクリックすると出てきます。)

キャプチャ.PNG

最後に
git clone そのURL

で、clone自体は終了です。
ファイルを確認してください。

しかし、ここから開発を始めようと思ってもrails serverが立ち上がりませんので、bundle installを実行します。

ここで注意点
私はrailsチュートリアルに乗っ取りGemfileを書き換えているため、開発用と本番用で使用するdbが異なります。この場合

bundle install --without production

も実行しなければbundle install時に本番環境のgemがインストールできないというエラーが発生し、結果的にrails serverが立ち上がりません。

ので
bundle install

bundle install --without production
も実行します。

あ、そうそう

yarnが入っていない場合もrails serverが立ち上がりませんので、yarnをインストールしてください。

このとき

Your Yarn packages are out of date!

Please run yarn install --check-files to update.

というメッセージが返ってくると思いますが、そもそもyarnが入っていない場合は
yarn install --check-files
をそもそも実行できませんので、

source <(curl -sL https://cdn.learnenough.com/yarn_install)
(cloud9環境)

を実行してくださいね。

これでrails serverが立ち上がります。

これで本当に最後です。

rails db:migrateを実行すれば、cloneしてきたアプリがエラーなく見れるようになります。

自分用ですが、為になれば嬉しいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby/Racc: パースに失敗した位置(行、桁)を得る

パースに失敗したとき、失敗した位置をたとえば次のように表示したいですよね?

parse error: line 2, col 5
foo bar
    ^

短いのでとりあえず全部貼ります。

# parser.y

class Parser

rule
  program: "a" "b" "c" "d" ";"
  {
    puts "program found"
    result = val
  }
end

---- header

TokenValue = Struct.new(:value, :pos)

---- inner

def initialize(src, tokens)
  @src = src
  @tokens = tokens
end

def next_token
  @tokens.shift
end

def parse
  do_parse
end

def get_lineno_and_range(pos)
  lineno = 1
  line_start = 0
  range = nil

  @src.each_line do |line|
    next_line_start = line_start + line.size
    range = line_start .. (next_line_start - 1)
    break if range.include?(pos)

    line_start = next_line_start
    lineno += 1
  end

  [lineno, range]
end

def on_error(token_id, val, vstack)
  lineno, range = get_lineno_and_range(val.pos)
  colno = val.pos - range.begin + 1
  line = @src[range]

  puts "parse error: line #{lineno}, col #{colno}"

  puts line
  print " " * (colno - 1), "^\n"
end

---- footer

def tokenize(src)
  tokens = []

  pos = 0
  while pos < src.size
    c = src[pos]
    case c
    when /\s/
      pos += 1
    else
      tokens << [c, TokenValue.new(c, pos)]
      pos += 1
    end
  end

  tokens
end

src = File.read(ARGV[0])
tokens = tokenize(src)
parser = Parser.new(src, tokens)
result = parser.parse()
puts "result: " + result.inspect

a b c d ; というトークン列だけを受理するパーサです。
入力となるソースコードではトークン同士をスペースや改行で区切ります。

パースに成功する例:

a
b

c d
;

たとえば dX に置き換えた次のソースコードを与えるとパースに失敗します。

a
b

c X
;

失敗する場合の実行例:

$ racc parser.y -o parser.rb

$ ruby parser.rb sample_ng.txt
parse error: line 4, col 3
c X
  ^
result: nil

短いので上に貼ったコードを読んだ方が早いと思いますが、一応簡単にメモ。

  • トークンの開始位置(元のソースコードで何文字目か)をトークン値に持たせておく
  • 元のソースコードを Parser#src として保持しておく
  • パースに失敗したら Parser#src と失敗したトークンの開始位置を使って 元のソースコード上の行と桁を割り出す

関連

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails奮闘記/Railsチュートリアル編】Herokuコマンドのまとめ

はじめに

あけましておめでとうございます。
今日から2021年になりました。
本年度もよろしくお願いします。
今回はRailsチュートリアルのHerokuコマンドについて自分で学習したことを簡単にまとめました。
ここではHerokuのアカウントの新規アカウントの作成とHerokuをインストールについては省略します。

Herokuコマンド

Herokuがインストールされてるかどうかを確認したいとき

$ heroku --version

Herokuを開かずにログインしたいとき

$ heroku login --interactive

Herokuに新しいアプリケーションを作成したいとき

$ heroku create

Gitを使ってHerokuにリポジトリをプッシュ

$ git push heroku master

アプリケーションの名前を変更したいとき

$ heroku rename (アプリの名前)

本番環境のログを取得したいとき

$ heroku logs

イベントが発生するたびに自動表示(Ctrl+Cで終了)

$ heroku logs --tail 

終わりに

今回はHerokuコマンドについて簡単に復習としてまとめました。
開発環境ばかりに集中して本番環境に手が回らないという人が多いのではないでしょうか?
実際、私も開発現場に入るまでは開発ばかりに気を取られてしまうことが多かったです。
本番環境にも目が行き届く余裕が欲しいと思う2021年になりそうです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gemを追加してbuildしたら「Could not find XXX in any of the sources」のエラーが出てハマった時に行ったこと

概要

「Could not find XXX in any of the sources」が出て1時間半ほどハマったので解決の過程をメモとして残しておく。

状況

「rails-controller-testing」をGemfileに追加してdocker-compose buildし、ビルドが完了したのを確認してテストを実行したところエラーをはいた。

Could not find rails-controller-testing-1.0.5 in any of the sources
Run `bundle install` to install missing gems.

追加したGemが見つからないと言われてるらしい。指示に従ってdocker-compose run web bundle installを実行。再びテストを実行するも同じエラーを吐いた。

試したこと

・Gemfileで指定されてる書き方をしているか
 →問題なし。指定通りに記載されている。
・rails-controller-testing-1.0.5のバージョンは対応しているか
 →rubygems.orgで確認したが、1.0.5は対応してるので問題なし。
・springを停止して試したらどうなるか
 →springを停止しようとすると上記のエラーを吐くのでそもそも停止できない。
・bundleのパスは正しいか
 →which bundleで確認したが、./rbenv配下をみてるので問題なし。
・変にキャッシュが残っているんじゃないか
 →docker-compose build —no-cacheを実行するも変わらず。

解決したやつ

Gemfile.lockを削除して再度bundle installし、テストを実行したところ別のエラーを吐いた。

ERROR: No container found for web_1

今度はコンテナが見つからないと言われているらしい。


docker-compose upでコンテナを起動してどんなログが出るか確認してみることに。そしたら以下のエラーを吐いているのを確認。

web_1  | Could not find sass-rails-5.1.0 in any of the sources
web_1  | Run `bundle install` to install missing gems.
rails_web_1 exited with code 7

今度は別のGemが見つからないと言われてるらしい。。。
今回も指示に従ってbundle installしてコンテナを再起動したけどエラーは変わらず。


bundle installでバージョンが変わったのかと思い、rubygems.orgでsass-railsのバージョンを確認してみるも、5.1.0は対応している。


installがダメだったので、何となくbuildを試してみたらエラーが消えて正常にコンテナを起動できた。テストを実行したところ無事にテストできた。

Finished in 0.67023 seconds (files took 17.96 seconds to load)
3 examples, 0 failures

結論

原因もなぜ解決できたのかも全くわからないけど、僕の場合はGemfile.lockを削除してbundle installとbuildすることで解決できた。

「Could not find XXX in any of the sources」とか情報少なすぎだしもうちょっと長いエラー吐いて欲しい。

参考記事

https://carefree-se.hatenablog.com/entry/2015/07/22/125904
https://qiita.com/jnchito/items/44ab1df134369ed76911
https://qiita.com/nakanowax/items/fe07e8ccd1721befebeb
https://fuqda.hatenablog.com/entry/2019/03/21/204118

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails でDB に保存されたenum の値をattribute_before_type_cast で取得する

はじめに

Rails でDB に保存されたenum の値をattribute_before_type_cast で取得する方法の備忘録です

環境

  • macOS 10.15.6
  • Ruby 2.5.7
  • Rails 5.2.3

参考URL

https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/BeforeTypeCast.html

https://railsdoc.com/page/options_for_select

https://310nae.com/rails-selectbox/

https://qiita.com/ozackiee/items/17b91e26fad58e147f2e

https://ja.stackoverflow.com/questions/72551/enum-%e3%81%a7%e3%82%bb%e3%83%ac%e3%82%af%e3%83%88%e3%83%9c%e3%83%83%e3%82%af%e3%82%b9%e3%82%92%e8%a1%a8%e7%a4%ba%e3%81%95%e3%81%9b%e3%81%a6%e3%82%a4%e3%83%b3%e3%83%87%e3%83%83%e3%82%af%e3%82%b9%e7%95%aa%e5%8f%b7%e3%82%92db-%e3%81%ab%e4%bf%9d%e5%ad%98%e3%81%97%e3%81%9f%e3%81%84

attribute_before_type_cast とは?

  • enum の文字列ではなくDB に保存された実態値を取得できる
  • enum 定義した際に利用できるインスタンスメソッド

enum の名前定義の値と実態値について

enum 使用時は値の見え方がRails 上とDB に保存される値が異なるので注意が必要です

Railsのenumはコードの表記上、文字列(またはシンボル)で透過的に扱えるように実装されています

SQLを直接叩いたり、DBのGUIクライアントを使って保存されたレコード情報を見るとわかりますが実際は文字列ではなく数値が保存されています。

参考URL: https://ja.stackoverflow.com/questions/72551/enum-%e3%81%a7%e3%82%bb%e3%83%ac%e3%82%af%e3%83%88%e3%83%9c%e3%83%83%e3%82%af%e3%82%b9%e3%82%92%e8%a1%a8%e7%a4%ba%e3%81%95%e3%81%9b%e3%81%a6%e3%82%a4%e3%83%b3%e3%83%87%e3%83%83%e3%82%af%e3%82%b9%e7%95%aa%e5%8f%b7%e3%82%92db-%e3%81%ab%e4%bf%9d%e5%ad%98%e3%81%97%e3%81%9f%e3%81%84

enum で定義した実態値はRails 上では名前定義として振る舞います。

sample.rb
# 血液型(blood_type) をenum 定義する

  enum blood_type: {
    A: 0,
    B: 1,
    O: 2,
    AB: 3,
    # Rails 上での名前定義: DB に保存される実態値
  }

動作確認

下記の想定で動作確認を行います

  • Person モデルにname カラム, blood_type カラムを作成
  • Person モデルでblood_type をenum で定義
schema.rb
# Parson モデルにname カラム, blood_type カラム を作成
  create_table "persons", force: :cascade do |t|
    t.string "name"
    t.integer "blood_type"
  end
parson.rb
# model にblood_type をenum で定義
class Person < ApplicationRecord

  enum blood_type: {
    A: 0,
    B: 1,
    O: 2,
    AB: 3,
  }
end

attribute_before_type_cast メソッド

rails console を使ってattribite_before_type_cast の動作確認

$ rails console
> person = Person.create(name: "Alice", blood_type: 0) # person オブジェクトを作成, DB 保存
:name => "Alice"
:blood_type => "A型"
> person.blood_type # rails 上の値を取得
"A型"
> person.blood_type # rails 上の値のクラスを取得
String < Object
> person.blood_type_before_type_cast # DB の値を取得
0
> person.blood_type_before_type_cast # DB の値のクラスを取得
Integer < Numeric

attributes, attributes_before_type_cast メソッド

  • attributes: インスタンスに対して属性名 => 値 が取得できるインスタンスメソッド

  • attributes_before_type_cast: attributes のbefore 版。DB 上の実態値を属性名 => 値 の形で取得できるインスタンスメソッド

rails console を使ってattributes, attributes_before_type_cast の動作の違いを確認します

$ rails console
> person = Person.create(name: "Alice", blood_type: 0) # person オブジェクトを作成, DB 保存
:name => "Alice"
:blood_type => "A型"
> person.attributes # rails 上の 名前定義を表示
"name" => "Alice"
"blood_type" => "A型"
> person.attributes_type_before_type_cast # DB の実態値を表示
"name" => "Alice"
"blood_type" => 0

使用例

私の場合はenum で定義した値から新規投稿フォームを生成するのに使用しました

# 出力したいHTML
<select>
  <option value="1">type1</option>
  <option value="2">type2</option>
  <option value="3">type3</option>
</select>
person.rb
# blood_type をenum で定義
class Person < ApplicationRecord

  enum blood_type: {
    A: 0,
    B: 1,
    O: 2,
    AB: 3,
  }
end
persons_controller.rb
# person オブジェクトを作成
def new
  @person = Person.new
end

デフォルト値(selected: value) の設定にattirbute_before_type_cast で取得した値を使用しています

new.html.erb
# enum の値からフォーム生成
<%= form_with model: @person local: true do |f| %>
  <%= f.select :blood_type, options_for_select(Person.blood_types, @person.expense_before_type_cast), {}  %>
  <%= f.submit %>
<% end %>
# フォーム生成時の各値
<%= form_with model: @モデル名 do |f| %>
  <%= f.select :カラム名, options_for_select(モデル名.カラム名複数形, @モデル名.カラム名_before_type_cast), {}  %>
  <%= f.submit %>
<% end %>

select タグの空カッコ({}) には注意点があります

必ず通常のオプションを設定しない場合でも空のカッコ{},をオプション部分に設定してください。
この空のカッコがないと、HTMLオプションが無視されてしまうので、id/classを設定したのに効いていない!ということになってしまいます。

参考URL: https://310nae.com/rails-selectbox/

# options_for_select の文法
options_for_select(要素の配列 or ハッシュ, デフォルト値, {オプション}, {htmlオプション})
  • オプション例
    • include_blank: true (先頭行を空にする)
    • selected: default_value (デフォルト値)
  • HTMLオプション例
    • id: id_name (id を付与)
    • class: class_name (class を付与)

まとめ

  • enum 使用時にDB に保存された実態値の取得にはattribute_before_type_castattributes_before_type_cast を使用する
  • enum の名前定義の値と実態値は取得方法が異なる

enum を使った事が無くポートフォリオ作成時に見事にハマってしまったので良い勉強になりました!
間違っている場合はコメント頂けると助かります!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyで住所文字列から最寄駅までの距離を取得する方法

rubyで住所から最寄駅の情報を取得できる様にしてみました。

やったことざっくり

  1. Yahoo APIで住所からを緯度経度を取得
  2. Heart Rails APIで緯度経度から最寄駅情報を取得

1. Yahoo APIで住所からを緯度経度を取得

1.1 yahoo app ID 取得

ここで取得する
https://e.developer.yahoo.co.jp/dashboard/

1.2 リクエストする

・リクエストする住所をurl様にエンコード
・open-uriでリクエストする

1.3 レスポンスから緯度経度をスクレイピング

REXMLを使って取得します。
coordinatesタグに格納されてます。

2. Heart Rails APIで緯度経度から最寄駅情報を取得

2.1 1.で取得した緯度経度でリクエストする

urlはこちら

http://express.heartrails.com/api/xml?method=getStations&x=#{lon}&y=#{lat}"

2.2 返ってきた最寄駅情報をスクレイピングする

タグに駅情報が入っています。
複数の駅情報が含まれるので最初のstationタグを指定します。

完成したコード

コードはこんな感じになります。

require 'open-uri'
require 'uri'
require "rexml/document"

## 1. request Yahoo API

### 1.1
YAHOO_APP_ID = '[Yahoo app_id]'

### 1.2
ADDRESS ='東京都千代田区千代田1番1号'
p ADDRESS
query = URI.encode_www_form(query: ADDRESS) ## エンコーディング

request_url = "https://map.yahooapis.jp/geocode/V1/geoCoder?appid=#{YAHOO_APP_ID}&#{query}"

### 1.3 
res = OpenURI.open_uri(request_url)

doc = REXML::Document.new(res.read)
coordinates = doc.elements['YDF/Feature/Geometry/Coordinates'].text
lon,lat = coordinates.split(',')

p "経度 = #{lon} , 緯度 = #{lat}"

## 2. Heart Rails API

### 2.1 1.で取得した緯度経度でリクエストする
heart_rails_request_url = "http://express.heartrails.com/api/xml?method=getStations&x=#{lon}&y=#{lat}"

### 2.2 返ってきた最寄駅情報をスクレイピングする
res = OpenURI.open_uri(heart_rails_request_url)
doc = REXML::Document.new(res.read)

station = doc.elements['response/station[1]']
name = station.elements['name'].text
line = station.elements['line'].text
distance = station.elements['distance'].text
p name
p line
p distance

実行結果

皇居(東京都千代田区千代田1番1号)で最寄駅をリクエストしてみた結果。

"経度 = 139.75381447 , 緯度 = 35.68382285"
"桜田門"
"東京メトロ有楽町線"
"740m"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

rubyで住所文字列から最寄駅までの距離を取得してみた

rubyで住所から最寄駅の情報を取得できる様にしてみました。

やったことざっくり

  1. Yahoo APIで住所からを緯度経度を取得
  2. Heart Rails APIで緯度経度から最寄駅情報を取得

 実行環境

  1. ruby2.7.2
  2. mac catalina
  3. 実行日時 2021/01/01

1. Yahoo APIで住所からを緯度経度を取得

1.1 yahoo app ID 取得

ここで取得する
https://e.developer.yahoo.co.jp/dashboard/

1.2 リクエストする

・リクエストする住所をurl様にエンコード
・open-uriでリクエストする
レスポンスはこんな感じ
スクリーンショット 2021-01-01 14.58.05.png

1.3 レスポンスから緯度経度をスクレイピング

REXMLを使って取得します。
coordinatesタグに格納されてます。

2. Heart Rails APIで緯度経度から最寄駅情報を取得

2.1 1.で取得した緯度経度でリクエストする

urlはこちら

http://express.heartrails.com/api/xml?method=getStations&x=#{lon}&y=#{lat}"

返ってくるレスポンスはこんな感じ (近い順に複数返ってきてます:eyes:)
スクリーンショット 2021-01-01 14.59.22.png

2.2 返ってきた最寄駅情報をスクレイピングする

上記の様にstationタグに駅情報が入っています。
複数の駅情報が含まれるので最初のstationタグを指定します。

完成したコード

コードはこんな感じになります。

require 'open-uri'
require 'uri'
require "rexml/document"

## 1. request Yahoo API

### 1.1
YAHOO_APP_ID = '[Yahoo app_id]'

### 1.2
ADDRESS ='東京都千代田区千代田1番1号'
p ADDRESS
query = URI.encode_www_form(query: ADDRESS) ## エンコーディング

request_url = "https://map.yahooapis.jp/geocode/V1/geoCoder?appid=#{YAHOO_APP_ID}&#{query}"

### 1.3 
res = OpenURI.open_uri(request_url)

doc = REXML::Document.new(res.read)
coordinates = doc.elements['YDF/Feature/Geometry/Coordinates'].text
lon,lat = coordinates.split(',')

p "経度 = #{lon} , 緯度 = #{lat}"

## 2. Heart Rails API

### 2.1 1.で取得した緯度経度でリクエストする
heart_rails_request_url = "http://express.heartrails.com/api/xml?method=getStations&x=#{lon}&y=#{lat}"


### 2.2 返ってきた最寄駅情報をスクレイピングする
res = OpenURI.open_uri(heart_rails_request_url)
doc = REXML::Document.new(res.read)

station = doc.elements['response/station[1]']
name = station.elements['name'].text
line = station.elements['line'].text
distance = station.elements['distance'].text
p name
p line
p distance

実行結果

皇居(東京都千代田区千代田1番1号)で最寄駅をリクエストしてみた結果。

"経度 = 139.75381447 , 緯度 = 35.68382285"
"桜田門"
"東京メトロ有楽町線"
"740m"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

騙されるな!プログラミングの勉強なんて最初はこれだけで十分!

「プログラマーなら最低限これくらい勉強しておかないと…」
「今時、〇〇を使わないなんて…」

うるせーーーーーーー!!!!!!!

近頃、プログラミングについての情報発信が多いので、色々と勉強しないといけないと思っている人が多いです。

初心者がDockerを勉強していたり、CI/CDツールを使おうとしていたり…

楽しいならそれでいいのですが、特に必要性もないのに無理に難しい技術を使っても頭に入ってこないと思います。

プログラミングを効率よく習得するなら、勉強するより開発することです。

料理も座学で勉強しまくるより、作りまくったほうが上達しますよね?
プログラミングもそんなもんです。

特にプログラミングをまだ面白いと思えていない方は、情報に振り回されて勉強ばかりしている可能性大です。
上達するためにも、楽しさを知るためにも、簡単なサービスでいいのでさっさと開発してみましょう。

とはいえ知識0では開発することが難しいという意見も分かります。
そこで下記の対象者のような方が、何をどれくらい勉強すればいいのか簡単に紹介します。

「対象者」

  • 勉強を始めたばかりのプログラミング初心者
  • 勉強はそこまで好きではないがプログラミングをしてみたい方
  • プログラミングの知識はないけど個人開発をしてみたい方
  • 開発せずに勉強に逃げている方

最後の「開発せずに勉強に逃げている方」は、もしかしたら今回紹介する勉強内容をすでに終えているかもしれません。
それなら次に勉強するものを探すのではなく、すぐに開発に取り組みましょう!

まずはProgateで勉強

  • HTML/CSS
  • JavaScript
  • Ruby
  • Ruby on Rails
  • Command Line
  • Git
  • SQL

分かりやすい良い教材です。

プログラミング初心者の方もとっつきやすいと思います。

https://prog-8.com/

様々なコースがありますが、まずは上記で紹介したコースを学ぶことをオススメします。

これらを学ぶことがどう役立つかは実際に作ってみないと分からない部分も多々あります。

とりあえず学んでみることをオススメします。

Ruby on Railsの知見を深める

さすがにProgateレベルの知識だけで開発をするのはしんどいと思います。

そこでRuby on Railsの知見を、もう少し深めましょう。

オススメは「Railsチュートリアル」という教材です。

https://railstutorial.jp/

(解説動画付きだと高額になりますが、テキストのみだと最新版でも980円+税で読めます)

ただし有名な教材だけあって質はいいのですが、難しいです。

そのため1周目で完璧に理解する必要はありません。

何なら挫折して当たり前くらいの気持ちで、挑戦してください。


Railsチュートリアルがあまりにも難しいと感じた方は、僕が作った「Railsビギナー」という教材に挑戦してみてください。

Railsチュートリアルに挫折する方が多いので、プロゲートとRailsチュートリアルの間の難易度の教材を自分で作ってみました。

もちろん無料で読めるので安心してください。

無料ですが合計で6万文字以上あるちゃんとした教材です。

https://maki-nomad.com/rails-beginner/


「Railsチュートリアルなんて初心者向けの教材なんだから簡単だよ!」というアドバイスは聞かなくていいです。

難しい教材を簡単だと紹介して自分のレベルを高く見せようとする、初心者に優しくないクソみたいなエンジニアのアドバイスは無視して大丈夫です。

さぁ、開発を始めよう

これだけ勉強すればさっさとサービスを作ってみてください。

めちゃくちゃしょぼいサービスしか作れないと思いますが、それでいいのです。

プログラミングの楽しさに気付けると思います。

こんなことを言うと「実際に仕事で使えるようになるには、もっと勉強が必要だ」とか、しょーもないことを言ってくるエンジニアもいます。

「仕事で使えるようになるのに必要な勉強量」なんて言っていません。
「まずはこれだけで十分」という話です。

まずは楽しさを知ってください。
最初から難しい技術に挑戦して楽しめるのは天才だけです。

楽しさに気づけたら自然と興味が湧いてきます。

「もっと分かりやすいコードを書くにはどうすれば?」
「もっとカッコよく動きのあるサービスを作ってみたいなぁ」

そうなれば自然と学習をしていて、以前より苦しまずに勉強に取り組めますよ。


今回紹介した学習の道筋は、勉強がそこまで好きではない方に向けたものです。

勉強がけっこう好きな方には、もう少し学習のボリュームを増やしたオススメの学習の道筋もあります。

https://note.com/maki_yuya/n/n24618b7087ac

↑以前まで有料の記事でしたが、2021年に無料化しました!また有料化するかもしれないので、お早めにチェック!


あと、もしあなたが個人開発に興味があるなら、無理に難しい技術を使う必要はないということも知っておいてください。

実際に僕も個人開発をするとき、Dockerは使いませんし、デプロイする先はAWSではなくHerokuです。

そんな感じで僕が作ったサービスはこちら↓

https://tanomun.net/about

お願い事ができる&届くサービスです。

初詣に行きにくい人が多いと思ったので、せめて気分だけでも初詣を味わってください!

まとめ

勉強することが目的ではありません。

あなたの頭の中に知識が詰まっているだけでは意味がありません。

開発してサービスを生み出して初めて価値になります。

勉強に逃げず、開発をしてみましょう。

それが楽しさと成長に繋がります。

これからプログラミングの勉強を始めてフリーランスを目指す方は、こちらの記事も参考にしてみてください↓

https://note.com/maki_yuya/n/nb3ddcc27daf6

フリーランスになるまでの悪い面もリアルに書いたので、参考になると思います。

それでは2021年を良い年にしていきましょう!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Heroku】デプロイ時 We're sorry, but something went wrong.解決方法

ローカル環境で構築して、Herokuにデプロイしたが、こんな画面がでてしまいました。
スクリーンショット 2020-12-19 18.43.35.png
We're sorry, but something went wrong.

原因

開発環境(deveropment)はMySQLとしていたが、Herokuの標準データベースはPostgreSQLだが、MySQLのままだった。

対処方法

gemに下記を追記して、本番環境ではpostgreSQLを適用する。

group :production do
  gem 'pg', '>= 0.18', '< 2.0'
end

group :production doで本番環境のみの適用となります。

bundle installしますが、開発環境のみで本番環境では不要なので、--without productionをつけます。

$ bundle install --without production

次にdatabase.ymlを編集します。
デフォルトではこのようになっていると思います。

database.yml
production:
  <<: *default
  database: <app_name>_production
  username: <app_name>
  password: <%= ENV['<app_name>_DATABASE_PASSWORD'] %>

これをこのように書き換えます。

database.yml
production:
  adapter: postgresql
  encoding: unicode
  pool: 5
  database: <app_name>_production
  username: <app_name>
  password: <%= ENV['<app_name>_DATABASE_PASSWORD'] %>

あとは、heroku pushして、忘れずにheroku run rails db:migrateすればOKです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker上のRailsでコントローラーの変更がブラウザに反映されない時の対処法

問題

controller.rbの変更がブラウザに反映されない。
コンテナを再起動すれば反映されるが、毎回再起動するのは手間がかかる。

環境

Windows 10 home
Docker for Windows
Rails 6.1
Ruby 2.7

解決策

config\environments\development.rb内の
config.file_watcher = ActiveSupport::EventedFileUpdateCheckerを下記コードに修正する。

config.file_watcher = ActiveSupport::FileUpdateChecker

わからないこと

下記コードを記載しても解決できる。
Railsガイドを見ても上記コードとの違いがわからなかったので、違いが分かる人がいましたら、ご教授いただければ幸いです。

config.reload_classes_only_on_change = false

参照:『Railsガイド』- Rails アプリケーションを設定する

参考

  1. Rails5でmodelやcontrollerの修正が反映されないやつ
  2. Vagrant + Rails 6.0.2.1 のdevelopmentでコード変更が反映されない現象を解決
  3. 【Rails6】Dockerコンテナを再起動しないとソースコードが反映されない
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

hashで存在しないキーを呼び出した場合のハンドリング

チェリー本で学んだことの雑メモ。
rubyのhashは、存在しないキーを呼び出してもnilが返るだけでミスに気付きにくかったりするので、明示的なメッセージを出力するようにしてみた。

hash = Hash.new('存在しないキーです。')
hash.merge!({key1: :value1})
hash[:key1]
# => :value1
hash[:key2]
# => "実在しないキーです。"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者の自分がコードレビューを受けて指摘されたことまとめ

はじめに

アプリのコードレビューを初めて受けて何点か指摘があったので次からは気を付けて行きたいといった意味を込めて指摘を受けた部分をまとめていく。
なお、指摘を受けた内容はフリマアプリの制作段階でのことである。

REDMEでのコードレビュー

・deviseを使用したのだがencrypted_passwordというカラムを記述するのを忘れていた。

・メールアドレスは一意性という条件が設けられているにも関わらず一意性制約の記述がなかったこと。

・見本アプリではユーザーの登録時、姓、名、姓(フリガナ)、名(フリガナ)と入力欄が細かく分かれているのに姓名を一緒にしてしまっていたこと。

・誕生日カラムをdate型で年、月、日と一つにするのではなく、単に数字だからという考えでinteger型にしていたこと。

・imageの実装はactive storageを使うためREDMEに記述する必要がないにも関わらず記述していたこと。

・郵便番号はハイフンも含めるためinteger型にする必要があるのにstring型にしていたこと。

・active hashを使用時データはidで保存されるため、integer型にしなければならないのに、string型にしていたこと。

・電話番号は数字の文字列で保存するためstring型にしなければならないこと。

新規登録・ログイン機能でのコードレビュー

・バリデーションをwith_optionsでまとめていなかったこと。可読性を上げるためにもまとめた方が良い。

・passwordでの文字数制限のバリデーションはdeviseでデフォルト設定になっているにも関わらずmaximumで設定しようとしていたこと。

・パスをprefixで記述していなかったこと。可読性を上げるためにもprefixで記述した方が良い。

・不要なコメントアウトの消し忘れ。可読性を上げるため。

商品を出品する機能でのコードレビュー

・before_action :authenticate_user!で制限を設ける際、application_controller.rb内に記述してしまっていたこと。

・with_optionsをネストせずに分けて記述していたこと。

・300円以下は登録できないというテストコードを実装する際、境界値の299で実装した方が良いのに200で実装していたこと。

商品の購入機能でのコードレビュー

@item = Item.find(params[:id])はdestroy,edit,update,showで共通なため、before_actionでまとめた方が可読性が上がるにも関わらずまとめていなかったこと。

・resources :items, only: [:index, :new, :create, :show, :edit, :update, :destroy]と全てのアクションが記述されているのに resources :itemとまとめて記述していなかったこと。

・フォームオブジェクトの中に、:user_idと:item_idを記述していなかったこと。ユーザーのIDと商品のIDが無い場合、確実に購入できないようにするために必要。

・任意である、建物名の「建物名が空でも購入できること」のテストコードを実装していなかったこと。建物名は任意入力となっているため建物名を記入せずに購入するユーザーが居る可能性を想定したテストコードを実施する。

最後に

改めてこのようにまとめてみると、初歩的なミスが多かったなと思った。同じミスせず効率よくコードを描けるように頑張って行きたい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【環境構築】Ruby on Rails 6 開発環境を1時間以内に手に入れる

内容

「プログラミングをはじめたい!」「Webエンジニアを目指したい!」という思いを持ち始めたプログラミング初学者の方が、Ruby及びそれを使ったフレームワークであるRuby on Railsに挑戦するというのは王道の学習パターンなのではないかと思います。

Ruby on Railsをはじめるにあたって最初に大きな壁として立ちはだかるのが環境構築です。私自身も約3ヶ月前に異業種からの転職でRubyエンジニアとして働きはじめたばかりなので、環境構築などの「プログラミング学習を始める以前の準備」に時間を取られた時のことは記憶に新しいです。

本記事では、一人でも多くのプログラミング初学者の方にRailsの魅力を感じて欲しいという思いから「Ruby on Rails 6 開発環境を1時間以内に手に入れる」ことを目標に、そのための手順を紹介します。

ゴール

Ruby on Rails6の開発環境を備えたUbuntuの仮想環境をmacOS上に構築する(目標1時間以内)。

前提環境

macOSCatalina バージョン 10.15.7

※ Windowsの方はゴメンナサイ。また、macOSの方でもバージョンの差異で多少の違いが発生する可能性はあります。

Virtual Box 6.1.16

※ インストール手順は後述

Vagrant 2.2.14

※ インストール手順は後述

想定する読者

・macOSユーザーの方
・macOSのターミナルを使った経験があり、基本的なLinuxコマンド(cd, mkdir, lsなど)の意味を知っている方
・ProgateなどでRuby on Railsの概要を学んだことがある方
・AWS Could9などのクラウドベースの統合開発環境に限界を感じている方
・過去にRuby on Railsの環境構築に挫折した経験のある方

仮想環境とは?

使っているOS(ホストOS:本記事ではmacOSを想定)の中に、あたかも別のOS(ゲストOS:本記事ではUbuntuを使用)が入っているような環境のことを言います。

ホストOS上に開発環境を直接構築する場合、誤った設定や削除を行ってしまったことによりホストOSに悪影響を与えてしまう可能性はありますが、仮想環境上で環境構築をする場合であれば、何かミスをしてしまった時はその仮想環境ごと削除してやり直せば良く、ホストOSに悪影響を与えることはありません。

また、AWS Could9などのクラウドベースの統合開発環境よりもリソース拡張の自由度が高く、CPUの性能限界やメモリ不足に悩まされることは(少なくともRailsでWebアプリケーションを開発するだけであれば)ほとんどないと言っていいと思います。

Virtual Box

仮想環境を構築するための「仮想化ソフト」として、まずはVirtualBoxをインストールします。

下記のダウンロードページから、(本記事ではmacOSを想定しているので)「OS X hosts」のリンクをクリックしてインストーラをダウンロードしてください。

ダウンロードしたインストーラを起動すれば、インストール手順がわかりやすく書いてあるので、それに従えばVirtual Boxのインストールは終了となります。

(※ 2021年1月1日現在の最新版は6.1.16なので、ここからはそのバージョンでの動作を前提としております)

Virtual Box ダウンロードページ

Vagrant

次に、仮想化ソフトを管理するツールであるVagrantをインストールします。
下記ダウンロードページにアクセスし、上記のVirtualBoxと同様に、インストーラのダウンロード、インストーラ起動、インストールという手順を踏めばほとんど迷うことなく完了すると思います。

仮想化ソフトである「VirtualBox」を操作するためのツールが「Vagrant」である、という認識を持っていただければとりあえず最低限の知識としてはOKです。

(※ 2021年1月1日現在の最新版は2.2.14なので、ここからはそのバージョンでの動作を前提としております)

Vagrant ダウンロードページ

環境構築手順

Virtual BoxとVagrantを問題なくインストールしたら、ここからは実際に仮想環境を構築して行きたいと思います。今回は、Ubuntu(18.04)というゲストOSが入っている仮想環境を構築します。

ここからの操作はmacOSのターミナルで行います。作業に入る前にvagrantが正しくインストールされていることを確認しましょう。ターミナルを開いてvagrant -vと入力してみてください。Vagrant 2.2.14という出力が返ってくれば準備はOKです、早速はじめていきましょう!

1. vagrant-vbguestのインストール

$ vagrant plugin install vagrant-vbguest

仮想マシンでの操作を簡単にしてくれる役割がある、という理解でとりあえずはOKです。

2. 任意の場所にディレクトリを作成

$ mkdir rails6_dev
$ cd rails6_dev

3. Vagrantfile作成

$ vagrant init

上記コマンドでデフォルトのVagrantfileが作成されます。

Vagrantfile(デフォルト|一部抜粋)
# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  ~
  ~
  config.vm.box = "base"
  ~
  ~
  # config.vm.network "forwarded_port", guest: 80, host: 8080
  ~
  ~
  # config.vm.network "private_network", ip: "192.168.33.10"
  ~
  ~
end

上記のデフォルト状態のVagrantfileの中身を、下記のコードに書き換えてください。

Vagrantfile(書き換え後)
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  GUEST_RUBY_VERSION = '2.6.6'
  GUEST_RAILS_VERSION = '6.0.3.4'
  config.vm.box = "bento/ubuntu-18.04"
  config.vm.box_check_update = false
  config.vm.network "forwarded_port", guest: 3000, host: 3000
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.synced_folder "./", "/home/vagrant/work"
  config.ssh.forward_agent = true
  config.vm.provider "virtualbox" do |vb|
      vb.gui = false
  end
  config.vm.provision "shell", inline: <<-SHELL
      echo '### installing tools ###'
      sudo timedatectl set-timezone Asia/Tokyo
      sudo apt update -y
      sudo apt upgrade -y
      sudo apt install build-essential -y
      sudo apt install -y libssl-dev libreadline-dev zlib1g-dev
      sudo apt install -y imagemagick
  SHELL
  config.vm.provision "shell", privileged: false, inline: <<-SHELL
      echo '### installing Ruby ###'
      git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
      echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
      echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
      source ~/.bash_profile
      git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
      rbenv install #{GUEST_RUBY_VERSION}
      rbenv global #{GUEST_RUBY_VERSION}
      echo '### installing Rails ###'
      gem install rails -v #{GUEST_RAILS_VERSION}
      echo '### installing SQLITE3 ###'
      sudo apt install libsqlite3-dev
      echo '### installing NodeJS ###'
      sudo apt install -y nodejs npm
      sudo npm install n -g
      sudo n lts
      sudo apt purge -y nodejs npm
      sudo apt -y autoremove
      sudo npm install yarn -g
      sudo chown -R $USER:$GROUP ~/.config
      echo ' -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
      echo 'You are now on Rails!'
      echo ' -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-'
  SHELL
end

本記事での解説は避けますが、このVagrantfileにはRailsを使えるようにするための設定が書いてあります。最初は難解に感じるかもしれませんが、ぜひ一度、読み解くことをオススメします。

もちろん私が書いたこのVagrantfileが唯一の正解では決してないので、「もっといい書き方があるよ」「こっちの方が使いやすいと思う」っていうご意見も大歓迎です!

(こちらのVagrantfileのソースコードはGithubにもあげてあります。)

4. 仮想環境構築

$ vagrant up

上記コマンドで仮想環境を構築します(PCのスペックにもよりますが、15〜30分ほどかかります)。

先ほどのVagrantfileに書かれた設定を1行ずつ実行していくことでRailsの開発環境が作られていっているということだけは抑えて頂けるといいかなと思います。

5. 仮想環境へSSH接続

$ vagrant ssh

上記コマンドで出来上がった仮想環境にSSH接続します。

SSH接続とは、ネットワークを経由して自身のPCから他のPC(今回は仮想環境)を安全に遠隔操作するための仕組みである、ということだけは抑えておいてください。

ここまでがmacOSのターミナルでの操作です。次の「6. Railsアプリケーション作成」と「7. ssh接続から抜ける」ではmacOSからssh接続をして、ゲストOSのUbuntuを操作しているということをご認識ください。

6. Railsアプリケーション作成

vagrant@vagrant:~$ cd work
vagrant@vagrant:~/work$ rails new sample-app
vagrant@vagrant:~/work$ cd sample-app
vagrant@vagrant:~/work/sample-app$ rails s -b 0.0.0.0

この状態でhttp://localhost:3000/にアクセスすると、Yay! You’re on Rails!のデフォルト画面が表示されます。

rails

立ち上げたRailsサーバーはcommand + Cで停止することができます。

7. ssh接続の終了

$ exit

上記コマンドでSSH接続を終了することができます。

8. 仮想環境のマシンをシャットダウンする

vagrant halt

上記コマンドで仮想環境上のゲストOSをシャットダウンすることができます。

再度立ち上げたい時は、vagrant upで立ち上げvagrant sshで接続します(2回目以降は数分で完了します)。

9. Railsアプリケーションの開発を進める方法

「2. 任意の場所にフォルダを作成」で作成したディレクトリ内に「6. Railsアプリケーション作成」で作成したRailsアプリと同名のファイルが作成されているはずなので、そこのコードを書き換えることで開発を進めます。

rails g controller Usersbundle install 等のコマンドはSSH接続をした状態で、作成したRailsアプリケーションのディレクトリ上で叩くことになります。

終わりに

いかがでしたでしょうか?スムーズに行けば、Yay! You’re on Rails!の表示まで1時間以内にいけるではないかなと思っております。

Rubyという言語、Railsというフレームワークは触っていると本当に面白くて飽きないものなので、この記事で環境構築をしたことをきっかけに少しでもその魅力にハマっていただければ嬉しい限りです。

本記事についての質問や改善点のご指摘等がございましたら、コメントやTwitterでのDMなどをいただければ幸いです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む