オブジェクト生成時にインスタンス変数の初期値を設定する
オブジェクト生成時にインスタンス変数の初期値を設定する Railsのmodelではmodel生成時に
user = User.new(name: "tarou", address: "東京都港区", phone: "08099999999"...)
このような形で初期値を設定することが出来ます。
でこれに慣れてるとRubyでこのようなクラスがあったとして、同じようにこんなふうにインスタンス変数設定したいなーと思って書いてもRubyのオブジェクトにそんな機能は備わってないので、Object#initializeを呼んでエラーになってしまいます。
class Bar attr_accessor :a, :b, :c end Bar.new(a: "A", b: "B", c: "C") ArgumentError: wrong number of arguments (1 for 0) from (pry):5:in `initialize'
よくあるやり方としてはinitializeで普通に引数を設定する方法があります。
class Foo attr_accessor :a, :b, :c def initialize(a, b, c) @a = a @b = b @c = c end end => :initialize Foo.new("A", "B", "C") => #<Foo:0x007fec0b61e690 @a="A", @b="B", @c="C">
ただしこのやりかただと変数の数がたくさん増えていった場合にいちいちinitializeの定義を確認してどの順番で引数を渡さないといけなかったかなーと確認しないといけなくなりとても不便です。
そこでもう少し便利な方法を考えます。
キーワード引数を使う
キーワード引数を使うことで引数を関数に与えられた順番ではなく、キーワードに紐付けて渡せるようになります。その結果上記のコードは以下のように書き直せます。これによってどの値にどの値をセットしているか がよくわかるようになるし、順番も気にする必要がなくなります。
class Bar attr_accessor :a, :b, :c def initialize(a:, b:, c:) @a = a @b = b @c = c end end Bar.new(a: "A", b: "B", c: "C") #keyword:値 なのでどの値にどの値をセットするかがわかりやすい => #<Bar:0x007fec06199c50 @a="A", @b="B", @c="C"> Bar.new(b: "B", a: "A", c: "C") #順番を入れ替えても問題ない => #<Bar:0x007fec0925c388 @a="A", @b="B", @c="C">
ただしこれでもたくさん変数があると
def initialize(a:, b:, c:, d:...) @a = a @b = b @c = c @d = d ... end
となってやや冗長な感じがします。
sendを使う
そこで今度はsendを使って初期値の代入を行います。与えられた引数をまずhash形式に直したうえでsendを使って初期値をセットします。こうすることでいくら初期値がたくさんあってもこれ以上コードが長くなることはなくなりました。ただし、ぱっと見何をやってるかわかりづらいのでここまでやるかは微妙なところかと思います。
class Foo attr_accessor :a, :b, :c, :d, :e, :f def initialize(a:, b:, c:, d:, e:, f:) # 与えられた引数をhash形式で取得 args_hash = self.method(__callee__).parameters.map do |arg_type,arg| [ arg, eval(arg.to_s) ] end.to_h # sendを使って初期値をセット args_hash.each do |prop_name, prop_value| self.send("#{prop_name}=",prop_value) end end end Foo.new(a: "A", b: "B", c: "C", d: "D", e: "E", f: "F") => #<Foo:0x007f8154748128 @a="A", @b="B", @c="C", @d="D", @e="E", @f="F"> Foo.new(a: "A", b: "B", c: "C") ArgumentError: missing keywords: d, e, f #何が足りていないかもわかりやすい。ただし何も入れたくない場合は明示的にnilをセットする必要がある。
まだまだ初学者+エンジニア1人でやっているのでおかしいところがあれば是非ご指摘いただけると嬉しいです。 参考: http://qiita.com/hidechae/items/5416a996529d7663c182