【问题标题】:Rails 2.3.8 named_scope chainingRails 2.3.8 named_scope 链接
【发布时间】:2026-01-15 01:10:02
【问题描述】:

我有以下嵌套的 if 语句毛球,我想知道是否有更有效的方法来编写此代码(代码行更少,不需要这么多条件)

每个方法在模型中都被命名为_scopes..

box = (params[:b] ? params[:b] : "inbox")
show = (params[:s] ? params[:s] : "all")

if box == "inbox"
  if show == "all"
    @messages = @current_user.received_messages.all  
  elsif show == "unread"
    @messages = @current_user.received_messages.unread.all  
  elsif show == "read"
    @messages = @current_user.received_messages.read.all  
  elsif show == "starred"
    @messages = @current_user.received_messages.starred.all  
  else
    @messages = []
  end
elsif box = "sent"
  @messages = @current_user.sent_messages.all  
else
  @messages = []
end

我的想法是我可以对框使用“调用”类型的方法并显示类似

@current_user.received_messages.call(:box).call(:show).all

也许..?

呃,应该多花点时间玩一下..和我想的一样,我只是使用了错误的方法解决方案

@current_user.send(box).send(show)

【问题讨论】:

    标签: ruby-on-rails named-scope


    【解决方案1】:

    您可以使用 rails 2.3.8 中的 scoped() 方法链接作用域:

    main_method = case (params[:s]||"inbox")
    when "inbox"
      :received_messages
    when "sent"
      :sent_messages
    end
    # create a scope. Don't execute the scope yet. 
    scope = @current_user.send(main_method).scoped({}) if main_method
    
    # now chain the scope if needed
    scope = begin
      if ["unread", "read", "starred"].include?(params[:s])
        scope.send(params[:s])
      elsif((params[:s] || "all") == "all")
        scope
      end
    end if main_method == :received_messages
    
    @messages = scope.nil? ? [] : scope.all
    

    参考:

    Scoped method source in Rails 2.3.x

    Rails casts on Anonymous scopes in 2.3.x

    【讨论】:

    • 我不知道作用域方法,这基本上是rails 3中arel魔法之前的步骤吗?所以我可以创建查询,然后在附加所有条件后运行它?
    • 我添加了一些对该方法的引用。观看指定的 Railscast 以了解更多信息。
    • 太棒了,谢谢!这是对 rails 2.3.9 的补充吗?在 2.3.8 中不可用?
    • 2.3.x 也有。
    【解决方案2】:

    您的答案非常接近,但无法转换 box 值。

    box  = params[:b] || 'inbox'
    show = params[:s] || 'all'
    
    box_scope = case box
      when 'inbox' then 'received_messages'
      when 'sent'  then 'sent_messages'
    end
    show_scope = show # no convertion needed at this point, maybe in the future
    
    # If box_scope is nil, don't query
    @messages = box_scope.nil? ? [] : @current_user.send(box_scope).send(show_scope)
    

    这假设您取消了您在 all 选项的原始代码中使用的.all,该选项在您的答案中消失了。

    【讨论】:

    • 让我想知道如果使用除all 之外的任何显示 选项选择sent 框会发生什么。
    • 是的,我实际上最终更改了链接,以便 :b 作为接收和发送的消息进入,但我喜欢你在那里进行的 box_scope = case gig,哈哈,谢谢!问题:box_scope 怎么会变成 nil?如果有一个未知的值进来,它不会是未定义的吗?
    • 对,我们要确保它是过滤器选项之一,否则只显示“全部”
    • @Rabbott,在 Ruby 中,当所有条件都不匹配时,所有 caseif 块都会返回 nil
    【解决方案3】:

    这是我想出来的,除非每个人都同意这是最好的,否则我不会将自己的答案标记为正确 - 还有其他想法吗?

    if params[:b].present? && ["received_messages", "sent_messages"].include?(params[:b])
      box = params[:b]
    
      if params[:s].present? && ["all", "unread", "starred"].include?(params[:s])
        show = params[:s]
      else
        show = "all"
      end
    
      @messages = @current_user.send(box).send(show)
    else
      @messages = []
    end
    

    【讨论】:

    • 请使用box = params[:b:] || "received_messages" 而不是三元语句。
    • 我实际上只是改变了它,因为我有 .include?() 我不需要 ||在所有..
    • 如果您已经检查了参数是否包含在数组中,则无需检查参数是否存在。它将检查是否包含nil,如果不存在,则返回false