Passing parameters into a singleton class def?

D

Doug Glidden

Hi,

I'm a little new to Ruby, so please feel free to suggest a completely
different and better way to do this, if you have one. I'm trying to
figure out a way to access a variable within a singleton class
definition that is defined outside the singleton class definition.
Basically, I want to be able to add new, user-defined instance variables
and readers and writers for the instance variables to an object. I can
do it fine if I construct the list of instance variables inline. Given
a definition of MyClass, of course, the following code works
beautifully:

var = MyClass.new
class << var
attr_accessor :field1, :field2, :field3
end

However, I need to be able to build the list prior to doing the
singleton class definition (ideally, I want to be able to pass the list
of instance variables into an instance method and have it take care of
the singleton class definition on itself). This is the way code is
written right now:

class MyClass
...

def add_fields(*field_list)
class << self
attr accessor *field_list
end
end
end

I've also tried the following in irb (identical to the above code that
works, except that the list of fields is assigned to a variable instead
of being constructed inline):

var = MyClass.new
list = :field1, :field2, :field3
class << var
attr_accessor list
end

Both throw an error because field_list is not defined within the
singleton class definition? Am I making a simple mistake, or should I
be doing this differently?

Thanks,
Doug G.
 
D

David Masover

class MyClass
...

def add_fields(*field_list)
class << self
attr accessor *field_list
end
end
end [snip]
Both throw an error because field_list is not defined within the
singleton class definition? Am I making a simple mistake, or should I
be doing this differently?

Well, if you actually have fields called :field1, :field2, and :field3, I
might suggest an array... Assuming that's not the case.

I don't even remember the actual Ruby way to do this, but _why's metaid gem
would let you do it like this:

def add_fields(*field_list)
meta_eval do
attr_accessor *field_list
end
end

The key, however, is that it's some sort of _eval method -- that it's taking a
block, which means you get to inherit the parent scope. In fact, if it's
actually a singleton (as in, only one instance of this class will be
defined), you can do this:

def add_fields(*field_list)
self.class.class_eval do
attr_accessor *field_list
end
end

In this case, self.class refers to the actual class that the current object is
an instance of. On the other hand, self.metaclass or meta_eval (both from
metaid) refer to the current object's metaclass, or eigenclass.

Given that you were doing "class << foo" stuff, I'm assuming you wanted the
metaclass.

One more hack before I'm through:

def add_fields(*field_list)
self.metaclass.send :attr_accessor, *field_list
end

I haven't tested any of this. Let me know how it works for you.
 
S

Sean O'Halpin

Hi,

I'm a little new to Ruby, so please feel free to suggest a completely
different and better way to do this, if you have one. I'm trying to
figure out a way to access a variable within a singleton class
definition that is defined outside the singleton class definition.
Basically, I want to be able to add new, user-defined instance variables
and readers and writers for the instance variables to an object. I can
do it fine if I construct the list of instance variables inline. Given
a definition of MyClass, of course, the following code works
beautifully:

var = MyClass.new
class << var
attr_accessor :field1, :field2, :field3
end

However, I need to be able to build the list prior to doing the
singleton class definition (ideally, I want to be able to pass the list
of instance variables into an instance method and have it take care of
the singleton class definition on itself). This is the way code is
written right now:

class MyClass
...

def add_fields(*field_list)
class << self
attr accessor *field_list
end
end
end

I've also tried the following in irb (identical to the above code that
works, except that the list of fields is assigned to a variable instead
of being constructed inline):

var = MyClass.new
list = :field1, :field2, :field3
class << var
attr_accessor list
end

Both throw an error because field_list is not defined within the
singleton class definition? Am I making a simple mistake, or should I
be doing this differently?

Thanks,
Doug G.
Something like this?

class A
end

a = A.new
fields = [:a, :b]
(class << a; self; end).class_eval do
attr_accessor *fields
end
a.a = 1
a.b = 2
p a
b = A.new
b.a = 3
__END__
#<A:0x25094 @a=1, @b=2>
rt.rb:13: undefined method `a=' for #<A:0x24f40> (NoMethodError)

Regards,
Sean
 
D

Doug Glidden

Sean said:
Something like this?

class A
end

a = A.new
fields = [:a, :b]
(class << a; self; end).class_eval do
attr_accessor *fields
end
a.a = 1
a.b = 2
p a
b = A.new
b.a = 3
__END__
#<A:0x25094 @a=1, @b=2>
rt.rb:13: undefined method `a=' for #<A:0x24f40> (NoMethodError)

Regards,
Sean

David said:
Well, if you actually have fields called :field1, :field2, and :field3, I
might suggest an array... Assuming that's not the case.
I don't even remember the actual Ruby way to do this, but _why's metaid gem
would let you do it like this:
def add_fields(*field_list)
meta_eval do
attr_accessor *field_list
end
end
The key, however, is that it's some sort of _eval method -- that it's taking a
block, which means you get to inherit the parent scope. In fact, if it's
actually a singleton (as in, only one instance of this class will be
defined), you can do this:
def add_fields(*field_list)
self.class.class_eval do
attr_accessor *field_list
end
end
In this case, self.class refers to the actual class that the current object is
an instance of. On the other hand, self.metaclass or meta_eval (both from
metaid) refer to the current object's metaclass, or eigenclass.
Given that you were doing "class << foo" stuff, I'm assuming you wanted the

One more hack before I'm through:
def add_fields(*field_list)
self.metaclass.send :attr_accessor, *field_list
end
I haven't tested any of this. Let me know how it works for you.

Thanks very much to both of you! I'm not using why's metaid gem, but I
am familiar with the way he does things. I had tried using class_eval,
as Sean suggested, but I wasn't using it quite correctly. Sean's code
worked perfectly, but I wanted to make it a little more
compartmentalized, so I used some of why's strategy. Here's the final
working example code:

class MyClass
def metaclass
class << self
self
end
end
...

def add_fields(*field_list)
metaclass.class_eval do
attr_accessor *field_list
end
end
end

Of course, it would be better to move the metaclass method into a mixin,
or just use why's metaid gem.

Thanks again,
Doug
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top