可以在Rails中有条件地链接范围吗?

时间:2018-12-17 00:34:33

标签: ruby-on-rails

考虑以下代码:

class Car
  scope :blue, -> { where(color: "blue") }
  scope :manual, -> { where(transmission: "manual") }
  scope :luxury, -> { where("price > ?", 80000) }
end

def get_cars(blue: false, manual: false, luxury: false)
  cars = Car.all
  cars = cars.blue if blue
  cars = cars.manual if manual
  cars = cars.luxury if luxury
end

是否可以有条件地像Car.blue.manual.luxury那样链接这些范围?即仅当arg为true时才作用域?

2 个答案:

答案 0 :(得分:2)

您可以使用 yield_self read more here),它是ruby 2.5中添加的新功能。

在您的示例中:

class Car
  scope :blue, -> { where(color: "blue") }
  scope :manual, -> { where(transmission: "manual") }
  scope :luxury, -> { where("price > ?", 80000) }
end

def get_cars(blue: false, manual: false, luxury: false)
  cars = Car.all
            .yield_self { |cars| blue ? cars.blue : cars }
            .yield_self { |cars| manual ? cars.manual : cars }
            .yield_self { |cars| luxury ? cars.luxury : cars }
end

答案 1 :(得分:1)

ActiveRecord范围可以有条件地应用,如下所示:

scope :blue, -> { where(color: 'blue') if condition }

您定义condition的地方返回true或false。如果条件返回true,则应用范围。如果条件为假,则范围将被忽略。

您还可以将值传递给范围:

scope :blue, ->(condition) { where(color: 'blue') if condition }

因此,您可以执行以下操作:

Task.blue(color == 'blue')

与OP要求的内容相似。但是,为什么呢?

更好的方法是这样的:

scope :color, ->(color) { where(color: color) if color.present? }

将这样称呼:

Car.color('blue')  # returns blue cars
Car.color(nil)     # returns all cars

Car.color(params[:color])  # returns either all cars or only cars of a specific color, depending on value of param[:color]

Car.color(params[:color]).transmission(params[:transmission]).price(params[:price])

您的里程可能会有所不同。