Lambdas and Procs in Named Scopes

Written by on Aug 9 2012

Most people don’t know this, but you can use a Proc instead of a lambda in your named scope. Here’s an example:

class User < ActiveRecord::Base

  scope :hello_lamb, ->(param) { param.blank? ? where("") : where("param = ?", param) }
  scope :hello_proc, Proc.new { |param| param.blank? ? where("") : where("param = ?", param) }

end

User.hello_lamb "a"
#  User Load (0.5ms)  SELECT * FROM users WHERE  (param = 'a')
#=> #<ActiveRecord::Relation:0x3fe6eedeb984>

User.hello_proc "a"
#  User Load (0.5ms)  SELECT * FROM users WHERE  (param = 'a')
#=> #<ActiveRecord::Relation:0x3fe6eedeb984>

User.hello_lamb ""
#  User Load (0.5ms)  SELECT * FROM users
#=> #<ActiveRecord::Relation:0x3fe6eedeb984>

User.hello_proc ""
#  User Load (0.5ms)  SELECT * FROM users
#=> #<ActiveRecord::Relation:0x3fe6eedeb984>

Here we have two scopes that do the same thing. They take a param and if it’s blank they don’t scope anything and if it’s not blank they scope based on it. And as you can see, thus far they are exactly the same thing. You can use a Proc.

But why would you? Because our rubyfu is strong we know that a Proc will ignore empty arguments but a lambda will raise on them. So we choose the one that best fits our needs. Here’s what the two look like:

class User < ActiveRecord::Base

  scope :hello_lamb, ->(param) { param.blank? ? where("") : where("param = ?", param) }
  scope :hello_proc, Proc.new { |param| param.blank? ? where("") : where("param = ?", param) }

end

User.hello_proc
#  User Load (0.5ms)  SELECT * FROM users

User.hello_lamb
#ArgumentError: wrong number of arguments (0 for 1)

In our last example we show the usage of calling the scope with a  Proc. The thing we are passing might be security code, an STI kind of switch, which direction to order, or more likely something specific to your app that is easiest to put right here. In the normal case we’re passing something into the scope that get’s evaluated. But in the rare case where we don’t need to pass anything, I prefer to pass nothing than passing a contrived parameter like an empty string or nil.

class User < ActiveRecord::Base

  scope :hello, Proc.new { |param| param.blank? ? where("") : where("param = ?", param) }

end

User.hello(current_user_or_other_security_thig_or_somethng)

User.hello( "" )
User.hello

I’m not saying you should do any of this. I’m just showing you that you can.

Meet
Steven

Hi I'm Steven,

I wrote the article you're reading... I lead the developers, write music, used to race motorcycles, and help clients find the right features to build on their product.

Get Blog Updates