redefining a method using Module#include

M

me

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 :eek:ld_accessor :[]
include AbsenceHandling
end


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


Thanks very much,
Mike
 
J

Jan Svitok

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 :eek:ld_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, :eek:ld_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 []
 
T

Trans

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 :eek:ld_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 :eek:ld_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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,611
Members
45,280
Latest member
BGBBrock56

Latest Threads

Top