はじめに
プログラミング学習を開始して1年。自己流でアプリ開発を続けることによる成長幅に限界を感じ、次のステップとしてコードリーディングをすることにしました。
こちらの記事では、コードリーディングで得た知識の備忘録を記していきます。
なお今回はこちらの記事を参考にし、コードリーディングの環境を整えることができました。
またvalidatesの解説も載っていましたのでそちらもかなり参考にしていますが、今回は私のような初学者に価値があるような内容を共有させて戴きます。
今回はmodelのvalidatesメソッドをリーディング
ターミナルで以下を実行。
rails c
[1] pry(main)> u = User.new(name: 'sample name')
nameの長さの最大が30というvalidatesの場合の処理を見ていきます。
class User < ApplicationRecord
#binding.pryで処理を止める
binding.pry
validates :name, length: { maximum: 30 }
end
validatesメソッドがどのように宣言されているのか。
UserクラスはApplicationRecord
を継承していますが、さらに元をたどるとActiveRecord::Base
を継承しています。
以下Railsのreadmeで以下のようにmodel層に関して解説がありました。
Model layer
models can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as provided by the Active Model module.
モデルは、通常のRubyクラス、またはActiveModelモジュールによって提供される一連のインターフェースを実装するRubyクラスにすることもできます。
https://github.com/rails/rails
いつも利用している便利なメソッドは、ActiveModelモジュールによって提供されたものということがわかりました。
本題のvalidates.rb
を読んでみた。
確かにActiveModelモジュールの中に宣言されていました。
module ActiveModel
module Validations
module ClassMethods
def validates(*attributes)
(省略)
end
end
end
end
処理①引数の調整
def validates(*attributes)
defaults = attributes.extract_options!.dup
validations = defaults.slice!(*_validates_default_keys)
extract_options!の継承元は以下の通り。
# frozen_string_literal: true
class Hash
# By default, only instances of Hash itself are extractable.
# Subclasses of Hash may implement this method and return
# true to declare themselves as extractable. If a Hash
# is extractable, Array#extract_options! pops it from
# the Array when it is the last element of the Array.
def extractable_options?
instance_of?(Hash)
end
end
class Array
# Extracts options from a set of arguments. Removes and returns the last
# element in the array if it's a hash, otherwise returns a blank hash.
#
# def options(*args)
# args.extract_options!
# end
#
# options(1, 2) # => {}
# options(1, 2, a: :b) # => {:a=>:b}
def extract_options!
if last.is_a?(Hash) && last.extractable_options?
pop
else
{}
end
end
end
defaultに代入する値では、
渡された引数の最後がハッシュクラスの直接のインスタンスで書かれている場合は、pop
それ以外は、{}
を代入しています。
学び① is_a?(mod)
is_a?(mod)メソッドを使用すれば、オブジェクトが指定されたクラス mod かそのサブクラスのインスタンスであるとき真を返します。
したがって、ここではattributes(validatesの引数)がハッシュクラスかどうか判定しています。
学び② instance_of?(klass)
オブジェクトがクラス klass の直接のインスタンスである時真を返します。
https://docs.ruby-lang.org/ja/latest/method/Object/i/instance_of=3f.html
学び③ popとpush
基本情報処理の試験勉強で学んでいたものでした。
popは配列の末尾から指定個数取り出し、取り出した要素を返す。
pushは末尾に新たな要素を加える。
https://docs.ruby-lang.org/ja/latest/method/Array/i/pop.html
https://docs.ruby-lang.org/ja/1.8.7/method/Array/i/push.html
学び④ dup
オブジェクトを複製することができます。
そのもの自体を変更したくない時に便利そうです。
https://docs.ruby-lang.org/ja/latest/method/Object/i/clone.html
疑問点
instance_of?(Hash)
のみで条件式として十分なのではないのか?
どのような意図があって、AND条件なのだろうか。
終わりに
本日はここまでにします。
たった1行読み込むだけで相当な勉強になりました。(コードリーディング恐るべし)
疑問点が残ってしまいましたが、わかり次第追記して修正します。