4798


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

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

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


CC BY-SA 3.0
2015-01-18T17:46:49.643
4795
2016-12-19T00:44:59.787
7526
question
ruby ruby-on-rails
Railsの「params」というメソッドがよく分からない


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

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

[code]

    # ActionController::Parameters
    class Parameters < ActiveSupport::HashWithIndifferentAccess
    
    # ActiveSupport::HashWithIndifferentAccess
    class HashWithIndifferentAccess < Hash
    
[/code]

> これって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](https://github.com/rails/rails/pull/14384) /
[コミット(一部)](https://github.com/rails/rails/commit/14a3bd520dd4bbf1247fd3e0071b59c02c115ce0))

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

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

  * <https://github.com/rails/strong_parameters/blob/master/lib/action_controller/parameters.rb>
  * <https://github.com/rails/rails/blob/master/activesupport/lib/active_support/hash_with_indifferent_access.rb>

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

[code]

    params = ActionController::Parameters.new(name: 'Alice', email: 'alice@example.com') 
    => {"name"=>"Alice", "email"=>"alice@example.com"}
    
[/code]

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

[code]

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

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

[code]

    params[:name]
    "Alice"
    
    params['name'] 
    "Alice"
    
    params['tel']
    "123456"
    
    params[:tel]
    "123456"
    
[/code]

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

[code]

    # 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
    
[/code]

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

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

**P.S.**

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

  * [RubyMineのコードジャンプ機能は本当にすごい！！困ったときはCommand+Bを押すべし！](http://qiita.com/jnchito/items/bcb2bb5311a44113fdd9)
  * [僕がRubyMineを使い始めたたった１つの理由](http://qiita.com/LuckOfWise/items/c961a046d40a20436932)

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


CC BY-SA 3.0
2015-01-18T20:30:13.207
4798
2016-12-19T00:44:59.787
2016-12-19T00:44:59.787
17037
85
4795
answer


`params` は、Rails 由来のものです。

`params` は、 `Action Controller.Parameters`
オブジェクトで、まるでハッシュをもつハッシュのように扱えます。Rails は、アクセスされた URL をルーティングルールに従い分割して
`Parameters` オブジェクトを作成しており、`params`
の主な役割としてはそこから必須属性、使用する属性をチェック・振り分けることになります。また、以下のように自分で作成することもできます。`params`
の詳しい使い方は、[Rails
Guides](http://guides.rubyonrails.org/action_controller_overview.html#parameters)を参照するのがよいでしょう。

[code]

    params = ActionController::Parameters.new({
      person: {
        name: 'Francesco',
        age:  22,
        role: 'admin'
      }
    })
    new_params = params.require(:person).permit(:name, :age)
    new_params            # => {"name"=>"Francesco", "age"=>22}
    
[/code]

`params` を純粋なハッシュにするには、`to_h`, `to_unsafe_h` を使用します。  
以下、[Rails
のソースコードから抜粋](https://github.com/rails/rails/blob/5923ee6fe04500ce46f7fc8fff6aafcfd64e68db/actionpack/lib/action_controller/metal/strong_parameters.rb#L155)します。

[code]

    params = ActionController::Parameters.new({
      name: 'Senjougahara Hitagi',
      oddity: 'Heavy stone crab'
    })
    params.to_h # => {}
    
    safe_params = params.permit(:name)
    safe_params.to_h # => {"name"=>"Senjougahara Hitagi"}
    
[/code]

`to_h` では、 `permit` の設定で、ハッシュに入るエントリがフィルタにかけられます。例では、`name`
しか指定されていませんから、出来上がったハッシュには `oddity` が入っていません。

参考:  
[Ruby on Rails : Action Controller
Parameters](http://edgeapi.rubyonrails.org/classes/ActionController/Parameters.html)  
[Rails Guides: ActionController Overview :
Paramemters](http://guides.rubyonrails.org/action_controller_overview.html#parameters)  
[Strong Parameter by Example](http://blog.trackets.com/2013/08/17/strong-
parameters-by-example.html)


CC BY-SA 3.0
2015-01-18T20:33:10.353
4799
2015-01-18T23:36:21.207
2015-01-18T23:36:21.207
4978
4978
4795
answer


Rubyでは`hash[key]`や`hash[key]=x`のように[一見すると演算子のように見えるメソッド呼び出し](http://docs.ruby-
lang.org/ja/2.2.0/doc/spec=2foperator.html)がいくつかあります。

これらのメソッドを再定義すると、JavaScriptでいうところのarray like
objectのような、`Hash`クラスではないが`[]`や`[]=`のインターフェイスを備えたクラスを実装できます。

[code]

    class Foo
      def initialize
        @hash = {}
      end
    
      def []=(key, value)
        @hash[key] = value * 2
      end
    
      def [](key)
        @hash[key] * 2
      end
    end
    
    foo = Foo.new
    foo["bar"] = 10
    p foo["bar"]
    
    # => 40
    
[/code]

Rubyの組み込みライブラリだと[ENV](http://docs.ruby-
lang.org/ja/2.2.0/class/ENV.html)などが、ハッシュと同様のインターフェイスを持ったオブジェクトとして実装されています。

[code]

    p ENV["USER"] # => your_user_name
    p ENV.class # => Object
    
[/code]

## 参考

  * [演算子式](http://docs.ruby-lang.org/ja/2.2.0/doc/spec=2foperator.html)
  * [クラス／メソッドの定義](http://docs.ruby-lang.org/ja/2.2.0/doc/spec=2fdef.html#operator)
  * [object ENV](http://docs.ruby-lang.org/ja/2.2.0/class/ENV.html)


CC BY-SA 3.0
2015-01-19T03:28:47.510
4813
2015-01-20T01:43:13.497
2015-01-20T01:43:13.497
2599
2599
4795
answer