Ruby on rails Rails 4:如何在这种长控制器方法中解耦逻辑?

Ruby on rails Rails 4:如何在这种长控制器方法中解耦逻辑?,ruby-on-rails,authentication,controller,refactoring,Ruby On Rails,Authentication,Controller,Refactoring,我正在使用has\u secure\u password和rails 4.1.5应用程序。我想将我的登录功能与我的sessioncontroller分离,这样我就可以在我的应用程序中的任何位置重用它来登录任何用户-例如注册后登录用户,记录分析事件等 因此,我将代码重构为一个LoginUser服务对象,我对此感到满意 问题是在重构之后,我的控制器仍然有一些耦合逻辑。我正在使用一个表单对象(通过reform gem)进行表单验证,然后将用户、会话和密码传递给LoginUser服务 以下是mySess

我正在使用
has\u secure\u password
rails 4.1.5
应用程序。我想将我的登录功能与我的
sessioncontroller
分离,这样我就可以在我的应用程序中的任何位置重用它来登录任何用户-例如注册后登录用户,记录分析事件等

因此,我将代码重构为一个
LoginUser
服务对象,我对此感到满意

问题是在重构之后,我的控制器仍然有一些耦合逻辑。我正在使用一个表单对象(通过reform gem)进行表单验证,然后将用户、会话和密码传递给
LoginUser
服务

以下是my
SessionController
中的创建方法的外观:

  def create
    login_form = Forms::LoginForm.new(User.new)

    if login_form.validate(params[:user]) # validate the form
      begin #find the user
        user = User.find_by!(email: params[:user][:email])
      rescue ActiveRecord::RecordNotFound => e
        flash.now.alert = 'invalid user credentials'
        render :new and return
      end
    else
      flash.now.alert = login_form.errors.full_messages
      render :new and return
    end

    user && login_service = LoginUser.new(user, session, params[:user][:password])
    login_service.on(:user_authenticated){ redirect_to root_url, success: "You have logged in" }

    login_service.execute
  end
一切正常,但我不满意的是验证表单,然后在将表单发送到服务对象之前找到用户之间的捆绑逻辑。而且,多个闪光警报感觉……嗯……不对

如何通过将这两种方法解耦,使此方法变得更好?现在看起来一个背着另一个

这里是我的
登录用户
服务对象,供您参考

class LoginUser
    include Wisper::Publisher

    attr_reader :user, :password
    attr_accessor :session

    def initialize(user, session, password)
        @user = user
        @session = session
        @password = password
    end

    def execute
        if user.authenticate(password)
            session[:user_id] = user.id
            publish(:user_authenticated, user)
        else
            publish(:user_login_failed)
        end
    end
end

这里最让我印象深刻的是,
create
是一种具有多重责任的方法,可以/应该被隔离

我看到的责任是:

  • 验证表单
  • 查找用户
  • 返回验证错误消息
  • 返回未知用户错误消息
  • 创建LoginService对象,在身份验证行为后设置并执行身份验证
  • 清理这个问题的设计目标是编写具有单一职责的方法,并在可能的情况下注入依赖项

    忽略UserService对象,我的第一次重构可能如下所示:

    def create
      validate_form(user_params); return if performed?
      user = find_user_for_authentication(user_params); return if performed?
    
      login_service = LoginUser.new(user, session, user_params[:password])
      login_service.on(:user_authenticated){ redirect_to root_url, success: "You have logged in" }
      login_service.execute
    end
    
    private
    
    def user_params
      params[:user]
    end
    
    def validate_form(attrs)
      login_form = Forms::LoginForm.new(User.new)
      unless login_form.validate(attrs)
        flash.now.alert = login_form.errors.full_messages
        render :new
      end
    end
    
    def find_user_for_authentication(attrs)
      if (user = User.find_by_email(attrs[:email]))
        user
      else
        flash.now.alert = 'invalid user credentials'
        render :new
      end
    end
    
    请注意,
    如果执行返回?
    条件将检查是否调用了
    渲染
    重定向到
    方法。如果是这样,将调用
    return
    ,并提前完成
    create
    操作,以防止出现双重渲染/重定向错误


    我认为这是一个很大的进步,因为责任被分成了几个不同的方法。这些方法在很大程度上注入了它们的依赖关系,因此它们在未来也可以继续自由发展。

    这是一个令人惊讶的答案。我的
    登录用户
    对象的问题在于,它需要原始密码输入作为依赖项,我发现很难通过需要使用我的
    登录用户
    服务的其他对象传递这些信息。有什么办法可以解决这个问题吗?对不起,我不太明白这个问题。为什么传递原始密码很难?我认为在任何登录场景中,您都可以使用原始密码。也许用代码提出一个新问题,并将其链接到我?