ユーザー登録用URL | rails チュートリアル 10

ユーザー登録用URLの勉強メモです。

URLを変更する

今まで「/users/new」で、表示出来ていたページを「/signu」で表示できるようにしたい。
routes.rbを修正する。

  get  '/signup',  to: 'users#new'

ユーザーのモデルを作成する

※ railsチュートリアルのページは、 第6章 ユーザーのモデルを作成するです。

自分で認証システムを作ってみる理由

このページのコラム 6.1.によると、

ある実践的な実験によると、多くのサイトの認証システムは膨大なカスタマイズを必要とするため、サードパーティ製品を変更して導入する場合にはシステムをゼロから作成するよりも多くの仕事を要するという結果が出ています。 加えて、既成品のシステムは内部がわかりづらいことが多く、ブラックボックスになっています。(中略)
最後に、あえて最終的にサードパーティの認証システムを導入することになったとしても、自分自身で認証システムを構築した経験があれば、サードパーティ製品を理解して変更することがずっと容易になるはずです。

ということで、以前作成したmigrateファイルを確認して見る。
ファイルは、、「db/migrate/[timestamp]_create_users.rb」こんな感じのファイルのはず。

class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end

上記のファイルが、以前の「db:migrate」コマンドで生成されているはず。
これにより、以下のデータモデルがDBに作成される。

id[integer]
自動的に作成され、Railsが各行を一意に識別するために使用する。
name[string]
名前を記録する。
email[string]
emailアドレスを記録する。
created_at[datetime]
t.timestampsで自動的に作成される2つのマジックカラムの1つ。ユーザーが作成された時刻を記録する。
updated_at[datetime]
t.timestampsで自動的に作成される2つのマジックカラムの1つ。ユーザーが更新された時刻を記録する。

また、「schema.rb」も確認してみると、

ActiveRecord::Schema.define(version: 20170701045004) do

  create_table "microposts", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.text "content"
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

  create_table "users", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
    t.string "name"
    t.string "email"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
  end

end

このようになっていました。この「schema.rb」というファイルは、
データベースの構造 (スキーマ (schema) と呼びます) を追跡するために使われます。

ユーザーオブジェクトを作成する

コンソールで、ためにし作成してみる。

[1] pry(main)> User.new
=> #
# User.newを引数なしで呼んだ場合は、すべての属性がnilのオブジェクトを返すようです。

[2] pry(main)> user = User.new(name: "Michael Hartl", email: "mhartl@example.com")
=> #
# ()の中に引数をいれて、読んだ場合、意図したように、nameとemailには、値が入っているのがわかります。

[3] pry(main)> user.valid?
=> true
# userオブジェクトが有効かどうかも、確認可能。

[4] pry(main)> user.save
   (1.8ms)  BEGIN
  SQL (10.6ms)  INSERT INTO `users` (`name`, `email`, `created_at`, `updated_at`) VALUES ('Michael Hartl', 'mhartl@example.com', '2017-07-10 19:56:16', '2017-07-10 19:56:16')
   (2.8ms)  COMMIT
=> true
#  データベースにUserオブジェクトを保存するためには、userオブジェクトからsaveメソッドを呼び出す必要がある。
# 成功すればtrueを、失敗すればfalseを返す。

[5] pry(main)> user
=> #
# 生成と保存を2つのステップに分けておくと何かと便利なときがあるらしい。

[6] pry(main)> User.create(name: "A Nother", email: "another@example.org")
   (0.2ms)  BEGIN
  SQL (0.3ms)  INSERT INTO `users` (`name`, `email`, `created_at`, `updated_at`) VALUES ('A Nother', 'another@example.org', '2017-07-10 19:59:15', '2017-07-10 19:59:15')
   (0.9ms)  COMMIT
=> #
[7] pry(main)> foo = User.create(name: "Foo", email: "foo@bar.com")
   (0.2ms)  BEGIN
  SQL (0.3ms)  INSERT INTO `users` (`name`, `email`, `created_at`, `updated_at`) VALUES ('Foo', 'foo@bar.com', '2017-07-10 20:00:18', '2017-07-10 20:00:18')
   (1.4ms)  COMMIT
=> #
# 生成と保存を同時におこなう方法もある。

[8] pry(main)> foo.destroy
   (0.2ms)  BEGIN
  SQL (7.1ms)  DELETE FROM `users` WHERE `users`.`id` = 4
   (2.5ms)  COMMIT
=> #
[9] pry(main)> foo
=> #
# createの逆はdestroy
# 削除されたオブジェクトは、まだメモリ上に残っています。

[10] pry(main)> user.email.class
=> String
[11] pry(main)> user.updated_at.class
=> ActiveSupport::TimeWithZone
# classも確認することが可能

ユーザーオブジェクトを検索する

idで検索

[12] pry(main)> User.find(1)
  User Load (3.5ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
=> #
[13] pry(main)> User.find(3)
  User Load (0.3ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 3 LIMIT 1
=> #
[14] pry(main)> User.find(5)
  User Load (0.3ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 5 LIMIT 1
ActiveRecord::RecordNotFound: Couldn't find User with 'id'=5
# User.findにユーザーのidを渡し、Active Recordから、そのidのユーザーを返してもらう。

特定の属性でユーザーを検索

[15] pry(main)> User.find_by(email: "mhartl@example.com")
  User Load (0.8ms)  SELECT  `users`.* FROM `users` WHERE `users`.`email` = 'mhartl@example.com' LIMIT 1
=> #

その他

[16] pry(main)> User.first
  User Load (0.3ms)  SELECT  `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
=> #
# データベースの最初のユーザーを返す。

[17] pry(main)> User.all.length
  User Load (0.3ms)  SELECT `users`.* FROM `users`
=> 3
# 個数を返す

ユーザーオブジェクトを更新する

[18] pry(main)> user
=> #
# 確認をする
[19] pry(main)> user.email = "mhartl@example.net"
=> "mhartl@example.net"
# 属性を個別に代入

[20] pry(main)> user.save
   (1.3ms)  BEGIN
  SQL (3.2ms)  UPDATE `users` SET `email` = 'mhartl@example.net', `updated_at` = '2017-07-10 21:08:19' WHERE `users`.`id` = 2
   (1.2ms)  COMMIT
=> true
# 保存

[22] pry(main)> user
=> #
# 確認をする

[23] pry(main)> user.email = "foo@bar.com"
=> "foo@bar.com"
# 代入する
[24] pry(main)> user.reload.email
  User Load (0.5ms)  SELECT  `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
=> "mhartl@example.net"
# reloadでキャンセルする

[25] pry(main)> user.update_attribute(:name, "El Duderino")
   (0.2ms)  BEGIN
  SQL (1.3ms)  UPDATE `users` SET `name` = 'El Duderino', `updated_at` = '2017-07-10 21:12:21' WHERE `users`.`id` = 2
   (0.6ms)  COMMIT
=> true
[26] pry(main)> user.name
=> "El Duderino"
# update_attributeには、検証を回避する(すぐに保存される)
# update_attributesは複数の属性の変更が可能

ユーザーのバリデートを強化する

user.rb

class User < ApplicationRecord
  # micropostと紐付ける
  has_many :microposts
  # 未入力を受け付けなくする
  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: true
end

generateを行う。

$ bundle exec rails generate migration add_index_to_users_email

db/migrate/[timestamp]_add_index_to_users_email.rbを更新

class AddIndexToUsersEmail < ActiveRecord::Migration[5.1]
  def change
    add_index :users, :email, unique: true #追記
  end
end

migrateを行う

$ bundle exec rails db:migrate
== 20170710214805 AddIndexToUsersEmail: migrating =============================
-- add_index(:users, :email, {:unique=>true})
   -> 0.0626s
== 20170710214805 AddIndexToUsersEmail: migrated (0.0635s) ====================

セキュアなパスワードを追加する

セキュアなパスワードの実装は、has_secure_passwordというRailsのメソッドを呼び出すだけらしい。

モデルにこのメソッドを追加すると、次のような機能が使えるようになります。

使える機能
セキュアにハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。
2つのペアの仮想的な属性 (passwordとpassword_confirmation) が使えるようになる。また、存在性と値が一致するかどうかのバリデーションも追加される。
authenticateメソッドが使えるようになる (引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalseを返すメソッド) 。

モデルを修正したので、migrate

$ bundle exec rails generate migration add_password_digest_to_users password_digest:string
Running via Spring preloader in process 36500
      invoke  active_record
      create    db/migrate/20170710220127_add_password_digest_to_users.rb
$ bundle exec rails db:migrate
== 20170710220127 AddPasswordDigestToUsers: migrating =========================
-- add_column(:users, :password_digest, :string)
   -> 0.0720s
== 20170710220127 AddPasswordDigestToUsers: migrated (0.0721s) ================

has_secure_passwordを使ってパスワードをハッシュ化するためには、最先端のハッシュ関数であるbcryptが必要になります。

gem 'bcrypt'

久々のbundle install

$ bundle install --path vendor/bundle
Fetching bcrypt 3.1.11
Installing bcrypt 3.1.11 with native extensions

パスワードの最小文字数も設定するため、user.rbも修正していく

class User < ApplicationRecord
  before_save { self.email = email.downcase }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

  # micropostと紐付ける
  has_many :microposts
  # 未入力を受け付けなくする
  validates :name,  presence: true, length: { maximum: 50 }
  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

ユーザーを表示

gravatar画像を表示できるように、show.html.erbを修正

<% provide(:title, @user.name) %>

<p id="flash"><%= flash %></p>

<div class="container">

  <h1><%= gravatar_for @user %></h1>

  <p>
    <strong>Name:</strong>
    <%= @user.name %>
  </p>

  <p>
    <strong>Email:</strong>
    <%= @user.email %>
  </p>

  <%= link_to 'Edit', edit_user_path(@user) %> |
  <%= link_to 'Back', users_path %>

</div>

users_helper.rbを修正

module UsersHelper

  # 引数で与えられたユーザーのGravatar画像を返す
  def gravatar_for(user)
    gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
    gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}"
    image_tag(gravatar_url, alt: user.name, class: "gravatar")
  end
end

という感じで、http://localhost:3000/users/1が表示されれば、OK!

次は、7.2 ユーザー登録フォームを勉強

コメント

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

内容に問題なければ、下記の「コメントを送信する」ボタンを押してください。


  1. KATOON.NET
  2. TRASH
  3. ユーザー登録用URL | rails チュートリアル 10