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.

On 7/15/06, [email protected] [email protected] wrote:

x/:a/:b/:c notation. No bad, that almost as nice. Okay, step back …

def x(n)

end

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

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

Phil T. 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 ara,

[email protected] wrote:

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]

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

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.

On Mon, 17 Jul 2006 [email protected] 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…

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

[email protected] wrote:

 "!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.

On Sun, 16 Jul 2006, Trans wrote:

end

and then, someone else comes along:

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.

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 Tue, 18 Jul 2006 [email protected] 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?

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
)

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