Reputation: 1266
I know how to check arity of a lambda, but I do not know how I can extract it from scope.
This question was asked 4 years ago:
https://groups.google.com/forum/#!topic/rubyonrails-core/7Cs0T34mj8c
All, In the course of working on a change for the meta-search gem I've run into an issue with the way that scopes are implemented in ActiveRecord. In order to include a scope in a search we pass the name of the scope to a search method like so:
{:name_of_my_scope => 1}
. Meta-search automatically passes the"1"
as an argument to the scope. This causes anArgumentError
with lambda scopes that don't take an argument.My intention was to check the arity of the scope before calling and dropping the
"1"
in the event the scope didn't take an argument. My issue is that the implementation of scope wraps scope_options up in a lambda that passes *args to the block (active_record/named_scope.rb: 106). This results in the call to arity always returning -1 regardless of the actual number of arguments required by the scope definition.Is there a different way to implement scopes that would allow exposing the arity from the scope definition?
Ex.
class Post < ActiveRecord::Base scope :today, lambda {where(:created_at => (Time.new.beginning_of_day...(Time.new.end_of_day)) } end irb> Post.today.arity # => -1
It asks for help in finding scope's arity before calling it.
Has a solution been found?
Upvotes: 5
Views: 744
Reputation: 18762
There are no explicit parameters for scope
methods as shown below
irb(main):070:0> User.method(:active_users).parameters
=> [[:rest, :args]]
The :rest
means that arguments are collected as an array. This allows for one to have no arguments to any number of arguments.
For the scopes that accept parameters, if you invoke with wrong number of arguments, you will receive an ArgumentError
- somewhat like below:
ArgumentError: wrong number of arguments (1 for 2)
We could take advantage of this ArgumentError to figure out the arity, as arity is present in the error message.
Let me caution you that it is kind of a hack, may be it can be useful if you are in dire need to figure out arity of lambda-based scopes.
We can define a method in the Model
which will try to invoke the scope
with uncommonly large number of arguments, say 100 arguments - and thus force an ArgumentError
- we can then capture the message and extract the arity from it.
A possible implementation of such method is shown below:
def self.scope_arity scope_symbol
begin
send(scope_symbol, (1..100).to_a)
rescue ArgumentError => e
f = e.message.scan(/for\s+(\d+)/).flatten.first
f.to_i.nonzero? || 0
end
end
Possible usage can be like below:
irb(main):074:0> User.scope_arity(:active_users)
=> 2
Upvotes: 3