Reputation: 12693
Consider this code:
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
Is there a way to chain these scopes like Car.blue.manual.luxury
conditionally? I.e. only scope if the arg is true?
Upvotes: 3
Views: 149
Reputation: 3615
ActiveRecord scopes can be applied conditionally, like this:
scope :blue, -> { where(color: 'blue') if condition }
Where condition
is something you define that returns true or false. If the condition returns true, the scope is applied. If the condition is false, the scope is ignored.
You can also pass values into a scope:
scope :blue, ->(condition) { where(color: 'blue') if condition }
So, you could do something like this:
Task.blue(color == 'blue')
Which is similar to what the OP requested. But, why would you?
A better approach is something like this:
scope :color, ->(color) { where(color: color) if color.present? }
Which would be called like this:
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])
Your mileage may vary.
Upvotes: 2
Reputation:
You can use yield_self(read more here), new functionality added in ruby 2.5 for it.
In your example:
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
Upvotes: 2