0

我正在尝试重构update我的 Rails 操作中的操作,以便users只有在通过单击我发送给他们的链接确认后才能更改他们自己的电子邮件地址。

class UsersController < ApplicationController

  before_filter :authorized_user

  def update
    current_email = @user.email
    new_email = params[:user][:email].downcase.to_s
    if @user.update_attributes(params[:user])    
      if new_email != current_email
        @user.change_email(current_email, new_email)     
        flash[:success] = "Please click on the link that we've sent you."
      else
        flash[:success] = "User updated."
      end
      redirect_to edit_user_path(@user)
    else
      render :edit
    end
  end

  def confirm_email
    @user = User.find_by_email_token!(params[:id])
    @user.email = @user.new_email
    @user.save
  end

  private

  def authorized_user
    @user = User.find(params[:id])
    redirect_to(root_path) unless current_user?(@user)
  end

end

此功能将新电子邮件保存到数据库字段new_emailemail只有在用户new_email通过 URL 确认后才会被替换:

class User < ActiveRecord::Base

  def change_email(old_email, new_email)
    self.email = old_email
    self.new_email = new_email.downcase    
    self.send_email_confirmation_link  
  end

end

代码部分有效,但我想知道是否有更优雅的方法来做到这一点,可能是通过使用after_save回调或至少将更多代码移动到模型中。

最好的方法是什么?

谢谢你的帮助!

PS请不要建议Devise用于此。我真的很想在这里建立自己的身份验证系统:-)

4

2 回答 2

0

我建议您不要使用 ActiveRecord 回调来执行业务逻辑:ActiveRecord 模型应该只是数据库持久层的一个薄包装器。

看看如何更改控制器代码:

def update
  if UpdatesUserCheckingEmail.new(@user, params[:user], flash).execute!
    redirect_to edit_user_path(@user)
  else
    render :edit
  end
end

所有的业务逻辑都由一个外部对象执行,它封装了你所有的业务逻辑(你可以把它放在 app/services/updates_user_checking_email.rb 中)

class UpdatesUserCheckingEmail
  attr_reader :user, :user_params, :flash

  def initialize(user, user_params, options = {})
    @user = user
    @user_params = user_params
    @flash = options[:flash]
  end

  def execute!
    if user.update_attributes(user_params)
      if new_email != current_email
        user.change_email(current_email, new_email)     
        flash[:success] = "Please click on the link that we've sent you."
      else
        flash[:success] = "User updated."
      end
    end
  end

  private

  def current_email
    user.email
  end

  def new_email
    user_params[:email].downcase.to_s
  end

end

我还建议您将发送电子邮件的逻辑移出 ActiveRecord 模型并放入专用服务对象中。这将使您的应用程序在未来更容易更改(和测试)!

你可以在这里找到更多关于这些概念的信息:http: //blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

于 2013-06-24T09:41:25.837 回答
0

我认为你不应该在数据库更新之后检查current_emailnew_email因为它应该在数据库更新之前。另一个是您在将电子邮件更新到数据库后向用户发送链接。因此,这无法达到您的目标,即“只有在用户new_email通过 URL 确认他的电子邮件后,才会替换电子邮件”。您应该创建用于更新用户电子邮件的新操作,或者当用户在 UserController 的更新操作中收到“重置电子邮件”的电子邮件时,您应该编写更新用户电子邮件的逻辑。以下是解决问题的简单方法:

class UsersController < ApplicationController
  def send_email_rest
    @user.change_email(@user.email, params[:new_email]) if params[:new_email].present?
  end
  def update
    if @user.update_attributes(params[:user])  
      #stuff you want to do
    end
  end
end

希望有帮助!!!

于 2013-06-24T09:42:08.060 回答