0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

アプリを作る PasswordResetsリソース

Posted at

###gitをチェックアウトをする

git checkout -b password-reset

###PasswordResetsコントローラを生成する

ubuntu:~/environment/my_app (password-reset) $ rails generate controller PasswordResets new edit --no-test-framework
Running via Spring preloader in process 10038
      create  app/controllers/password_resets_controller.rb
       route  get 'password_resets/new'
get 'password_resets/edit'
      invoke  erb
      create    app/views/password_resets
      create    app/views/password_resets/new.html.erb
      create    app/views/password_resets/edit.html.erb
      invoke  helper
      create    app/helpers/password_resets_helper.rb
      invoke  assets
      invoke    scss
      create      app/assets/stylesheets/password_resets.scss

###config/routes.rb

config/routes.rb
Rails.application.routes.draw do
  get 'password_resets/new'
  get 'password_resets/edit'
  get 'sessions/new'
  root 'static_pages#home'
  get  '/signup',  to: 'users#new'
  # signupのurlを入力してuser/newのアクションが動き、htmlが表示される。
  get    '/login',   to: 'sessions#new'
  # newアクション
  post   '/login',   to: 'sessions#create'
  # creteアクション
  delete '/logout',  to: 'sessions#destroy'
  # destroyアクション
  resources :users
  resources :account_activations, only: [:edit]
  resources :password_resets,     only: [:new, :create, :edit, :update]
  # password_resetsコントローラ にonly以降のアクションを与える
  # resourcesを宣言するだけで、コントローラのindex、show、new、edit、create、update、destroyアクションを個別に宣言しなくても1行で宣言が完了
  # 特定のコントローラ内アクションにマッピングさせるようルーターに要求しています。
  # Railsのリソースフルルーティングでは、各種HTTP verbと、コントローラ内アクションを指すURLが対応付けられます。1つのアクションは、データベース上での特定のCRUD(Create/Read/Update/Delete)操作に対応付けられるルールになっています。
  # resources :photosというルーティングがget 'photos/poll'よりも上の行にあれば、resources行のshowアクションがget行の記述よりも優先される
end

###app/views/sessions/new.html.erb

app/views/sessions/new.html
.erb

<% provide(:title, "Log in") %>
<h1>Log in</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_with(url: login_path, scope: :session, local: true) do |f| %>

      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.label :password %>
      <%= link_to "(forgot password)", new_password_reset_path %>
      <!--forgetのリンク new_password_reset_pathに移動するようにする-->
      <%= f.password_field :password, class: 'form-control' %>

      <%= f.label :remember_me, class: "checkbox inline" do %>
        <%= f.check_box :remember_me %>
        <span>Remember me on this computer</span>
      <% end %>

      <%= f.submit "Log in", class: "btn btn-primary" %>
    <% end %>

    <p>New user? <%= link_to "Sign up now!", signup_path %></p>
  </div>
</div>

###add_reset_to_users.rbを生成する

ubuntu:~/environment/my_app (password-reset) $ rm db/migrate/20220120051051_add_reset_to_users.rb

ubuntu:~/environment/my_app (password-reset) $ rails generate migration add_reset_to_users reset_digest:string \
> reset_sent_at:datetime
Running via Spring preloader in process 17050
      invoke  active_record
      create    db/migrate/20220120061244_add_reset_to_users.rb
rails db:migrate

###app/views/password_resets/new.html.erb

app/views/password_resets/new.html
.erb
<!--新しいパスワード再設定画面ビュー-->

<% provide(:title, "パスワードを忘れたら") %>
<h1>Forgot password</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_with(url: password_resets_path, scope: :password_reset,
                    local: true) do |f| %>
      <%= f.label :email %>
      <%= f.email_field :email, class: 'form-control' %>

      <%= f.submit "Submit", class: "btn btn-primary" %>
    <% end %>
  </div>
</div>

###app/controllers/password_resets_controller.rb

app/controllers/password_resets_controller.rb
class PasswordResetsController < ApplicationController
  def new
  end
  
  def create
  # パスワード再発行の場合
    @user = User.find_by(email: params[:password_reset][:email].downcase)
    # インスタンス変数にdbからメアドを基に探す
    if @user
    # 有効性があれば
      @user.create_reset_digest
      # reset_digestカラムの値を作成
      @user.send_password_reset_email
      # パスワード再発行用のメールを送信
      flash[:info] = "パスワード再発行のためにメールを送信します。" 
      redirect_to root_url
      # ホーム画面に自動で移動する
      # view の表示には直接は関係なく、新たな HttpRequest が発行されます。
    else
      flash.now[:danger] = "Eメールアドレスが見つかりませんでした。"
      render 'new'
      # 新規登録画面に移動する
    end
  end
  
  def edit
  end
end

###app/models/user.rb

app/models/user.rb
class User < ApplicationRecord
# 継承させる
  attr_accessor :remember_token, :activation_token, :reset_token
  # 属性remember_token,activation_token,reset_tokenを定義する
  # インスタンス変数の読み取り専用のメソッドと書き込み専用のメソッド(セッター/ゲッター)の両方を定義することが出来るメソッド

  # 読み取りメソッドとは?
    # インスタンス変数の読み取り専用のインスタンスメソッドを「ゲッターメソッド」と呼びます。
    # メソッドが呼び出された時に各インスタンス変数を返すように定義します
    # ゲッターメソッド(読み取り専用のインスタンスメソッド)を経由することでインスタンス変数(@name)をクラスの外部から参照することが出来ています。
   
  # 書き込みメソッドとは?
    # インスタンス変数の書き込み専用のインスタンスメソッドを「セッターメソッド」と呼びます。
    # セッターメソッド(書き込み専用のインスタンスメソッド)を経由することでインスタンス変数(@age)をクラスの外部から変更することが出来ます。
    
  # ゲッターメソッドやセッターメソッドなど「インスタンス変数の読み取りや書き込みが出来るメソッド」のことを総じて「アクセサメソッド」と呼ぶ
  before_save   :downcase_email
  before_create :create_activation_digest
  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 }, allow_nil: true
  def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
                                                  BCrypt::Engine.cost
    # コストパラメータはテストでは最小にする
    BCrypt::Password.create(string, cost: cost)
  end
  
  # ランダムなトークンを返す
  def User.new_token
    SecureRandom.urlsafe_base64
  end
  
  # 永続セッションのためにユーザーをデータベースに記憶する
  def remember
    self.remember_token = User.new_token
    update_attribute(:remember_digest, User.digest(remember_token))
  end
  
  # トークンがダイジェストと一致したらtrueを返す
  def authenticated?(attribute, token)
    digest = send("#{attribute}_digest")
    return false if digest.nil?
    BCrypt::Password.new(digest).is_password?(token)
  end
  
  # ユーザーのログイン情報を破棄する
  def forget
    update_attribute(:remember_digest, nil)
  end
  
  def activate
  # # アカウントを有効にする
    update_attribute(:activated,    true)
    update_attribute(:activated_at, Time.zone.now)
  end

  # 有効化用のメールを送信する
  def send_activation_email
    UserMailer.account_activation(self).deliver_now
  end
  
  # パスワード再設定の属性を設定する
  def create_reset_digest
    self.reset_token = User.new_token
    # 新しいトークンを生成して代入
    update_attribute(:reset_digest,  User.digest(reset_token))
    # 属性reset_tokenを更新する
    update_attribute(:reset_sent_at, Time.zone.now)
  end

  # パスワード再設定のメールを送信する
  def send_password_reset_email
    UserMailer.password_reset(self).deliver_now
    # 再発行用のメールを送信
  end
  
  private

    # メールアドレスをすべて小文字にする
    def downcase_email
      self.email = email.downcase
    end

    # 有効化トークンとダイジェストを作成および代入する
    def create_activation_digest
      self.activation_token  = User.new_token
      self.activation_digest = User.digest(activation_token)
    end
end
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?