Forum: Ruby redefining methods

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
ara.t.howard (Guest)
on 2007-07-31 18:48
(Received via mailing list)
i was playing with this last night, thought some here might be
interested:

   http://drawohara.tumblr.com/post/7241442

cheers.

a @ http://drawohara.com/
dohzya (Guest)
on 2007-07-31 21:28
(Received via mailing list)
Le mardi 31 juillet 2007 à 23:48 +0900, ara.t.howard a écrit :
> better. simply reflect on that.
> h.h. the 14th dalai lama

Just for the syntax, I don't like :
---
redefining :foo do
  def foo(*a, &b) super.reverse end
end
---
I prefer :
---
redefining :foo do |*a, &b|
  super.reverse
end
---
Greater, it isn't ?
---

However, there is an other way :
---
redefining :foo do |&old| # &old or old, see after
  def foo(*a, &b) old.call.reverse end
end
---
and maybe...
---
redefining :foo do |*olds| # list, ordered (asc or desc ?)
  def foo(*a, &b) (olds.shift.call + olds.shift.call).reverse end
  # maybe add a shortcut for shift.call...
end
---

Regards
ara.t.howard (Guest)
on 2007-07-31 21:49
(Received via mailing list)
On Jul 31, 2007, at 11:27 AM, dohzya wrote:

>> we can deny everything, except that we have the possibility of being
> ---
> redefining :foo do |*a, &b|
>   super.reverse
> end
> ---
> Greater, it isn't ?

absolutely.  and when define_method takes a block that would indeed
by the way to do it.

> ---
>
> However, there is an other way :
> ---
> redefining :foo do |&old| # &old or old, see after
>   def foo(*a, &b) old.call.reverse end
> end
> ---

this can't work though - old is not in scope inside foo - only with
define_method would that be possible, but there you lose the ability
to define methods which themselves take blocks...

> and maybe...
> ---
> redefining :foo do |*olds| # list, ordered (asc or desc ?)
>   def foo(*a, &b) (olds.shift.call + olds.shift.call).reverse end
>   # maybe add a shortcut for shift.call...
> end
> ---

again - same issue with scoping.  although i agree and more closure
based solution would be nice.  for now though - i don't think it's
possible.  i'd be very happy to be proven wrong though!  ;-)

kind regards.

a @ http://drawohara.com/
Pit C. (Guest)
on 2007-07-31 22:11
(Received via mailing list)
2007/7/31, ara.t.howard <removed_email_address@domain.invalid>:
> i was playing with this last night, thought some here might be
> interested:
>
>    http://drawohara.tumblr.com/post/7241442

Hi Ara, since I don't know the implementation of Rails'
alias_method_chain, I can't comment on which is better, so just a few
remarks concerning your code:

* Syntax: like Étienne, I, too, don't like that it is necessary to
repeat the method name in both the #redefining call and the method
definition. Couldn't you just call #redefining without an argument and
look in the new module which methods have been defined there?

* Implementation: I haven't checked in detail, but I think you could
get problems with the class variable when there's a class hierarchy.

* Implementation: currently, the method chains are only used as a flag
whether to create the "initial" module or not. Do you plan to use it
for other things? Otherwise you don't need an array with all the
intermediate modules.

Btw. nice use of the BEGIN block!

Regards,
Pit
ara.t.howard (Guest)
on 2007-07-31 23:23
(Received via mailing list)
On Jul 31, 2007, at 12:09 PM, Pit C. wrote:

> * Syntax: like Étienne, I, too, don't like that it is necessary to
> repeat the method name in both the #redefining call and the method
> definition. Couldn't you just call #redefining without an argument and
> look in the new module which methods have been defined there?

indeed you could/would.  just playing now.  patches welcome! ;-)

>
> * Implementation: I haven't checked in detail, but I think you could
> get problems with the class variable when there's a class hierarchy.

yeah - a class instance var would be the way to go - i was just
saving on typing for demo..

>
> * Implementation: currently, the method chains are only used as a flag
> whether to create the "initial" module or not. Do you plan to use it
> for other things? Otherwise you don't need an array with all the
> intermediate modules.
>

hmmm - you mean use only the two: the initial and subsequent?  i
think that would work... i was indeed thinking of being able to call
a 'specific super' though...

> Btw. nice use of the BEGIN block!

i like the idiom, but the error reporting and require semantics stink
- it's nice for examples though ;-)

cheers.

a @ http://drawohara.com/
ara.t.howard (Guest)
on 2007-08-01 00:34
(Received via mailing list)
On Jul 31, 2007, at 12:09 PM, Pit C. wrote:

> * Syntax: like Étienne, I, too, don't like that it is necessary to
> intermediate modules.
>
> Btw. nice use of the BEGIN block!
>
> Regards,
> Pit
>


updated:

   http://drawohara.tumblr.com/post/7241442

i played some cannot seem to come up with a way to avoid having the
stack of modules and also maintain super semantics across multiple
invocations - anyone else have a go?


a @ http://drawohara.com/
Robert D. (Guest)
on 2007-08-01 00:39
(Received via mailing list)
On 7/31/07, ara.t.howard <removed_email_address@domain.invalid> wrote:
>
> i was playing with this last night, thought some here might be
> interested:
>
>    http://drawohara.tumblr.com/post/7241442
>
> cheers.
>
> a @ http://drawohara.com/
As Etienne and Pit pointed it out (guys are you using 1.9 and pointing
fingers at us ;)?) the &blk sntax in block is really missing.
This is a big issue here

  if method_chain.empty?
          m = Module.new do
            class_variable_set "@@#{ method }", this.module_eval{
instance_method method }
            module_eval "def #{ method }(*a, &b) @@#{ method
}.bind(self).call(*a, &b) end"
          end
          remove_method method
          include m
        end
As it is soo easy to have a class with @@#{method} in your class :(
I do not see any solution, if only we could use closures with a
define_method m do |*args,&blk|

I know I am not DROP here :(

BTW
I would write

 def method_chains
        @method_chains ||= Hash.new
  end

Cheers
Robert

P.S.
Nice work :)
Robert D. (Guest)
on 2007-08-01 00:42
(Received via mailing list)
On 7/31/07, ara.t.howard <removed_email_address@domain.invalid> wrote:
> > alias_method_chain, I can't comment on which is better, so just a few
> > * Implementation: currently, the method chains are only used as a flag
>
> --
> we can deny everything, except that we have the possibility of being
> better. simply reflect on that.
> h.h. the 14th dalai lama
>
>
>
>
>
Wow can you read my mind :), you fixed it already!! Very impressive
technique, but what a pain...

Robert
Pit C. (Guest)
on 2007-08-01 01:52
(Received via mailing list)
2007/7/31, ara.t.howard <removed_email_address@domain.invalid>:
> i played some cannot seem to come up with a way to avoid having the
> stack of modules and also maintain super semantics across multiple
> invocations - anyone else have a go?

Ara, I didn't mean you don't need a stack of modules, but you don't
need to store the modules in an array (yet).

> just playing now.  patches welcome! ;-)

Not a patch, but a slightly different implementation based on your idea:

class C
    def foo() 'f' end
    def bar() 'b' end
    def foobar() foo + bar end
  end
  c = C.new
  p c.foobar        # => "fb"

  class C
    redefining do
      def foo() super + 'oo' end
    end
  end
  p c.foobar        # => "foob"

  class C
    redefining do
      def bar() super + 'ar' end
    end
  end
  p c.foobar        # => "foobar"

  class C
    redefining do
      def foo() super.reverse end
      def bar() super.reverse end
    end
  end
  p c.foobar        # => "oofrab"


  ### the implementation

  BEGIN {
    class Class
      def redefining &block
        unless defined? @_org
          org_mod = Module.new
          @_org = { :mod => org_mod }
          include org_mod
        end

        m = Module.new(&block)

        (m.instance_methods(false) & instance_methods(false)).each do
|method|
          @_org[method] = instance_method method
          @_org[:mod].module_eval <<-EOC
            def #{method}(*a, &b)
              org = self.class.instance_variable_get("@_org")
              org["#{method}"].bind(self).call(*a, &b)
            end
          EOC
          remove_method method
        end

        include m
      end
    end
  }

I used the old way of getting at the original method (storing it
somewhere), because I'm not sure whether the garbage collector could
remove it if you only remember it's object_id.

Regards,
Pit
ara.t.howard (Guest)
on 2007-08-02 01:41
(Received via mailing list)
On Jul 31, 2007, at 3:51 PM, Pit C. wrote:

> Not a patch, but a slightly different implementation based on your
> idea:
>
> class C

<snip>

>
> I used the old way of getting at the original method (storing it
> somewhere), because I'm not sure whether the garbage collector could
> remove it if you only remember it's object_id.
>
> Regards,
> Pit
>

updated (and new commenting finally ;-) )

   http://drawohara.tumblr.com/post/7241442

nice work pit!

a @ http://drawohara.com/
Pit C. (Guest)
on 2007-08-02 12:20
(Received via mailing list)
2007/8/1, ara.t.howard <removed_email_address@domain.invalid>:
> (...)
> nice work pit!

Thanks, Ara, but it's still your original idea and design. I only
changed a part of the implementation.

Regards,
Pit
Bil K. (Guest)
on 2007-09-26 01:10
(Received via mailing list)
Pit C. wrote:
>
> Btw. nice use of the BEGIN block!

Yes.

That was a question that came up at the Advanced Ruby Studio
in Reston a couple weeks ago: of what practical use are those
BEGIN/END things anyway?  ;)

Regards,
This topic is locked and can not be replied to.