This page looks best with JavaScript enabled
⚠️

『パーフェクトRuby on Rails』1, 2章

 ·   ·  ☕ 5 分で読めます
✏️

増補改訂版が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に対して使うことのできるメソッドは例えば以下のものがある。

  • joins
    • inner_joinでくくる
  • left_outer_joins
    • left outer joinでくくる

クラスメソッドとスコープの違い

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::SafeBufferStringのサブクラス。そして、安全な文字列という意味なので、これに対してhtml_safeをしてもエスケープ処理をしない。
つまり、何度同じ処理をしても多重エスケープの問題を起こさない!

Share on

END
END
@aiandrox

 
目次