paramsメソッドとは何か？

# はじめに

この記事は[株式会社ラグザイア Advend Calendar 2023](https://qiita.com/advent-calendar/2023/luxiar) の記事です

Ruby on Railsを用いて実装を行っているとき、controller内でよくparamsメソッドを用いて、リクエストで送られてきたデータを受け取るということをやると思います

ふと、**paramsは何者で、なぜリクエストで送られてきたデータを取り出すことができるのだろう？** と興味が湧いたので調べてみました

# 調査

https://api.rubyonrails.org/classes/ActionController/Metal.html#method-i-params-3D によると、paramsはActionController::Metal※1 のインスタンスメソッドで、以下のように定義されていました

```jsx
def params
  @_params ||= request.parameters
end
```

@_paramsがnilでなければそのまま返し、nilの場合はrequest.parameters※2を代入して返すというものです

続いて、@_paramsのソースコードを調べてみると、ActionController::Metalのinitializeに定義されていました（https://github.com/rails/rails/blob/c80fa6a0697656a522b7688d116a9a6fca1fc0fd/actionpack/lib/action_controller/metal.rb#L185）

```jsx
def initialize
      @_request = nil
      @_response = nil
      @_response_body = nil
      @_routes = nil
      @_params = nil
      super
    end
```

ここまでで、

- @_paramsはActionControllerインスタンスが作成される時に定義されるインスタンス変数
- paramsメソッドは、@_paramsを参照するメソッド

ということがわかりました。

次に、実際に手元で、リクエスト時に@_paramsに何が入るのかをみてみます

## 準備

rails newで検証のためのrailsアプリを作成する。アプリ名は「params_inspection」とする

params_inspects_controller.rbとparams_inspectアクションを作成し、中にdebuggerを仕込む

```jsx
def params_inspect
	debugger
end
```

routes.rbにルート追加

```jsx
get 'params_inspects/params_inspect', to: 'params_inspects#params_inspect'
```

これで準備ok

また、使用した環境は以下です

mac OS Ventura 13.6.1

Ruby 3.1.4

Ruby on Rails 7.1.2

## 実践

rails sでサーバーを立ち上げた後、postmanでリクエストを飛ばし、railsのログ内でデバッガを使ってインスタンス変数（@_params）の中身を見てみます

```jsx
Started GET "/params_inspects/params_inspect?hoge=foo_1" for ::1 at 2023-12-09 14:48:57 +0900
Processing by ParamsInspectsController#params_inspect as */*
  Parameters: {"hoge"=>"foo_1"}
[1, 6] in ~/params_inspection/app/controllers/params_inspects_controller.rb
     1| class ParamsInspectsController < ApplicationController
     2|   def params_inspect
=>   3|     debugger
     4|     Rails.logger.debug "Received params: #{params.inspect}"
     5|   end  
     6| end
=>#0    ParamsInspectsController#params_inspect at ~/params_inspection/app/controllers/params_inspects_controller.rb:3
  #1    ActionController::BasicImplicitRender#send_action(method="params_inspect", args=[]) at ~/.rbenv/versions/3.1.4/lib/ruby/gems/3.1.0/gems/actionpack-7.1.2/lib/action_controller/metal/basic_implicit_render.rb:6
  # and 79 frames (use `bt' command for all frames)
(rdbg) p params    # command
=> #<ActionController::Parameters {"hoge"=>"foo_1", "controller"=>"params_inspects", "action"=>"params_inspect"} permitted: false>
(rdbg) p @_request    # command
=> #<ActionDispatch::Request GET "http://localhost:3000/params_inspects/params_inspect?hoge=foo_1" for ::1>
(rdbg) p instance_variables    # command
=> [:@_action_has_layout, :@rendered_format, :@_routes, :@_request, :@_response, :@_response_body, :@_params, :@_url_options, :@marked_for_same_origin_verification, :@_view_runtime, :@_db_runtime, :@_lookup_context, :@_action_name, :@_config]
(rdbg) p instance_variable_get('@_params')    # command
=> #<ActionController::Parameters {"hoge"=>"foo_1", "controller"=>"params_inspects", "action"=>"params_inspect"} permitted: false>
```

@_paramsインスタンス変数に、リクエストのパラメータが格納されたActionController::Parametersインスタンスが入っていることがわかりました

## まとめ

今回のまとめです

- paramsメソッドは、@_paramsを参照するメソッド
- @_paramsはActionControllerインスタンスが作成される時に定義されるインスタンス変数
- リクエスト後、@_paramsには、リクエストのパラメータが格納されたActionController::Parametersインスタンスが入っている

今度は、httpプロトコルで送られたリクエストがどのように解析されて、最終的にパラメーターがActionController::Parametersインスタンスに格納されるのか？を調べてみたいと思います

ここまで読んでいただき、ありがとうございました！

## 注釈

※1 ActionController::BaseはActionController::Metalを継承している

※2 request.parametersは `ActionDispatch::Request`オブジェクトのメソッドで、クエリパラメータ、リクエストボディ（POSTデータ）、ルートパラメータなど、リクエストに含まれるすべてのパラメータを結合して返す(https://github.com/rails/rails/blob/c80fa6a0697656a522b7688d116a9a6fca1fc0fd/actionpack/lib/action_dispatch/http/parameters.rb#L50)