Define_method inside instance_eval

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 http://gist.github.com/407035).

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

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…

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

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

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…

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…

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

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

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…

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/

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 :slight_smile:

On Thu, May 20, 2010 at 09:42, Adriano N. [email protected] wrote:

Posted via http://www.ruby-forum.com/.

Thanks & Regards,
Dhruva S…

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 :slight_smile:

This is on Ruby 1.8.7. Maybe this is a bug?

Regards,


Adriano