Is it possible to negate a scope in Rails 3?

五迷三道 提交于 2019-12-03 22:06:02
Ryan Bigg

I wouldn't use a single scope for this, but two:

scope :with_missing_coins, joins(:coins).where("coins.is_missing = ?", true)
scope :without_missing_coins, joins(:coins).where("coins.is_missing = ?", false)

That way, when these scopes are used then it's explicit what's happening. With what numbers1311407 suggests, it is not immediately clear what the false argument to with_missing_coins is doing.

We should try to write code as clear as possible and if that means being less of a zealot about DRY once in while then so be it.

In Rails 4.2, you can do:

scope :original, -> { ... original scope definition ... }
scope :not_original, -> { where.not(id: original) }

It'll use a subquery.

numbers1311407

There's no "reversal" of a scope per se, although I don't think resorting to a lambda method is a problem.

scope :missing_coins, lambda {|status| 
  joins(:coins).where("coins.is_missing = ?", status) 
}

# you could still implement your other scopes, but using the first
scope :with_missing_coins,    lambda { missing_coins(true) }
scope :without_missing_coins, lambda { missing_coins(false) }

then:

Collection.with_missing_coins
Collection.without_missing_coins

this might just work, did not test it much. uses rails 5 I guess rails 3 has where_values method instead of where_clause.

        scope :not, ->(scope_name) { query = self
      send(scope_name).joins_values.each do |join|
      query = query.joins(join)
    end
                                    query.where((send(scope_name).where_clause.send(:predicates).reduce(:and).not))}

usage

Model.not(:scope_name)

Update . Now Rails 6 adds convenient and nifty negative enum methods.

# All inactive devices
# Before
Device.where.not(status: :active)
#After 
Device.not_active

Blog post here

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!