[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

redefining a method using Module#include

me

11/12/2006 12:57:00 PM

Hi,

How can I redefine Array#[] using an included Module? Here is my
attempt:

module AbsenceHandling
def if_absent_call(p)
@if_absent = p
self
end

def [](index)
result = old_accessor(index)
if(result.nil? && !@if_absent.nil?)
result = @if_absent.call(index)
self[index]=result
end
result
end
end

class Array
alias :old_accessor :[]
include AbsenceHandling
end


x = [1,2,3].if_absent_call(proc{|index|
"#{index}"
})
puts x[5]


Thanks very much,
Mike

2 Answers

Jano Svitok

11/12/2006 5:37:00 PM

0

On 11/12/06, me@mikehogan.net <me@mikehogan.net> wrote:
> Hi,
>
> How can I redefine Array#[] using an included Module? Here is my
> attempt:
>
> module AbsenceHandling
> def if_absent_call(p)
> @if_absent = p
> self
> end
>
> def [](index)
> result = old_accessor(index)
> if(result.nil? && !@if_absent.nil?)
> result = @if_absent.call(index)
> self[index]=result
> end
> result
> end
> end
>
> class Array
> alias :old_accessor :[]
> include AbsenceHandling
> end
>
>
> x = [1,2,3].if_absent_call(proc{|index|
> "#{index}"
> })
> puts x[5]

Hi,

If a class defines a method, it has higher precedence than methods of
any of its included modules. So the "override" never happens in this
case. You can work around it by defining the [] operator directly in
Array method, via Module#included and Module#define_method. You can
alias the old [] in the included as well.

module AbsenceHandling
def if_absent_call(p)
@if_absent = p
self
end

def self.included(base)
# base = Array in this case
# alias_method and define_method are private, so we must
# use the send trick
base.send(:alias_method, :old_accessor, :[])
base.send(:define_method, :[]) do |index|
result = old_accessor(index)
if(result.nil? && !@if_absent.nil?)
result = @if_absent.call(index)
self[index]=result
end
result
end
end
end

class Array
include AbsenceHandling
end

Note: [] in Array is a special case (as are some other methods in base
C classes) as sometimes their C versions are called directly for the
sake of speed. So e.g. Array#to_s will use the old []

Trans

11/12/2006 6:12:00 PM

0


me@mikehogan.net wrote:
> Hi,
>
> How can I redefine Array#[] using an included Module? Here is my
> attempt:
>
> module AbsenceHandling
> def if_absent_call(p)
> @if_absent = p
> self
> end
>
> def [](index)
> result = old_accessor(index)
> if(result.nil? && !@if_absent.nil?)
> result = @if_absent.call(index)
> self[index]=result
> end
> result
> end
> end
>
> class Array
> alias :old_accessor :[]
> include AbsenceHandling
> end
>
>
> x = [1,2,3].if_absent_call(proc{|index|
> "#{index}"
> })
> puts x[5]

There are a number of ways. To be clear you are asking for code inject
rather then inheritance. traditionally subclasses are used add
functionality, But with Ruby we have the power to directly effect core
classes. In doing so first ask yourself if you really need a module. If
not the simple do:

class Array
def if_absent_call(p)
...
alias :old_accessor :[]
def [](index)
...

The next levelup gets more complicated one way is to use the #included
callback to inject the code directly, but this is a misrepresentation
of what include should do (i.e. inheritance) and I strong discourage
it. Another related approach is alias_method_chain supported by both
ActiveSupport and Facets. Here is an example:

module X
def self.included(base)
base.module_eval {
alias_method_chain :foo, :feature
}
end
def foo_with_feature
foo_without_feature + '!'
end
end

class Y
def foo ; "FOO" ; end
include X
end

y = Y.new
assert_equal( "FOO!", y.foo )

But this would be a little tricker with an operator and it also has
it's own downsides. Other solution you can look into are Facets'
Module#prepend and Facets' Cut implementation.

Having said all that, you know what you are attempting to do is
dangerous stuff. Muckng with Array#[] may have sever side effects. In
this case, I highly recommend using singleton instead:

a = []
a.extend AbsenceHandling

Just replace #old_accessor with #super. It's a case be case basis, but
it's much safer and hek, it's easy :-)

T.