User.new end scaffold で生成した コントローラーとほぼ同じ def create @user = User.new(user_params) if @user.save redirect_to action: :pending_verification else render :new, status: :unprocessable_content end end end 12
class_name: "UserIdentity", inverse_of: :user def self.from_omniauth(provider:, uid:, email:, email_verified:) User.new(email:) do |user| user.identities.build(provider:, uid:) user.save(context: :google_signup) User を保存すると、新規の 紐付け情報も保存される end end end 24
:email_verified_at, presence: true, on: :google_signup after_create_commit :send_verification_email, unless: :email_verified_at def self.from_omniauth(provider:, uid:, email:, email_verified:) User.new(email:) do |user| user.email_verified_at = Time.current if email_verified user.save(context: :google_signup) end end end 25
User.from_omniauth(**user_params) が保存済みかどうかで分岐 if @user.persisted? User start_new_session_for(@user) redirect_to after_authentication_url, notice: "User was ..." else redirect_to new_user_path, alert: "Could not authenticate you ..." end end private def user_params = request.env["omniauth.auth"].then { … } end 注 : Google アカウントによる認証のリクエスト・レスポンスの処理に OmniAuth を利用する場合の例 26
save return false if invalid? || !user.save UserMailer.with(user:).email_verification.deliver_later true end unless: :email_verified_at で実行条件を private def user @user ||= User.new(email:) end end 指定していたコールバックに相当 38
:user # In this example, we choose to keep the email validations in User. validates :email, format: URI::MailTo::EMAIL_REGEXP, uniqueness: true generates_token_for :email_verification, expires_in: 1.day do [ email, email_verified_at ] end end 41
%i[ email ].freeze validate :user_must_be_valid private def user_must_be_valid return if user.valid? User のエラーを操作を表現するモデルの エラーとして取り込む user.errors.each do |error| next unless error.attribute.in?(USER_ATTRIBUTES) errors.import(error) end end end 注 : ActiveModel::Errors#import は Rails 6.1 から利用できる 42
EmailSignUp.new end 呼び出すモデルが変わっても コントローラーの構成は同じ def create @email_sign_up = EmailSignUp.new(email_sign_up_params) if @email_sign_up.save redirect_to action: :pending_verification else render :new, status: :unprocessable_content end end end 43
EmailSignUp email_verification Google auth callback Controller action Omniauth::Sessions… User Save GoogleSignUp user.identites.build Model operation Database operation User#save INSERT INTO users ... Common behaviour validates :email リソースの作成操作とモデルの作成操作は 引き続き一対一に対応 44
true belongs_to :user validate :user_must_be_valid, if: :user EmailSignUp を保存すると、 新規Userも保存される after_create_commit :send_verification_email delegate :email, to: :user, allow_nil: true private # NOTE: Same implementation as in the previous examples. def user_must_be_valid; end def send_verification_email; end end 注 : 2 つのモデルを STI で同じテーブルに対応させる設計も可能だが、本題から脱線するため割愛 50
if @email_sign_up.save redirect_to action: :pending_verification else render :new, status: :unprocessable_content end end private def email_sign_up_params { user: User.new(params.expect(…)), referrer_id: session[:referrer_id] } end end 51
def save return false if invalid? || !user.save UserMailer.with(user:) .email_verification.deliver_later true end private def user @user ||= User.new(email:) end end class EmailSignUpsController < ApplicationController def create @email_sign_up_form = EmailSignUpForm.new( email_sign_up_form_params ) if @email_sign_up_form.save redirect_to action: :pending_verification else render :new, status: :unprocessable_content end end end モデルの実装は、記録が不要な 操作を表現するモデルと同じ 53
true belongs_to :user class EmailSignUpsController < ApplicationController def create @email_sign_up = EmailSignUp.new( email_sign_up_params ) validate :user_must_be_valid, if: :user after_create_commit :send_verification_email delegate :email, to: :user, allow_nil: true private # NOTE: Same implementation # as in the previous examples. def user_must_be_valid; end def send_verification_email; end end if @email_sign_up.save redirect_to action: :pending_verification else render :new, status: :unprocessable_content end end end モデルの実装は、記録が必要な 操作を表現するモデルと同じ 54
entity domain activities Does the operation need a record? No Implementation level EmailSignUp(Form) uses ActiveModel Yes EmailSignUp uses ActiveRecord 55
:email, :string def save return false if invalid? || !user.save UserMailer.with(user:).email_verification.deliver_later true end private def user @user ||= User.new(email:) end end class User < ApplicationRecord validates :email, format: URI::MailTo::EMAIL_REGEXP, uniqueness: true end 73