I did a quick spike to see if Kernel.as could provide a solution.
protected
"James "
end
end
protected
"Lynn "
end
end
attr_accessor :person
include James
include Lynn
as(person).name
end
"Holbrook"
end
end
member = FamilyMember.new
member.person = James
member.name # => "James Holbrook"
member.person = Lynn
member.name # => "Lynn Holbrook"
member.first_name rescue "first_name is not defined" # => "first_name is not defined"
There's a few interesting things about this solution. I can change the behavior at runtime by changing the person attribute. Also, the behavior is actually on the class instead of living on an independent state object that delegates back to the class. Lastly, each method is protected so they must be accessed via the delegations, and an error will occur if they are accessed directly.
"each method is protected so they must be accessed via the delegations, and an error will occur if they are accessed directly"
ReplyDeleteYou got me here. Till now I used to think that Ruby copies all the definition of a module into the definition of the class that includes it. Going by that logic, I couldn't explain your program's behavior as name method would be redefined and the modules' methods would be lost... I consulted "Programming Ruby":
If a module is included within a class definition, the module's constants, class variables, and instance methods are effectively bundled into an anonymous (and inaccessible) superclass for that class. In particular, objects of the class will respond to messages sent to the module's instance methods.
Your program made better sense now. The original definitions were not lost as they reside in a superclass (anonymous one). However this brought an interesting question -- what happens when two modules are included in a class... surely one class cannot have two superclasses! I wrote some code that proved there was only one superclass and any included module overwrote any methods that were included already by an earlier included module.
I'll have to check what Kernel#as does 'cos I still don't get it how you have not lost the definitions of any name() methods... ie, unless you explain the same here to save me all that hunting. ;-)
Hello Aman,
ReplyDeleteAs always, thanks for the comment.
Here's an explanation: http://blog.jayfields.com/2007/08/ruby-calling-methods-of-specific.html
As always, thanks for the explanation, Jay. :-)
ReplyDeleteHmm... I really should have checked the class's ancestors. A class cannot have multiple inheritence but it sure can have multi-level inheritence. That approach pretty much explains it all. :-)
Thanks for bringing it to my notice and also explaining the "As" stuff. Really cool.
For anyone interested, here's another state pattern implementation http://github.com/dcadenas/state_pattern
ReplyDelete