If you're just trying to get a working Expando to use, use OpenStruct . But if you do this for educational value, let them fix the mistakes.
Arguments method_missing
When you call person.name = "Michael" , this translates to a call to person.method_missing(:name=, "Michael") , so you do not need to stretch the parameter using a regular expression. The value you assign is a separate parameter. Hence,
if method_id.to_s[-1,1] == "="
instance_variable_set
Instance variable names begin with the @ symbol. This is not just syntactic sugar, it is actually part of the name. Therefore, to set the instance variable, you need to use the following line:
instance_variable_set("@#{name}", arguments[0])
(Notice also how we pulled out the value that we assign from the arguments array)
class_eval
self.class refers to the Expando class as a whole. If you define attr_accessor on it, then each expando will have an accessor for this attribute. I do not think you want.
Rather, you need to do this inside the class << self block (is it a single class or eigenclass self ). This works inside eigenclass for self .
So we would do
class << self; attr_accessor name.to_sym ; end
However, the name variable is not actually available internally, so we need to first select the singleton class, and then run class_eval . The usual way to do this is to do this using the eigenclass native method eigenclass So, we define
def eigenclass class << self; self; end end
and then call self.eigenclass.class_eval { attr_accessor name.to_sym } )
Decision
Put it all together and the final solution works
class Expando def eigenclass class << self; self; end end def method_missing(method_id, *arguments) if method_id.to_s[-1,1] == "=" name=method_id.to_s[0...-1] eigenclass.class_eval{ attr_accessor name.to_sym } instance_variable_set("@#{name}", arguments[0]) else super.method_missing(method_id, *arguments) end end end