増補改訂版が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.4ms) SELECT "books".* FROM "books" WHERE (title LIKE '%ab%') LIMIT ? [["LIMIT", 1]]
Book Load (0.1ms) 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.2ms) SELECT "books".* FROM "books" WHERE (title LIKE '%ab%') LIMIT ? [["LIMIT", 1]]
=> nil
|
pluck
メソッドで必要なカラムの配列だけ取得できる。
1
2
3
4
5
6
7
|
irb(main):028:0> Book.all.pluck(:title)
(0.7ms) SELECT "books"."title" FROM "books"
=> ["aaのついての本", "たいとるだよ", "たいとるそのに"]
irb(main):029:0> Book.all.pluck(:title, :created_at)
(1.0ms) 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
をしてもエスケープ処理をしない。
つまり、何度同じ処理をしても多重エスケープの問題を起こさない!