LoginSignup
10
10

More than 5 years have passed since last update.

Ruby2.0.0のキーワード引数でハマる

Last updated at Posted at 2013-03-25

やりたかったこと

user.rb
class User < ActiveRecord::Base
    attr_accessible :name

    def self.search(opts)
        find_by_name(opts && opts[:name])
    end
end
users_controller.rb
class UsersController < ApplicationController
    def search
        @users = User.search(params[:user])
    end
end

のような、Ruby1.9で実装したモデルとコントローラがあった

Ruby2.0に移行する際、User.searchメソッドをキーワード引数を使って

user.rb#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で抑制できるようなので、

user.rb#search
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: "なまえ"

のように、呼び出し側で明示すること前提に設計すべきなのかな
と思った

10
10
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10