Ruby on Rails
入門(第
2
回)
コンピュータ系実験
IV
資料
担当:冨永
1
検証
モデルがデータベースに格納されるとき(.saveが呼ばれたときなど)に、属性値が妥当な値であるか確認す
るための機能がモデルのクラスにある。これを検証validationと言う。検証を使うと、利用者がおかしな入力を
した時の処理がしやすい。
Productモデルをデータベースに格納する際に、品名と説明が必ずあり、値段が数値かつ0.01以上であること
を保証するには、app/models/product.rbを以下のようにする。
class Product < ActiveRecord::Base
validates :name, :description, presence: true
validates :price, numericality: {greater_than_or_equal_to: 1} end
このような制約の機能はRDBMSもたいていは持っている。RDBMSのこの機能は、データベース内のデー
タが正しいことを保証するために用いられる。
演習1 Userモデルに検証を加えよう
Userモデルに、ユーザID、パスワード、メールアドレスの全ての属性があるべしという検証を追加しよう。 New画面やEdit画面で、検証がうまく働いているか確認しよう。
2
セッション
ウェブブラウザがウェブアプリケーションに接続し、そこから続けてそのアプリケーションを使い続けるとき のブラウザとサーバの一連のやりとりをセッションsessionと呼ぶ。
2.1
Ruby on Rails
のセッション
ウェブの仕組みは状態を持たないstatelessので、セッションを維持するための何らかの仕組みが必要である。
Ruby on Railsではクッキーを使ってセッションを維持している。
さらに、セッション情報はデータベースに格納した方が何かと都合がいい。以下の設定をするとセッション情 報をデータベースに格納するようになる。
1. rails generate session migrationを実行する。セッションを格納するテーブルを作るマイグレー ションが生成される。
2. rake db:migrateを実行し、そのマイグレーションを実行する。
3. config/initializers/session store.rbファイルを編集し、まず3行目を次のようにコメントにする(先頭に#を
# Shop::Application.config.session_store :cookie_store, key: ’_shop_session’
さらに、下の方にある次の行をコメントでなく有効にする(先頭の#を取り除く)。
Shop::Application.config.session_store :active_record_store
4. サーバを一度停止し(コントロールC)、再起動する(rails server)。
ブラウザでアプリケーションにアクセスしてから、データベース内にあるsessionsテーブルの内容を見て、
セッションの情報らしきレコードがあることを確認しよう。
$ sqlite3 -line db/development.sqlite3
sqlite> select * from sessions;
2.2
セッションへのデータの格納
個々のセッションは各々データを持つことができる。コントローラはsessionという名のハッシュを使える。
ここにデータを入れると、そのデータは同じセッションで(のみ)使うことができる。 最後に登録した商品の名前を常に表示するために、以下のようにしてみよう。
1. sessionハッシュに「:lastitem」というキーで、最後に登録した商品の名前を入れる。そのために以下のよ
うにapp/controllers/products controller.rbのメソッドcreateに1行を加える。
def create
@product = Product.new(params[:product])
respond_to do |format|
if @product.save
session[:lastitem] = @product.name
format.html { redirect_to @product, notice: ’Product was ...
2. レイアウトファイルapp/views/layouts/products.html.erbを以下のように新しく作り、その商品名がProduct
モデルを表示する足場で常に表示されるようにする。
<%= yield %>
<p>Last created item: <%= session[:lastitem] %></p>
商品を何度か登録して、最後に登録した商品名が常に表示されるか確認しよう。
2.3
ログイン機能の実装
セッションが維持できると、ログイン/ログアウトの機能を実装できる。
• セッション開始時にはsessionハッシュは空である。これによって、ログインしていない状態を判断できる。
• ユーザIDとパスワードによって認証したら、sessionハッシュに特定のデータを入れる。例えばsession[:uid]
に、認証したユーザに対応するUserモデルのインスタンスuが持つu.idを入れるなどする。
• ログアウトしたら、session[:uid] = nil、session.clearなどをする。
Userモデルには既にユーザIDとパスワードの属性があるので、後はコントローラとビューのコードを書けば
2.3.1 コントローラ
データを直接いじるためのProductsControllerやUsersControllerとは別に、利用者が使うネット商店のための
コントローラStoreControllerを作成する。
まずrails generateを用いてコントローラの大枠と、それに対応するビューのテンプレートを生成する。
$ rails generate controller store index login logout
これにより、StoreControllerクラスの定義ファイルapp/controllers/store controller.rbが生成され、その中にindex, login, logoutというメソッド(これらがアクションになる)の外枠が定義される。また同時に、それらのアクショ
ンのためのテンプレートも生成される。
config/route.rbに以下の行が加わり、GETに対する経路が設定される。
get "store/index"
get "store/login"
get "store/logout"
このファイルを編集する。loginとlogoutについてPOSTの経路を加え、さらにhttp://localhost:3000/ store/にアクセスした時にindexが呼ばれるようにするために、以下の3行を上の行の後に追加する(順序は
あまり関係ないが)。
get "store", to: "store#index" post "store/login"
post "store/logout"
次にstore controller.rbを編集し、StoreControllerクラスを以下のように定義する。
class StoreController < ApplicationController
def index
if session[:uid].nil?
redirect_to store_login_url
else
@products = Product.all
respond_to do |format|
format.html
end
end
end
def login
session[:uid] = nil
if params[:name]
u = do_auth(params)
if u
# 認証成功;u は User のインスタンス
session[:uid] = u.id
redirect_to store_index_url
# 認証失敗;再入力
respond_to do |format|
format.html
end
end
else
session.clear
# ログインのための入力欄を表示
respond_to do |format|
format.html
end
end
end
def logout
session.clear
redirect_to store_login_url
end
private
def do_auth(params)
u = User.find_by_name(params[:name])
if u and u.password == params[:password]
# ユーザ名登録があって、パスワードが一致
u
else
nil
end
end
end
2.3.2 ビュー
rails generate controllerを実行したことによって、テンプレートファイルが3つ、app/views/store/の下に生成
される:index.html.erb, login.html.erb, logout.html.erb。
index.html.erb これは店のトップページになる。後の演習で作成する。
login.html.erb ログインのためのフォームを表示し、入力されたものを/store/loginに送るページにする。生成
された内容を消して、以下の内容とする。
<%= form_tag do %>
<%= label_tag :name, ’Name:’ %>
<%= text_field_tag :name, params[:name] %><br />
<%= text_field_tag :password, params[:password] %><br />
<%= submit_tag "login" %>
<% end %>
logout.html.erb ログアウトのページは表示されないので、このファイルは不要である。
2.3.3 確認
以下の動作を確認しよう。
1. サーバを一度停止し(コントロールC)、再起動する(rails server)。
2. http://localhost:3000/store にアクセスすると、ログイン画面http://localhost:3000/ store/loginに飛ばされて、ユーザIDとパスワードの入力を求められる。
3. ログイン画面で登録されていないユーザIDや誤ったパスワードを入力すると、ログイン画面に戻る。
4. ログイン画面で正しいユーザIDとパスワード(root, naisho)を入力すると、indexのためのテンプレート
(まだ生成したままのもの)が表示される。
5. その後、ブラウザのアドレスバーに直接http://localhost:3000/store/logoutを入力すると、 ログイン画面に飛ばされる。その状態からhttp://localhost:3000/storeにアクセスすると、ログ イン画面に戻らされる(つまりログアウトしている)。
演習2 レイアウトファイルでユーザIDを表示しよう
ログインしている状態のときはいつも画面にそれが表示されている方が利用者にとって便利である。ログイ ン中は「ログイン中:hユーザIDiさん」と表示され、ログインしていない時は「ゲストさん」と表示される
ように、レイアウトファイルapp/views/layouts/application.html.erbを変更してみよう。
演習3 ログアウトボタンをつけよう
レイアウトファイルを使って、ログアウトするためのボタンを表示しよう。レイアウトやテンプレートの中に
<%= button_to ’foobar’, xxx_yyy_path %>
と書くと、foobarという文字列が表示されたボタンが現われ、それを利用者が押すとxxxコントローラのアク
ションyyyが呼び出される。
ボタンではなくアンカーを表示するには、button toではなくlink toを使う。使い方は同様である。
3
フラッシュ
あるアクションの結果を利用者に知らせたいが、そのアクションがredirect toなどによってブラウザを別の
ページに飛ばす場合には、別のコントローラのインスタンスが処理するので、前のアクションからインスタンス 変数を使って結果を渡せない。
このような時に便利なのがフラッシュflash である。フラッシュはハッシュで1、その内容は次のブラウザの要 求を処理するアクションまで有効である。それが終わると値が無効になる。
例えばflash[:notice]にエラーメッセージを入れておき、次のページで利用者が直前に行なった操作の結果を画
面に表示したりできる。
flashの代わりにflash.nowを使うと(「flash.now[:notice] =メッセージ」などとして使う)現在のアクションの
処理だけで有効なデータを保持できる。
演習4 ログアウトの表示
フラッシュとレイアウトを使って、以下のようにしてみよう。
• ログインせずにhttp://localhost:3000/store/にアクセスしてログイン画面に飛ばされたとき に、「ログインして下さい」と表示される。
• ログイン画面で誤ったユーザIDやパスワードを入力すると「ユーザIDまたはパスワードが違います」
と表示される。
• ログアウトをした直後のログイン画面で「ログアウトしました」と表示される。
演習5 商品一覧の表示
indexアクションでは@productsに商品の配列を入れている。これをテンプレートindex.html.erbで使って、
ネット商店風に商品の一覧を表示してみよう。
4
おわりに
Ruby on Railsはウェブアプリケーション自体の開発(特にその初期段階)を支援するが、ウェブアプリケー
ションによるサービスを実際に提供するためには、ここで一部を見たアプリケーション自体の開発の他にも、 ウェブサイトのデザイン、サーバ構成の設計、データベースの一貫性保持、アプリケーションとRDBMSの配