Ruby
Rails
Rails6

Rails6 のちょい足しな新機能を試す5(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 に値を設定します。


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 メソッドが使えます。


app/controllers/users_controller.rb

  def show

@created_user_name = session.dig(:user, "name")
end

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


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 に値を設定するときに

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

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

session.dig(:user, :name)

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

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


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 には


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


と書いてあるんですが

keys = keys.map(&:to_s)

とか

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 メソッドの中

[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 メソッドの中

[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."}}}

うーん。良くわからん。


参考情報