Ruby on Rails控制器代码,是否需要重构最佳方法以使其更加干燥? [英] Ruby on rails controller code, needs refactor best way to approach for more dry?

查看:69
本文介绍了Ruby on Rails控制器代码,是否需要重构最佳方法以使其更加干燥?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个欢迎的向导,它在首次登录时会建立用户配置文件。问题是实现起来很杂乱,但是我尝试对其进行多次重构和重写,但无法提供比下面更好的东西。

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆