Caveats with #method_missing

T

Trans

Even after the public/private send issue is resolved with #funcall,
#send!, #instance_send or what ever is method decided upon, how will we
be ablesto distinguish between a private and a public method call in
method_missing?

Also when delaing with writers, the need to do things like:

def method_missing( sym, *args, &blk )
if sym.to_s =~ /=$/
@data[ sym.to_s.chomp('=').to_sym ] = args[0]

Is, well... Blech! I'd like to see something like:

def writer_missing( name, value )
@data[ name ] = value
end

Thoughts?
T.
 
T

Trans

/what ever is method decided upon, how will we be ablesto distinguish/
whichever method is decided upon, how will we be able to distinguish

T.
 
B

Brian Mitchell

Even after the public/private send issue is resolved with #funcall,
#send!, #instance_send or what ever is method decided upon, how will we
be ablesto distinguish between a private and a public method call in
method_missing?

Also when delaing with writers, the need to do things like:

def method_missing( sym, *args, &blk )
if sym.to_s =~ /=$/
@data[ sym.to_s.chomp('=').to_sym ] = args[0]

Is, well... Blech! I'd like to see something like:

def writer_missing( name, value )
@data[ name ] = value
end

Thoughts?
T.

I've thought of proposing something like define_method but for missing methods:

# Just some rough ideas...

class Example
define_missing_method /^find_\w+$/ do |sym, *args, &block|
puts "Handling #{sym}"
end
end

ex1 = Example.new

ex1.respond_to? :find_something #=> true
ex1.respond_to? :not_found #=> false

We could probably extend a few methods around this as well:

ex1.missing_methods # Maybe return patterns/strings
ex1.methods # We could possibly return something here.. big change
ex1.all_methods # This might be a better alternative for a combined list

Both of these ideas should be simple enough to implement prototypes in
pure Ruby. I'll have to spend time on this and get some code that we
can play with.

I do like the simpler method definition but I don't really like the
idea of having magically connected names. It would also mean we would
have problems around "method_missing" as a name. I would rather get
some nice helpers that hook into a much smarter method_missing system.

Brian.
 
K

kbloom

Brian said:
Even after the public/private send issue is resolved with #funcall,
#send!, #instance_send or what ever is method decided upon, how will we
be ablesto distinguish between a private and a public method call in
method_missing?

Also when delaing with writers, the need to do things like:

def method_missing( sym, *args, &blk )
if sym.to_s =~ /=$/
@data[ sym.to_s.chomp('=').to_sym ] = args[0]

Is, well... Blech! I'd like to see something like:

def writer_missing( name, value )
@data[ name ] = value
end

Thoughts?
T.

I've thought of proposing something like define_method but for missing methods:

# Just some rough ideas...

class Example
define_missing_method /^find_\w+$/ do |sym, *args, &block|
puts "Handling #{sym}"
end
end

ex1 = Example.new

ex1.respond_to? :find_something #=> true
ex1.respond_to? :not_found #=> false

We could probably extend a few methods around this as well:

ex1.missing_methods # Maybe return patterns/strings
ex1.methods # We could possibly return something here.. big change
ex1.all_methods # This might be a better alternative for a combined list

Both of these ideas should be simple enough to implement prototypes in
pure Ruby. I'll have to spend time on this and get some code that we
can play with.

I do like the simpler method definition but I don't really like the
idea of having magically connected names. It would also mean we would
have problems around "method_missing" as a name. I would rather get
some nice helpers that hook into a much smarter method_missing system.

Brian.

Roll your own (or use mine). I decided here to pass the matchinfo,
rather than the symbol, so that you could take advantage of match
groups. Modify to suit.

module MissingMethod
def method_missing symbol,*args
matchinfo=nil
code=nil
if self.class.methods_missing.any? do |pattern,code|
matchinfo= pattern.match(symbol.to_s)
end
code.call(matchinfo,*args) { |*args| yield *args}
else
super
end
end

module ClassMethods
def define_missing_method pattern,&code
methods_missing << [pattern,code]
end
def methods_missing
@methods_missing ||= []
end
end
def self.included(other)
other.extend(ClassMethods)
end
end

class A
include MissingMethod
define_missing_method /^(.*)=/ do |matchinfo,newval|
@data ||= Hash.new
@data[matchinfo[1]] = newval
end
define_missing_method /^(.*)/ do |matchinfo|
@data[matchinfo[1]]
end
end

a=A.new
a.myfunc="a"
p a.myfunc
 
T

Tomasz Wegrzanowski

Even after the public/private send issue is resolved with #funcall,
#send!, #instance_send or what ever is method decided upon, how will we
be ablesto distinguish between a private and a public method call in
method_missing?

I have an modest proposal - can't we just get rid of private methods
completely in Ruby 2 ?

They are very ugly and not very useful.
* You don't get a separate namespace for them (unlike C++), so they
aren't really that private
* In most other object-oriented systems, public/private distinction is
useful to protect instance variables. In Ruby instance variables are
private anyway. And how often private methods are actually useful ?
* Private methods are counterproductive. People just keep finding new
ways of using objects. A random example - Class#define_method is
private but shouldn't be. And unit testing becomes more difficult
(every method should be tested, whether marked private or not).
* It complicates mental model of Ruby. Instead of having only a single
operation "send message to an object" it needs two - "send message to
an object" and "send message to self", which are very similar but just
different enough to cause confusion.
* Having private methods causes all sorts of ugly problems. We need
two versions of send (and __send__), two versions of method_missing,
two versions of define_method, etc. It all goes away if all methods
are private.

There's still a problem with global functions, as they are going to
pollute method namespace. However the method namespace is already
getting
polluted that way, but only for "send message to self" operation, not for
generic "send message to an object". So it's not that bad.

For cases where we really need to avoid this polution (and want to
catch everything by method_missing), we have BasicObject.
Reflection becomes a bit uglier as Class#methods now returns
"real" methods and global functions on a single list.
That's the only problems I see.

This proposal is radical. Maybe too radical.
But wouldn't Ruby be nicer this way ?
 
D

David Vallner

--=-CSL3sACh7uuU0QBHo+a6
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

I have an modest proposal - can't we just get rid of private methods
completely in Ruby 2 ?
[snip justification]

The private modifier in Ruby is usually more advisory than anything
else. That's why #define_method is private, for example. Just like
private instance variables indicate data that clients of the class
shouldn't touch, private methods indicate operations clients of the
class shouldn't touch. Note "shouldn't" as opposed to "can't". Certainly
clients of a class have no business redefining its methods on a regular
basis - if that was intended, the class would be engineered for
inheriting and clients could create their own customized versions. Using
metaprogramming for this is more of an emergency measure when the author
of the modified code didn't foresee the need for this and code
accordingly.

In my code, I use private methods for helper operations to dissect an
algorithm into separate steps - these serve only to give fragments of
code a name, not encapsulate any standalone functionality. Calling them
out-of-order from client code would usually clobber an object's
internals beyond repair.

In all OO systems, the private / public distinction is to hide
information about the inner workings of a class. While instance
variables are obviously such information, often also how a class
achieves what it does qualifies.

If you have to call __methods__ directly, that's one more indication
that you're Doing Something Weird.

The difficulty in unit-testability is more of a failure on the side of
the test framework. Including a "whitebox" mode in Test::Unit
(generating public wrappers for private methods of tested classes) would
be a more concise solution to this problem.

The feature of private methods in Ruby is one I like. It's an
encapsulation safety net as opposed to a roadblock in Ruby, but makes it
more apparent in code that something out-of-the-ordinary is being done.

I might be biased, since I'm opposed to strong usage of Ruby's
metaprogramming capacities in "serious" code, at least without
encapsulating such code very tightly. But removing all method-level
encapsulation to facilitate meta hacks or get rid of some scenarios when
they are necessary seems just overkill.

David Vallner

--=-CSL3sACh7uuU0QBHo+a6
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2.2 (GNU/Linux)

iD8DBQBFICOUy6MhrS8astoRAoDbAJ90zLbbDRuT32fp6M+ymI4opsaKcQCcCDP7
JAej0P3dFkYAEjeG/evrsKI=
=FGPP
-----END PGP SIGNATURE-----

--=-CSL3sACh7uuU0QBHo+a6--
 

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

No members online now.

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,233
Latest member
AlyssaCrai

Latest Threads

Top