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.
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" <s...@fhwang.net> wrote in message >> 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.
On May 28, 2005, at 11:42 AM, Francis Hwang wrote:
> 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-prompt >> require 'builder/blankslate' => false >> class Empty < Builder::BlankSlate >> def inspect >> "<empty>" >> end >> def method_missing(sym, *args) >> puts "Calling #{sym}(#{args.join(',')})" >> end >> end => nil >> e = Empty.new => <empty> >> e.hi Calling hi() => nil >> def hi() "HI" end => nil >> e.hi Calling hi() => nil
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?
> On May 28, 2005, at 11:42 AM, Francis Hwang wrote:
>> 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.
On Saturday 28 May 2005 11:05 pm, Francis Hwang wrote:
> 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.
-- -- Jim Weirich j...@weirichhouse.org http://onestepback.org ----------------------------------------------------------------- "Beware of bugs in the above code; I have only proved it correct, not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)
> On Saturday 28 May 2005 11:05 pm, Francis Hwang wrote: >> 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.
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.
> 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.
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
>> 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.
> 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 wrote: >> 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.
Should I submit an RCR? Haven't done one before. Would it be called? Class#inherited_finished ?
Francis Hwang wrote: > On May 30, 2005, at 9:02 PM, Glenn Parker wrote: >> Francis Hwang wrote:
>>> 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.
> 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.
In message "Re: preventing Object#send from dispatching to a global method?" on Sun, 29 May 2005 00:42:46 +0900, Francis Hwang <s...@fhwang.net> writes:
|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.
> Francis Hwang wrote: >> On May 30, 2005, at 9:02 PM, Glenn Parker wrote: >>> Francis Hwang wrote:
>>>> 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. >> 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.
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 ...
> In message "Re: preventing Object#send from dispatching to a global > method?" > on Sun, 29 May 2005 00:42:46 +0900, Francis Hwang > <s...@fhwang.net> writes:
> |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:
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.)
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 ...