重构这个控制器?

时间:2011-06-29 01:49:52

标签: ruby-on-rails-3 refactoring

class ArticlesController < ApplicationController

  def index
    @articles = Article.by_popularity

    if params[:category] == 'popular'
      @articles = @articles.by_popularity
    end

    if params[:category] == 'recent'
      @articles = @articles.by_recent
    end

    if params[:category] == 'local'
      index_by_local and return
    end

    if params[:genre]
      index_by_genre and return
    end

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @articles }
    end
  end

  def index_by_local
    # 10 lines of code here

    render :template => 'articles/index_by_local'
  end

  def index_by_genre
    # ANOTHER 10 lines of code here

    render :template => 'articles/index_by_genre'
  end
end

从上面可以看出。我的控制器不完全。它的作用是,根据传递的参数,它与模型交互以过滤掉记录。

如果params[:local]params[:genre]通过了。然后它分别调用自己的方法(def index_by_localdef index_by_genre)来进行进一步处理。这些方法也会加载自己的模板,而不是index.html.erb

控制器看起来很典型吗?或者我应该以某种方式重构这个?

2 个答案:

答案 0 :(得分:1)

我们可以将前几行移到模型中(article.rb):

def get_by_category(category)
  # Return articles based on the category.
end

通过这种方式,我们可以使用单元测试完全测试文章获取逻辑。

通常移动与获取模型内部记录相关的所有代码。 一般的控制器

  1. 应该授权用户
  2. 使用参数获取记录并将它们分配给实例变量 [这些通常必须是功能 调用模型]
  3. 渲染或重定向

答案 1 :(得分:1)

我将为您要使用的每个集合定义范围。

class Article < ActiveRecord::Base
  ...
  scope :popular, where("articles.popular = ?", true) # or whatever you need
  scope :recent, where(...)
  scope :by_genre, where(...)
  scope :local, where(...)
  ...

  def self.filtered(filter)
    case filter
    when 'popular'
      Article.popular, 'articles/index'
    when 'recent'
      Article.recent, 'articles/index'
    when 'genre'
      Article.by_genre, 'articles/index_by_genre'
    when 'local'
      Article.local, 'articles/index_by_local'
    else
      raise "Unknown Filter"
    end
  end
end

然后在你的控制器动作中,像这样:

def index
  @articles, template = Article.filtered(params[:category] || params[:genre])
  respond_to do |format|
    format.html { render :template => template }
    format.xml  { render :xml => @articles }
  end
end