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

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/