やりたかったこと
class User < ActiveRecord::Base
attr_accessible :name
def self.search(opts)
find_by_name(opts && opts[:name])
end
end
class UsersController < ApplicationController
def search
@users = User.search(params[:user])
end
end
のような、Ruby1.9で実装したモデルとコントローラがあった
Ruby2.0に移行する際、User.search
メソッドをキーワード引数を使って
def search(name: nil)
find_by_name(name)
end
のように書きなおせたら可読性高くて && nil考慮しなくてよくてステキだなと
やってみたこと
User.search
の引数に取りうる値はハッシュとnilのはずなので、実装して実験してみた
User.search name: nil #=> OK
User.search any_key: nil #=> ArgumentError: unknown keyword: namae
User.search nil #=> ArgumentError: wrong number of arguments (1 for 0)
1番目は推測通りだが、2番目と3番目はまずい
動作が変わる
るびま読んだ感じだと、可変長引数を受け取るようにしてると最後のハッシュがキーワード引数になり、unknown keywordエラーは**any_key
で抑制できるようなので、
def search(*, name: nil, **kwargs)
find_by_name(name)
end
としてやればOKだった
できないこと
user = User.search "name" => "JOJO"
テストとして記述したこのコードが期待通り動かず、小一時間悩んだ
Ruby力が足りず混同してしまっていたのだが、{ "hoge" => nil }
と{ hoge: nil }
は別物
文字列を渡しても、キーワード引数として解釈してくれないようだ
Ruby的にはオブジェクトをキーにできるし、シンボルと文字列は別物なのだから当たり前っちゃ当たり前の話
ただ、RailsのParams
では文字列とシンボル両方でアクセスできるようになっているので、params
を直接渡してもちゃんと動く
ちなみに、文字列とシンボルどちらでもアクセスできるハッシュ(のようなもの)は、Rails環境下ではHash#with_indifferent_access
で生成できるようだ
http://rubyist.g.hatena.ne.jp/muscovyduck/20061224
思ったこと
メソッドを、何が入ってるかよくわからないハッシュが渡されることを前提に実装しようとすると、考慮すべき点が多い
また、一応実装はできるが、意味のない変数も定義しなければならなくなる
キーワード引数を使う場合は、
User.search name: "なまえ"
のように、呼び出し側で明示すること前提に設計すべきなのかな
と思った