Sudhir Jonathan Sudhir Jonathan - 7 months ago 8
Ruby Question

Ruby scope / ActiveRecord query no-ops

I'm trying to run add a scope in Rails that kicks in only when arguments are passed into it - is there a neat way to avoid the messy

1=1
default argument?

scope :tagged, -> (tags) { [tags].flatten.compact.empty? ? where('1 = 1') : where("#{self.table_name}.tags @> ARRAY[?]::varchar[]", [tags].flatten.compact)}


Basically, what's the equivalent of
where('1=1')
inside a scope that will return the current query and allow continued chaining? Returning
self
doesn't work - it returns the object and all chains need to be constructed again from scratch.

Answer

The scope all solves your problem. Splitting up your huge on-liner into something more readable gives:

scope :tagged, -> (tags) do
  if [tags].flatten.compact.empty? 
    all
  else
    where(
      "#{self.table_name}.tags @> ARRAY[?]::varchar[]",
      [tags].flatten.compact
    )
  end
end

However, this can be taken a step further: If a rails scope returns nil, then it will implicitly call all anyway! In other words, we can simply write:

scope :tagged, -> (tags) do
  if [tags].flatten.compact.present? 
    where(
      "#{self.table_name}.tags @> ARRAY[?]::varchar[]",
      [tags].flatten.compact
    )
  end
end