増補改訂版がRails6.0対応で、とにかく「めっちゃいい」らしい。ということで、読んでいく。
ここには知らなかったこと・すげぇ!ええやん!と思ったことを書きます。
パーフェクト Ruby on Rails 【増補改訂版】
1章
binstub
bundle exec
を付けなくてもいい実行ファイルのこと。./bin
内に入っている。
railsコマンド
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ rails stats
+----------------------+--------+--------+---------+---------+-----+-------+
| Name | Lines | LOC | Classes | Methods | M/C | LOC/M |
+----------------------+--------+--------+---------+---------+-----+-------+
| Controllers | 556 | 297 | 21 | 34 | 1 | 6 |
| Helpers | 2 | 2 | 0 | 0 | 0 | 0 |
| Models | 222 | 174 | 6 | 20 | 3 | 6 |
| Mailers | 4 | 4 | 1 | 0 | 0 | 0 |
| Channels | 8 | 8 | 2 | 0 | 0 | 0 |
| Libraries | 16 | 15 | 0 | 0 | 0 | 0 |
| Loyalty specs | 275 | 257 | 0 | 0 | 0 | 0 |
| Model specs | 618 | 586 | 0 | 0 | 0 | 0 |
| Request specs | 1117 | 1105 | 0 | 0 | 0 | 0 |
| Serializer specs | 29 | 29 | 0 | 0 | 0 | 0 |
| Service specs | 201 | 195 | 0 | 0 | 0 | 0 |
+----------------------+--------+--------+---------+---------+-----+-------+
| Total | 3048 | 2672 | 30 | 54 | 1 | 47 |
+----------------------+--------+--------+---------+---------+-----+-------+
Code LOC: 500 Test LOC: 2172 Code to Test Ratio: 1:4.3
rails runner
rails runner 'puts Rails.env'
, rails runner hoge.rb
てな感じで実行できる。
rails dbconsole
, rails db
2章
Query Interface
Active Record クエリインターフェイス - Railsガイド
「インターフェイス」は何かと何かをつなぐ間という概念。
ここではSQLのクエリとActiveRecord
の間。
ActiveRecord::Relation
に対して使うことのできるメソッドは例えば以下のものがある。
クラスメソッドとスコープの違い
find_by
を使ったときのnil
の挙動に違いがある。
クラスメソッドだとnil
になるが、スコープだとそのスコープをなかったことにして再度実行する。
where
だとnil
ではないので、どちらも#<ActiveRecord::Relation []>
になる。
スコープはフィルタリングするもの、という意味合い。nilになりうるものはクラスメソッドにしよう!
1
2
3
4
5
6
7
class Book < ApplicationRecord
scope :written_about_scope , -> ( theme ) { find_by ( "title LIKE ?" , "% #{ theme } %" ) }
def self . written_about_class ( theme )
find_by ( "title LIKE ?" , "% #{ theme } %" )
end
end
1
2
3
4
5
6
7
8
irb ( main ): 016 : 0 > Book . written_about_scope ( 'ab' )
Book Load ( 0 . 4 ms ) SELECT "books" . * FROM "books" WHERE ( title LIKE '%ab%' ) LIMIT ? [[ "LIMIT" , 1 ]]
Book Load ( 0 . 1 ms ) SELECT "books" . * FROM "books" LIMIT ? [[ "LIMIT" , 11 ]]
=> #<ActiveRecord::Relation [#<Book id: 1, title: "aaのついての本", created_at: "2020-09-01 05:20:51", updated_at: "2020-09-01 05:20:51">]>
irb ( main ): 017 : 0 > Book . written_about_class ( 'ab' )
Book Load ( 0 . 2 ms ) SELECT "books" . * FROM "books" WHERE ( title LIKE '%ab%' ) LIMIT ? [[ "LIMIT" , 1 ]]
=> nil
pluck
メソッドで必要なカラムの配列だけ取得できる。
1
2
3
4
5
6
7
irb ( main ): 02 8 : 0 > Book . all . pluck ( :title )
( 0 . 7 ms ) SELECT "books" . "title" FROM "books"
=> [ "aaのついての本" , "たいとるだよ" , "たいとるそのに" ]
irb ( main ): 02 9 : 0 > Book . all . pluck ( :title , :created_at )
( 1 . 0 ms ) SELECT "books" . "title" , "books" . "created_at" FROM "books"
=> [[ "aaのついての本" , Tue , 01 Sep 2020 05 : 20 : 51 UTC + 00 : 00 ] , [ "たいとるだよ" , Tue , 01 Sep 2020 14 : 17 : 53 UTC + 00 : 00 ] , [ "たいとるそのに" , Tue , 01 Sep 2020 14 : 17 : 58 UTC + 00 : 00 ]]
pluckメソッドが便利な件について - Qiita
コールバック
コールバックポイント
create
update
destroy
before_validation
○
○
×
after_validation
○
○
×
before_save
○
○
×
around_save
○
○
×
after_save
○
○
×
before_create
○
×
×
around_create
○
×
×
after_create
○
×
×
before_update
×
○
×
around_update
×
○
×
after_update
×
○
×
before_destroy
×
×
○
around_destroy
×
×
○
after_destroy
×
×
○
after_initialize
after_find
first
, last
, find
, find_by
によってインスタンス化した後に呼び出される。
順番などは以下を参照
Railsのcallbackについて調べた - Qiita
enum
book.enum_column_before_type_cast
のように_before_type_cast
を付けると、でDBに保存されている値(integer)を取得できる。
Book.published
でwhere絞り込み検索、Book.not_published
のようにnot_
を付けるとnot検索ができる。
enumerize というgemもあるのでオススメ
コントローラ
before_action
around_action
after_action
around_action
の場合、アクションを囲むような感じになるので、該当アクションをyield
とする。
1
2
3
4
5
6
7
around_action :hoge
def hoge
logger . info 'この後アクションがあるやで'
yield
logger . info 'アクションが実行されたやで'
end
ビュー
variantsによるテンプレート切り替え
1
2
3
4
5
6
7
8
9
class ApplicationController < ActionController :: Base
before_action :detect_mobile_variant
private
def detect_mobile_variant
request . variant = :mobile if request . user_agent =~ /iPhine/
end
end
この場合、UserAgent が iPhone だったときはshow.html+mobile.erb
を表示する。
ヘルパーメソッド
url_for
URLパスを文字列にする。
引数
root_path
=> "/"
result_url
=> "http://www.example.com/result"
controller: :hoge, action: :edit
=> "/hoge/edit"
controller: :hoge, action: :edit, id: 1234, detailed: 'true'
=> "/hoge/edit?detailed=true&id=1234"
time_ago_in_words
現在時刻と引数の差をざっくり文字列として返す。
i18nに対応する。
1
2
3
4
5
6
7
8
9
10
irb ( main ): 010 : 0 > helper . time_ago_in_words ( Time . now )
=> "less than a minute"
irb ( main ): 011 : 0 > helper . time_ago_in_words ( Time . now - 1 . hours )
=> "about 1 hour"
irb ( main ): 012 : 0 > helper . time_ago_in_words ( Time . now + 1 . hours )
=> "about 1 hour"
irb ( main ): 013 : 0 > helper . time_ago_in_words ( Time . now + 1 . hours + 1 . minutes )
=> "about 1 hour"
irb ( main ): 014 : 0 > helper . time_ago_in_words ( Time . now + 1 . minutes )
=> "1 minute"
number_with_delimiter
引数の数字を3桁ごとに,
で区切った文字列を返す。
delimiterで間の文字を指定することもできる(誰得?)。
1
2
3
4
5
6
irb ( main ): 015 : 0 > helper . number_with_delimiter ( 11123444321 )
=> "11,123,444,321"
irb ( main ): 016 : 0 > helper . number_with_delimiter ( 11123444321 , delimiter : '@' )
=> "11@123@444@321"
irb ( main ): 017 : 0 > helper . number_with_delimiter ( 11123444321 , delimiter : 'おりゃ' )
=> "11おりゃ123おりゃ444おりゃ321"
コンソールで呼び出すとき
app.result_path
helper.time_ago_in_words
余談
特定のメソッドの引数を確認する。
1
2
irb ( main ): 025 : 0 > Book . method ( :new ) . parameters
=> [[ :opt , :attributes ] , [ :block , :block ]]
Rubyメソッドの引数の数を知る - Qiita
エスケープ処理
クロスサイトスクリプティング(XXS)を防ぐために、ただの文字列はHTMLにせずにエスケープするようになっている。
<%= raw "<script>alert('sample');</script>" %>
<%= "<script>alert('sample');</script>".html_safe %>
上記の処理によってActiveSupport::SafeBuffer
オブジェクトになり、jsが実行される。
(raw
メソッドは内部でhtml_safe
を呼んでいる)
ActiveSupport::SafeBuffer
はString
のサブクラス。そして、安全な文字列という意味なので、これに対してhtml_safe
をしてもエスケープ処理をしない。
つまり、何度同じ処理をしても多重エスケープの問題を起こさない!