43

I'm working through the 'Ruby On Rails 3 Essential Training' and have received a problem when using name scopes. When finding records and using queries withing the Rails console everything went smoothly until I tried to use a name scope in my subject.rb file. This is my code in the subject.rb file.

Class Subject < ActiveRecord::Base

  scope :visible, where(:visible => true)

end   

I saved the .rb file and restarted my Rails console but when I run from my rails console:

subjects = Subject.visible

I get: ArgumentError: The scope body needs to be callable.

Does anyone know why I'm getting this error.

Arslan Ali
  • 17,418
  • 8
  • 58
  • 76
Sam Gruse
  • 488
  • 1
  • 5
  • 11

4 Answers4

86

The scope's body needs to be wrapped in something callable like a Proc or Lambda:

scope :visible, -> {
  where(:visible => true)
}

The reason for this is that it ensures the contents of the block is evaluated each time the scope is used.

infused
  • 24,000
  • 13
  • 68
  • 78
  • Okay this makes sense and I can't wait to try it tomorrow morning so I can continue learning. Thanks for the quick response. Any guesses to why the instructor didn't use any wrapping in his example. It seemed to work with him. I also noticed that the newest version of ruby doesn't use the: "self.up" "self.down" labeling. The instructor said ruby doesn't need that in the newer versions but I had to include it in order to make migrations work. It's gets confusing when there are different versions of rails, since the training videos were made. Thanks again. – Sam Gruse Mar 10 '15 at 00:16
  • 2
    @SamGruse this is the Rails 4 way of doing it, the course seems to use Rails 3 – reillyse Apr 09 '15 at 23:36
6

I got the same error , while before my solution I had a space between where and ( like below

scope :registered , -> { where ( place_id:  :place_id , is_registered: :true ) }

after i removed the space between where and ( like below i made my page working

scope :registered , -> { where( place_id:  :place_id , is_registered: :true ) }
Kick Buttowski
  • 6,709
  • 13
  • 37
  • 58
Mani
  • 2,391
  • 5
  • 37
  • 81
3

Yes, indeed, this is rails 4 way of calling scopes. You'd need to change it, if you're upgrading to Rails 4 from Rails 3.

Pradeep S
  • 2,289
  • 2
  • 16
  • 6
0

What you are using: scope :visible, where(:visible => true) goes for eager loading, and has been deprecated in Rails 4.

scope :visible, where(:visible => true)

This line of code gets evaluated when the particular class is loaded, and not at the time, this very scope is called.

There are a few cases when this thing does matter, like:

scope :future, where('published_at > ?', Time.now)
scope :future, -> { where('published_at > ?', Time.now) }

In first case, ? will be replaced with the very time the class would have been loaded, but the second & correct case, that time will be used at which the scope would have been called on the class.

Arslan Ali
  • 17,418
  • 8
  • 58
  • 76