読者です 読者をやめる 読者になる 読者になる

techium

このブログは何かに追われないと頑張れない人たちが週一更新をノルマに技術情報を発信するブログです。もし何か調査して欲しい内容がありましたら、@kobashinG or @muchiki0226 までいただけますと気が向いたら調査するかもしれません。

Rails Tutorial 5 Modeling users

Chapter 6/ Modeling users | Ruby on Rails Tutorial (Rails 5) | Softcover.io

ユーザー登録ページを作っていく。
ユーザー用のデータモデルの作成と、データを保存する手段の確保について学ぶ。

データベースの移行

generate modelというコマンドを使ってnameやemailといった属性を付けたUserモデルを作成する。

$ rails generate model User name:string email:string
Running via Spring preloader in process 225848
      invoke  active_record
      create    db/migrate/20161023011602_create_users.rb
      create    app/models/user.rb
      invoke    test_unit
      create      test/models/user_test.rb
      create      test/fixtures/users.yml

これでマイグレーションファイルが出来上がる。
マイグレーションを適用することでSQLiteデータベースが出来上がる。

$ rails db:migrate
== 20161023011602 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0019s
== 20161023011602 CreateUsers: migrated (0.0021s) =============================

以下を実行するだけでマイグレーションの取り消しも可能。

$ rails db:rollback
== 20161023011602 CreateUsers: reverting ======================================
-- drop_table(:users)
   -> 0.0015s
== 20161023011602 CreateUsers: reverted (0.0041s) =============================

もう一度マイグレーションを適用すれば元に戻る。

$ rails db:migrate
== 20161023011602 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0093s
== 20161023011602 CreateUsers: migrated (0.0094s) =============================

modelファイル

app/models/ディレクトリにuser.rbというファイルができている。

class User < ApplicationRecord
end

Rails 4.2 ではUserクラスはActiveRecord::Baseを継承しているが、Rails 5 では ApplicationRecordを継承している。(ApplicationRecord は ActiveRecord::Base を継承している。)

ということでここからActiveRecord::Baseについて学んでいく。

ユーザオブジェクトの作成

Railsコンソールを使用してデータモデルを調べていくが、コンソールをサンドボックスモードで起動すればデータベースを変更することなく操作を試すことができるそうな。

$ rails console --sandbox
Running via Spring preloader in process 451803
Loading development environment in sandbox (Rails 5.0.0.1)
Any modifications you make will be rolled back on exit
>> 

今回はRailsコンソール起動時にRailsの環境を自動的に読み込み、モデルも自動的に読み込むため以下を実行するだけで新しいユーザオブジェクトができる。

>> User.new
=> #<User id: nil, name: nil, email: nil, created_at: nil, updated_at: nil>

引数なしだと全ての属性はnilを返す。
Active Recordの設計に基づき、オブジェクトの属性を設定するための初期化ハッシュ (hash) を引数に取ることもできる。

>> user = User.new(name: "Michael Hartl", email: "mhartl@example.com")
=> #<User id: nil, name: "Michael Hartl", email: "mhartl@example.com", created_at: nil, updated_at: nil>

「有効性 (Validity)」を確認することで、データベースにデータがあるかどうかによらず、オブジェクトが有効かどうかを確認することができる。

>> user.valid?
=> true

saveメソッドを呼び出してデータベースにUserオブジェクトを保存する。

>> user.save
   (0.1ms)  SAVEPOINT active_record_1
  SQL (0.5ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Michael Hartl"], ["email", "mhartl@example.com"], ["created_at", 2016-10-24 00:41:30 UTC], ["updated_at", 2016-10-24 00:41:30 UTC]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> true

save を実行することで初めて created_at や updated_at が更新される。
new, save をいっぺんに実行する create

>> User.create(name: "A Nother", email: "another@example.org")
   (5.1ms)  SAVEPOINT active_record_1
  SQL (3.2ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "A Nother"], ["email", "another@example.org"], ["created_at", 2016-10-24 21:40:33 UTC], ["updated_at", 2016-10-24 21:40:33 UTC]]   (0.1ms)  RELEASE SAVEPOINT active_record_1=> #<User id: 2, name: "A Nother", email: "another@example.org", created_at: "2016-10-24 21:40:33", updated_at: "2016-10-24 21:40:33">

create はオブジェクト自身を返すのでそれを変数に代入できる。
destroycreate の逆。

>> foo = User.create(name: "Foo", email: "foo@bar.com")
   (0.1ms)  SAVEPOINT active_record_1
  SQL (0.1ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Foo"], ["email", "foo@bar.com"], ["created_at", 2016-10-24 21:41:33 UTC], ["updated_at", 2016-10-24 21:41:33 UTC]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2016-10-24 21:41:33", updated_at: "2016-10-24 21:41:33">
>> foo
=> #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2016-10-24 21:41:33", updated_at: "2016-10-24 21:41:33">
>> foo.destroy
   (0.1ms)  SAVEPOINT active_record_1
  SQL (0.1ms)  DELETE FROM "users" WHERE "users"."id" = ?  [["id", 3]]
   (0.0ms)  RELEASE SAVEPOINT active_record_1
=> #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2016-10-24 21:41:33", updated_at: "2016-10-24 21:41:33">
>> foo
=> #<User id: 3, name: "Foo", email: "foo@bar.com", created_at: "2016-10-24 21:41:33", updated_at: "2016-10-24 21:41:33">
>> foo.valid?
=> true

find でユーザの id による検索が可能。

>> User.find(1)  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-10-24 00:41:30", updated_at: "2016-10-24 00:41:30">

find_by で id 以外の属性を使った検索が可能。

>> User.find_by(email: "mhartl@example.com")  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "mhartl@example.com"], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-10-24 00:41:30", updated_at: "2016-10-24 00:41:30">

ユーザーオブジェクトの更新には、属性を個別に代入するか、update_attributes を使用する。
前者は別途 save が必要で、後者は保存までをまとめて実行する。

>> user.email = "mhartl@example.net"
=> "mhartl@example.net"
>> user.save
   (0.1ms)  SAVEPOINT active_record_1
  SQL (0.1ms)  UPDATE "users" SET "email" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["email", "mhartl@example.net"], ["updated_at", 2016-10-24 22:27:09 UTC], ["id", 1]]
   (0.0ms)  RELEASE SAVEPOINT active_record_1
=> true

>> user.update_attributes(name: "The Dude", email: "dude@abides.org")
   (0.1ms)  SAVEPOINT active_record_1
  SQL (1.6ms)  UPDATE "users" SET "email" = ?, "updated_at" = ?, "name" = ? WHERE "users"."id" = ?  [["email", "dude@abides.org"], ["updated_at", 2016-10-26 03:26:00 UTC], ["name", "The Dude"], ["id", 1]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1

ユーザの検証

モデルの属性が取れる値に制約を与えて、意図しない値の混入を防ぐ。
test/models/user_test.rb が自動でできているので書き換えていく。

require 'test_helper'

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end

  test "should be valid" do
    assert @user.valid?
  end
end

$ rails test:models でモデルに関するテストのみ実行できる。

$ rails test:models
Started with run options --seed 50541

  1/1: [=========================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.02565s
1 tests, 1 assertions, 0 failures, 0 errors, 0 skips

Validating presence

存在性を検証する。

User の name が空だとそのユーザは有効でない、とするためのテストを書く。

require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end
  
  test "should be valid" do
    assert @user.valid?
  end
  
  test "name should be present" do
    @user.name = "     "
    assert_not @user.valid?
  end
end

assert_not で有効でないことを確認することができる。この時点ではテストは失敗するので成功になるようコードを修正していく。

と言ってもvalidatesメソッドの引数にpresence: trueを与えるだけ。 app/models/user.rb

class User < ApplicationRecord
  validates :name, presence: true
end
$ rails c --sandbox
Running via Spring preloader in process 85201
Loading development environment in sandbox (Rails 5.0.0.1)
Any modifications you make will be rolled back on exit
>> user = User.new(name: "", email: "mhartl@example.com")
=> #<User id: nil, name: "", email: "mhartl@example.com", created_at: nil, updated_at: nil>
>> user.valid?
=> false

errors オブジェクトを見ればどの検証が失敗したのかがわかる。

>> user.errors.full_messages
=> ["Name can't be blank"]

save は自然と失敗する。

>> user.save
   (0.1ms)  SAVEPOINT active_record_1
   (0.0ms)  ROLLBACK TO SAVEPOINT active_record_1
=> false

email 属性についても同様の修正を加えていく。
test/models/user_test.rb

require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end
  
  test "should be valid" do
    assert @user.valid?
  end
  
  test "name should be present" do
    @user.name = "     "
    assert_not @user.valid?
  end
  
  test "email should be present" do
    @user.email = "     "
    assert_not @user.valid?
  end
end

Length validation

各属性の長さを制限する。
名前は適当に50を上限とし、メールアドレスは、ほとんどのデータベースで文字列の上限を255としていることから255を上限とする。

test/models/user_test.rb

require 'test_helper'

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end
  .
  .
  .
  test "name should not be too long" do
    @user.name = "a" * 51
    assert_not @user.valid?
  end

  test "email should not be too long" do
    @user.email = "a" * 244 + "@example.com"
    assert_not @user.valid?
  end
end

app/models/user.rb

class User < ApplicationRecord
  validates :name,  presence: true, length: { maximum: 50 }
  validates :email, presence: true, length: { maximum: 255 }
end

Format validation

メールアドレスのフォーマット検証を行う。

有効なメールアドレスと無効なメールアドレスをいくつか用意して、バリデーション内のエラーを検知していく。

まずは有効なメールアドレス。 test/models/user_test.rb

require 'test_helper'

class UserTest < ActiveSupport::TestCase
  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end
  
  test "should be valid" do
    assert @user.valid?
  end
  
  test "name should be present" do
    @user.name = "     "
    assert_not @user.valid?
  end
  
  test "email should be present" do
    @user.email = "     "
    assert_not @user.valid?
  end
  
  test "name should not be too long" do
    @user.name = "a" * 51
    assert_not @user.valid?
  end
  
  test "email should not be too long" do
    @user.email = "a" * 244 + "@example.com"
    assert_not @user.valid?
  end
  
  test "email validation should be accept valid addresses" do
    valid_addresses = %w[user@example.com USER@foo.COM A_US-ER@foo.bar.org
                         first.last@foo.jp alice+bob@baz.cn]
    valid_addresses.each do |valid_address|
      @user.email = valid_address
      assert @user.valid?, "#{valid_address.inspect} should be valid"
    end
  end
end

assert @user.valid?, "#{valid_address.inspect} should be valid" と、assert の第2引数にエラーメッセージを追加することで、どのメールアドレスでエラーになったかがわかる。

続いて無効なメールアドレス。

test/models/user_test.rb

require 'test_helper'

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end
  .
  .
  .
  test "email validation should reject invalid addresses" do
    invalid_addresses = %w[user@example,com user_at_foo.org user.name@example.
                           foo@bar_baz.com foo@bar+baz.com]
    invalid_addresses.each do |invalid_address|
      @user.email = invalid_address
      assert_not @user.valid?, "#{invalid_address.inspect} should be invalid"
    end
  end
end

メールアドレスのフォーマット検証は、formatオプションを使う。

class User < ApplicationRecord
  validates :name, presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX }
end

これで無効なメールアドレスは無効として扱われるようになりテストが通る。

$ rails test:models
Started with run options --seed 30805

  7/7: [=========================================] 100% Time: 00:00:00, Time: 00:00:00

Finished in 0.03859s
7 tests, 15 assertions, 0 failures, 0 errors, 0 skips

Uniqueness validation

メールアドレスの重複を許さないように一意性を強制する。

User.new ではメモリ上にRubyオブジェクトを作るだけだが、一意性のテストのためには実際にレコードをデータベースに登録する必要があるそうな。

test/models/user_test.rb

require 'test_helper'

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(name: "Example User", email: "user@example.com")
  end
  .
  .
  .
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    @user.save
    assert_not duplicate_user.valid?
  end
end

app/models/user.rb を書き換えて大文字小文字区別せずに一意になるよう設定する。

class User < ApplicationRecord
  validates :name, presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
end

これでテスト自体は GREEN となり成功するが、データベースレベルでの一意性を保証する必要があるそう。
マイグレーションでインデックスを追加する。

migration ジェネレーターを使用してマイグレーションを作成する。

$ rails generate migration add_index_to_users_email
Running via Spring preloader in process 510723
      invoke  active_record
      create    db/migrate/20161106170056_add_index_to_users_email.rb

db/migrate/20161106170056_add_index_to_users_email.rb

class AddIndexToUsersEmail < ActiveRecord::Migration[5.0]
  def change
    add_index :users, :email, unique: true
  end
end

add_index メソッドを使用して users テーブルの email 属性にインデックスをつける。
マイグレートを実行する。

$ rails db:migrate
== 20161106170056 AddIndexToUsersEmail: migrating =============================
-- add_index(:users, :email, {:unique=>true})
   -> 0.0101s
== 20161106170056 AddIndexToUsersEmail: migrated (0.0102s) ====================

テストDB用のサンプルデータを含む fixtures が一意性に反しているためテストが失敗する。
fixtures は一旦空にしておくことでテストが通るようになる。

あとは、データベースアダプタが大文字小文字を区別するインデックスを使っている場合に備えてユーザーをデータベースに保存する前に全て小文字に変えてやる。

app/models/user.rb

class User < ApplicationRecord
  before_save { self.email = email.downcase }
・
・
・
end

before_save コールバックで self の email を小文字にかえる。

Adding a secure password

セキュアパスワードという手法では、各ユーザーにパスワードとパスワードの確認を入力させ、それを (そのままではなく) ハッシュ化したものをデータベースに保存します。

ということでセキュアパスワードを追加していく。

User モデルに下記のように has_secure_password を追加するだけ。

class User < ApplicationRecord
  before_save { self.email = email.downcase }
  validates :name,  presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
  has_secure_password
end

ただし、has_secure_password を使用するにはモデルに password_digest という属性を持っている必要がある。

password_digest カラムを追加するためのマイグレーションを作成する。

$ rails generate migration add_password_to_users password_digest:string
Running via Spring preloader in process 73555
      invoke  active_record
      create    db/migrate/20161109220735_add_password_to_users.rb

マイグレーションを実行する。

$ rails db:migrate
== 20161109220735 AddPasswordToUsers: migrating ===============================
-- add_column(:users, :password_digest, :string)
   -> 0.0044s
== 20161109220735 AddPasswordToUsers: migrated (0.0045s) ======================

has_secure_password を使ってパスワードをハッシュ化するのに必要な bcrypt をインストールする。(Rails 5 tutorial では 3.1.11 を使用しているので合わせる。)

source 'https://rubygems.org'

gem 'rails',          '5.0.0.1'
gem 'bcrypt',         '3.1.11'
.
.
.
$ bundle install
Fetching gem metadata from https://rubygems.org/
Fetching version metadata from https://rubygems.org/
Fetching dependency metadata from https://rubygems.org/
Resolving dependencies...
Using rake 11.2.2
Using concurrent-ruby 1.0.2
Using i18n 0.7.0
Using minitest 5.9.0
Using thread_safe 0.3.5
Using builder 3.2.2
Using erubis 2.7.0
Using mini_portile2 2.1.0
Using pkg-config 1.1.7
Using rack 2.0.1
Using nio4r 1.2.1
Using websocket-extensions 0.1.2
Using mime-types-data 3.2016.0521
Using arel 7.1.1
Using ansi 1.5.0
Using execjs 2.7.0
Installing bcrypt 3.1.11 with native extensions
Using sass 3.4.22
Using bundler 1.12.5
Using byebug 9.0.0
Using coderay 1.1.1
Using coffee-script-source 1.10.0
Using method_source 0.8.2
Using thor 0.19.1
Using debug_inspector 0.0.2
Using ffi 1.9.14
Using formatador 0.2.5
Using rb-fsevent 0.9.7
Using lumberjack 1.0.10
Using nenv 0.3.0
Using shellany 0.0.1
Using slop 3.6.0
Using guard-compat 1.2.1
Using multi_json 1.12.1
Using ruby-progressbar 1.8.1
Using puma 3.4.0
Using tilt 2.0.5
Using spring 1.7.2
Using sqlite3 1.3.11
Using turbolinks-source 5.0.0
Using tzinfo 1.2.2
Using nokogiri 1.6.8
Using rack-test 0.6.3
Using sprockets 3.7.0
Using websocket-driver 0.6.4
Using mime-types 3.1
Using autoprefixer-rails 6.5.0.1
Using uglifier 3.0.0
Using coffee-script 2.4.1
Using rb-inotify 0.9.7
Using notiffany 0.1.1
Using pry 0.10.4
Using guard-minitest 2.4.4
Using minitest-reporters 1.1.9
Using turbolinks 5.0.1
Using activesupport 5.0.0.1
Using loofah 2.0.3
Using mail 2.6.4
Using bootstrap-sass 3.3.6
Using listen 3.0.8
Using rails-dom-testing 2.0.1
Using globalid 0.3.7
Using activemodel 5.0.0.1
Using jbuilder 2.4.1
Using rails-html-sanitizer 1.0.3
Using guard 2.13.0
Using spring-watcher-listen 2.0.0
Using activejob 5.0.0.1
Using activerecord 5.0.0.1
Using actionview 5.0.0.1
Using actionpack 5.0.0.1
Using actioncable 5.0.0.1
Using actionmailer 5.0.0.1
Using railties 5.0.0.1
Using sprockets-rails 3.2.0
Using rails-controller-testing 0.1.1
Using coffee-rails 4.2.1
Using jquery-rails 4.1.1
Using web-console 3.1.1
Using rails 5.0.0.1
Using sass-rails 5.0.6
Bundle complete! 22 Gemfile dependencies, 81 gems now installed.
Gems in the group production were not installed.
Use `bundle show [gemname]` to see where a bundled gem is installed.

has_secure_password は仮想的なpassword属性とpassword_confirmation属性に対してバリデーションする機能があるため、これを通すようにテストを書き換える。

test/models/user_test.rb

  def setup
    @user = User.new(name: "Example User", email: "user@example.com",
                     password: "foobar", password_confirmation: "foobar")
  end

これでテストが通る。

Minimum password standards

パスワードの最小文字数を設定する。

まずテストを書き換える。

test/models/user_test.rb

require 'test_helper'

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(name: "Example User", email: "user@example.com",
                     password: "foobar", password_confirmation: "foobar")
  end
  .
  .
  .
  test "password should be present (nonblank)" do
    @user.password = @user.password_confirmation = " " * 6
    assert_not @user.valid?
  end

  test "password should have a minimum length" do
    @user.password = @user.password_confirmation = "a" * 5
    assert_not @user.valid?
  end
end

テストが通るように制約を加える。

class User < ApplicationRecord
  before_save { self.email = email.downcase }
  validates :name,  presence: true, length: { maximum: 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, presence: true, length: { maximum: 255 },
                    format: { with: VALID_EMAIL_REGEX },
                    uniqueness: { case_sensitive: false }
  has_secure_password
  validates :password, presence: true, length: { minimum: 6 }
end

これでテストが通る。

Creating and authenticating a user

Rails コンソールで実際にユーザを作成してみる。
今回は作成したユーザを後で使用するのでサンドボックスは使用せず、実際にデータベースに反映する。

$ rails c
Running via Spring preloader in process 87215
Loading development environment (Rails 5.0.0.1)

create を使う。

>> User.create(name: "Michael Hartl", email: "mhartl@example.com", password: "foobar", password_confirmation: "foobar")
   (0.1ms)  begin transaction
  User Exists (0.2ms)  SELECT  1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ?  [["email", "mhartl@example.com"], ["LIMIT", 1]]
  SQL (0.3ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES (?, ?, ?, ?, ?)  [["name", "Michael Hartl"], ["email", "mhartl@example.com"], ["created_at", 2016-11-09 23:41:18 UTC], ["updated_at", 2016-11-09 23:41:18 UTC], ["password_digest", "$2a$10$DBDRkhzw2K1ufegQ7VKtlu5G8UUXRhlozjcXLk4AW/KfzA51adpQq"]]
   (9.9ms)  commit transaction
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-11-09 23:41:18", updated_at: "2016-11-09 23:41:18", password_digest: "$2a$10$DBDRkhzw2K1ufegQ7VKtlu5G8UUXRhlozjcXLk4AW/K...">

これで保存されたので、下記のようにしてハッシュ化されたパスワードが見れる。

>> user = User.find_by(email: "mhartl@example.com")
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "mhartl@example.com"], ["LIMIT", 1]]
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-11-09 23:41:18", updated_at: "2016-11-09 23:41:18", password_digest: "$2a$10$DBDRkhzw2K1ufegQ7VKtlu5G8UUXRhlozjcXLk4AW/K...">
>> user.password_digest
=> "$2a$10$DBDRkhzw2K1ufegQ7VKtlu5G8UUXRhlozjcXLk4AW/KfzA51adpQq"

User モデルに has_secure_password を追加したので、authenticate メソッドが使用できる。

>> user.authenticate("not_the_right_password")
=> false
>> user.authenticate("foobaz")
=> false
>> user.authenticate("foobar")
=> #<User id: 1, name: "Michael Hartl", email: "mhartl@example.com", created_at: "2016-11-09 23:41:18", updated_at: "2016-11-09 23:41:18", password_digest: "$2a$10$DBDRkhzw2K1ufegQ7VKtlu5G8UUXRhlozjcXLk4AW/K...">
>> !!user.authenticate("foobar")
=> true

これでパスワード認証の準備ができた。

デプロイ

これまで通りの要領で bitbucket 及び heroku にプッシュするが、本番環境でUserモデルを使うためには、heroku runコマンドを使ってHeroku上でもマイグレーションを走らせる必要がある。

$ git push heroku
$ heroku run rails db:migrate

まとめ

  • マイグレーションを使うことで、アプリケーションのデータモデルを修正することができる
  • Active Recordを使うと、データモデルを作成したり操作したりするための多数のメソッドが使えるようになる
  • Active Recordのバリデーションを使うと、モデルに対して制限を追加することができる
  • よくあるバリデーションには、存在性・長さ・フォーマットなどがある
  • 正規表現は謎めいて見えるが非常に強力である
  • データベースにインデックスを追加することで検索効率が向上する。また、データベースレベルでの一意性を保証するためにも使われる

次章以降で、ユーザーを作成するためのユーザー登録フォームを作成していく。