27

Upgrading Rails 3.2. to Rails 4. I have the following scope:

# Rails 3.2
scope :by_post_status, lambda { |post_status| where("post_status = ?", post_status) }
scope :published, by_post_status("public")
scope :draft, by_post_status("draft")

# Rails 4.1.0
scope :by_post_status, -> (post_status) { where('post_status = ?', post_status) }

But I couldn't find out how to do the 2nd and 3rd lines. How can I create another scope from the first scope?

Victor
  • 13,010
  • 18
  • 83
  • 146

2 Answers2

56

Very simple, just same lambda without arguments:

scope :by_post_status, -> (post_status) { where('post_status = ?', post_status) }
scope :published, -> { by_post_status("public") }
scope :draft, -> { by_post_status("draft") }

or more shorted:

%i[published draft].each do |type|
  scope type, -> { by_post_status(type.to_s) }
end
Roman Kiselenko
  • 43,210
  • 9
  • 91
  • 103
7

From the Rails edge docs

"Rails 4.0 requires that scopes use a callable object such as a Proc or lambda:"

scope :active, where(active: true)

# becomes 
scope :active, -> { where active: true }


With this in mind, you can easily rewrite you code as such:

scope :by_post_status, lambda { |post_status| where('post_status = ?', post_status) }
scope :published, lambda { by_post_status("public") }
scope :draft, lambda { by_post_status("draft") }

In the event that you have many different statuses that you wish to support and find this to be cumbersome, the following may suit you:

post_statuses = %I[public draft private published ...]
scope :by_post_status, -> (post_status) { where('post_status = ?', post_status) }

post_statuses.each {|s| scope s, -> {by_post_status(s.to_s)} }
wvandaal
  • 4,265
  • 2
  • 16
  • 27
  • So what's the difference between method and scope. I mean, if you want for example limit the number of user, you can do it either by method or by scope, right ? Is it just a best practice to use scope instead of method in this case ? – Fabien Dobat Aug 11 '15 at 16:00