Preventing Object#send from dispatching to a global method?

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/

Hi,

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

···

Am Sonntag, 29. Mai 2005, 00:42:46 +0900 schrieb Francis Hwang:

--
Bertram Scharpf
Stuttgart, Deutschland/Germany
http://www.bertram-scharpf.de

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" <sera@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.

Francis Hwang
http://fhwang.net/

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

···

On May 28, 2005, at 11:42 AM, Francis Hwang wrote:

Hi,

···

In message "Re: preventing Object#send from dispatching to a global method?" on Sun, 29 May 2005 00:42:46 +0900, Francis Hwang <sera@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.

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.

···

On May 28, 2005, at 3:50 PM, itsme213 wrote:

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" <sera@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.

Francis Hwang
http://fhwang.net/

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?

···

On May 28, 2005, at 10:29 PM, Jim Weirich wrote:

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

Francis Hwang

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

···

On Jun 2, 2005, at 9:55 AM, Yukihiro Matsumoto wrote:

Hi,

In message "Re: preventing Object#send from dispatching to a global > method?" > on Sun, 29 May 2005 00:42:46 +0900, Francis Hwang > <sera@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.

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.

···

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?

--
-- Jim Weirich jim@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)

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.

···

--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoil.com/&gt;

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

···

On May 29, 2005, at 8:54 PM, Jim Weirich wrote:

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.

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

Francis Hwang

···

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.

Francis Hwang wrote:

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

Francis Hwang 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.

···

On May 30, 2005, at 9:02 PM, Glenn Parker wrote:

--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoil.com/&gt;

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

···

On Jun 1, 2005, at 11:39 PM, Nicholas Seckar wrote:

Francis Hwang wrote:

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.

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

Francis Hwang 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 ...

Francis Hwang

···

On Jun 2, 2005, at 8:57 AM, Glenn Parker wrote:

On May 30, 2005, at 9:02 PM, Glenn Parker wrote:

Francis Hwang wrote:

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

*cough* That would make sense. My apologies to the related parties...