Railsの「params」というメソッドがよく分からない

投稿者: Anonymous

こんにちは、Ruby初心者で恐縮ですが、Railsを勉強していて、コントローラー内で使っている一見ハッシュのように見えるparams[xxx]が気になって仕方がないのです。

ハッシュじゃないのにあたかもハッシュのような書き方になっています。ActionController::Parametersのインスタンスであること以外何も分からなくてちょっと気持ち悪いです。

これってjsの言うところの「array like object」的なものなのでしょうか?実際どうやって作られているのでしょうか?できれば簡単なサンプルコードを頂ければ幸いです。

解決

ハッシュじゃないのにあたかもハッシュのような書き方になっています

Rails 4 では、 ActionController::Parameters の親クラスの親クラスがHashです。
つまり、ハッシュと継承関係にあるのでハッシュと同じように振る舞います。

# ActionController::Parameters
class Parameters < ActiveSupport::HashWithIndifferentAccess

# ActiveSupport::HashWithIndifferentAccess
class HashWithIndifferentAccess < Hash

これってjsの言うところの「array like object」的なものなのでしょうか?

“array like object”という用語が初耳だったので、こちらのページを読んでみました。

http://nfriedly.com/techblog/2009/06/advanced-javascript-objects-arrays-and-array-like-objects/

なるほど、「Arrayと同じメソッドをいくつか持っているが、完全な互換性はないオブジェクト」のことを指すようですね。

その定義からすると、ActionController::Parametersは”Array(Hash) like object”ではありません。
ハッシュを継承しているので、 “ActionController::Parameters is a Hash”と言えます。
つまりハッシュと完全な互換性があります。(後述するキーの扱いを除いて)

なお、Rails5では、使い方によっては安全のためのフラグがクリアされてしまうなど、パラメータへのアクセス以外のメソッド(Enumerable由来のものなど)が使えてしまうことの弊害があることから、 ActionController::parameters は HashWithIndifferentAccess のサブクラスではなくなっています。(PR / コミット(一部))

実際どうやって作られているのでしょうか?できれば簡単なサンプルコードを頂ければ幸いです。

クラス定義は上に載せたとおりです。詳しくはRailsのコードを読んでみてください。

インスタンスの作り方という点では、以下のように new + keyとvalue でインスタンスを作成できます。
(とはいえ、自分でActionController::Parametersをnewすることは滅多にないですが)

params = ActionController::Parameters.new(name: 'Alice', email: '[email protected]') 
=> {"name"=>"Alice", "email"=>"[email protected]"}

ハッシュと同じように扱えるので、keyとvalueを追加することもで来ます。

params['tel'] = '123456'
=> "123456"

ただし、HashWithIndifferentAccess(ActionController::Parametersの親クラス)はkeyとしてシンボルと文字列を区別しないようにハッシュを拡張しています。

params[:name]
"Alice"

params['name'] 
"Alice"

params['tel']
"123456"

params[:tel]
"123456"

ちなみにRails本体でActionController::Parametersのインスタンスを作成しているコードは以下のようになっています。

# https://github.com/rails/strong_parameters/blob/master/lib/action_controller/parameters.rb
module ActionController
  # ...
  module StrongParameters
    # ...
    def params
      @_params ||= Parameters.new(request.parameters)
    end

def params がcontrollerでよく登場する params の正体です。

こんな感じで疑問点は解消されたでしょうか?
他に不明な点があればコメントください。

P.S.

余談ですがこういう疑問を持ったときはRubyMineのコードジャンプ機能を使うと便利です。
有料のIDEですが、使いこなせばデバッグやコードリーディングの時間をかなり節約できるので、導入を検討してみると良いかもしれません。

僕もこの回答を書くにあたり、RubyMineを使ってクラスやメソッドの定義元を探していきました。

回答者: Anonymous

Leave a Reply

Your email address will not be published. Required fields are marked *