Save me from the method_missing

I'm find the the whole method_missing, privative clashing methods,
except essential ones, call send otherwise . . . is just a pain. The
whole thing is just to have that nice interface "obj.foo, obj.foo=x".
That might not seem like much but it really stand out in contrast:

  obj[:a][:b][:c] -vs- obj.a.b.c

So, dang, if that's all we want, some beauty in our code, and the whole
method_missing thing is just too problematic why not just have another
means? Anuone having a look at Hpricot recently will note the
x/:a/:b/:c notation. No bad, that almost as nice. Okay, step back ...

I was thinking... soon we will become acustom to space significance
with the new Hash notation

  { x: :a } != { x::a }

If we're gogin to be dealing with space significance here anyway, why
not go with it give us the ability to reusue ':'?

  class X < Hash

    # parameter is always a symbol

    def :(x)
      self[x]
    end

    def :=(x,y)
      self[x] = y
    end

    def x(n)
      n
    end

  end

  x = X
  x:y = 4
  x:y #=> 4
  x(:y) #=> :y
  x :y #=> :y

So then we can easily do

  obj:a:b:c

That's just about as nice. Of course the disadvantage is that it's not
very ducky --if you take my meaning. Of course, thinking about that,
one might be inclined to ask, why not just have a special Dispatcher
superclass?

  class MyClass < Dispatcher

    # only two methods allowed

    def .(x, *args, &blk)
      ...
    end

    def .=(x,y)
      ...
    end

  end

If only .... then I wouldn't be in so deep. Someone save me!

T.

So what is it exactly that you're trying to do? Are you creating a
DSL, or something?

method_missing can certainly have it's problems and it needs to be
used with care, but I'm certainly glad it's there.

Phil

···

On 7/15/06, transfire@gmail.com <transfire@gmail.com> wrote:

I'm find the the whole method_missing, privative clashing methods,
except essential ones, call send otherwise . . . is just a pain. The
whole thing is just to have that nice interface "obj.foo, obj.foo=x".
That might not seem like much but it really stand out in contrast:

  obj[:a][:b][:c] -vs- obj.a.b.c

So, dang, if that's all we want, some beauty in our code, and the whole
method_missing thing is just too problematic why not just have another
means? Anuone having a look at Hpricot recently will note the
x/:a/:b/:c notation. No bad, that almost as nice. Okay, step back ...

I was thinking... soon we will become acustom to space significance
with the new Hash notation

  { x: :a } != { x::a }

If we're gogin to be dealing with space significance here anyway, why
not go with it give us the ability to reusue ':'?

  class X < Hash

    # parameter is always a symbol

    def :(x)
      self
    end

    def :=(x,y)
      self = y
    end

    def x(n)
      n
    end

  end

  x = X
  x:y = 4
  x:y #=> 4
  x(:y) #=> :y
  x :y #=> :y

So then we can easily do

  obj:a:b:c

That's just about as nice. Of course the disadvantage is that it's not
very ducky --if you take my meaning. Of course, thinking about that,
one might be inclined to ask, why not just have a special Dispatcher
superclass?

  class MyClass < Dispatcher

    # only two methods allowed

    def .(x, *args, &blk)
      ...
    end

    def .=(x,y)
      ...
    end

  end

If only .... then I wouldn't be in so deep. Someone save me!

Phil Tomson wrote:

So what is it exactly that you're trying to do? Are you creating a
DSL, or something?

Basically, albiet a lower-level sort of one. It all goes back to
Facet's Annotations lib. Annotations work like this:

  class X
    ann :x, :class => String, :default => 10, :foo => :bar
  end

  X.ann.x.class #=> String
  X.ann.x.default #=> 10
  X.ann.x.foo #=> :bar

  X.ann.x.foo = :baz
  X.ann.x.foo #=> :baz

  X.ann.x => #<Annotation(X#x) {:class => String, :default => 10, :foo
=> :baz}>

Annotation class is a subclass of OpenObject, which is much like
OpenStruct, but it dynamically prevents method visibility --hence my
previous post on #private_filter. And I need that to ensure no new
methods pop up that would interfere with OpenObject's functionality.
For example:

  o = OpenObject.new
  o.yep = 10
  o.yep #=> 10

and then, someone else comes along:

  class Object
    def yep ; "your sunk" ; end
  end

My method_missing in OpenObject wouldn't catch #yep anymore and she'd
be sunk.

method_missing can certainly have it's problems and it needs to be
used with care, but I'm certainly glad it's there.

No doubt. No suggestion here of getting rid, or anything of the sort.
Just looking for a sounder means for the above type of usecase.

T.

hi tom-

i use method_missing too but, in this case, you'd have to provide an extremely
strong argument why an interface like this won't suffice:

   harp:~ > cat a.rb
   class Module
     def ann meth, arg = nil
       @ann ||= {}
       if Hash === arg
         arg ? (@ann[meth] ||= {}).update(arg) : @ann[meth]
       else
         arg ? @ann[meth][arg] : @ann[meth]
       end
     end
   end

   class X
     ann :x, :class => String, :default => 10, :foo => :bar
   end

   X.ann :x, :class #=> String
   X.ann :x, :default #=> 10
   X.ann :x, :foo #=> :bar

   X.ann :x #=> #<Annotation(X#x) {:class => String, :default => 10, :foo > :baz}>

   harp:~ > ruby -W0 a.rb
   String
   10
   :bar
   {:default=>10, :class=>String, :foo=>:bar}

it's simple and rather concise - adding only a char or so. more importanlty,
however, the impl above actually conforms to the specification i imagine your
annotation class requires, which is that __any__ token might be used as an
annotation tag, for instance

   ann 'x', __id__ => method('x').__id__

which no method_missing approach will ever quite be able to skin.

anyhow - just playing devil's advocate - but i think it's an important
consideration.

cheers.

ps.

   klass.ann.x.prop

becomes much longer if the arg list is in a variable, for instance, with my
impl above it would be

   args = %w( x prop )

   klass.ann *args

but, using a method_missing approach it becomes

   args = %w( x prop )

   klass.ann.send(args.first).send(args.last)

this applies to many method_missing approaches: they are more consise until
another layer of abstraction is added, like having the methods be read from a
config file, food for thought...

-a

···

On Sun, 16 Jul 2006, Trans wrote:

Phil Tomson wrote:

So what is it exactly that you're trying to do? Are you creating a
DSL, or something?

Basically, albiet a lower-level sort of one. It all goes back to
Facet's Annotations lib. Annotations work like this:

class X
   ann :x, :class => String, :default => 10, :foo => :bar
end

X.ann.x.class #=> String
X.ann.x.default #=> 10
X.ann.x.foo #=> :bar

X.ann.x.foo = :baz
X.ann.x.foo #=> :baz

X.ann.x => #<Annotation(X#x) {:class => String, :default => 10, :foo
=> :baz}>

Annotation class is a subclass of OpenObject, which is much like
OpenStruct, but it dynamically prevents method visibility --hence my
previous post on #private_filter. And I need that to ensure no new
methods pop up that would interfere with OpenObject's functionality.
For example:

o = OpenObject.new
o.yep = 10
o.yep #=> 10

and then, someone else comes along:

class Object
   def yep ; "your sunk" ; end
end

My method_missing in OpenObject wouldn't catch #yep anymore and she'd
be sunk.

method_missing can certainly have it's problems and it needs to be
used with care, but I'm certainly glad it's there.

No doubt. No suggestion here of getting rid, or anything of the sort.
Just looking for a sounder means for the above type of usecase.

--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

hi ara,

good food for thought. thanks ara. ...

i use method_missing too but, in this case, you'd have to provide an extremely
strong argument why an interface like this won't suffice:

[snip]

   X.ann :x, :class #=> String
   X.ann :x, :default #=> 10
   X.ann :x, :foo #=> :bar

   X.ann :x #=> #<Annotation(X#x) {:class => String, :default => 10, :foo > :baz}>

   harp:~ > ruby -W0 a.rb
   String
   10
   :bar
   {:default=>10, :class=>String, :foo=>:bar}

it's simple and rather concise - adding only a char or so.

yea that's a nice implemementation esspecially for it's simplicity.
while i *tend* toward your opinion there are couple of problems. the
big one is of course my end users really likes the dot notation. and i
can understand, it certainly has a certain elegance and ease to it.
unfortunately it's a nightmare to code. btw, the notation X.ann(:x)
does work already in what I have, but another thing is this use:

  X.ann :a, :b, :c, :class => String

  X.ann.a.class #=> String
  X.ann.b.class #=> String
  X.ann.c.class #=> String

Also there's a shortcut for setting the :class annotation.

  X.ann :a, :b, :c, String

I imagine that still can be worked in to your impl, but it does starts
to get a little thicker.

I should also note that it's not just annotations, I use it lots else
where too. I esspecially like using Facets' OpenCascade on YAML load
configs.

  s = %{
    a:
      b:
        c: "abc"
  }

  data = OpenCascade[ YAML.load(s) ]

  data.x.y.z #=> "abc"

(OT. Notice BTW how I use :: on OpenCascade. It's a subclass of Hash.
In an earlier post I mentioned how I felt that ::new should be able to
take a hash rather than a default value --well there's why. I actually
just spent ~2 hours tracknig a bug that came down to using ::new when I
should have used . That sucked!)

more importanlty,
however, the impl above actually conforms to the specification i imagine your
annotation class requires, which is that __any__ token might be used as an
annotation tag, for instance

   ann 'x', __id__ => method('x').__id__

which no method_missing approach will ever quite be able to skin.

You're right about that. There are limitations to using method_missing.
Though I don't mind so much if the limitations are *well defined* and
minimial, but the current state of affairs is too iffy.

ps

   klass.ann.x.prop

becomes much longer if the arg list is in a variable, for instance, with my
impl above it would be

   args = %w( x prop )

   klass.ann *args

but, using a method_missing approach it becomes

   args = %w( x prop )

   klass.ann.send(args.first).send(args.last)

this applies to many method_missing approaches: they are more consise until
another layer of abstraction is added, like having the methods be read from a
config file, food for thought...

Well, that could be solved with a special interface, eg.

  klass.ann.cascade_send *args

or something. But it's a good point too.

T.

···

ara.t.howard@noaa.gov wrote:

I imagine that still can be worked in to your impl, but it does starts
to get a little thicker.

yes, a bit. very doable though...

I should also note that it's not just annotations, I use it lots else where
too. I esspecially like using Facets' OpenCascade on YAML load configs.

s = %{
   a:
     b:
       c: "abc"
}

data = OpenCascade[ YAML.load(s) ]

data.x.y.z #=> "abc"

(OT. Notice BTW how I use :: on OpenCascade. It's a subclass of Hash.
In an earlier post I mentioned how I felt that ::new should be able to
take a hash rather than a default value --well there's why. I actually
just spent ~2 hours tracknig a bug that came down to using ::new when I
should have used . That sucked!)

yikes. i've done exactly this kind of this many times but i'm tending away
from it. the reason is that the spec just doesn't quite fit when s is defined
thusly:

   s = <<-yml

     "hard to use" : "forty-two"

     42 : "impossible to use"

     "!can't work" : "etc"

   yml

i suppose it's ok to contrain configs you have keys that are valid ruby
methods, still i've just found myself in a corner with that a few times and
have strayed away from it every since. like i said, i still have lots of code
doing stuff with method_missing, but i thought i'd put my thought out there
for posterity and for people starting new designs.

You're right about that. There are limitations to using method_missing.
Though I don't mind so much if the limitations are *well defined* and
minimial, but the current state of affairs is too iffy.

what do you mean 'iffy' exactly? that the list of 'require' methods seems to
change every few months?

regards.

-a

···

On Mon, 17 Jul 2006 transfire@gmail.com wrote:
--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

yikes. i've done exactly this kind of this many times but i'm tending away
from it. the reason is that the spec just doesn't quite fit when s is defined
thusly:

   s = <<-yml

     "hard to use" : "forty-two"

     42 : "impossible to use"

     "!can't work" : "etc"

   yml

i suppose it's ok to contrain configs you have keys that are valid ruby
methods, still i've just found myself in a corner with that a few times and
have strayed away from it every since. like i said, i still have lots of code
doing stuff with method_missing, but i thought i'd put my thought out there
for posterity and for people starting new designs.

Yes, I do constrain the config keys to single word case insensitive
strings. Unfortuate I just don't see a nicer way. "y[:a][:b][:c]" just
lacks all elegance. And path notation (y/:a/:b/:c) while a _little_
better can't use setting at the end (eg. y/:a/:b/:c = x). So what else
is there?

> You're right about that. There are limitations to using method_missing.
> Though I don't mind so much if the limitations are *well defined* and
> minimial, but the current state of affairs is too iffy.

what do you mean 'iffy' exactly?

Well, for instance. If a public method is added to Kernel or Object (or
any subclass for that matter) it will show up in ones class and block
out method_missing.

that the list of 'require' methods seems to change every few months?

Not sure what you mean.

T.

···

ara.t.howard@noaa.gov wrote:

brainstorming:

harp:~ > cat a.rb
require 'yaml'

class Config < ::Hash
   def self.new(yml) super().update YAML.load(yml.to_s) end
end
def Config(*a, &b) Config.new(*a, &b) end

c =
   Config %(
     a:
       b:
         c: 42
   )

···

On Tue, 18 Jul 2006 transfire@gmail.com wrote:

Yes, I do constrain the config keys to single word case insensitive strings.
Unfortuate I just don't see a nicer way. "y[:a][:b][:c]" just lacks all
elegance. And path notation (y/:a/:b/:c) while a _little_ better can't use
setting at the end (eg. y/:a/:b/:c = x). So what else is there?

#
# just an idea
#
   class Config < ::Hash
     def munge_keys *keys
       keys.compact!
       keys.flatten!
       keys.map!{|key| key.to_s.strip.split %r/\s/}
       keys
     end
     def *keys
       keys = munge_keys keys
       h = self
       nil while Hash === (h = h.fetch keys.shift.to_s)
       h
     end
     def = *argv
       v, keys = argv.pop, argv
       keys = munge_keys keys
       h, h2, k = self, nil, nil
       h = h2 while Hash === (h2 = h.fetch(k = keys.shift.to_s))
       h[k] = v
     end
   end

   p c[ :a, :b, :c ]

   c[ :a, :b, :c ] = 'forty-two'

   p c[ :a, :b, :c ]

   p c

#
# another, sillier one
#
   class Config
     alias_method '^', ''
   end

   p c ^ %w( a b c )

harp:~ > ruby a.rb
42
"forty-two"
{"a"=>{"b"=>{"c"=>"forty-two"}}}
"forty-two"

what do you mean 'iffy' exactly?

Well, for instance. If a public method is added to Kernel or Object (or
any subclass for that matter) it will show up in ones class and block
out method_missing.

i see three ways to get around this:

   a) use block form methods

       obj.configure{

         # push method_missing handler and dsl methods onto stack

         # instance_eval the block

         # pop them off

       }

     so

       obj.configure{
         a 4
         b 2
       }

     this gives you a chance to 'clean-out' the obj each time.

   b) use block form with blank slate proxy

     obj.configure{
       # setup blank-slate proxy

       # instance_eval the block on proxy!

       # copy all state from proxy into object
     }

   c) use block form with an easily identifiable method name. i do this in my
   xx html/xml generation lib where

     puts xhtml_{

       p_{ 'note that the "p" tag works! }

       b_{ i_{ u_{ "bold-italic-underline } } }

     }

   i can filter __only__ those methods ending in '_'. this has many
   advantages:

     - easy to distiguish ruby's methods like 'p' from html methods like 'p_'

     - easy to grep for 'special' methods in you source.

i realize none of these really apply to a config style class.

that the list of 'require' methods seems to change every few months?

Not sure what you mean.

gsub/require/required/

meaning __id__, __send__, et al.

cheers.

-a
--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama