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
--
Posted via http://www.ruby-forum.com/.

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 Nagel <anr@safira.com> wrote:

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
--
Posted via http://www.ruby-forum.com/\.

--

Thanks & Regards,
Dhruva Sagar.

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 Nagel <anr@safira.com> wrote:

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
--
Posted via http://www.ruby-forum.com/\.

--

Thanks & Regards,
Dhruva Sagar.

Dhruva Sagar 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
--
Posted via http://www.ruby-forum.com/\.

Dhruva Sagar 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
--
Posted via http://www.ruby-forum.com/\.

Adriano Nagel 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
--
Posted via http://www.ruby-forum.com/\.

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 Nagel <anr@safira.com> wrote:

Dhruva Sagar 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
--
Posted via http://www.ruby-forum.com/\.

--

Thanks & Regards,
Dhruva Sagar.

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 Nagel <anr@safira.com> wrote:

Dhruva Sagar 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
--
Posted via http://www.ruby-forum.com/\.

--

Thanks & Regards,
Dhruva Sagar.

Dhruva Sagar 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
--
Posted via http://www.ruby-forum.com/\.

Dhruva Sagar 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
--
Posted via http://www.ruby-forum.com/\.

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/&gt;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 Nagel <anr@safira.com> wrote:

Dhruva Sagar 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
--
Posted via http://www.ruby-forum.com/\.

--

Thanks & Regards,
Dhruva Sagar.