preventing Object#send from dispatching to a global method?

F

Francis Hwang

Is there a way to prevent Object#send from dispatching to a global
method? By which I mean:

def something; end

class SomeClass
def method_missing( sym, *args ) .... do magic; end
end

SomeClass.new.something # I'd like this to go to
SomeClass#method_missing, not Kernel#something

I can manage this other ways, but I'd love to use something like send,
if possible.

Francis Hwang
http://fhwang.net/
 
B

Bertram Scharpf

Hi,

Am Sonntag, 29. Mai 2005, 00:42:46 +0900 schrieb Francis Hwang:
Is there a way to prevent Object#send from dispatching to a global
method? By which I mean:

def something; end

class SomeClass
def method_missing( sym, *args ) .... do magic; end
end

SomeClass.new.something # I'd like this to go to
SomeClass#method_missing, not Kernel#something

class SomeClass
undef something
end

Probably you thought of no specific method, so one should
remove them all. This causes an error:

class SomeClass
Kernel.methods.each { |x| undef_method x.to_sym }
end

Is there a way to un-include?

Bertram
 
F

Francis Hwang

Yeah, that makes sense. I can use that workaround, but the code I'm
looking at is in a library that others might use, and I'd rather not
impose that workaround on others who might accidentally define global
methods that trip over my library's reflection. I wonder if this an
appropriate place for an RCR ... seems to me that with all the Ruby
libs out there that use some intense reflection, somebody else must be
worrying about this? Maybe not.


Imo, this is a result of global methods becoming methods on the Object
class. If they had been made singleton methods of the "main" object
your
method_missing would work as you expect.

Francis Hwang
http://fhwang.net/
 
J

Jim Weirich

--Apple-Mail-2--559893624
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
charset=US-ASCII;
delsp=yes;
format=flowed


Is there a way to prevent Object#send from dispatching to a global
method? By which I mean:

You could look at how Builder::BlankSlate handles it. It uses method
defined hooks to undefine any methods added to any ancestors. You
can find BlankSlate in the builder gem.

For example

traken$ irb --simple-promptCalling hi()
=> nilCalling hi()
=> nil


--Apple-Mail-2--559893624--
 
F

Francis Hwang

Okay, thanks for the tip, that's definitely giving me some ideas.
Builder::BlankSlate's strategy seems like something for me to follow.
Here's an added piece of complexity: this logic has to depend on values
set during the class definition. I'm talking, BTW, about Lafcadio's
DomainObjects. So:

def fname; "here's an unfortunate coincidence"; end

class User < Lafcadio::DomainObject
text 'fname'
text 'lname'
end

I wanted to trigger some methods to undefine instance methods of
'fname' in User, but I can't do that with Class.inherited -- because at
the time that the User class is being defined, I don't know that I'm
looking for global methods named 'fname' and 'lname'.

It feels like, from perusing the archives, there is currently no way to
be notified when a class definition is finished. Correct?


You could look at how Builder::BlankSlate handles it. It uses method
defined hooks to undefine any methods added to any ancestors. You can
find BlankSlate in the builder gem.

For example

traken$ irb --simple-prompt
Calling hi()
=> nil
Calling hi()
=> nil

Francis Hwang
http://fhwang.net/
 
J

Jim Weirich

I wanted to trigger some methods to undefine instance methods of
'fname' in User, but I can't do that with Class.inherited -- because at
the time that the User class is being defined, I don't know that I'm
looking for global methods named 'fname' and 'lname'.

It feels like, from perusing the archives, there is currently no way to
be notified when a class definition is finished. Correct?

I don't know of any ... however, would this help?

class User < Lafcadio::DomainObject
fields do
text 'fname'
text 'lname'
end
end

You can have fields perform actions at the end of the block. I don't know if
this is good enough, or if you really need it at the end of the class def.
 
G

Glenn Parker

Francis said:
It feels like, from perusing the archives, there is currently no way to
be notified when a class definition is finished. Correct?

Not that I could find, and I've mentioned this as an annoying limitation
of Ruby myself.
 
F

Francis Hwang

I don't know of any ... however, would this help?

class User < Lafcadio::DomainObject
fields do
text 'fname'
text 'lname'
end
end

You can have fields perform actions at the end of the block. I don't
know if
this is good enough, or if you really need it at the end of the class
def.

Thanks for the suggestion, but that's probably too cumbersome. For the
time being I suppose I'll just have to warn users of the lib not to
have global methods named the same as the auto-methods. It's probably
not a common problem, anyway.

Francis Hwang
http://fhwang.net/
 
N

Nicholas Seckar

Francis said:
Thanks for the suggestion, but that's probably too cumbersome. For the
time being I suppose I'll just have to warn users of the lib not to
have global methods named the same as the auto-methods. It's probably
not a common problem, anyway.

Francis Hwang
http://fhwang.net/

Perhaps Jamis Buck's BlankSlate might be what you're looking for?

See here: http://onestepback.org/index.cgi/Tech/Ruby/BlankSlate.rdoc

Feel free to echo this to the list if it's the correct answer.


IMPOSSIBLE, adj:

(1) I wouldn't like it and when it happens I won't approve;
(2) I can't be bothered;
(3) God can't be bothered.
- The Hipcrime Vocab by Chad C. Mulligan
 
F

Francis Hwang

Perhaps Jamis Buck's BlankSlate might be what you're looking for?

See here: http://onestepback.org/index.cgi/Tech/Ruby/BlankSlate.rdoc

Feel free to echo this to the list if it's the correct answer.

Don't you mean Jim's BlankSlate? Seeing as http://onestepback.org is
Jim's blog and all.

Anyway, as I said before, that doesn't work in my odd case because
Lafcadio uses class methods within the class definition, and my code
would need to know that stuff, but it's invoked after the class
definition begins, so it wouldn't work in this case.

Francis Hwang
http://fhwang.net/
 
G

Glenn Parker

Francis said:
Should I submit an RCR? Haven't done one before. Would it be called?
Class#inherited_finished ?

I would say it's worth an RCR, but a class definition is never really
finished, so a different name is needed. Maybe Module#end_update would
make more sense. This would be a callback that is invoked when the
interpreter gets to the final "end" of a Module/Class block. Extra
points if it passed a list of constants that were added, removed, or
redefined.

Can you accomplish your original goal with Module#method_added? If
Lafcadio::DomainObject#text defines new methods, that seems like a
sufficiently solid hook.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: preventing Object#send from dispatching to a global method?"

|Is there a way to prevent Object#send from dispatching to a global
|method?

How about undef'ing global methods from the class? See delegate.rb in
the distribution.

matz.
 
F

Francis Hwang

Coming back to this thread after a long hiatus, sorry about that ...

I would say it's worth an RCR, but a class definition is never really
finished, so a different name is needed. Maybe Module#end_update
would make more sense. This would be a callback that is invoked when
the interpreter gets to the final "end" of a Module/Class block.
Extra points if it passed a list of constants that were added,
removed, or redefined.

Can you accomplish your original goal with Module#method_added? If
Lafcadio::DomainObject#text defines new methods, that seems like a
sufficiently solid hook.

I can't; DomainObject#text doesn't actually define a new method, it
just adds to a class-level array of fields that are accessed in
method_missing. I actually solved part of the problem by using
undef_method, as Matz suggested. Another part of the problem is still
unsolved, so I'll be responding to Matz' email in a few seconds,
explaining it in more detail ...

Francis Hwang
http://fhwang.net/
 
F

Francis Hwang

Hi,

In message "Re: preventing Object#send from dispatching to a global
method?"
on Sun, 29 May 2005 00:42:46 +0900, Francis Hwang

|Is there a way to prevent Object#send from dispatching to a global
|method?

How about undef'ing global methods from the class? See delegate.rb in
the distribution.

matz.

Part of the problem is solved, part is not.

Part 1, the solved part:
Children of Lafcadio::DomainObject. This is normally subclassed by
somebody using the library, and then fields are set using one-line
directives:

def name; "global name method"; end

class Client < Lafcadio::DomainObject
text :name
integer :standard_rate
datetime :created
end

c = Client.new( 'name' => 'My first client', 'standard_rate' => 100,
'created' => Time.now )
puts c.name # was returning 'global name method', I fixed it to return
'My first client'

I fixed this by finding the spot in a class method where all the class
fields are being collated for the first time:

class Lafcadio::DomainObject
def self.class_fields #:nodoc:
class_fields = @@class_fields[self]
unless class_fields
@@class_fields[self] = self.get_class_fields
class_fields = @@class_fields[self]
class_fields.each do |class_field|
begin
undef_method class_field.name.to_sym
rescue NameError
# not defined globally or in an included Module, skip it
end
end
end
class_fields
end
end

that solved it, though I'm not crazy about that begin..rescue..end
block, I couldn't think of a less cumbersome way to do it.

Part 2, the much worse, as-of-yet-unsolved part:
Lafcadio has a query inference facility, inspired mostly by Criteria,
that is useful for expressing queries in ways that can be either turned
into SQL or run against an in-memory store. Here's an example:

one_year = 60 * 60 * 24 * 365
fave_clients = object_store.get_clients { |cli|
Query.And( cli.standard_rate.gte( 100 ), cli.created.gte( Time.now -
one_year ) )
}

What actually happens is, inside that block, Lafcadio pushes through an
object of class DomainObjectImpostor, which uses method_missing to
create query clauses when it receives calls like #standard_rate and
#created. (It actually spits out ObjectFieldImpostors, which then
listen to methods like #gte.)

But here's the problem:

def name; "global name method"; end

first_client = object_store.get_clients { |cli|
cli.send( :name ).equals( 'My first client' )
}[0]

This won't work, because #name will get dispatched to the global
method. And I can't use undef_method in this case, because I shouldn't
undef the global #name method to _every_ instance of
DomainObjectImpostor, because only some DomainObjectImpostors will be
pretending to be Clients. Others will be pretending to be Users,
Invoices, Projects, etc., etc., and they should be able to dispatch
calls to the global #name method.

Is there a way to undef a method for just one instance? Or maybe I
should consider instantiating new classes for every "instance", in the
same way that you can call DelegateClass( some_class ) when using
delegate.rb?

Or maybe I'm just making the whole thing overly complicated and there's
some simple answer right in front of my nose ...

Francis Hwang
http://fhwang.net/
 

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,768
Messages
2,569,574
Members
45,050
Latest member
AngelS122

Latest Threads

Top