【问题标题】:How to refactor complex search logic in a Rails model如何在 Rails 模型中重构复杂的搜索逻辑
【发布时间】:2012-11-01 13:51:37
【问题描述】:

我的搜索方法又臭又臃肿,我需要一些帮助来重构它。我是 Ruby 新手,还没有弄清楚如何有效地利用它,这导致了像这样的臃肿方法:

  # discussion.rb
  def self.search(params)
    # If there is a search query, use Tire gem for fulltext search
    if params[:query].present?
      tire.search(load: true) do
        query { string params[:query] }
      end

    # Otherwise grab all discussions based on category and/or filter
    else

      # Grab all discussions and include the author
      discussions = self.includes(:author)

      # Filter by category if there is one specified
      discussions = discussions.where(category: params[:category]) if params[:category]

      # If params[:filter] is provided, user it
      if params[:filter]
        case params[:filter]
        when 'hot'
          discussions = discussions.open.order_by_hot
        when 'new'
          discussions = discussions.open.order_by_new
        when 'top'
          discussions = discussions.open.order_by_top
        else
          # If params[:filter] does not match the above three states, it's probably a status
          discussions = discussions.order_by_new.where(status: params[:filter])
        end
      else

        # If no filter is passed, just grab discussions by hot
        discussions = discussions.open.order_by_hot
      end
    end
  end

  STATUSES   = {
    question:   %w[answered],
    suggestion: %w[started completed declined],
    problem:    %w[solved]
  }

  scope :order_by_hot,  order('...') DESC, created_at DESC")
  scope :order_by_new,  order('created_at DESC')
  scope :order_by_top,  order('votes_count DESC, created_at DESC')

这是一个可以按类别过滤(或不过滤)的讨论模型:questionproblemsuggestion

可以通过hotnewvotesstatus 进一步过滤所有讨论或单个类别。状态是模型中的一个哈希值,它有几个取决于类别的值(状态过滤器只有在 params[:category] ​​存在时才会出现)。

复杂的事情是使用轮胎的全文搜索功能

但我的控制器看起来又漂亮又整洁:

  def index
    @discussions = Discussion.search(params)
  end

我可以把它干掉/重构一下,也许使用元编程或块?我设法将其从控制器中提取出来,但随后就没有想法了。我对 Ruby 的了解还不够深入,无法更进一步。

【问题讨论】:

    标签: ruby-on-rails ruby refactoring dry


    【解决方案1】:

    对于初学者,“基于类别和/或过滤器获取所有讨论”可以是一种单独的方法。

    params[:filter] 重复了很多次,所以把它放在顶部:

    filter = params[:filter]
    

    你可以使用

    if [:hot, :new, :top].incude? filter
      discussions = discussions.open.send "order_by_#{filter}"
    ...
    

    另外,排除 if then else if case else 语句。我更喜欢拆分成单独的方法并尽早返回:

    def do_something
      return 'foo' if ...
      return 'bar' if ...
      'baz'
    end
    

    discussions = discussions... 出现了很多次,但看起来很奇怪。可以改用return discussions...吗?

    为什么常量STATUSES会出现在末尾?通常常量出现在模型的顶部。

    确保在重构​​之前编写所有测试。

    回复关于return 'foo' if ...的评论:

    考虑:

    def evaluate_something
      if a==1
        return 'foo'
      elsif b==2
        return 'bar'
      else
        return 'baz'
      end
    end
    

    我建议将其重构为:

    def evaluate_something
      return 'foo' if a==1
      return 'bar' if b==2
      'baz'
    end
    

    也许你可以重构一些 if..then..else..if 语句。

    推荐书:Clean Code

    【讨论】:

    • 完美。谢谢你。我最终得到了这个:pastie.org/5166966 -- 至于常量,它们在顶部;为了清楚起见,我只是将它们粘贴在底部。
    • PS:不过,不确定您所说的 return 'foot' if 是什么意思。还有重复的discussions = discussions,我把它拿出来了,但我必须把它留给where子句,否则什么都不会回来。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-05
    • 1970-01-01
    • 2017-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多