anr
May 20, 2010, 3:28am
1
Hi,
I’m trying to create a class macro that, when called, defines a class
method that returns the array passed to the class macro.
For example, I’d have something like this in an included module:
def status_field(*args)
instance_eval { define_method(:status_all) { args } }
end
(I’ve put a complete example at status_field.rb · GitHub ).
I was under the impression that methods created inside instance_eval
would be defined inside the singleton class, but apparently
define_method seems to ignore that (why?).
So I used the string form of instance_eval and produced this eyesore:
instance_eval %{
def status_all
“#{args.join(’ ')}”.split
end
}
I must be missing something. Any hints on how to solve this problem
properly? I’d also like to avoid using eval on strings.
Thanks!
–
Adriano
anr
May 20, 2010, 3:52am
2
Well the first thing that I see is that your missing the (*args) in the
method
instance_eval %{
def status_all
“#{args.join(’ ')}”.split
end
}
Should be
instance_eval %{
def status_all(*args)
“#{args.join(’ ')}”.split
end
}
On Thu, May 20, 2010 at 06:59, Adriano N. [email protected] wrote:
“#{args.join(’ ')}”.split
Posted via http://www.ruby-forum.com/ .
–
Thanks & Regards,
Dhruva S…
anr
May 20, 2010, 4:04am
3
Adriano N. wrote:
Sorry, I wasn’t clear. This snippet is actually wrapped inside the class
macro, like this:
def status_field(*args)
instance_eval %{
def status_all
“#{args.join(’ ')}”.split
end
}
end
BTW, it sort of works. But I don’t like it at all… It is brittle,
probably unsafe and slow.
I’d like to use a block syntax, for instance.
Thanks,
–
Adriano
anr
May 20, 2010, 3:58am
4
Dhruva S. wrote:
Should be
instance_eval %{
def status_all(*args)
“#{args.join(’ ')}”.split
end
}
Sorry, I wasn’t clear. This snippet is actually wrapped inside the class
macro, like this:
def status_field(*args)
instance_eval %{
def status_all
“#{args.join(’ ')}”.split
end
}
end
Thanks,
–
Adriano
anr
May 20, 2010, 4:11am
5
You can do this :
def status_field(*args)
instance_eval { define_method(:status_all) {|*args| “#{args.join(’
')}”.split } }
end
or you can do this :
def status_field(*args)
class << self
def status_all(*args)
“#{args.join(’ ')}”.split
end
end
end
On Thu, May 20, 2010 at 07:28, Adriano N. [email protected] wrote:
macro, like this:
–
Adriano
Posted via http://www.ruby-forum.com/ .
–
Thanks & Regards,
Dhruva S…
anr
May 20, 2010, 4:42am
6
Ok I reread your problem.
In your status_all method you can simply return a args.to_a . That
should
be the simplest approach.
def status_field(*args)
instance_eval { define_method(:status_all) {|*args| args.to_a } }
end
But I am not sure exactly why your taking this approach or what your
intentions are. What should be the behavior on subsequent calls to
status_field ?
On Thu, May 20, 2010 at 06:59, Adriano N. [email protected] wrote:
“#{args.join(’ ')}”.split
Posted via http://www.ruby-forum.com/ .
–
Thanks & Regards,
Dhruva S…
anr
May 20, 2010, 5:24am
7
Dhruva S. wrote:
Ok I reread your problem.
In your status_all method you can simply return a args.to_a . That
should
be the simplest approach.
def status_field(*args)
instance_eval { define_method(:status_all) {|*args| args.to_a } }
end
Actually, args already is an array and you don’t need it as an argument
for status_all (it was passed to status_field).
But the problem here is that this will produce an instance method, not a
class method.
But I am not sure exactly why your taking this approach or what your
intentions are. What should be the behavior on subsequent calls to
status_field ?
I’m extending ActiveRecord, and status_field does more than just define
status_all. For example:
class User < ActiveRecord::Base
status_field :inactive, :active, :suspended
end
will create scopes, instance methods like status_inactive,
status_inactive?, etc.
User.status_all should return all the possible user status. I have it
working, but I would like to learn a better way to do it.
Thanks,
–
Adriano
anr
May 20, 2010, 4:29am
8
Dhruva S. wrote:
You can do this :
def status_field(*args)
instance_eval { define_method(:status_all) {|*args| “#{args.join(’
')}”.split } }
end
or you can do this :
def status_field(*args)
class << self
def status_all(*args)
“#{args.join(’ ')}”.split
end
end
end
Dhruva, I’d like to get rid of the string manipulation, it would be much
cleaner without it. BTW, the args variable is passed to status_field but
not to status_all to keep things DRY.
Thanks,
–
Adriano
anr
May 20, 2010, 5:45am
9
To define class methods from a class method :
def status_field(*args)
self.metaclass.send(:define_method, :status_all) { args }
end
OR
def status_field(*args)
(class << self; self; end).instance_eval { define_method(:status_all) {
args } }
end
On Thu, May 20, 2010 at 08:54, Adriano N. [email protected] wrote:
I’m extending ActiveRecord, and status_field does more than just define
working, but I would like to learn a better way to do it.
Thanks,
–
Adriano
Posted via http://www.ruby-forum.com/ .
–
Thanks & Regards,
Dhruva S…
anr
May 20, 2010, 6:49am
10
Its not a bug imo
I am pretty much a amateur and so I keep getting confused between the
various eval methods myself.
Check out this article -
http://web.elctech.com/2009/01/14/the-difference-between-eval-class_eval-module_eval-and-instance_eval/ Seems
like if your method status_field was a class method
def self.status_field(*args)
then instance_eval should perhaps create a class method as demonstrated
in
the article.
Maybe someone who knows better should also comment
On Thu, May 20, 2010 at 09:42, Adriano N. [email protected] wrote:
Posted via http://www.ruby-forum.com/ .
–
Thanks & Regards,
Dhruva S…
anr
May 20, 2010, 6:12am
11
Dhruva S. wrote:
def status_field(*args)
(class << self; self; end).instance_eval { define_method(:status_all) {
args } }
end
Yep, this works.
Notice that, once you open the metaclass, you can use either
instance_eval or class_eval to define a singleton method.
I guess I still can’t come to terms with define_method not generating a
singleton method when used inside instance_eval
This is on Ruby 1.8.7. Maybe this is a bug?
Regards,
–
Adriano