Ruby on RailsのStrong Parametersの使い方
Ruby on Rails 4から、Strong Parametersというものが導入されたのですね。 これはRails 3でGitHubがMass Assignment脆弱性なるものを突かれた、という経緯があるらしいです。
このStrong Parametersの使い方についてまとめました。 もちろん自分のためです。。。
Mass Assignment脆弱性とは
たとえば、以下の内容がフォームからPOSTされたとします。
p params[:user] # => { :name => "foo", :email => "foo@foo.com" }
それを、このようにしてupdate
したとします。
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
# 処理
end
このままだと特に問題ないように思えます。 ……が、不正なフォームの使用などにより、以下のような内容がPOSTされるとします。
p params[:user] # => { :name => "foo", :email => "foo@foo.com", :token => "XXXXXXXXXXXXXXXX" }
Rails 3まではattr_accessible
で受け取るパラメータをホワイトリスト指定できたのですが、これを知らない開発者は:token
の更新を許容してしまう、という問題です(たぶん)。
これを防ぐのがStrong Parameters、という訳です。
Strong Parametersとは
以下のように、params[:user]
を直接渡すのではなく、以下のように一度フィルタリングします。
@user = User.find(params[:id])
if @user.update_attributes(user_params)
# 処理
end
private
def user_params
params.require(:user).permit(:name, :email)
end
require
は、ここで指定したキーを持っている場合のみ値を返します。
いわば一次フィルタです。
permit
は、ここで指定したシンボルのみを返します。
先ほどの不正な値がPOSTされた場合でも、ここで:token
をpermit
していなければ、値が変えられることはない、ということです。
朗報だね(ニッコリ)。
参考記事
Ruby on Railsのflashの使い方
Ruby on Railsを学んでいると度々出てくるflash
について、しっかり学ぼうと思い、使い方をまとめてみました。
基本的な使い方
コントローラでflashをセット
まず、以下のようにflash
にメッセージをセットします。
flash[:success] = 'アカウントを作成しました。'
redirect_to root_url
# このようにも書ける
# redirect_to root_url, :flash => { success: 'アカウントを作成しました。' }
ビューでflashを表示
あとは、以下のようにflash
を表示させるだけです。
content_tag
を使うとスッキリします。
<% flash.each do |key, value| %>
<%= content_tag :div, value, class: "flash flash-#{key}" %>
<% end %>
生成されるHTML
生成されるHTMLは以下のようになります。
<div class="flash flash-success">アカウントを作成しました。</div>
flashのメソッド
以下に詳しいのですが、特徴的なものをいくつか書きます。
flash.now
flash.now
は、そのアクションでflashを消す際に用います。
たとえば、先ほど以下のように書きました。
flash[:success] = 'アカウントを作成しました。'
普通はこれでよいのですが、たとえばrender
でビューを指定すると、アクションが進む訳ではないので、flash
が2回表示されることになります。
これを避けるために、flash.now
を用います。
flash.now[:success] = 'アカウントを作成しました。'
flash.keep
これは、flash
を保持したいときに用います。
通常であれば消えるところを、flash.keep
を用いることによって、次のアクションまで保持することができます。
flash.keep # flash 全体を保持する
flash.keep(:success) # :success のみを保持する
flash.discard
discard
は「破棄する」という意味ですが、その名のとおりflash
を破棄します。
flash.discard # flash 全体を破棄する
flash.discard(:success) # :success のみを破棄する
おわりに
flash
を効果的に使うとユーザビリティが向上すると思うので、しっかり使いこなせるようになります。。。
Ruby on Railsでユーザ登録/ログイン機能を自作する方法
先日Ruby on Railsチュートリアルを完了しました。 疲れた。。。
それで、ちょっとごちゃごちゃしてきたので、ユーザ登録〜ログイン機能までの実装のみを抜き出して自分なりに整理してみました。
ちなみに今回長くなるのでバリデーションやテストは省略しています。
準備
ディレクトリ構成
編集するファイルは以下のような感じです。
app/
controllers/
application_controller.rb
sessions_controller.rb
users_controller.rb
helpers/
sessions_helper.rb
models/
user.rb
views/
layouts/
application.html.erb
sessions/
new.html.erb
users/
edit.html.erb
index.html.erb
new.html.erb
show.html.erb
config/
routes.rb
テーブル
テーブルは以下のようにUser
モデルのみ利用します。
ちなみにイカの頭以下の図はRails ERDを用いて生成しました(なんつー誤字だ……)。
上記を準備するコマンド
とりあえずパパパパーンッと上記を準備します。
$ rails new Login
$ cd Login
$ rails g controller Sessions
$ rails g controller Users
$ rails g model User name:string email:string password_digest:string remember_token:string
$ touch app/views/sessions/new.html.erb
$ touch app/views/users/edit.html.erb
$ touch app/views/users/index.html.erb
$ touch app/views/users/show.html.erb
$ touch app/views/users/new.html.erb
$ bundle exec rake db:migrate
$ vi Gemfile # bcrypt-ruby をコメントアウト
$ bundle install
各ファイル
各ファイルの中身と、コメントをちらほら記します。 それではディレクトリ構成の上から順に(適当)。
application_controller.rb
コントローラ全体からSessionsHelper
を呼び出すことになるので、ここでこれをinclude
しておきます。
以上。
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
include SessionsHelper
end
sessions_controller.rb
Sessionコントローラはその名のとおりセッションを司ります。
RESTな考えでいくと、new
はログインフォーム、create
はログイン処理、destroy
はログアウトですね。
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
sign_in user
redirect_to user
else
render 'new'
end
end
def destroy
sign_out
redirect_to root_url
end
end
users_controller.rb
こちらはよくあるUserコントローラです。
今回はedit
とupdate
のみ権限を課しているので、before_action
でそれっぽいことをします。
class UsersController < ApplicationController
before_action :signed_in_user, only: [:edit, :update]
before_action :correct_user, only: [:edit, :update]
def index
@users = User.all
end
def show
@user = User.find(params[:id])
end
def new
@user = User.new
end
def edit
end
def create
@user = User.new(user_params)
if @user.save
sign_in @user
redirect_to @user
else
render 'new'
end
end
def update
if @user.update_attributes(user_params)
redirect_to @user
else
render 'edit'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, :password_confirmation)
end
def correct_user
@user = User.find(params[:id])
redirect_to root_url unless current_user?(@user)
end
end
sessions_helper.rb
今回のメインっぽい部分。
ログイン時にトークンを更新して、以降は該当するトークンをfind
してcurrent_user
にセットします。
この辺でインスタンスメソッドとクラスメソッドの理解がだいぶ深まりました。。。
module SessionsHelper
def sign_in(user)
remember_token = User.new_remember_token
cookies.permanent[:remember_token] = remember_token
user.update_attribute(:remember_token, User.encrypt(remember_token))
self.current_user = user
end
def sign_out
self.current_user = nil
cookies.delete(:remember_token)
end
def current_user=(user)
@current_user = user
end
def current_user
remember_token = User.encrypt(cookies[:remember_token])
@current_user ||= User.find_by(remember_token: remember_token)
end
def current_user?(user)
user == current_user
end
def signed_in?
!current_user.nil?
end
def signed_in_user
redirect_to signin_url unless signed_in?
end
end
user.rb
Userモデル。
このhas_secure_password
がいろいろよしなにやってくれます。
というかやってくれ過ぎでしょ。。。
後ろ2つはインスタンスに関係ない部分なのでクラスメソッドとして実装しています。
class User < ActiveRecord::Base
before_save { email.downcase! }
has_secure_password
def self.new_remember_token
SecureRandom.urlsafe_base64
end
def self.encrypt(token)
Digest::SHA1.hexdigest(token.to_s)
end
end
application.html.erb
ここからはただのViewです。
application_controller.rb
でSessionsHelper
をinclude
したので、signed_in?
で便利に振り分けることができます。
<!DOCTYPE html>
<html>
<head>
<title>Login</title>
<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
<%= javascript_include_tag "application", "data-turbolinks-track" => true %>
<%= csrf_meta_tags %>
</head>
<body>
<ul>
<li><%= link_to 'ユーザ一覧', users_path %></li>
<% if signed_in? %>
<li><%= link_to 'プロフィール', current_user %></li>
<li><%= link_to 'ログアウト', signout_path, method: :delete %></li>
<% else %>
<li><%= link_to 'ログイン', signin_path %></li>
<li><%= link_to 'アカウント作成', signup_path %></li>
<% end %>
</ul>
<hr>
<%= yield %>
</body>
</html>
sessions/new.html.erb
何の変哲もないViewです。
Sessionはモデルがないので、form_for
がちょっと特殊です。
<h1>ログイン</h1>
<%= form_for(:session, url: sessions_path) do |f| %>
<table border="1">
<tr>
<th><%= f.label :email, 'メールアドレス' %></th>
<td><%= f.text_field :email %></td>
</tr>
<tr>
<th><%= f.label :password, 'パスワード' %></th>
<td><%= f.password_field :password %></td>
</tr>
</table>
<%= f.submit 'ログインする' %>
<% end %>
users/edit.html.erb
これもただのViewです。
<h1>アカウント編集</h1>
<%= form_for(@user) do |f| %>
<table border="1">
<tr>
<th><%= f.label :name, '名前' %></th>
<td><%= f.text_field :name %></td>
</tr>
<tr>
<th><%= f.label :email, 'メールアドレス' %></th>
<td><%= f.text_field :email %></td>
</tr>
<tr>
<th><%= f.label :password, 'パスワード' %></th>
<td><%= f.password_field :password %></td>
</tr>
<tr>
<th><%= f.label :password_confirmation, '確認' %></th>
<td><%= f.password_field :password_confirmation %></td>
</tr>
</table>
<%= f.submit 'アカウントを編集する' %>
<% end %>
users/index.html.erb
ただ一覧を表示するだけです。
link_to
でuser
を渡すとshow
してくれるらしいです。
この辺のRailsのノリについていけない。。。
<h1>ユーザ一覧</h1>
<ul>
<% @users.each do |user| %>
<li><%= link_to user.name, user %></li>
<% end %>
</ul>
users/new.html.erb
そのうちPartialの記事も書きます。。。
<h1>アカウント作成</h1>
<%= form_for(@user) do |f| %>
<table border="1">
<tr>
<th><%= f.label :name, '名前' %></th>
<td><%= f.text_field :name %></td>
</tr>
<tr>
<th><%= f.label :email, 'メールアドレス' %></th>
<td><%= f.text_field :email %></td>
</tr>
<tr>
<th><%= f.label :password, 'パスワード' %></th>
<td><%= f.password_field :password %></td>
</tr>
<tr>
<th><%= f.label :password_confirmation, '確認' %></th>
<td><%= f.password_field :password_confirmation %></td>
</tr>
</table>
<%= f.submit 'アカウントを作成する' %>
<% end %>
users/show.html.erb
自分のプロフィールページのみ、編集リンクを表示するようにします。
<h1>ユーザ情報</h1>
<table border="1">
<tr>
<th>名前</th>
<td><%= @user.name %></td>
</tr>
<tr>
<th>メールアドレス</th>
<td><%= @user.email %></td>
</tr>
</table>
<% if current_user?(@user) %>
<p><%= link_to '編集', edit_user_path %></p>
<% end %>
routes.rb
この辺は無抵抗に書きます。
Login::Application.routes.draw do
resources :users, only: [:index, :show, :new, :edit, :create, :update]
resources :sessions, only: [:new, :create, :destroy]
match 'signup', to: 'users#new', via: 'get'
match 'signin', to: 'sessions#new', via: 'get'
match 'signout', to: 'sessions#destroy', via: 'delete'
root 'users#index'
end
おわりに
以上です。
便利なGemにdevise
とかcancan
とかあるらしいですが、私みたいなシロウトはまず自分で書いて理解しなければならないと思うのです。。。
Rubyのor equal演算子の使い方
Rubyには、以下のような書き方があるらしいです。
a ||= b
これはor equal演算子と呼ぶらしいのですが、これについて動きなどを調べてみたのでメモします。
or equal演算子について
コード例
以下のコードについて、or_equal(name)
の部分がそれにあたります。
これはつまり、インスタンス変数@name
が存在すればそれを返し、なければ引数のname
を返す、という動きになります。
class Foo
def name=(name)
@name = name
end
def or_equal(name)
@name ||= name
end
end
インスタンス変数がない場合
name=(name)
でインスタンス変数を生成しない場合、そのままor_equal(name)
の引数が返ります。
foo = Foo.new
p foo.or_equal('foo') # => "foo"
インスタンス変数がある場合
name=(name)
でインスタンス変数を生成した場合、or_equal(name)
の引数によらず、@name
が返されます。
foo.name = 'bar'
p foo.or_equal('foo') # => "bar"
おわりに
使い方が分かりやすいですし、使いこなせればコードがシンプルになると思うので、積極的に使っていこうと思います。 まずはコードを書く時間を確保しよう。。。
Rubyのイコールを用いた要素代入関数の書き方
Rubyでは、関数名にイコールを使えると風のうわさで聞きました。 その書き方についてのメモです。
要素代入関数とは
コード例
以下のようなコードがあるとします。
この中のname=
を要素代入関数というらしいです。
要素代入関数とは、簡単にいうと「代入演算子(=
)を用いて引数を渡せる関数」だと思います。
たぶん……。
class Foo
def name=(name)
@name = name
end
def name
@name
end
end
代入する
上のコード例について、インスタンスの生成〜代入までは以下のように書きます。
こうすると、@name
というインスタンス変数が生成されます。
foo = Foo.new
foo.name = 'foo'
インスタンス変数を表示する
で、こうすると、インスタンス変数が表示される、という訳です。
同じname
という名前を持つ関数なのに、=
の有無で違うメソッドが呼び出されるんですね(当たり前)。
p foo.name # => "foo"
おわりに
Rubyのインスタンスメソッドとクラスメソッドの使い分けについて
Ruby on Railsの学習中にModel周りで悩んでしまったのでメモ。
ひとことで言うと「クラス内のdef self.〜
ってなんぞ?」という話についてです。
インスタンスメソッドとクラスメソッドの使い分け
コード例
たとえば、以下のようなコードがあるとします。
このとき、self.bar
がクラスメソッド、baz
がインスタンスメソッドとなります。
class Foo
def self.bar
p 'bar'
end
def baz
p 'baz'
end
end
インスタンスメソッド
インスタンスメソッドとは、new
でインスタンスを生成したときにのみ呼び出せるメソッドです。
生成するインスタンスによって異なる結果を返す場合に用いる、といったところでしょうか(となると上のコード例は微妙ですね……)。
foo = Foo.new
foo.bar # => undefined method `bar' for #<Foo:0x007f7f6413aae8> (NoMethodError)
foo.baz # => "baz"
クラスメソッド
クラスメソッドとは、ダブルコロン(::
)によってクラス経由で直接呼び出すメソッドです。
返る結果がインスタンスによらず一定の場合に用いる、という感じだと思います。
Foo::bar # => "bar"
Foo::baz # => undefined method `baz' for Foo:Class (NoMethodError)
また、インスタンスメソッドからクラスメソッドを呼び出すには、以下のように書きます。
def baz
self.class.bar # => "bar"
end
おわりに
昔Javaを少し学んでいたときに曖昧にしていたのですが、改めて学習したことで理解が深まりました。 さて、次の記事を書き始めます。。。
参考記事
Rubyの関数名の感嘆符「!」と疑問符「?」の意味
Ruby on Railsをお勉強中なのです。
ところどころ出てくるメソッドの!
と?
について、すこし整理してみます。
感嘆符「!」
これは破壊的メソッドを表します。 Rubyのリファレンスには、以下のようにあります。
「!」はメソッド名の一部です。慣用的に、 同名の(! の無い)メソッドに比べてより破壊的な作用をもつメソッド(例: tr と tr!)で使われます。
例えば、sort
関数の場合、以下のようになります。
array = [3, 1, 5, 2, 4]
a = array.sort
p a # => [1, 2, 3, 4, 5]
p array # => [3, 1, 5, 2, 4]
b = array.sort!
p b # => [1, 2, 3, 4, 5]
p array # => [1, 2, 3, 4, 5]
一言で言うと「メソッドを呼びだしたオブジェクト自身が影響を受ける」、という感じでしょうか。。。
疑問符「?」
これは真偽値を返すメソッドを表します。 Rubyのリファレンスには、以下のようにあります。
この場合の「?」はメソッド名の一部分です。 慣用的に、真偽値を返すタイプのメソッドを示すために使われます。
これは直感的に分かりやすいです。
p @foo.nil? # => true
@foo = 'foo'
p @foo.nil? # => false
おわりに
自分でメソッドを定義するときは、この2点をしっかりおさえて作るようにします。。。