Ruby on rails控制器代码,需要重构最好的方法才能更干燥?

我有一个欢迎wizzard,在首次登录时构建用户个人资料。 问题是它是非常混乱的实现,但我试图重构它几次并重写它,但不能用更好的东西,然后下面。

在理想的世界中,它将全部在welcome_controller.rb中,但是这引起了很大的麻烦所以现在我改写了profile_controller的更新方法。

有关如何改善这一点的任何想法使它更干燥和干净? 是否愿意收到一些关于此的好的意见和想法,或许将所有更新的东西转移到欢迎控制器?

WelcomeController:

class WelcomeController  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可:

 class ProfileController  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 / *

众所周知,奇才很难做到正确,我从来没有见过完全满足我的实施。 我通常会使用所谓的“表单对象”并为每一步创建一个安静的控制器。

关于这个主题有一个很好的(但付费的) Railscast 。

要点是这样的:使用ActiveModel创建一个像常规ActiveRecord模型一样嘎嘎叫的对象。

例如:

 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 

你基本上每一步都要创建一个这样的模型。

然后,您可以为每个步骤创建控制器,并为所有步骤使用专门的ApplicationController:

 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 

并为每个步骤创建一条路线:

 namespace :welcome do resource :basic_information, only: [:new, :create] resource :some_other_step, only: [:new, :create] end 

这只会留下一些自动重定向,例如禁止用户进入他们尚未被允许访问的步骤。 由于您为每个步骤使用单独的URL,因此这可能不那么重要。

您可以在表单对象中存储有关要访问的步骤的信息:

 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 

这可能不会更短,但我确实感觉它是多么灵活,每个步骤的重点是什么,如何在每个步骤上进行不同的validation等等。 每个控制器/模型组合都非常容易理解,对其他人来说是可以理解的。

我会做一些事情,但首先是一些想法。

  1. 有时候你必须稍微rest一下才能使代码更具可读性。 情况就是这样
  2. 像在这里一样,在控制器之间重定向并不是一种好方法

那么,我要做什么。

  1. 将有关这些步骤的所有代码放在一个控制器(最好是配置文件)中,并使用路由调整URL。
  2. 创建单个节目和单个保存操作

如果我理解正确,将向用户显示的步骤仅取决于current_user上设置的用户#步骤。 因此我认为没有必要传递任何url变量,你可以从current_user获取当前/下一步。

代码重构(可能是一些错误,没有测试)All in ProfileController

  def edit @profile = Profile.find(current_user.id) @next_step = current_user.step.to_i + 1 # I imply that there's just single permissable next step render :template => "/profiles/edit/#{@next_step}", :layout => "welcome" end def update @profile = Profile.find(params[:id]) authorize! :update, @profile if @profile.update_attributes(params[:profile]) # you should pass step number in params so I get's updated by default. redirect_to "/welcome/basics", notice: t('notice.saved') else end end