我正在使用has_secure_password
个rails 4.1.5
应用。我想将我的登录功能与我的SessionsController
分离,以便我可以重复使用它来从我的应用程序中的任何位置登录任何用户 - 例如在注册后登录用户,记录分析事件等。
所以我将我的代码重构为LoginUser
服务对象,我很满意。
问题是我的控制器在重构后仍然有一些耦合逻辑。我正在使用表单对象(通过改革gem)进行表单验证,然后将用户,会话和密码传递给LoginUser
服务。
以下是SessionsController
中的创建方法:
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
一切都按预期工作,但我不满意的部分是验证表单之间的绑定逻辑,然后找到用户,然后再将其发送到服务宾语。此外,多重闪光警报感觉......还不错。
如何通过解耦这两种方法来改善这种方法?现在似乎是一个人背着另一个人。
这里有我的LoginUser
服务对象
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
答案 0 :(得分:3)
最重要的是create
是一种具有多重责任的方法,可以/应该被隔离。
我看到的责任是:
清理它的设计目标是编写具有单一责任的方法,并尽可能注入依赖项。
忽略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 if performed?
条件会检查是否已调用render
或redirect_to
方法。如果是这样,则会调用return
并提前完成create
操作,以防止出现双重渲染/重定向错误。
我认为这是一个很大的改进,因为责任已被分解为几种不同的方法。并且这些方法在很大程度上注入了它们的依赖性,以便它们在未来也可以继续自由发展。