Challenge

In most cases, this works:

class Foo
  def self.open(*args, &block)
    p [args, block]
    yield if block_given?
  end
end

Foo.open(1) do
  puts "hello"
end

open_m = Foo.method 'open'

class Foo
  def self.open(*a) p 42 end
end

Foo.open(2) do
  puts "hello"
end

(class << Foo; self; end).instance_eval do
  define_method(:open, open_m)
end

Foo.open(3) do
  puts "bye"
end
__END__
[[1], #<Proc:0x02869018@c:/rubylib/experiments/redefine-class-method.rb:8>]
hello
42
[[3], #<Proc:0x02867728@c:/rubylib/experiments/redefine-class-method.rb:26>]
bye

but as we've seen, it doesn't work in the case of the File(IO)
singleton. To be honest, I'm not clear about exactly what kind of
object the File(IO) singleton is. Anyone offer any enlightenment?

Regards,
Sean

···

On 7/14/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:

On Fri, 14 Jul 2006, Sean O'Halpin wrote:

> Hmmm. I'm still getting "singleton method called for a different
> object (TypeError)" for this (ruby 1.8.4 (2005-12-24) [i386-mswin32]).

me too.

   harp:~ > cat a.rb
   class X # role of IO
     class << self
       def x(*args,&block) # role of open
           block.call(1764) if block
       end
     end
   end

   OLD = X.method :x

   class Y < X # role of File
     class << self
       def x; "rubbish"; end
     end
   end

   class Y
     class << self
       define_method(:x, OLD )
     end
   end

   Y.x{|a| puts a }

   harp:~ > ruby a.rb
   a.rb:23:in `x': singleton method called for a different object (TypeError)
           from a.rb:23

   harp:~ > ruby -v && cat /etc/redhat-release && uname -srm
   ruby 1.8.4 (2005-12-01) [i686-linux]
   Red Hat Enterprise Linux WS release 3 (Taroon Update 7)
   Linux 2.4.21-40.EL i686

you robert?

btw. this challenge was not arbitrary: the increase in metaprogramming
popularity means people are doing things like

   def class_method
     alias '__instance_method__', 'instance_method' # push

     define_method 'instance_method' do
       # ...
       __instance_method__ 42
       # ...
     end

   ensure
     alias 'instance_method', '__instance_method__' # pop
   end

but this is neither thread safe (easy to fix) or re-entrant (not easy to fix).
what i mean is doing

   class_method{
     class_method{
     }
   }

blows up.

to avoid this i wanted to develop this pattern

   def class_method
     __instance_method__ = method 'instance_method' # push

     define_method 'instance_method' do
       # ...
       __instance_method__.call 42
       # ...
     end

   ensure
     define_method 'instance_method', __instance_method__ # pop
   end

but, of course, we are seeing that it doesn't work.

regards.

-a
--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

In most cases, this works:

class Foo
def self.open(*args, &block)
   p [args, block]
   yield if block_given?
end
end

[...]

(class << Foo; self; end).instance_eval do
define_method(:open, open_m)
end

[...]

but as we've seen, it doesn't work in the case of the File(IO)
singleton. To be honest, I'm not clear about exactly what kind of
object the File(IO) singleton is. Anyone offer any enlightenment?

Open is a singleton method of IO, not File; this is why the call fails (in
1.9, it'd happen earlier, as soon as you try to rebind the method). There's
nothing that special about File itself:

class X
  def self.foo; yield + 1 end
end

class Y < X
end

Y.foo{1} # => 2
m = Y.method(:foo)
def Y.foo; 10 end
Y.foo{1} # => 10
class << Y; self end.class_eval{define_method(:foo, m)}
Y.foo{1} # =>
# ~> -:13:in `foo': singleton method bound for a different object (TypeError)
# ~> from -:13

As for the File(IO) notation, it seems to mean that the singleton method
belongs to IO:

class X; def self.foo; yield + 1 end end
class Y < X; end
Y.method(:foo) # => #<Method: Y(X).foo>
X.method(:foo) # => #<Method: X.foo>

Keep in mind that the singleton class of a class is derived from that of its
superclass:

class X; def self.foo; 1 end end
class Y < X; end
X.methods(false) # => ["foo"]
Y.methods(false) # =>
Y.foo # => 1

···

On Sat, Jul 15, 2006 at 10:33:09AM +0900, Sean O'Halpin wrote:

On 7/14/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
>On Fri, 14 Jul 2006, Sean O'Halpin wrote:

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

I think that certainly fits the description of enlightenment :slight_smile:

I can see now that my example was missing the essential aspect of
using a parent's singleton method in a derived class.

And so back to Ara's challenge...

Regards,
Sean

···

On 7/18/06, Mauricio Fernandez <mfp@acm.org> wrote:

On Sat, Jul 15, 2006 at 10:33:09AM +0900, Sean O'Halpin wrote:
> On 7/14/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
> >On Fri, 14 Jul 2006, Sean O'Halpin wrote:
> In most cases, this works:
>
> class Foo
> def self.open(*args, &block)
> p [args, block]
> yield if block_given?
> end
> end
[...]
> (class << Foo; self; end).instance_eval do
> define_method(:open, open_m)
> end
[...]
> but as we've seen, it doesn't work in the case of the File(IO)
> singleton. To be honest, I'm not clear about exactly what kind of
> object the File(IO) singleton is. Anyone offer any enlightenment?

Open is a singleton method of IO, not File; this is why the call fails (in
1.9, it'd happen earlier, as soon as you try to rebind the method). There's
nothing that special about File itself:

class X
  def self.foo; yield + 1 end
end

class Y < X
end

Y.foo{1} # => 2
m = Y.method(:foo)
def Y.foo; 10 end
Y.foo{1} # => 10
class << Y; self end.class_eval{define_method(:foo, m)}
Y.foo{1} # =>
# ~> -:13:in `foo': singleton method bound for a different object (TypeError)
# ~> from -:13

As for the File(IO) notation, it seems to mean that the singleton method
belongs to IO:

class X; def self.foo; yield + 1 end end
class Y < X; end
Y.method(:foo) # => #<Method: Y(X).foo>
X.method(:foo) # => #<Method: X.foo>

Keep in mind that the singleton class of a class is derived from that of its
superclass:

class X; def self.foo; 1 end end
class Y < X; end
X.methods(false) # => ["foo"]
Y.methods(false) # =>
Y.foo # => 1

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

Mauricio Fernandez schrieb:

Open is a singleton method of IO, not File; this is why the call fails (in
1.9, it'd happen earlier, as soon as you try to rebind the method). There's
nothing that special about File itself:
(...)

The problem is, if you change the method of the superclass (IO), and then later restore the original version, that you can't call it on the subclass (File):

   class X; def self.foo; 1 end end
   class Y < X; end

   Y.foo # => 1

   m = X.method(:foo)
   def X.foo; 2 end

   Y.foo # => 2

   class << X; self end.class_eval{define_method(:foo, m)}

   X.foo # => 1
   Y.foo # => singleton method called for a different object

Do you know why this happens?

Regards,
Pit

yeah - i never solved satisfactorily...

good luck!

-a

···

On Wed, 19 Jul 2006, Sean O'Halpin wrote:

I think that certainly fits the description of enlightenment :slight_smile:

I can see now that my example was missing the essential aspect of using a
parent's singleton method in a derived class.

And so back to Ara's challenge...

--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

Just another thaught about it, what is wrong with my OPEN.call solution, is
it not general enough?

Cheers
Robert

···

On 7/19/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:

On Wed, 19 Jul 2006, Sean O'Halpin wrote:

> I think that certainly fits the description of enlightenment :slight_smile:
>
> I can see now that my example was missing the essential aspect of using
a
> parent's singleton method in a derived class.
>
> And so back to Ara's challenge...

yeah - i never solved satisfactorily...

good luck!

-a
--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

--
Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui
concerne l'univers, je n'en ai pas acquis la certitude absolue.

- Albert Einstein

It'd require one constant per method call for Ara's intended use, in the
general case (when several "layers" can be added at any point in time, i.e.
you want it to be reentrant).

···

On Wed, Jul 19, 2006 at 06:22:18PM +0900, Robert Dober wrote:

On 7/19/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> wrote:
>On Wed, 19 Jul 2006, Sean O'Halpin wrote:
>> I can see now that my example was missing the essential aspect of using a
>> parent's singleton method in a derived class.
>>
>> And so back to Ara's challenge...
>
>yeah - i never solved satisfactorily...
>
Just another thaught about it, what is wrong with my OPEN.call solution, is
it not general enough?

--
Mauricio Fernandez - http://eigenclass.org - singular Ruby

Nothing wrong with it - it's just not an answer to the challenge!

In Ara's second post he notes: "using a const makes it too easy and
will accomplish what i need to do."

The reason it's not so useful to use consts is that it's not
re-entrant (so you can't nest and it's not thread-safe), it pollutes
the constant namespace, it becomes harder to manage the more instances
you have, it's not contained within local scope, etc. so I guess it
wouldn't pass as a general solution. You could probably overcome some
of these issues by using thread-local stacks but then that would
quickly start to get messy and wouldn't address the using out-of-scope
information issue.

However, back in the real world ;), yours is a pragmatic solution that
works as long as you understand its limitations.

Regards,
Sean

···

On 7/19/06, Robert Dober <robert.dober@gmail.com> wrote:

Just another thaught about it, what is wrong with my OPEN.call solution, is
it not general enough?

Cheers
Robert

I guess I fail to understand the challange :frowning:
Nevertheless the constant is just one way to store a reference to the
original methods, it would be very simple to export references of all
methods into a hash or a delegation object or whatever.
The constant was just part of the original challange.

I also have difficulties to see why

X.some
is any different from
X.method("some").call
concerning Thread Safety or even reentrance.

But anyway I too would prefer to reinstall the original method structure,
which is perfectly possible if the method was not from a superclass. And if
it was from the superclass we do not need to redefine it, so how about

class File
     class << self
         remove_method :open
         define_method( :open, OPEN ) unless respond_to? :open
     end
end

Cheers
Robert

···

On 7/19/06, Sean O'Halpin <sean.ohalpin@gmail.com> wrote:

On 7/19/06, Robert Dober <robert.dober@gmail.com> wrote:
> Just another thaught about it, what is wrong with my OPEN.call solution,
is
> it not general enough?
>
> Cheers
> Robert
>
Nothing wrong with it - it's just not an answer to the challenge!

In Ara's second post he notes: "using a const makes it too easy and
will accomplish what i need to do."

The reason it's not so useful to use consts is that it's not
re-entrant (so you can't nest and it's not thread-safe), it pollutes
the constant namespace, it becomes harder to manage the more instances
you have, it's not contained within local scope, etc. so I guess it
wouldn't pass as a general solution. You could probably overcome some
of these issues by using thread-local stacks but then that would
quickly start to get messy and wouldn't address the using out-of-scope
information issue.

However, back in the real world ;), yours is a pragmatic solution that
works as long as you understand its limitations.

Regards,
Sean

--
Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui
concerne l'univers, je n'en ai pas acquis la certitude absolue.

- Albert Einstein