はじめに
https://qiita.com/engineer_ikuzou/items/e78fe4d5b4977bbe86c2
こちらの続編です。
前提
名前の長さ30文字制限をかけています。
u = User.new(name: 'sample name')
class User < ApplicationRecord
binding.pry
validates :name, length: { maximum: 30 }
end
validatesメソッド2行目から見ていきます。
def validates(*attributes)
defaults = attributes.extract_options!.dup
validations = defaults.slice!(*_validates_default_keys)
defaultsには前回確認した値がは格納されています。
未確認の引数_validates_default_keysが出てきたので、確認してみると、ハッシュで条件文が格納されていました。
https://railsguides.jp/active_record_validations.html
条件付きvalidationの箇所と、そうでない部分を分けるための処理のようです。
[4] pry(User)> defaults
=> {:length=>{:maximum=>30}}
[5] pry(User)> _validates_default_keys
=> [:if, :unless, :on, :allow_blank, :allow_nil, :strict]
validations: dafaultsでsliceの対象外のhashを格納
dafaults: sliceの対象となったifなどがあれば格納
上記のように格納される処理でした。
学び① sliceメソッド
引数で指定されたキーとその値だけを含む Hash を返します。
A=[ a:100, b:300, c:500 ]という配列に対して、A.slice(:a)は{:a=>100}を返します。
https://docs.ruby-lang.org/ja/latest/method/Hash/i/slice.html
また、全く意識していなかったのですが破壊的メソッド自体も定義されており、その中でsliceを使用しているのですね。
少し考えたらわかることだと思うのですが、意識できていませんでした。
また破壊的メソッドはそれ自体を更新する一方で、返り値としては更新されなかった部分を返してくれるのですね。
From:
rails/activesupport/lib/active_support/core_ext/hash/slice.rb:11 Hash#slice!:
10: def slice!(*keys)
=> 11: omit = slice(*self.keys - keys)
12: hash = slice(*keys)
13: hash.default = default
14: hash.default_proc = default_proc if default_proc
15: replace(hash)
16: omit
17: end
10: omitに、引数keysを除くhashがあれば避けておき、返り値とする。
15: hashはこちらで更新。
validatesメソッド3,4行目
raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
raise ArgumentError, "You need to supply at least one validation" if validations.empty?
[:if, :unless, :on, :allow_blank, :allow_nil, :strict]に該当するもののみvalidatesの引数にある場合は、エラーを起こします。
条件付きvalidatesの場合、条件true時のvalidationを記載する必要があるため、記載があるかどうかチェックしています。
validatesメソッド 5,6行目
validations.each do |key, options|
key = "#{key.to_s.camelize}Validator"
validations => {:length=>{:maximum=>30}}
なのでkeyは:length
, optionsは{:maximum=>30}
です。
キャメルケースへの変換が行われ、keyは、LengthValidator
となります。
学び② camelizeメソッド
デフォルトでアッパーキャメルケースに変換します。
https://www.rubydoc.info/gems/activesupport/String:camelize
validatesメソッド 7~11行目
begin
validator = key.include?("::") ? key.constantize : const_get(key)
rescue NameError
raise ArgumentError, "Unknown validator: '#{key}'"
end
validatorが既にモジュールの形で表記されているかで条件式が分かれています。
今回は、モジュール形式ではないので、const_getメソッドでActiveRecord::Validations::LengthValidator
が返ります。
学び③ constantizeメソッド
引数の文字列で指定した名前で定数を探す。
https://railsdoc.com/page/constantize
学び④ const_getメソッド
name で指定される名前の定数の値を取り出します。
https://docs.ruby-lang.org/ja/latest/method/Module/i/const_get.html
終わりに
今回はここまでです。
メソッドもそれぞれどのような挙動をしているのか確認することもできるのですが、そこの深掘りはしませんでした。あくまでvalidatesメソッドの深掘りです。全てを読み込むと永遠に終わりのない勉強なので、どこをどこまで深掘りするか、学習の目的をしっかり意識することが大事だと思いました。