ror:ユーザーモデリング

14942 ワード

前文を受け継ぐ.
ユーザーモデル
ブランチの作成と切り替え
git checkout -b modeling-users

ユーザーモデルの生成
rails generate model User name:string email:string

データベースの移行
rails db:migrate
# rails db:rollback(    )

モデルの使用
コンソールを開く:
➜  microblog git:(modeling-users) rails c --sandbox  #          
Running via Spring preloader in process 23202
Loading development environment in sandbox (Rails 5.0.1)
Any modifications you make will be rolled back on exit

ユーザーの新規保存:
2.3.3 :001 > User.new  #         
 => # 
2.3.3 :002 > User.new name:"Joshua", email:"[email protected]"  #         (Hash       ,    { })
 => # 
2.3.3 :003 > user = User.new name:"Joshua", email:"[email protected]"  #        
 => # 
2.3.3 :004 > user.valid?  #       
 => true 
2.3.3 :005 > user.save  #      
   (0.2ms)  SAVEPOINT active_record_1
  SQL (31.0ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Joshua"], ["email", "[email protected]"], ["created_at", 2017-01-06 14:55:02 UTC], ["updated_at", 2017-01-06 14:55:02 UTC]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => true   #   true/false
2.3.3 :006 > user  #         (  :ID/    /    )
 => # 
2.3.3 :007 > user.name  #   
 => "Joshua" 
2.3.3 :008 > user.email  # 
 => "[email protected]" 
2.3.3 :009 > user.updated_at  #
 => Fri, 06 Jan 2017 14:55:02 UTC +00:00 
2.3.3 :010 > user = User.create(name:"Joshuaber", email:"[email protected]")  #   create  new+save
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.3ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "Joshuaber"], ["email", "[email protected]"], ["created_at", 2017-01-06 14:58:04 UTC], ["updated_at", 2017-01-06 14:58:04 UTC]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => # 
2.3.3 :011 > user  # 
 => # 
2.3.3 :012 > user.destroy  #   
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.3ms)  DELETE FROM "users" WHERE "users"."id" = ?  [["id", 2]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => # 
2.3.3 :013 > user  #            
 => # 

ユーザーの検索:
2.3.3 :014 > User.find(1)  #   id=1   (    )
  User Load (573.5ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => # 
2.3.3 :015 > User.find(2)
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=2
... ...
2.3.3 :016 > user  #     id=2        
 => # 
2.3.3 :017 > User.find(2)  # 
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=2
... ...
2.3.3 :018 > User.find_by(email:"[email protected]")  #   find_by      
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "[email protected]"], ["LIMIT", 1]]
 => # 
2.3.3 :019 > User.first  #
  User Load (0.3ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
 => # 
2.3.3 :020 > User.last  #
  User Load (0.3ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT ?  [["LIMIT", 1]]
 => # 
2.3.3 :021 > User.all  #
  User Load (0.4ms)  SELECT "users".* FROM "users"
 => #<:relation id:="" name:="" email:="" created_at:="" updated_at:="">]> 

ユーザー情報の変更:
2.3.3 :022 > user = User.find(1)  #      
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => # 
2.3.3 :023 > user.email = "[email protected]"  #     
 => "[email protected]" 
2.3.3 :024 > user.save  #     
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.3ms)  UPDATE "users" SET "email" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["email", "[email protected]"], ["updated_at", 2017-01-07 03:33:54 UTC], ["id", 1]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => true 
2.3.3 :025 > user.email = "[email protected]"  #     
 => "[email protected]" 
2.3.3 :026 > user.reload.email  #     (    )
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => "[email protected]" 
2.3.3 :027 > user  # 
 => # 
2.3.3 :028 > user.created_at  #
 => Fri, 06 Jan 2017 14:55:02 UTC +00:00 
2.3.3 :029 > user.updated_at  #
 => Sat, 07 Jan 2017 03:33:54 UTC +00:00 
2.3.3 :030 > user.update_attributes(name:"Joshuaber", email:"[email protected]")
   (0.2ms)  SAVEPOINT active_record_1
  SQL (0.4ms)  UPDATE "users" SET "email" = ?, "name" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["email", "[email protected]"], ["name", "Joshuaber"], ["updated_at", 2017-01-07 03:37:20 UTC], ["id", 1]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => true 
2.3.3 :031 > user.update_attributes(email:"[email protected]")  #     (    )
   (0.1ms)  SAVEPOINT active_record_1
  SQL (0.2ms)  UPDATE "users" SET "email" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["email", "[email protected]"], ["updated_at", 2017-01-07 03:38:10 UTC], ["id", 1]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => true 
2.3.3 :032 > User.find(1)  #
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => #  

コードは簡単ですので、コメントを見てください.
ユーザーデータ検証
TDDによる開発
テストコマンドの実行
rails test:models

有効性テスト
# test/models/user_test.rb

... ...
  def setup
    @user = User.new(name: "Example User", email: "[email protected]")
  end

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

存在性の検証
# test/models/user_test.rb

... ...
  test "name should be present" do
    @user.name = "  "
    assert_not @user.valid?  #     
  end
... ...

テストは失敗しました.存在検証を追加する必要があります.
# app/models/user.rb

validates :name, presence: true

同様に、emailの存在性検証を追加します.
長さの検証
# test/models/user_test.rb

... ...
  test "name should not be too long" do
    @user.name = "a" * 51  #      50
    assert_not @user.valid?
  end

  test "email should not be too long" do
    @user.email = "a" * 256
    assert_not @user.valid?
  end

このテストは失敗しました.長さ制限を追加します.
# app/models/user.rb

    validates :name, presence: true, length: { maximum: 50 }
    validates :email, presence: true, length: { maximum: 255 }

フォーマット検証
# test/models/user_test.rb

  test "email validation should accept valid addresses" do
    valid_addresses = %w[[email protected] [email protected] [email protected] [email protected] [email protected]]
    valid_addresses.each do |valid_address|
      @user.email = valid_address
      assert @user.valid?, "#{valid_address.inspect} should be valid"
    end
  end

  test "email validation should reject invalid addresses" do
    invalid_addresses = %w[user@example,com user_at_foo.org [email protected]@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

フォーマット制限の追加:
# app/models/user.rb

    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i  #             
    validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }

一意性の検証
# test/models/user_test.rb

... ...
  test "email addresses should be unique" do
    duplicate_user = @user.dup
    duplicate_user.email = @user.email.upcase
    @user.save
    assert_not duplicate_user.valid?
  end

一意性の制限を追加するには、次の手順に従います.
# app/models.user.rb

#            
before_save { self.email = email.downcase }
... ...
validates :email, presence: true, length: { maximum: 255 }, format: { with: VALID_EMAIL_REGEX }, uniqueness: { case_sensitive: false }

索引の追加
移行ファイルを生成するには、次の手順に従います.
rails generate migration add_index_to_users_email

一意性制約の移行を追加するには、次の手順に従います.
# db/migrate/20170107075525_add_index_to_users_email.rb

def change
  add_index :users, :email, unique: true
end

データベース移行の実行:
rails db:migrate

セキュリティパスワードの追加
パスワード列の追加
rails generate migration add_password_digest_to_users password_digest:string
rails db:migrate

gemの追加
# Gemfile

... ...
gem 'bcrypt'
... ...

bundleの実行:
bundle

コードの追加
# app/models/user.rb

... ...
  has_secure_password
end

テストの変更
# test/models/user_test.rb

... ...

  def setup
    @user = User.new(name: "Example User", email: "[email protected]", password: "joshua", password_confirmation: "joshua")
  end
... ...

パスワード最短長
テストの追加:
# test/models/user_test.rb

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

パスワードの長さ制限を追加するには、次の手順に従います.
# app/models/user.rb

validates :password, length: { minimum: 6 }

ユーザーの作成
2.3.3 :001 > User.all  # 
  User Load (17.5ms)  SELECT "users".* FROM "users"
 => #<:relation> 
2.3.3 :002 > User.create(name:"Joshua", email:"[email protected]", password:"joshua", password_confirmation:"joshua")  #     
   (0.1ms)  begin transaction
  User Exists (24.0ms)  SELECT  1 AS one FROM "users" WHERE LOWER("users"."email") = LOWER(?) LIMIT ?  [["email", "[email protected]"], ["LIMIT", 1]]
  SQL (12.9ms)  INSERT INTO "users" ("name", "email", "created_at", "updated_at", "password_digest") VALUES (?, ?, ?, ?, ?)  [["name", "Joshua"], ["email", "[email protected]"], ["created_at", 2017-01-08 04:40:15 UTC], ["updated_at", 2017-01-08 04:40:15 UTC], ["password_digest", "$2a$10$vWuRpVTzhdwVHzRlJBBJi.0jVUSuUJvIUnzybK6QQ1t8FQavFoz5i"]]
   (123.8ms)  commit transaction
 => # 
2.3.3 :003 > User.find_by(email:"[email protected]")  #      
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "[email protected]"], ["LIMIT", 1]]
 => nil 
2.3.3 :004 > User.find_by(email:"[email protected]")  #   
  User Load (0.3ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "[email protected]"], ["LIMIT", 1]]
 => # 
2.3.3 :005 > user = User.find_by(email:"[email protected]")  #
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."email" = ? LIMIT ?  [["email", "[email protected]"], ["LIMIT", 1]]
 => # 
2.3.3 :006 > user.password_  #   Tab    
user.password_confirmation                
... ...   
2.3.3 :006 > user.password_digest  #   
 => "$2a$10$vWuRpVTzhdwVHzRlJBBJi.0jVUSuUJvIUnzybK6QQ1t8FQavFoz5i" 
2.3.3 :007 > user.authenticate("wrongpwd")  #     
 => false 
2.3.3 :008 > user.authenticate("joshua")  #     
 => # 

の最後の部分
連結ブランチの削除
git checkout master
git merge modeling-users
git branch -d modeling-user
git branch

Herokuに配備してテストする
git push heroku master
heroku rake db:migrate
heroku run console --sandbox

リファレンス
《Rails Tutorial 3th》