Rails6 のちょい足しな新機能を試す５（session編)

# はじめに

Rails 6 に追加されそうな新機能を試す第5段。今回は、session (`ActionDispatch::Request::Session`) に追加された `dig` メソッド編です。
`Hash#dig` と同様の機能です。...が、若干、消化不良な内容です。

記載時点では、Rails は 6.0.0.beta3 です。　`gem install rails --prerelease` でインストールできます。

```
$  rails --version
Rails 6.0.0.beta3``
```

# 単純なCRUD機能をscaffold で作る

新機能を試すために、scaffold で単純なCRUD機能を作ってみます。

```
$ rails new sandbox_6_0_0b3
$ cd sandbox_6_0_0b3
$ rails g scaffold User name
```

# session を使ってみる

全く実用的ではないですが、session を使ってみましょう。

`UsersController#create` で session に値を設定します。

```ruby:app/controllers/users_controller.rb
  def create
    @user = User.new(user_params)

    respond_to do |format|
      if @user.save
        session[:user] = {"name" => @user.name} # この行を追加
        format.html { redirect_to @user, notice: 'User was successfully created.' }
        format.json { render :show, status: :created, location: @user }
      else
        format.html { render :new }
        format.json { render json: @user.errors, status: :unprocessable_entity }
      end
    end
  end
```

`UsersController#show` で session の値を取り出します。このとき `dig` メソッドが使えます。

```ruby:app/controllers/users_controller.rb
  def show
    @created_user_name = session.dig(:user, "name")
  end
```

View で取り出した session の値を表示します。

```erb:app/views/users/show.html.erb
<p id="notice"><%= notice %></p>
<p><%= @created_user_name %></p> <!-- この行を追加 -->
<p>
  <strong>Name:</strong>
  <%= @user.name %>
</p>

<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>
```

試してみると値を取り出せていることが確認できます。

# ちょっとした落とし穴

session に値を設定するときに

```ruby
session[:user] = {name: @user.name}
```

として、値を取り出すときに

```ruby
session.dig(:user, :name)
```

とすると取り出せません :persevere: 

`dig` のソースを見ると、最初の `key` だけ　`key.to_s` して他はしてないからですね。

```ruby:actionpack/lib/action_dispatch/request/session.rb
      # Returns the nested value specified by the sequence of keys, returning
      # +nil+ if any intermediate step is +nil+. =
      def dig(*keys)
        load_for_read!
        keys = keys.map.with_index { |key, i| i.zero? ? key.to_s : key }
        @delegate.dig(*keys)
      end
```

うーん:thinking: [PR](https://github.com/rails/rails/pull/32446) には

> Converts only the first key to a string adjust to the fetch method.

と書いてあるんですが

```ruby
keys = keys.map(&:to_s)
```

とか

```ruby
keys = keys.map.with_index { |key, i| i.zero? || key.kind_of?(Symbol) ? key.to_s : key }
```

とかしたら駄目なんですかねえ...

もうちょっと調べてみたら、　`UsersController#create` で設定した直後は、 `session.dig(:user, :name)` で取り出せるんですが、`UsersController#show` で取り出そうとしたら、 `session.dig(:user, "name")` でないと取り出せなくなってました...。
使い方間違ってる？

`session.to_h` を調べてみたら、こんな感じで変化してました。

`create` メソッドの中

```ruby
[30, 39] in /app/sandbox6_0_0b3/app/controllers/users_controller.rb
   30:
   31:     respond_to do |format|
   32:       if @user.save
   33:         session[:user] = {name: @user.name}
   34:         byebug
=> 35:         format.html { redirect_to @user, notice: 'User was successfully created.' }
   36:         format.json { render :show, status: :created, location: @user }
   37:       else
   38:         format.html { render :new }
   39:         format.json { render json: @user.errors, status: :unprocessable_entity }
(byebug) session.to_h
{..., "user"=>{:name=>"Rails Session"}}
```

`show` メソッドの中

```ruby
[9, 18] in /app/sandbox6_0_0b3/app/controllers/users_controller.rb
    9:
   10:   # GET /users/1
   11:   # GET /users/1.json
   12:   def show
   13:     byebug
=> 14:     @created_user_name = session.dig(:user, :name)
   15:   end
   16:
   17:   # GET /users/new
   18:   def new
(byebug) session.to_h
{..., "user"=>{"name"=>"Rails Session"}, "flash"=>{"discard"=>[], "flashes"=>{"notice"=>"User was successfully created."}}}
```

うーん。良くわからん。

# 参考情報

* [What is new in Rails 6.0](https://bogdanvlviv.com/posts/ruby/rails/what-is-new-in-rails-6_0.html)
* [Add #dig to ActionDispatch::Request::Session](https://github.com/rails/rails/pull/32446)