Save me from the method_missing

T

transfire

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

Phil Tomson

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!


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
 
T

Trans

Phil said:
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.
 
A

ara.t.howard

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.


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
 
T

transfire

hi ara,
(e-mail address removed) 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]
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.
 
A

ara.t.howard

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
 
T

transfire

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

ara.t.howard

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"
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.
Not sure what you mean.

gsub/require/required/

meaning __id__, __send__, et al.


cheers.

-a
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,780
Messages
2,569,607
Members
45,241
Latest member
Lisa1997

Latest Threads

Top