Userモデルで必要な必要な検証を下表に示します。
id | integer | - |
name | string | 必須、一意,50文字以内 |
★password | string | 必須,6文字以上40文字以内 |
encrypted_password | string | |
salt | string | - |
admin | boolean | - |
created_at | datetime | - |
updated_at | datetime | - |
ただしUserオブジェクト生成時では必須なので必須指定としています。
この辺りの事情は前回の解説をみてください。
というわけでまずはこの「name」と「password」に対する検証がちゃんと動作しているかを確認するテストコードを作成します。
モデル生成時に「spec/model/user_spec.rb」というファイルが出来ているのでこれを日以下のように編集します。
# -*- coding: utf-8 -*-最初の「before」で正しいデータを用意しておいて、各検証コードで「merge」メソッドを使って検証対象のフィールドをいじりながら検証しています。
require 'spec_helper'
describe User do
before(:each) do
@attr = {
:name => "hogetarou",
:password => "foobar",
:password_confirmation => "foobar",
}
end
it "名前がないと検証NGであること" do
user = User.new(@attr.merge(:name => ""))
user.should_not be_valid
end
it "名前が50文字を超えると検証NGであること" do
long_name = "a"* 51
user = User.new(@attr.merge(:name => long_name))
user.should_not be_valid
end
it "パスワードがないと検証NGであること" do
user = User.new(@attr.merge(:password => "", :password_confirmation => ""))
user.should_not be_valid
end
it "パスワードと確認用が一致しないと検証NGであること" do
user = User.new(@attr.merge(:password_confirmation => "invalid"))
user.should_not be_valid
end
it "パスワードが6文字未満だと検証NGであること" do
short = "a" * 5
user = User.new(@attr.merge(:password => short, :password_confirmation => short))
user.should_not be_valid
end
it "パスワードが41文字以上だと検証NGであること" do
long = "a" * 41
user = User.new(@attr.merge(:password => long, :password_confirmation => long))
user.should_not be_valid
end
it "全て満たしたとき検証OKであること" do
user = User.new(@attr)
user.should be_valid
end
it "ユーザー名が重複したらダメ" do
User.create!(@attr)
user = User.new(@attr)
user.should_not be_valid
end
end
rspec spec/user_spec.rbと実行すると全てNGで返ってきます。
ちなみに「:password_confirmationなんて無いぞ!」というエラーばかりだと思います。
今はこれでOK。
ではモデルの方を作ってみましょう。
「app/model/user.rb」を以下のように編集します。
モデル生成時に指定したフィールドはActiveModelが自動で生成してくれますが、passwordフィールドは生成していません(何度も言いますがDBに格納するのは暗号化した「encrypted_password」です)。なのでここでpasswordというフィールドを定義しているわけです。
次のattr_accessibleは指定した以外のフィールドを保護するメソッドです。
例えば前々回作成したモデルには管理者を示す「admin」フィールドが存在しますが、ユーザーインターフェース上に「admin」を示す入力項目が無かったとしても、悪意あるユーザーがPOSTメソッドで「admin=true」としたパラメータを送りつけて来た場合に、無条件に「update_attributes」メソッドなどでUserオブジェクトを更新してしまうと、そのユーザーに管理者権限を与えてしまうことになります。
attr_accessibleではコンストラクタや「update_attributes」メソッドで更新可能なフィールドを指定し、指定されなかったフィールドについては更新を許可しません。
ちょっと確認してみましょう。
「rails console --sandbox」としてコンソールを起動してみます。
「attr_accessible」と似たものに「attr_protected」があり、こちらは逆に指定したフィールドだけを保護するメソッドです。
長期的にはフィールドが増加したときに「指定し忘れ」が起きてしまうことを考えると「attr_accessible」にしておいた方が安全でしょう。
以降は「validates」メソッドで必要な検証を定義しています。
「password」に対して「:confirmation => true」としています。
こうすることで「password_confirmation」というフィールドが自動で付加されて、「password」と一致していることをチェックしてくれます。
さて、user.rbを保存したらもう一度RSpecを実行してみましょう。
今度は全て成功するハズです。
今はこれでOK。
ではモデルの方を作ってみましょう。
「app/model/user.rb」を以下のように編集します。
class User < ActiveRecord::Baseまず1行目の「attr_accessor」ですが、これはRailsではなくRubyの仕様で、渡したパラメータのgetterとsetterを生成してくれるメソッドです。
attr_accessor :password
attr_accessible :name, :password, :password_confirmation
validates :name, :presence => true, :uniqueness => true, :length => { :maximum => 50 }
validates :password, :presence => true, :confirmation => true, :length => { :within => 6..40}
end
モデル生成時に指定したフィールドはActiveModelが自動で生成してくれますが、passwordフィールドは生成していません(何度も言いますがDBに格納するのは暗号化した「encrypted_password」です)。なのでここでpasswordというフィールドを定義しているわけです。
次のattr_accessibleは指定した以外のフィールドを保護するメソッドです。
例えば前々回作成したモデルには管理者を示す「admin」フィールドが存在しますが、ユーザーインターフェース上に「admin」を示す入力項目が無かったとしても、悪意あるユーザーがPOSTメソッドで「admin=true」としたパラメータを送りつけて来た場合に、無条件に「update_attributes」メソッドなどでUserオブジェクトを更新してしまうと、そのユーザーに管理者権限を与えてしまうことになります。
attr_accessibleではコンストラクタや「update_attributes」メソッドで更新可能なフィールドを指定し、指定されなかったフィールドについては更新を許可しません。
ちょっと確認してみましょう。
「rails console --sandbox」としてコンソールを起動してみます。
rails console --sandboxこのように「:admin => true」と指定しているのに結果は「admin: false」となっていることからちゃんと保護されていることが分かります。
>> user = User.new(:name => "hogehoge", :password => "foobar", :password_confirmation => "foobar", :admin => true)
=> #
「attr_accessible」と似たものに「attr_protected」があり、こちらは逆に指定したフィールドだけを保護するメソッドです。
長期的にはフィールドが増加したときに「指定し忘れ」が起きてしまうことを考えると「attr_accessible」にしておいた方が安全でしょう。
以降は「validates」メソッドで必要な検証を定義しています。
「password」に対して「:confirmation => true」としています。
こうすることで「password_confirmation」というフィールドが自動で付加されて、「password」と一致していることをチェックしてくれます。
さて、user.rbを保存したらもう一度RSpecを実行してみましょう。
今度は全て成功するハズです。
0 件のコメント:
コメントを投稿