Redefining a method using Module#include

Hi,

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

module AbsenceHandling
def if_absent_call§
@if_absent = p
self
end

def [](index)
    result = old_accessor(index)
    if(result.nil? && [email protected]_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

On 11/12/06, [email protected] [email protected] wrote:

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§
@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 []

[email protected] wrote:

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§

alias :old_accessor :[]
def

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 :slight_smile:

T.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs