Method_missing question

Hello,

I am using method_missing to build a structure (similar to a tree) like
this:

P.book do
  P.title
  P.price
end

class P
  def self.method_missing(method_name, *args, &block)
    #add a node with name 'method_name.to_s' to the tree etc.
  end
end

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?

TIA,
Peter

···

__
http://www.rubyrailways.com

Hello,

I am using method_missing to build a structure (similar to a tree) like
this:

P.book do
  P.title
  P.price
end

class P
  def self.method_missing(method_name, *args, &block)
    #add a node with name 'method_name.to_s' to the tree etc.
  end
end

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?

actually, there is - it is called script and you can find it described here:
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/100429

···

On 12/15/06, Peter Szinek <peter@rubyrailways.com> wrote:

TIA,
Peter

__
http://www.rubyrailways.com

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:...>>

···

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?

George Ogata wrote:

Now, the problem is I would like to write the above example like this:

book do
  title
  price
end

...

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... ?)

Yes, "script" is something else. It's for loading an external "script" inside of a module, to confine it to a namespace. It's like "load(..., true)", but you can access the anonymous module.

It has nothing to do with defining a special syntax like the OP wanted.

···

On 12/15/06, Peter Szinek <peter@rubyrailways.com> wrote:

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

So anyway, here's a starting point. The trick is to use instance_eval
to change the value of 'self' within the block.

More straightforward to grab the block, make it a method of the current
object, then call that method?

require 'pp'

class ReadableThing
  def book &block
    ugly_call_to_get_the_eigenclass = class << self; self; end
    ugly_call_to_get_the_eigenclass.send :define_method, :___secret_method,
block
    send :___secret_method
  end
  
  def title
    pp 'a title'
  end
end

r = ReadableThing.new

r.book do
  puts "self is: #{self.inspect}\n"
  title
end

C:\rx\rv\bin>ruby r.rb
self is: #<ReadableThing:0x28e40fc>
"a title"

It does leave a reference to the block lying around - you'd probably want to
clean that up for production code.

-- James Moore