20191008のRubyに関する記事は23件です。

Webhookで使われる署名検証をRubyで実装する

自分用メモ

digest = OpenSSL::HMAC.digest("sha256", "key", "data")
signature = Base64.strict_encode64(digest)    // UDH+PZicbRU3oBP6bnOdojRj/a7DtwE32Cjjas4iG9A=
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】viewにおけるページタイトルの扱い方

開始タグ

ページタイトルの存在がすっぽ抜けていたので設定しました。

方法

ページタイトル例:ログイン画面|テストアプリ

最初に/app/views/layouts/内にある何かのテンプレート内の<title></title>の中に以下を記述。

application.html.erb
<title>
  <%= content_for?(:html_title) ? yield(:html_title) : "" %> |テストアプリ
</title>

最後に/app/views/以下のビューファイルに以下を記述。

login.html.erb
  <% content_for(:html_title) { 'ログイン画面' } %>

簡単2STEP!

自分は究極にざっくりとこう解釈。

content_for=コンテンツを1箇所にまとめて他のビューでも使用できるようにしてるんじゃない?
yield(:html_title)=yieldとあるから、ビューファイルにcontent_for(:html_title)があればそこから持ってくるんじゃない?

閉じタグ

「全然仕組み分かってないなー!」と文字に起こそうとする思い知らされますね。
「知らなければ知ればいい」と自分を励ましておきます。

参考サイト

ActionView::Helpers::CaptureHelper(リファレンス)
Rails: ページタイトルはビューテンプレートのcontent_forで表示すること(翻訳)

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

Date.today.since(7.days) ←7日後

何日後:since

何日前:ago

どちらも`Dateクラスが持っているメソッドである。

[1] pry(main)> Date.today
=> Tue, 08 Oct 2019

[2] pry(main)> Date.today.since(3.days)
=> Fri, 11 Oct 2019 00:00:00 JST +09:00

[3] pry(main)> Date.today.ago(3.days)
=> Sat, 05 Oct 2019 00:00:00 JST +09:00
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】RSpecでモデルのテストをする

概要

モデルのテストの記述方法を解説します

詳しくはRSpecの公式サイトのモデルに関するページを参考にしてください
今回は一例としてPostモデルのテストを解説します。
前提としてPostにはアソシエーションされたCommentモデルが存在します。

spec/models/post_spec.rb
require "rails_helper"

RSpec.describe Post, :type => :model do
  context "with 2 or more comments" do
    it "orders them in reverse chronologically" do
      post = Post.create!
      comment1 = post.comments.create!(:body => "first comment")
      comment2 = post.comments.create!(:body => "second comment")
      expect(post.reload.comments).to eq([comment2, comment1])
    end
  end
end

基本はRubyの書き方と同じです。コントローラーの記述する感覚で書きます。

  • post = Post.create!

    • postを作成、保存します
    • createメソッドはnew+saveです
    • !をつけると保存できなかった時にその原因を出力してくれます
  • comment1 = post.comments.create!(:body => "first comment")

    • postに紐づいたcomment:body => "first comment"で作成する
    • comment2も同様です
  • expect(post.reload.comments).to eq([comment2, comment1])

    • post.reload.commentsの値が[comment2, comment1]と一致するかどうかを確かめる
    • post.reload.commentsreloadはデータベースの値を再び取得するためのメソッドです

以上がモデルのテストの記述方法の一例の解説です。
これを元に自身のアプリケーションにも適応し、テストしてみてください。

疑問、気になるところがございましたら、質問、コメントよろしくお願いいたします!!

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

returnを使った後置 if の戻り値

returnを使った後置 if の戻り値 が解らなかったから試してみた

def fofofo(dd)
 return if dd == 1
 c = 2
end

fofofo(1)
=> nil

fofofo(2)
=> 2

ーーーーー
def fififi(dd)
 return unless dd == 1
 c = 2
end

fififi(1)
=> 2

ififi(2)
=> nil

ーーーーー
def fafafa(dd)
 return true if dd == 1
 c = 2
end

fafafa (1)
=> true

fafafa (2)
=> 2

tureになる時しか返さないのね

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

Railsチュートリアル 第4章<復習>

第4章の復習メモです。
個人的に重要と思ったことを書きます。
調べたことや、知っていたことも含めて書きます。

ヘルパー

ビューやコントローラ、モデルから呼び出せるメソッド。
以下の二種類がある。

  1. 組み込みヘルパー
  2. 新しく定義できるカスタムヘルパー

1は、Railsで元々用意されている。
2は、自分で定義でき、以下のファイルに記述する。
 app/helpers/application_helper.rb

※以下のサイトも参考にさせていただきました
https://www.sejuku.net/blog/28563

文字列の表現

文字列は、シングルクォート(')またはダブルクォート(")で囲む。
前者は、囲った物をそのまま表現する。
よって、正規表現(\n等)や、式展開(文字列中に変数を埋め込む)は無効となる。
後者は、両方とも有効。

引数、カッコの省略

メソッドにデフォルト引数を設定している場合、引数、カッコを省略できる。

# メソッドの定義
def string_message(str = '')   # デフォルト引数として''を設定
 .
 .
end

# 呼び出す側
string_message

最後の引数がハッシュの場合、波カッコを省略できる。

# 以下の二つは同じ意味となる
stylesheet_link_tag 'application', { media: 'all',
                                     'data-turbolinks-track': 'reload' }

stylesheet_link_tag 'application', media: 'all',
                                   'data-turbolinks-track': 'reload'

範囲 (range) オブジェクト

1..10a..zのように表現する。
to_aメソッドで配列に変換すると、範囲内の値が全て、配列に格納される。

ブロック

{}で囲むパターンと、do~endで囲むパターンがある。
すいません、文章にできるほど理解できていないので、
リンクを貼らせていただきます・・・
https://www.sejuku.net/blog/14291

inspectメソッド

対象のリテラル(プログラムに記述するデータ値。コードに書いた値。)を返却する。

>> puts (1..5).to_a            # 配列を文字列として出力
1
2
3
4
5
>> puts (1..5).to_a.inspect    # 配列のリテラルを出力
[1, 2, 3, 4, 5]
>> puts :name, :name.inspect
name
:name
>> puts "It worked!", "It worked!".inspect
It worked!
"It worked!"

以下を参考にさせていただきました
https://www.sejuku.net/blog/77039
http://www.designmap.info/2016/12/16/javascript-3/#i-2

クラス、継承

Rubyにおけるすべてのクラスは、最終的にスーパークラスを持たない
BasicObjectクラスを継承している。
image.png

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

Railsチュートリアル 第3章<復習>

第3章の復習メモです。
個人的に重要と思ったことを書きます。
調べたことや、知っていたことも含めて書きます。

Webページの作成

Railsで、Webページ作成に最低必要なのは、以下の3つ。

  1. URLのルーティング
  2. 1に対応するコントローラ及び、アクションの作成
  3. 2に対応するビューの作成

以下のコマンドを実行すると、上記3つを自動生成してくれる。

$ rails generate controller <コントローラ名> <アクション名1> <アクション名2> <アクション名xx>
  • ルーティングについて
    • config/routes.rbファイルにルーティングが追記される。
  • コントローラについて
    • 慣習的に、コントローラ名はキャメルケース(頭だけ大文字)で記載する。
    • 生成されるコントローラは、スネークケース(単語間を_でつないだ形)になる。
    • アクション名は全て小文字にする。また、いくつ書いても良い。
  • ビューについて
    • コントローラのアクション毎、ビューが生成される。

切り戻しについて

コマンドを打ち間違えて、変なファイルを作ってしまった場合、
元の状態に戻すためのコマンドが用意されている。

  • コントローラを戻す
$ rails destroy  controller <モデル名> <アクション名1> <アクション名2>
  • モデルを戻す
$ rails destroy model <モデル名>
  • DBへの反映を1つ戻す
$ rails db:rollback
  • DBへの反映を全部戻す
$ rails db:migrate VERSION=0

テストについて

$ rails test

で、テストを実行できる。
テストコードは、あらかじめ生成されている物もある。
また、Guardを使ってテストを自動化できるみたい。
詳しく理解するのはまたの機会に。

Gitについて

git push origin <ブランチ名>

で、GitHub上のブランチにPUSHされる。
GitHubにブランチが存在しない場合、新規作成される。

最低限、3章まではやろうと思っていたので、目標は達成しました。
4章以降についても、できれば続けていきたいと思います。

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

Bundler の動作で疑問に思っていたことが理解できた

Bundlerの動作に疑問を持ったきっかけ

数か月前の話になりますが、@jnchitoさんの以下のQiita記事を読ませていただいて --path vendor/bundle について改めて考える機会がありました。

bundle install時に--path vendor/bundleを付ける必要性は本当にあるのか、もう一度よく考えてみよう
https://qiita.com/jnchito/items/99b1dbea1767a5095d85

この記事で特に心動かされたのは、(--path vendor/bundleを付けても付けなくても)どっちでもいいなら付けない方を好む理由として挙げられていた「systemにインストールするのがBundlerのデフォルトで、pathを指定するのがオプションだから」という一文でした。確かにデフォルト設定で問題が起きないならデフォルトのまま使うに越したことがないなぁと思います。

ただ、gemをグローバルにインストールした場合で1個だけ疑問に思った動作がありました。

上記記事にもありますが、「グローバルにインストールされる」というのはすなわち、Bundlerを使ってインストールしたgemが開発マシン内のどこでも使える状態になる(bundle installしたプロジェクト以外の場所でも使える)ことを意味します。

どこでも使えるとは、例えば静的解析ツールRuboCopbundle installでグローバルにインストールすれば、どこのディレクトリにいようがrubocopコマンドを実行できる!ということですが、自分の環境ではそのような動作にはならなかったのです…。

$ echo "source 'https://rubygems.org'\n\ngem 'rubocop'" >>| Gemfile
$ bundle install
$ bundle exec rubocop -v # bundle execを付けるともちろん大丈夫
0.75.0
$ rubocop -v # bundle execを付けないとコマンドが見つからない…
zsh: command not found: rubocop

これについて、何故そうなるのかやっと理解できました。

先に結論

  • rbenvを使用している環境の場合、bundle installしただけではshimsディレクトリが更新されない

参考
https://github.com/rbenv/rbenv/pull/638#issuecomment-59375024

  • rbenv rehashを明示的に行うことで、shimsディレクトリが更新されて、どこのディレクトリにいようがrubocopコマンドを実行できる!
  • rbenvを使用していない環境(aptで直接インストール等)であれば、今回の事象はそもそも起きない

自分の環境

  • Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-65-generic x86_64)
  • rbenv 1.1.2-4-g577f046
  • ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-linux]
  • Bundler version 2.0.2

色々試したこと

gem install rubocopしたらどうなのか??

そもそもBundlerを経由しなかった場合どうなるのか知りたかったので試してみました。

$ gem install rubocop
$ rubocop -v # ちゃんとコマンドが見つかる
0.75.0

大丈夫そうです。ということはBundlerが原因…と思いましたが、そういえばrbenvを当たり前のように使っていたので、rbenvを使わなかったらどうなるのか試してみました。

sudo apt install rubyで入れたRubyだとどうなのか??

厳密にはrbenvでインストールするrubyバージョンと合わせるべきですが、今回は横着しました…。
rbenvを使用していないまっさらな環境で以下を実行します。

$ sudo apt install -y ruby
$ ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-gnu]
$ sudo gem install bundler
$ echo "source 'https://rubygems.org'\n\ngem 'rubocop'" >>| Gemfile
$ bundle install # 本件と関係ないエラーが出る場合があるが、ruby-dev等 足りないライブラリをインストールすると解消する
$ bundle exec rubocop -v # bundle execを付けるともちろん大丈夫
0.75.0
$ rubocop -v # bundle execを付けなくても大丈夫!
0.75.0

なるほど、ということはrubyのバージョンに因る問題か、rbenvに何かあるのかな?

…詳細は書きませんが、rbenvでruby 2.5.1をインストールして試しても同様だったので、rbenvに何かありそうです。

rbenvの仕組み

rbenvのGitHubリポジトリを読むと、rbenvのshimsについて記述があります。
rbenvはPATHの先頭にshimsディレクトリを挿入することで、rubyのバージョンを変更した際のコマンド切替をよしなに制御してくれています。

その他参考
rbenvの使い方と仕組みについて
https://qiita.com/Kodak_tmo/items/73147ed4f0eec54d6e94

古い記事だとrbenv install実行後はshimsを更新する為にrbenv rehashを実行すること!と書いてあったりするのですが、
5年ほど前のPull requestsrbenv rehashを自動で処理するように変更されています。
なので、普段rbenv rehashを実行することはあんまりないんじゃないかなー…と思います。(違っていたらごめんなさい…)

今回の件、なんとなくshimsが更新されてないんじゃない?と思ったので明示的にrbenv rehashしたらどうだろうと思い試してみました。

$ echo "source 'https://rubygems.org'\n\ngem 'rubocop'" >>| Gemfile
$ bundle install
$ bundle exec rubocop -v # bundle execを付けるともちろん大丈夫
0.75.0
$ rubocop -v # bundle execを付けないとコマンドが見つからない…
zsh: command not found: rubocop
$ rbenv rehash # shimsディレクトリを更新すると…
$ rubocop -v # bundle execを付けなくても大丈夫!
0.75.0

思っていた動作になりました!

Pull requestsをよくよく読んだら書いてあった

先ほどのrbenv rehashが不要になったPull requestsをよくよく読んだら書いてありました!
bundle installした際 複数回rbenv rehashが実行されるのを防ぐ為にそうなっているとのこと。Bundlerの並列性の為にrbenv rehashを一回だけ処理するのは難しかったとのことです。

そもそもBundlerを使用している時点でbundle execを付けてコマンド実行すると思うので実用上何も問題は起きないと思います。ですが、引っかかっていた些細な疑問が解決して良かったです!

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

Ruby on Rails アプリ作成のはじめのはじめ

Ruby on Rails作成におけるアウトプット用も兼ねて記載していきます。
間違い等あればご連絡ください。

新規Railsアプリケーションの作成
 アプリケーションを配置するディレクトリを作る。
  自分の作成したい場所までターミナルで移動する。
  cd
現在のディレクトリ(カレントディレクトリ)を移動する
mkdir
新しくディレクトリを作成する
pwd
現在のディレクトリ(カレントディレクトリ)のパスを表示する
ls
現在のディレクトリ(カレントディレクトリ)のファイル一覧を表示する

新規アプリケーションを作成
ターミナルで「rails new」コマンドの実行
  rails new アプリケーション名 -オプション名

これでRuby on Railsのアプリ制作における雛形ができるので、ここから色々と編集していくことで立派なアプリへと成長させていくことが可能となる。

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

Procの実行方法いろいろ

Procに引数を渡して処理を実行する方法はいくつかあります。
以下は全て同じ結果を返します。

proc = Proc.new do |a|
  puts "This is proc: #{a}"
end

# オーソドックスな記法
proc.call(1)

# callを省いたシンタックスシュガーな記法
proc.(2)

# 添字で引数を指定して呼び出す記法
proc[3]

# ===で引数を指定して呼び出す記法
# ===が実装されていることでwhen句に手続きを渡せるようになっている
proc === 4

出力結果

This is proc: 1
This is proc: 2
This is proc: 3
This is proc: 4
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rubyの定数について

概要

Rubyの定数の仕様が他のプログラミング言語と異なるという話は有名です。

定数はオブジェクトを割り当てる"ラベルのようなもの"です。
オブジェクト(数値リテラル等を含む)=定数そのものではない、ということですね。

本記事はコンテナオブジェクトの凍結についてのTipsを主にしています。

本題

例えばC言語で定数を定義し、それを変更してみるとこうなります。

sample.c
#include<stdio.h>

int main(void)
{
    const int TEISUU = 30;

    printf("%d\n",TEISUU);

    TEISUU = 20;
    printf("%d\n",TEISUU);

    return 0;
}
$gcc sample.c
> error assignment of read-only variable 'TEISUU'

こんな感じでエラー吐きます。
そりゃ当たり前ですよね。定数って定義してますから。

さて、そんなTEISUUをRubyでも定義してみましょう。

sample.rb
TEISUU = 30
TEISUU = 25
puts "できたよ" if TEISUU == 25

$ruby sample.rb
>sample.rb:2: warning: already initialized constant TEISUU
>sample.rb:1: warning: previous definition of TEISUU was here
>できたよ

ふむふむ、エラー出てるなよしよし…
!?

>できたよ

なんじゃこりゃあ


Rubyは定数を変更しても警告を出すにとどまり、
実際に変更できてしまいます。自由だあああああああああああああ

警告の内容は、

二行目:TEISUU定数はすでに初期化してるぜ
一行目:以前のTEISUUを定義したのはここだぜ

ですね。優しい。

Rubyではファイル実行時に’-w’オプションを指定することで警告が表示されますが、
Ruby1.8.7から定数の再代入の際にオプション無しでもエラーが出るようになりました。
ちょうど序盤の"できたよ"ってところで起きてる警告ですね。
ご指摘感謝します。

定数の扱い方

実はそもそも、Rubyではクラスやモジュール名も定数なんですよね。
Rubyは先頭が大文字の語句を定数だと識別します。
TEISUUも定数だし、Arrayも定数です。

クラス等は変化できますよね。
あとからメソッドを追加したりもできます。

でも、特にゲームのタイトルとかURLとか、固定しておきたいものは変更しなくてもいいですよね。

配列オブジェクトなどのコンテナに要素を追加、要素を削除するといった挙動を再現したい場合には、コンテナオブジェクトを凍結させてみましょう。

sample.rb
module Defaults
    NETWORKS = ["192.168.1","192.168.2"].freeze
end

class Klass
    include Defaults

    def foo
        Defaults::NETWORKS << "192.168.3"
    end
end

Klass.new.foo()                        #=>can't modify frozen array (FrozenError)

エラー内容は、
凍結した配列は修正できないよん」です。

このように、 Rubyについてもオブジェクトを凍結させることで他言語の定数と同じような挙動を得られます。

さて、定数という言葉で騙される人が多いのだが、定数というのは「いったん 指すオブジェクトを記憶したら二度と変えない」という意味である。定数の指すオブジェクトそれ自体が変わらないわけではない。英語で言うなら、 constantよりもread onlyのほうがよりよく意図を示しているだろう (図2)。 ちなみにオブジェクト自体が変化しないよう指示するにはfreezeという 別の方法を使う。

RHGより引用しました。

凍結の落とし穴

さて、オブジェクトを凍結させる方法を学んだ私達ですが、
実は先程の凍結方法には、1つの落とし穴があります。

次のコードを見てみましょう。

sample.rb
module Defaults
    NETWORKS = ["192.168.1","192.168.2"].freeze
end

class Klass
    include Defaults

    def foo
        Defaults::NETWORKS.each do |item|
            item[3] = "hai" if item.class == String
        end
        p Defaults::NETWORKS
    end

end

Klass.new.foo()                        #=> ["192hai168.1","192hai168.2"]

Stringオブジェクトにインデックスを与えると、
先頭から[i]番目の文字を指すのはご存知かと思いますが、

今回の場合は空白ですね。

なんと、

配列オブジェクトをフリーズしても要素はフリーズされない

ということです。

私達がフリーズする動機は、

  • もちろん配列NETWORKSに新たに要素が加えられる事も許さない
  • 要素それぞれのアドレスが変わる事も許したくない

からですよね。(そんな前置きなかったですけど)

しかし、このように要素は書き換わってしまいました。

これには、次のような解決策があります。

1.map!などのメソッドで要素それぞれをフリーズしてみる。

EffectiveRubyに書いてあるやり方の内の1つです。

sample.rb
module Defaults
    NETWORKS = ["192.168.1","192.168.2"].map!(&:freeze).freeze
end

自分も最初何をやっているのか全くわからなかったですが、

map(&:メソッド名)で、各要素にメソッドを適用することが出来るみたいです。

Rubyではやたら使う記法だそうですし、実際かっこいい。

前半のmap!メソッドで各要素をフリーズして、その後のメソッドチェーンで
配列自体をフリーズしている、ということです。

これで、先程のコードを実行した時にエラーが出ます。

sample.rb
module Defaults
    NETWORKS = ["192.168.1","192.168.2"].map!(&:freeze).freeze
end

class Klass
    include Defaults

    def foo
        Defaults::NETWORKS.each do |item|
            item[3] = "hai" if item.class == String
        end
        p Defaults::NETWORKS
    end

end

Klass.new.foo()                        #=> '[]=' can't modify frozen String FrozenError

2.%記法を使う

sample.rb
module Default
    NETWORKS = %w(192.168.1 192.168.2).map!(&:freeze).freeze

これはRuby2.1以上で実装されたものですが、

(訂正)少なくともRuby1.8.7での実装が確認されました。ご指摘感謝します。

%記法かっこよくないですか?簡潔でみやすい。

どうやらこの記法はメモリ管理に一役買っているようで、
リソースの消費を少し軽減できるとか。

すっきりしましたか?

Rubyのエラーメッセージでよく[]=だとか+が出てきますが、
Rubyではこういった演算子や識別子なども立派なメソッドだからです。

Arrayクラスの公式リファレンスを見てみるとよくわかります。
インスタンスメソッドの欄に記号がずらずら並んでるわけですね。

EffectiveRubyの中でも特にディープな部分だと思う(そして私がスルーした)項目に、
演算子のオーバーライドの話があります。

sample.rb
def <=>(other)
    return nil unless other.is_a?(Version)
    ...
end

とかいうやつです。
いつか手を出そうとは思いますが、いかんせんディープな話題過ぎてあまりついていけませんでした…

凍結の落とし穴その2

注意・Effective Rubyに載っているコードを基盤に、’frozen?’メソッドを使ってわかりやすくした以下の文章ですが、情報に誤りがある可能性があります。
Effective Rubyに実際に記載されているので、現在進行形でこの本の勉強をしている方の為に残しておきますが、コメント欄をご一読ください。

さて、凍結について理解を深めた私達は、
もう怖いものはありません、とばかりに次のコードを書きました。

sample.rb
NUMBER = 10
NUMBER.freeze

p NUMBER.frozen?                     #=>true

frozen?メソッドは、そのオブジェクトが凍結しているか判定して真偽値を返します。わかりやすい。

凍結すれば不変的なオブジェクトというわけですから、

当然次のようなコードはエラーを起こすはずです。

sample.rb
NUMBER = 10
NUMBER.freeze

NUMBER = 50        
p NUMBER                        #=> 50

WHY JAPANESE PEOPLE!?

はい、最大の混乱ポイントの登場です。

最初の、

sample.rb
TEISUU = 30
TEISUU = 25
puts "できたよ" if TEISUU == 25

このコードのような形ですね。

Rubyでは、
既存の定数に新しい値を代入しても文法違反にはなりません。

EffectiveRubyでも「この方法は不格好で状況によっては面倒すぎるが単純である。
と述べられています。

解決策はこちら。

sample.rb
module Namespace
    NUMBER = 10
end

Namespace.freeze

Namespace::NUMBER = 50       #=>  FrozenError

簡単ですね。

モジュールやクラスで定数をラッピングして、それごと凍結させる。

場合によっては定数を格納する専用のクラスやモジュールを定義して、
freezeする手間を省く事を検討してもいいのだとか。

注意!

こちらの記事で述べたことを思い出してください。

クラスパスセパレータを記述せずNUMBERに50を代入すると、
Namespace内の定数とは別のスコープに新たな定数NUMBERが定義されます!

これは間違えやすいミスなので気をつけて下さいね。

最後に

Rubyの定数とオブジェクトの凍結について解説しました。

EffectiveRuby挑戦中の皆様一緒に頑張りましょうね。

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

【コピペで済ませたい人向け】 rbenvのインストールを環境構築からパスチェックまとめ [2019版]

コマンドを探しにきた人に

# macOS
brew tap homebrew/core && brew install apple-gcc42

# Ubuntu/Debian/Mint
sudo apt install autoconf bison build-essential libssl-dev libyaml-dev libreadline6-dev zlib1g-dev libncurses5-dev libffi-dev libgdbm5 libgdbm-dev

# CentOS/Fedora
yum install -y gcc-6 bzip2 openssl-devel libyaml-devel libffi-devel readline-devel zlib-devel gdbm-devel ncurses-devel

# openSUSE
zypper install -y gcc6 automake gdbm-devel libffi-devel libyaml-devel libopenssl-devel ncurses-devel readline-devel zlib-devel

# Arch Linux
pacman -S --needed base-devel libffi libyaml openssl zlib
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
eval $(rbenv init)
source ~/.bashrc

rbenv install 2.x.x
rbenv local 2.x.x

動かない場合(既存の環境など)

パスとバージョンを確認

which rbenv
rbenv -v

which ruby
ruby -v

which gem
gem -v

which bundle
bundle -v

※インストールしてもパスが適用されていない場合は手動で通す。

# [command]はパスが間違っているコマンド名
cd $(which ruby)
unlink [command]
ln -s $HOME/.rbenv/bin/[command] [command]

解説

コマンドの最新はWiki参照
ここでは執筆時点の最新を公式Wikiより引用。

また、bash_profileではなくbashrcに書く理由はリンク先の通り。

筆者環境

WSLを使用している。
実行環境にbashを使用しているのでzshやfishでは実行できないかもしれない。

cat /etc/os-release 
# NAME="Ubuntu"
# VERSION="18.04.3 LTS (Bionic Beaver)"
# ...

動機

rbenvの情報が多いので解説はいいとして、そういうのは既に分かっている側の立場としては検索するのも面倒なんでお手軽にできるものが欲しかった。
執筆時点では、後述の筆者環境のみ検証しており、必要に応じて他環境も追記したい。

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

Rails6 のちょい足しな新機能を試す91(ActiveRecord annotate編)

はじめに

Rails 6 に追加された新機能を試す第91段。 今回は、 ActiveRecord annotate 編です。
Rails 6 では、 ActiveRecord で、発行される SQL にコメントを含めることができるように annotate メソッドが追加されました。
ログに出力して解析したり、デバッグしたりするときに便利そうです。

Ruby 2.6.4, Rails 6.0.0 で確認しました。

$ rails --version
Rails 6.0.0

今回は、User の CRUD を作り、一覧ページを表示するとき、そこで実行される SQL がどのコントローラのどのアクションから呼ばれているのかわかるようにしてみます。

プロジェクトを作る

rails new rails_sandbox
cd rails_sandbox

User の CRUD を作る

name をもつ User の CRUD を作ります

bin/rails g scaffold User name

コントローラとアクションの名前を返すメソッドを定義する

full_action_name というプライベートメソッドを ApplicationController に追加します

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  private

  def full_action_name
    "#{self.class.name}##{action_name}"
  end
end

ApplicationRecord に scope を追加する

annotate を使った scope を1つ ApplicationRecord に追加します。

app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  scope :called_from, ->(from) { annotate("called from #{from}") }
end

UserController#index を変更する

User.allUsersController#index から呼ばれていることがわかるように、修正します。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  ...
  def index
    @users = User.all.called_from(full_action_name)
  end
  ...
end

実際に一覧ページを表示してコンソールを確認する

rails server を実行し、ブラウザから http://localhost:3000/users にアクセスし、コンソールを確認します。
SQL 文にコメント"called from UsersController#index" が含まれていることがわかります。

...
  User Load (0.4ms)  SELECT "users".* FROM "users" /* called from UsersController#index */
...

試したソース

試したソースは以下にあります。
https://github.com/suketa/rails_sandbox/tree/try091_activerecord_annotate

参考情報

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

Ruby 2.6 から CSV の空行のパース結果が変わった

Ruby で CSV を処理するときに、空行を無視するために empty? を使って下記のように書いていました。

require "csv"

CSV.foreach("foo", headers: :first_row) do |row|
  next if row.empty?
  p row
end

headers を指定した場合、行は CSV::Row で表現されますが、Ruby 2.6 からは CSV::Row#empty?false を返すようです。

#!/bin/sh

RBENV_VERSION=2.5.1 ruby -rcsv <<EOS
CSV.parse("\n", headers: %w(a)) do |row|
  p row.empty? # true
end
EOS

RBENV_VERSION=2.6.4 ruby -rcsv <<EOS
CSV.parse("\n", headers: %w(a)) do |row|
  p row.empty? # false
end
EOS

p row などでみてみると、2.5 ではフィールドの情報を持っていないのに対し、2.6 ではフィールドの情報を持っています。

#!/bin/sh

RBENV_VERSION=2.5.1 ruby -rcsv <<EOS
CSV.parse("\n", headers: %w(a)) do |row|
  p row # #<CSV::Row>
  p row.to_a # []
  p row.to_hash # {}
end
EOS

RBENV_VERSION=2.6.4 ruby -rcsv <<EOS
CSV.parse("\n", headers: %w(a)) do |row|
  p row # #<CSV::Row "a":nil>
  p row.to_a # [["a", nil]]
  p row.to_hash # {"a"=>nil}
end
EOS

ドキュメント には、empty? などは Array に delegate されると記載があります。上記のように 2.6 では to_a の返り値が空ではないので empty?false を返すようです。

空行の読み飛ばしは CSV::Row#empty? を使うのではなく、CSV.new 等のオプションに skip_blanks: true を指定するのがよさそうです (2.5, 2.6 とも)。

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

wp2txtを使い捨てで使う

wp2txt

機械学習における自然言語処理等のチュートリアルで「訓練データようにWikipediaを全文ダウンロードしてplain textにして使いましょう」というシーンで出てくるツールです。Rubygems で bin/ にインストールされる、Ruby のプログラム。

この記事は

ぼくもまさに上述の流れで使おうと思ったのですが、ちょっと調べると付随するgemがいろいろインストールされるのでローカル環境でbundler使った方が良いよ、という記事をいくつか見ました。うん、確かにそのとおり。使わないgemをやたらシステムワイドにインストールするのは躊躇しちゃうし、wp2txt自体恐らくWikipediaをplain textに変換したらもう使わないだろうから、最後は rm -r あたりで全部まとめて簡単に消したいですもんね。

…が、そういう記事のとおりにやってもなぜかぼくの環境では

/usr/local/lib/site_ruby/2.5.0/rubygems.rb:284:in `find_spec_for_exe': can't find gem wp2txt (>= 0.a) with executable wp2txt (Gem::GemNotFoundException)

というエラーが出てうまく行きません。自分自身が bundler のしくみを良く理解していないのが原因と言えばそれまでなのですが、簡単な検索では解決法が出てなかったので同じエラーに悩む方がいるかも知れないと思いここに書いておきます。

解決策

wp2txt のスクリプトの先頭の辺りに require 'bundler/setup' と書く。それだけです。ようはbundler使ったスクリプトの標準的な書き方にすれば良いということです。

最初から順を追って手順解説 (面倒な人はここから読み始めればok)

まずは何はなくとも Ruby と bundler をインストールしてください。Ruby は各 OS の流儀で入れるとして、Bundler は gem install bundlerで入るはずです。

さて本題。今から tutorialというフォルダを掘って > そこでwp2txtを使って > 終わったら消す』 という流れで解説します。

$ mkdir tutorial
$ cd tutorial
$ bundler init
Writing new Gemfile to /PATH_TO/tutorial/Gemfile
$ echo 'gem "wp2txt"' >> Gemfile
$ bundler install --path .        # ここでローカルディレクトリを指定すれば後腐れなく rm できる

ここまでで必要なファイルが tutorial/ruby 以下にインストールされます。wp2txttutorial/ruby/2.5.0/bin/ 等に入っています (2.5.0の部分は各自の環境依存)。これを前項の『解決策』のとおり改変します。と言っても1行追記するだけです。

#!/usr/bin/env ruby2.5
#
# This file was generated by RubyGems.
#
# The application 'wp2txt' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'bundler/setup'   # <- この1行を追記
require 'rubygems'

version = ">= 0.a"

あとは実行するだけでokです。tutorial にWikipediaをダウンロードしているならこんな感じ。PATHが通っていないのできちんと書く必要があります。

$ ./ruby/2.5.0/bin/wp2txt --input-file jawiki-latest-pages-articles.xml.bz2

あとかたづけ

もうwp2txt使わないと思ったら、そのフォルダ (この例ならtutorial) 内の ruby/, .bundle/, Gemfile, Gemfile.lock を消してしまってokです。

$ pwd                # まとめて rm するときは念のため自分の居場所を再確認しましょう。
/PATH_TO/tutorial
$ rm -rf ruby .bundle Gemfile Gemfile.lock

参考

ちなみにwp2txtの場合、どんなgemが一緒にインストールされたか確認してみたらこんなでした。

$ ls ruby/2.5.0/gems/
htmlentities-4.3.4   nokogiri-1.10.4  trollop-2.9.9
mini_portile2-2.4.0  parallel-1.18.0  wp2txt-0.9.1
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

capybaraとselenium-webdriverとは

capybaraとselenium-webdriverとは

capybara

統合テスト(Feature Spec/フィーチャースペック)を書くときに、ブラウザにを仮想的に操作するためのgem。
細かい動作まで検証できる。

selenium-webdriver

capybaraはシンプルなブラウザシミュレータ(つまりドライバ)を使って、 テストに書かれたタスクを実行していきます。このドライバは Rack::Test というドライバで、速くて信頼性が高いのですが、JavaScript の実行はサポートしていません。
javascriptをテストするためにselenium-webdriverというgemをを使います。

CapybaraでもデフォルトのJavaScript ドライバになっていて、
デフォルトでは Capybara は selenium-webdriver に対して Firefox を使ってテストを実行するように伝えます。ですのでChromeを使いたい場合はそのように設定しなければなりません。

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

【個人開発】プログラミングを勉強したので誰かの書き込みを表示するだけのWebサイトを作った

kotoniwa

https://tzanarkand.com

何をするwebサイトなのか

ユーザーそれぞれが好きな言葉を書き込む、そして画面をクリックすると誰かが書いた言葉がランダムでぼんやりと出てくるだけのサイト。

スクリーンショット 2019-10-04 1.54.09.png
スクリーンショット 2019-10-04 1.57.32.png

使ったもの

ruby
rails
javascript
jquery
Nginx
unicorn
postgresql

自分のスキル

専門学校でサーバーとネットワークの勉強をしていたけど、プログラミングの経験はほぼ0(シェルスクリプトちょっとやったかなくらい)。

他人が書いたrailsアプリを自分が立てたapacheサーバーで公開した事はある。

railsもjavascriptもコードを見たことはあります程度。

自分で1からコードを書いてというのはこれが初めて。

勉強したこと

udemyでjavascriptとrailsとhtmlとcssの講座を購入してみたけど最初にざっと見ただけであまり活用しなかった。結局これ実際にやってみないとよくわからないな、と思ったので。

実装したいと思ったことをその都度ググって、よくわからなかったらそこを深掘りして、みたいな感じで作りながら学習を進めた。

作るまでの流れ

現在はサーバー運用系のお仕事をしていて、web系の方に転職してみようかなという思いが出てきたのでとりあえず何かを作ってみることに。

今まで自分で一からwebサイトを作ったことはなかったのであんまり難しいことをやろうとすると挫折するだろうと思い、とにかくシンプルなものを作りたかった。

wikipediaをランダムで表示したりするやつとかのランダム系のサイトが好きだったのでそんな感じのをイメージしていて、「なんでも好きに書いていい」となった時に皆がどんなことを書くのかに興味があったのでこれを作ろうと思った。

感想

とりあえず公開まで持っていけてよかった。
こんな簡単な感じのサイトでもここまで躓きポイントが大量にあるのか…と思ったので、作ろうと思っても途中で諦めてしまう人は大量にいると思う。
次はもうちょい真面目なwebサービス感のあるサイトを作る予定。
もっと技術つけてはやく転職したい。

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

Rails on Railsにおける命名規則

目的

Ruby on Railsを使ってコントローラーやモデルを作成する際の命名規則を備忘録のためにまとめておく

命名規則

コントローラー
命名
コントローラー名 tasks
コントローラークラス名 TaskController
ファイル名 tasks_controller.rb
モデル
命名
モデル名 task
モデルクラス名 Task
モデルファイル名 task.rb
テーブル名 tasks

ポイント

コントローラーを作成するとき、コントローラー名は複数形
モデルを作成するとき、コントローラー名は単数形

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

Vagrant + RailsでHerokuにHello ,World!する

vagrantローカル環境でhello world!を表示するアプリを作成しherokuにデプロイするまでの忘備録

環境

  • Ruby 2.4.0

  • Ruby on Rails 5.1.7

  • rbenv 1.1.2-2-g4e92322

  • Node.js v4.9.1

  • bundler 2.0.2

Vagrantローカル環境構築

https://qiita.com/karlley/items/0812bd33a3952ea40de5

上記URLを参考に環境構築

上記URLではRails 5.1.6で進めていますが今回は5.1.7を使用

順番に進めれば/home/vagrant/centos67/your_workspace/your_appにアプリが作成され「Yay! Your on Rails!」まで表示できるはずです

まずはローカル環境でHello World!

作成されたアプリのファイルを編集し「Hello World!」が表示されるようにします

修正する使用するファイルは2つ

  • app/controllers/application_controller.rb

  • config/route.rb

上記のファイルを以下のように修正

app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception

  def hello
    render html: "Hello, World!"
  end
end
config/route.rb
Rails.application.routes.draw do
  root 'application#hello'
end

http://192.168.33.10:3000で「Hello World!」が表示されているか確認

Git

  1. 初期設定
  2. Initial commit

1. 初期設定

HerokuのデプロイにはGitを使用するので初期設定

設定を確認

$ git config --list
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true

usernameとemailが未設定の場合は下記コマンドで設定

$ git config --global user.name "Your Name"
$ git config --global user.email your.email@example.com
$ git congit --list
user.name=Your Name
user.email=your.email@example.com
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true

設定を反映し、初期化

$ git init
Reinitialized existing Git repository in /home/vagrant/centos67/workspace/hello_app/.git/

上記コマンドを実行するとアプリのルートディレクトリに.gitが作成されます

2. Initial commit

Git設定後、最初のコミットを作成

ファイルをリポジトリに追加、確認

$ git add -A
$ git status
# On branch master
#
# Initial commit
.
.

リポジトリにコミット、確認

$ git commit -m "initialize repository"
56 files changed, 1181 insertions(+), 0 deletions(-)
 create mode 100644 .gitignore
.
.
$ git log
commit 28b...
Author: Your Name <your.email@example.com>
.
.

最初のコミットが作成されました

データベース

  1. postgreSQL
  2. sqlite3
  3. 設定したGemをインストール

1. postgreSQL

Herokuで使用するデータベースはデフォルトでpostgreSQLなのでGemの設定

Railsの開発環境でのデフォルトはsqlite3

Gemfileに下記を追記

Gemfile
group :production do
  gem 'pg', '0.20.0'
end

:production doは本番環境のみで使用するという意味

2. sqlite3

既存のsqlite3の記述をコメントアウト、または削除

Gemfile
#gem 'sqlite3'

開発/テスト環境のみの仕様に変更する為、group :development, :test dogem 'sqlite3'を追加

Gemfile
group :development, :test do
  # sqlite3 for development/test only ← 追記
  gem 'sqlite3'                       ← 追記
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
  # Adds support for Capybara system testing and selenium driver
  gem 'capybara', '>= 2.15'
  gem 'selenium-webdriver'
end

3. 設定したGemをインストール

$ bundle install --without production

--without productonをオプションで付ける事で本番環境へのデプロイの失敗を防止

pg gemを追加したことやRubyバージョンを指定したことをGemfile.lockに反映させないと、本番環境へのデプロイで失敗してしまうためです。

引用:RailsTutorilal

Gemfileを変更したので再度コミット

$ git commit -a -m "Update Gemfile for Heroku"

-aオプションは変更のあったすべてのファイルという意味

Heroku

  1. Herokuとは
  2. Heroku CLI インストール
  3. ssh鍵を追加
  4. デプロイ

1. Herokuとは

  • webアプリ用ホスティングサービス

  • Gitを使う事で簡単に本番環境にデプロイできる

  • Heroku本番環境ではデータベースにpostgreSQLを使用(Railsの開発環境でのデフォルトはsqlite3)

2. Heroku CLI インストール

Herokuをコマンドラインで使用するために必要

下記URLを参考にしてください

https://qiita.com/karlley/items/c423d02eee2292dab1f9

3. Heroku SSH鍵追加

/home/vagrant/.sshにSSH鍵があるか確認

$ cd ~/.ssh
$ ls -a
authorized_keys

id_rsa, id_rsa.pubが無い場合はSSH鍵が無いので下記URLを参考に生成

https://git-scm.com/book/ja/v1/Git-サーバー-SSH-公開鍵の作成

$ cd ~/.ssh
$ ssh-keygen
$ ls -a
authorized_keys  id_rsa  id_rsa.pub

SSH鍵が生成されたのでHerokuにログインし鍵を追加

Herokuアカウントが無い場合は下記URLから作成

https://jp.heroku.com

HerokuアカウントのEmail,Passwordを入力しログイン

$ heroku login --interactive
heroku: Enter your login credentials
Email:
Password:

Herokuにログイン後、SSH鍵を追加

$ heroku keys:add
Found an SSH public key at /home/vagrant/.ssh/id_rsa.pub
? Would you like to upload it to Heroku?
Uploading /home/vagrant/.ssh/id_rsa.pub SSH key... done

HerokuにSSH鍵が追加されました
HerokuのアカウントページのSSH Keysで追加された鍵を確認できます

4. デプロイ

Herokuにアプリケーションを作成

$ heroku create your-app-name
Creating app... done, ⬢ your-app-name
.
.

アプリ名が表示されアプリケーションが作成されます

リポジトリをプッシュ

$ git push heroku master
.
.
remote: Verifying deploy... done.
.
.
 * [new branch]      master -> master

デプロイ成功したのでブラウザで確認

$ heroku open
.
.
▸    Manually visit https://your-app-url/ in your browser.

表示されたURLをブラウザで確認

helloworld.png

無事「Hello world!」できました!

あっさり進んでいるように見えますが私のようなプログラミング初心者にとってローカル環境からデプロイするのは一つの大きな壁でした汗

同じように悩んでいる方に少しでも参考になればと思います!

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

RuboCop 0.75で導入された --disable-uncorrectable で、指摘を無視しつつTODOコメントに置き換える

はじめに

RuboCop 0.75 が、2019/10/01にリリースされました。

その中でも --disable-uncorrectable という、 --auto-correct と合わせて付与するオプションが追加されました。

本記事ではそちらについて紹介したいと思います。

--auto-correct とは

--disable-uncorrectable は、 --auto-correct とセットで使われるため、一応そちらについても触れておきます。

例えば以下のようなスタイルの崩れたコードは、rubocop実行時に指摘を受けます。

sample.rb
def hoge
  {
    a: 1,
      b: 2
  }
end
$ rubocop sample.rb
Inspecting 1 file
C

Offenses:

sample.rb:6:7: C: Layout/AlignHash: Align the elements of a hash literal if they span more than one line.
      b: 1
      ^^^^

1 file inspected, 1 offense detected

その際、 --auto-correct というオプションをつけてrubocopを実行するとrubocopが自動で修正できる範囲で勝手に修正してくれるという機能です。

$ rubocop sample.rb --auto-correct
Inspecting 1 file
C

Offenses:

sample.rb:6:7: C: [Corrected] Layout/AlignHash: Align the elements of a hash literal if they span more than one line.
      b: 1
      ^^^^

1 file inspected, 1 offense detected, 1 offense corrected
sample.rb
def hoge
  {
    a: 1,
    b: 1
  }
end

--disable-uncorrectable とは

--disable-uncorrectable は、 --auto-correct によって自動修正できなかった違反について、TODOコメントを付与して、以降指摘が発生しないようにする機能です。

例えば以下のコードは、メソッドの行数違反でありますが、rubocopが勝手に行数を減らすような自動修正をかけることはできません。

sample.rb
def hoge
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
end
$ rubocop --auto-correct sample.rb
Inspecting 1 file
C

Offenses:

sample.rb:3:1: C: Metrics/MethodLength: Method has too many lines. [11/10]
def hoge ...
^^^^^^^^

1 file inspected, 1 offense detected

そんなときは、 --disable-uncorrectableをあわせて付与すると、以下のようにTODOコメントを付与して、指摘を部分的に無効にしてくれます。

$ rubocop --auto-correct --disable-uncorrectable sample.rb
Inspecting 1 file
C

Offenses:

sample.rb:3:1: C: [Todo] Metrics/MethodLength: Method has too many lines. [11/10]
def hoge ...
^^^^^^^^

1 file inspected, 1 offense detected, 1 offense corrected
sample.rb
def hoge # rubocop:todo Metrics/MethodLength
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
end

これによって、hoge メソッドに対しては Metrics/MethodLength の指摘が行われなくなるので、以降では新たに発生した違反についてのみ指摘を受けることになります。

$ rubocop sample.rb
Inspecting 1 file
.

1 file inspected, no offenses detected

補足

  • --auto-correct-a で置き換えることも可能です
  • --ignore-disable-comments を付けた場合は、コメントがついてても容赦なく指摘されます
  • 部分的な指摘無視コメントはもちろん手動でも入れられますし、以前のバージョンからあった仕組みです
  • 無視コメント入れただけの対応を続けると本末転倒なのでちゃんと治そうね
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RuboCop 0.75で導入された --disable-uncorrectable で、指摘/警告を無視しつつTODOコメントに置き換える

はじめに

RuboCop 0.75 が、2019/10/01にリリースされました。

その中でも --disable-uncorrectable という、 --auto-correct と合わせて付与するオプションが追加されました。

本記事ではそちらについて紹介したいと思います。

--auto-correct とは

--disable-uncorrectable は、 --auto-correct とセットで使われるため、一応そちらについても触れておきます。

例えば以下のようなスタイルの崩れたコードは、rubocop実行時に指摘(警告)を受けます。

sample.rb
def hoge
  {
    a: 1,
      b: 2
  }
end
$ rubocop sample.rb
Inspecting 1 file
C

Offenses:

sample.rb:6:7: C: Layout/AlignHash: Align the elements of a hash literal if they span more than one line.
      b: 1
      ^^^^

1 file inspected, 1 offense detected

その際、 --auto-correct というオプションをつけてrubocopを実行するとrubocopが自動で修正できる範囲で勝手に修正してくれるという機能です。

$ rubocop sample.rb --auto-correct
Inspecting 1 file
C

Offenses:

sample.rb:6:7: C: [Corrected] Layout/AlignHash: Align the elements of a hash literal if they span more than one line.
      b: 1
      ^^^^

1 file inspected, 1 offense detected, 1 offense corrected
sample.rb
def hoge
  {
    a: 1,
    b: 1
  }
end

--disable-uncorrectable とは

--disable-uncorrectable は、 --auto-correct によって自動修正できなかった違反について、TODOコメントを付与して、以降指摘が発生しないようにする機能です。

例えば以下のコードは、メソッドの行数違反でありますが、rubocopが勝手に行数を減らすような自動修正をかけることはできません。

sample.rb
def hoge
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
end
$ rubocop --auto-correct sample.rb
Inspecting 1 file
C

Offenses:

sample.rb:3:1: C: Metrics/MethodLength: Method has too many lines. [11/10]
def hoge ...
^^^^^^^^

1 file inspected, 1 offense detected

そんなときは、 --disable-uncorrectableをあわせて付与すると、以下のようにTODOコメントを付与して、指摘を部分的に無効にしてくれます。

$ rubocop --auto-correct --disable-uncorrectable sample.rb
Inspecting 1 file
C

Offenses:

sample.rb:3:1: C: [Todo] Metrics/MethodLength: Method has too many lines. [11/10]
def hoge ...
^^^^^^^^

1 file inspected, 1 offense detected, 1 offense corrected
sample.rb
def hoge # rubocop:todo Metrics/MethodLength
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
  fuga
end

これによって、hoge メソッドに対しては Metrics/MethodLength の指摘が行われなくなるので、以降では新たに発生した違反についてのみ指摘を受けることになります。

$ rubocop sample.rb
Inspecting 1 file
.

1 file inspected, no offenses detected

補足

  • 近い機能で、--auto-gen-config というオプションもあり、こちらはTODOリストをyamlファイルに書き出します
  • --auto-correct-a で置き換えることも可能です
  • --ignore-disable-comments を付けた場合は、コメントがついてても容赦なく指摘されます
  • 部分的な指摘無視コメントはもちろん手動でも入れられますし、以前のバージョンからあった仕組みです
  • 無視コメント入れただけの対応を続けると本末転倒なのでちゃんと治そうね
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【48日目】【技術日記】コメント機能の作成 アソシエーションとかパーシャルとか

ここ数日は新しくお願いしたメンターさんとの面談や、
そのメンタリングの中で出された課題などを行なっていたので技術に触れていなかったため更新中断しておりました。
今日からまた技術の学習を進めていきます。

なお今日はトピックというよりは、学習ログの掲載という感じになります。
主にアソシエーションの設定であったり、掲示板のコメントフォームのパーシャルの作成部分になります。
コメントフォームはviewを持たないので、親であるboardと紐づいた形で、@commentを作るところなんかが自分では思いつけない部分だと思いました。
まだ完璧に理解できていないと思います。

65 Commentモデルの作成とアソシエーション

Commentモデルの作成

Commentモデルのマイグレーションファイルを作成

docker-compose exec web bundle exec rails g model comment board:references name:strings comment:text 
# rails g model モデル名 親テーブル名:references カラム名:型
# referencesによって「親テーブル名_id」という外部キーカラムが作成され、アソシエーションされる

出来上がったマイグレーションファイルは下記

comments.rb
class CreateComments < ActiveRecord::Migration[5.0]
    def change
        create_table :comments do |t|
            t.references :board, foreign_key: true # 引数によって親テーブルにないidが保存できなくなる
            t.string :name, null: false #引数によってnullの場合エラーとなる
            t.text :comment, null: false

            t.timestamps
        end
    end
end

マイグレートを実行し、DBにマイグレーション内容を反映

docker-compose exec web bundle exec rake db:migrate

アソシエーションの設定

親テーブル

ApplicationRecordのクラス継承の下に

board.rb
has_many :comments
#1対他のためhas_many:子テーブル名(複数形)

子テーブル

comments.rb
belongs_to :board

66 コメント書き込み機能のルーティング追加 &コントローラ作成

コメントコントローラの作成

コメント機能には作成と削除のみを付与する。
また、コメントページは用意せず、掲示板のshowの下部に表示する。

docker-compose exec web rails g controller comments create destroy --skip-template-engine
# viewの作成をskipする

ルーティングの修正

rails g によって自動的にルーティングされるが、リソースベースルーティングに合わせて修正する。

routes.rb
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
    resources :boards
    resources :comments, only: %i[create destroy]
end

コメントオブジェクトの作成

form_forヘルパーでコメントのフォームを作成するために、board_controllerのshowメソッドで@commentオブジェクトを作成する。
ただし、何かしらの方法で掲示板とコメントを紐付ける必要がある。

boards_controller.rb
def show
@comment = @board.comments.new
binding.pry
end

boardオブジェクトのcommentにnew メソッドを呼び出すことで掲示板に紐ついた@commentが作成できる。
binding.pryで@board.commentsの中を見ておく。
ちなみにこのオブジェクトのクラスはComment::ActiveRecord_Associations_CollectionProxy

67 コメントフォームの作成

show の修正

パーシャルを用いて作成

show.html.erb
<%= render 'boards/detail', detail: @detail %>
<%= render partial: 'comment_form', locals: { comment: @comment} %>
# commentというローカル変数で@commentのオブジェクトを渡すようにしている

パーシャルの作成

_comments_form.html.erb
<div class="p-comment__formBox">
<p class="p-comment__formTitle">コメント記入</p>
    <%= form_for comment do |f| %>
        <%= f.hidden_field :board_id %>
          # フォームの内容と一緒に外部キーを渡すために必要になる
          # フォーム自体は存在しない
        <div class="form-group">
            <%= f.label :name, '名前' %>
            <%= f.text_field :name, class: 'form-control' %>
        </div>
        <div class="form-group">
            <%= f.label :comment, 'コメント' %>
            <%= f.text_area :comment, class: 'form-control', rows: 4 %>
        </div>
        <%= f.submit '送信', class: 'btn btn-primary' %>
    <% end %>
</div>

CSSの作成

comments.scss
.p-comment__formBox {
    margin: 30px auto;
    border: 5px solid #e8e8e8;
    padding: 25px;
    border-radius: 14px;
}

.p-comment__formTitle {
    border-bottom: 2px solid #dfdfdf;
    padding-bottom: 5px;
}

これだけでは読み込まれないので、application.scssでインポートするよう追記

applocateion.scss
@import "comments"

68 コメント保存処理の実装

rails-flogの導入

binding.pryのログを見やすくしてくれるらしい。
明日から使っていくことになる。

gem 'rails-flog', require 'flog'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

attr_encryptedなカラムを検索する方法

初めまして、ガッシーです。Qiita初投稿となります。(なんか癖でQuiitaって打っちゃうんですよね)
日頃Railsの開発で躓いて調べたことなどを書き留めていければなと思ってます。
そんなところで本題へ。

railsでカラムを暗号化する際には一般的にattr_encryptedを利用するかと思いますが
READMEを翻訳しますと 

暗号化されたデータを検索することはできません。検索できないため、インデックスを作成することもできません。

との記載があります。
でも暗号化しつつ検索したいことって結構あると思うんですよね。

そこで登場するGemがblind_indexです。
ただし LIKE検索はできない ので注意してください。

blind_indexとは

例えばnameというカラムでattr_encryptedを使うときは encrypted_nameencrypted_name_iv の2つのカラムを用意すると思いますが、
それに加えて encrypted_name_bidx というカラムを追加してあげることで、そこに検索できる値を保存する感じです。早速やってみましょう。

設定

READMEに書いてあることをそのままやっていけばいいのですが一応書きます。
Gemfileに下記を追加してbundle install

gem 'blind_index'

コンソールを起動して下記コードを実行しランダムなキーを取得します。

irb(main):001:0> BlindIndex.generate_key
# SecureRandom.hex(32).force_encoding(Encoding::US_ASCII) でも同じ
=> "222189cbba7ba0381c66faf8f687197b4bd4256a99bf81c917256c2871ca5289"

キーを保存・設定します。

credentials.yml.enc
blind_index_master_key: "222189cbba7ba0381c66faf8f687197b4bd4256a99bf81c917256c2871ca5289"
config/initializers/blind_index.rb
BlindIndex.master_key = Rails.application.credentials.blind_index_master_key

(initializerを読み込むためサーバーは再起動しておいてください)

blind_index用のカラムを追加します。

db/migrate/add_name_bidx_to_users.rb
add_column :users, :encrypted_name_bidx, :string

modelも変更を加えます。

app/models/user.rb
class User < ApplicationRecord
  attr_encrypted :name, key: Rails.application.credentials.encrypted_key
  blind_index :name_bidx, key: Rails.application.credentials.blind_index_master_key #←こちら追加
end

以上で設定は完了です!

※既に暗号化カラムが保存されている方はコンソールにて下記コマンドを実行することで既存のレコードにも対応できます。

User.unscoped.where(encrypted_name_bidx: nil).find_each do |user|
  user.compute_name_bidx
  user.save(validate: false)
end

実行

User.where(name: "山田太郎")

これができるようになっているはずです。

感想

完全一致検索しかできないけど何もできないよりはマシか・・・
知識がないのですが、最初こちらの記事 attr_encryptedされたカラムに対してwhere likeしたかった。を見ましたが恐らく現在ivカラムを利用した暗号化を行っている場合は適応できないかな?と思ってます。
Qiita初めての記事となりましたのでもし誤りがありましたらご指摘いただければ幸いです。

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