George
12/16/2006 2:18:00 AM
On 12/15/06, Peter Szinek <peter@rubyrailways.com> wrote:
> Now, the problem is I would like to write the above example like this:
>
> book do
> title
> price
> end
>
> i.e. without the Ps.
> P's sole purpose is to define method_missing - I did not want to
> override Object.method_missing since I would like to release this code
> to the wild and I think it could collide with my potential future user's
> Object.method_missing.
>
> I have then experimented with modules (mimicking namespace
> functionality) but that still did not provide the possibility to omit
> the class name. I would need something equivalent to include - you can
> omit the module name if you include the module - but with classes.
>
> I have no idea if this is possible in Ruby, but is there something like
> run this code in a different context or something?
Hi Peter,
This sort of thing probably already exists somewhere, but I don't know
what it's called. (The 'script' that Michael mentioned looks like
something else to me... ?)
So anyway, here's a starting point. The trick is to use instance_eval
to change the value of 'self' within the block.
class Node
# Rename all existing methods to start with '__', so they're not
# likely to collide with your attribute names. We'll leave some
# methods alone, since they're necessary for some basic operations.
methods = (instance_methods + private_instance_methods)
methods = methods - %w'__send__ __id__ initialize inspect'
methods.each{|m| alias_method("__#{m}", m); undef_method(m)}
def method_missing(*args, &block)
key, val = *args
if block
args.length == 1 or
raise ArgumentError, "value and block given"
node = Node.new
__instance_variable_set("@#{key}", node)
node.__instance_eval(&block)
return node
else
args.length <= 2 or
raise ArgumentError, "too many arguments"
if args.length > 1
__instance_variable_set("@#{key}", val)
end
return __instance_variable_get("@#{key}")
end
end
end
### Usage
node = Node.new
node.root do
a 1
b 2
c do
d 3
e 4
end
end
p node.root.c.d #=> 3
p node.root.c #=> #<Node:0xb7dd55c4 @d=3, @e=4>
p node #=> #<Node:0xb7ddb8ac @root=#<Node:...>>