Ruby on Rails控制器代码,是否需要重构最佳方法以使其更加干燥? [英] Ruby on rails controller code, needs refactor best way to approach for more dry?
问题描述
我有一个欢迎的向导,它在首次登录时会建立用户配置文件。问题是实现起来很杂乱,但是我尝试对其进行多次重构和重写,但无法提供比下面更好的东西。
I have a welcome wizzard that builds a user profile when first login.Problem is it is quite messy implemented but I have tried to refactor it several times and rewrite it but cannot comeup with something better then below.
在理想的世界中,所有内容都在welcome_controller.rb中,但这引起了很多麻烦,所以现在我改写了profile_controller的更新方法。
In ideal world it would be all inside welcome_controller.rb but this have caused much headache so Now i rewrote the update method for profile_controller instead.
是否有任何关于如何改善此效果的想法,使其变得更加干燥和清洁?是否希望在此方面获得一些好的建议,并且可能会提出一些建议
Any thoughts on how to improve this make it more dry and clean? Would love to recieve some good input on this and thoughts perhaps to move all update stuff to welcome controller instead?
WelcomeController:
WelcomeController:
class WelcomeController < ApplicationController
before_filter :authenticate_user!
before_filter :load_step
layout "welcome"
def sub_layout
"center"
end
def edit
# form updates post to edit since
# profile is non existant yet
params[:step] = "photos" unless params[:step]
@photos = Photo.where(:attachable_id => current_user.id)
@profile = Profile.where(:user_id => current_user.id).first
@photo = Photo.new
if ["photos", "basics", "details", "test"].member?(params[:step])
# force rendering the correct step
case current_user.profile.step
when 1
render :template => "/profiles/edit/edit_photos", :layout => "welcome"
when 2
render :template => "/profiles/edit/edit_basics", :layout => "welcome"
when 3
render :template => "/profiles/edit/edit_details", :layout => "welcome"
when 4
render :template => "/profiles/edit/edit_test", :layout => "welcome"
end
else
render :action => "/profiles/edit/edit_photos"
end
end
def load_step
redirect_to root_path if current_user.profile.complete
case current_user.profile.step
when 1
redirect_to "/welcome" unless params[:controller] == "welcome"
when 2
redirect_to "/welcome/basics" unless params[:controller] == "welcome" && params[:action] == "edit" && params[:step] == "basics"
when 3
redirect_to "/welcome/details" unless params[:controller] == "welcome" && params[:action] == "edit" && params[:step] == "details"
when 4
redirect_to "/welcome/test" unless params[:controller] == "welcome" && params[:action] == "edit" && params[:step] == "test"
end
end
end
ProfileController:
ProfileController:
class ProfileController < ApplicationController
...
def update
@profile = Profile.find(params[:id])
@tags = Session.tag_counts_on(:tags)
@profile.form = params[:form]
@match = Match.where(:user_id => current_user.id).first
authorize! :update, @profile
respond_to do |format|
if @profile.update_attributes(params[:profile])
if current_user.profile.complete
format.html { redirect_to "/profiles/#{ current_user.username }/edit/#{ @profile.form }", notice: t('notice.saved') }
else
case current_user.profile.step
when 1
current_user.profile.update_attributes(:step => 2)
format.html { redirect_to "/welcome/basics", notice: t('notice.saved') }
when 2
current_user.profile.update_attributes(:step => 3)
format.html { redirect_to "/welcome/details", notice: t('notice.saved') }
when 3
current_user.profile.update_attributes(:step => 4)
format.html { redirect_to "/welcome/test", notice: t('notice.saved') }
end
end
else
if current_user.profile.complete
format.html { render action: "/edit/edit_" + params[:profile][:form], :what => @profile.form }
else
case current_user.profile.step
when 1
current_user.profile.update_attributes(:step => 2)
format.html { redirect_to "/welcome/basics", notice: t('notice.saved') }
when 2
current_user.profile.update_attributes(:step => 3)
format.html { redirect_to "/welcome/details", notice: t('notice.saved') }
when 3
current_user.profile.update_attributes(:complete => 1)
format.html { redirect_to root_path }
end
end
end
end
end
...
end
视图位于/ profiles / edit / *
Views are in /profiles/edit/*
推荐答案
众所周知,向导很难正确解决,而且我从未见过完全令我满意的实现。我通常会使用所谓的表单对象,并为每个步骤创建一个宁静的控制器。
Wizards are notoriously difficult to get right and I've never seen an implementation that fully satisfied me. I usually go with so called "form objects" and create a restful controller for each step.
有一个很好的(但收费)关于此主题的铁路广播。
There is an excellent (but paid) Railscast on the subject.
要点是:您使
例如:
class Welcome::BasicInformation
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
def persisted?
false
end
def initialize(user)
@user = user
end
attr_reader :user
delegate :some_field, :some_other_field, to: :user
validates_presence_of :some_field
def save(params)
user.some_field = params[:some_field]
user.some_other_field = params[:some_other_field]
if valid?
user.step = 2
user.save
end
end
def photo
@photo ||= Photo.new
end
def profile
@profile ||= user.profiles.first
end
end
基本上,每个步骤都要创建一个这样的模型。
You'd basically create a model like this for every step.
然后您可以为每个步骤创建控制器,并为所有步骤使用专用的ApplicationController:
Then you can create controllers for each step, with a specialized ApplicationController for all the steps:
class Welcome::ApplicationController < ::ApplicationController
layout "welcome"
before_filter :authentice_user!
end
每一步:
class Welcome::BasicInformationsControlller < Welcome::ApplicationController
def new
@step = Welcome::BasicInformation.new(current_user)
end
def create
@step = Welcome::BasicInformation.new(current_user)
if @step.save(params[:welcome_basic_information])
redirect_to welcome_some_other_step_path, notice: "Yay"
else
render :new
end
end
end
为每个步骤创建一条路线:
And create a route for each step:
namespace :welcome do
resource :basic_information, only: [:new, :create]
resource :some_other_step, only: [:new, :create]
end
这只会留下一些自动重定向,例如禁止用户进入他们尚未被允许访问的步骤。由于您在每个步骤中使用单独的URL,因此这可能不再那么重要。
This only leaves some automatic redirects to do, like prohibiting users from going to steps that they're not yet allowed to visit. This might not be as important now that you're using separate URLs for each step.
您可以在表单对象中存储有关要访问的步骤的信息:
You can store information about which step to visit in the form objects:
class Welcome::BasicInformation
# ...
def allowed?
user.profile.step == 1
end
end
然后重构一下控制器:
class Welcome::BasicInformationsController < Welcome::ApplicationController
before_filter :allowed?
def new
end
def create
if step.save(params[:welcome_basic_information])
redirect_to welcome_some_other_step_path, notice: "Yay"
else
render :new
end
end
private
def step
@step ||= Welcome::BasicInformation.new(current_user)
end
helper_method :step
def allowed?
redirect_to previous_step_path unless step.allowed?
end
end
这可能不会更短,但是我确实喜欢它的灵活性,每个步骤的重点,如何对每个步骤进行不同的验证等等。每个控制器/模型组合都很容易理解,其他人也可以理解。
This might not be shorter, but I do like how flexible it is, how focussed each step is, how you can do different validations on each step and so on. Each controller/model combination is very easy to follow and will be understandable for others.
这篇关于Ruby on Rails控制器代码,是否需要重构最佳方法以使其更加干燥?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!