When an Array is not

M

Michael Schuerig

(1) Under what circumstances is an array not an Array? I'm confronted
with such a case in one of my unit tests. The test failed when run in
conjunction with other tests and I narrowed it down to this: In the
tested code I use something.kind_of?(Array). I have a mixin module that
extends Array with additional methods. When this module is required
directly or indirectly in my test class then kind_of?(Array) doesn't
behave as expected anymore.

(2) Maybe I'm not understanding Ruby's object system well enough. That
appears to be true in another case, too. I have a mixin module for
Enumerable whose methods I want to use on arrays, too, of course. But
apparently it is not enough to include it in Enumerable

Enumerable.module_eval do
include MyEnumExt
end

I have to do the same for Array to make it available there

Array.class_eval do
include MyEnumExt
end

Incidentally, I get the idea that (1) and (2) are related. But still I
don't understand what's happening.

Michael
 
D

David A. Black

Hi --

(1) Under what circumstances is an array not an Array? I'm confronted
with such a case in one of my unit tests. The test failed when run in
conjunction with other tests and I narrowed it down to this: In the
tested code I use something.kind_of?(Array). I have a mixin module that
extends Array with additional methods. When this module is required
directly or indirectly in my test class then kind_of?(Array) doesn't
behave as expected anymore.

Can you provide a complete (non-)working example? I can't quite
reconstruct it from this.
(2) Maybe I'm not understanding Ruby's object system well enough. That
appears to be true in another case, too. I have a mixin module for
Enumerable whose methods I want to use on arrays, too, of course. But
apparently it is not enough to include it in Enumerable

Enumerable.module_eval do
include MyEnumExt
end

I have to do the same for Array to make it available there

Array.class_eval do
include MyEnumExt
end

You could also re-include Enumerable in Array after you've changed
Enumerable. Still, it does seem like some kind of optimization or
special case for built-ins. If you make your own the changes are
propagated:

module M
def x; 1; end
end

class D
include M
end

D.new.x # 1

module M
def y; 2; end
end

D.new.y # 2


David
 
P

Peter Vanbroekhoven

You could also re-include Enumerable in Array after you've changed
Enumerable. Still, it does seem like some kind of optimization or
special case for built-ins. If you make your own the changes are
propagated:

module M
def x; 1; end
end

class D
include M
end

D.new.x # 1

module M
def y; 2; end
end

D.new.y # 2

Actually the corresponding example would be something like this:

module M
def y ; 'y' ; end
end

module N
def x ; 'x' ; end
end

class A
include N
end

module N
include M
end

A.new.x # 'x'
A.new.y # NoMethodError
A.ancestors # [A, N, Object, Kernel]
N.ancestors # [N, M]

It has nothing to do with built-ins, it's a restriction of the way Ruby
handles module inclusion. For more information, see the thread starting at
[ruby-core:2068] http://rubyurl.com/vFI

Peter
 
D

David A. Black

Hi --

Actually the corresponding example would be something like this:

module M
def y ; 'y' ; end
end

module N
def x ; 'x' ; end
end

class A
include N
end

module N
include M
end

A.new.x # 'x'
A.new.y # NoMethodError
A.ancestors # [A, N, Object, Kernel]
N.ancestors # [N, M]

It has nothing to do with built-ins, it's a restriction of the way Ruby
handles module inclusion. For more information, see the thread starting at
[ruby-core:2068] http://rubyurl.com/vFI

Yes, thanks for correcting. I somehow transmuted the whole thing into
classes....


David
 
M

Michael Schuerig

David said:
Hi --



Can you provide a complete (non-)working example? I can't quite
reconstruct it from this.

Attached below with resolution. When copying and pasting the minimally
necessary pieces to recreate the failure I found the cause. The module
where the failure occurs has a sibling module named Array. Ruby
resolved the unadorned Array to this module, not the built-in class
Array.

Michael


require 'test/unit'

module BoilerPlate #:nodoc:
module CoreExtensions #:nodoc:
module Array #:nodoc:
end
end
end

module BoilerPlate # :nodoc:
module CoreExtensions # :nodoc:
module Hash # :nodoc:
module Misc
def value_to_a(key, default = [])
if value = self[key]
# ::Array is necessary to refer to the globally scoped Array
# Array alone refers to the sibling module Array
value.kind_of?:):Array) ? value : [ value ]
else
default
end
end
end
end
end
end

Hash.class_eval do
include BoilerPlate::CoreExtensions::Hash::Misc
end

class HashMiscTest < Test::Unit::TestCase
def test_value_to_a_array
options = { 'mykey' => [ 'thevalue', 'anothervalue' ] }
assert_equal(['thevalue', 'anothervalue'],
options.value_to_a('mykey'))
end
end
 
M

Michael Schuerig

Peter said:
It has nothing to do with built-ins, it's a restriction of the way
Ruby handles module inclusion. For more information, see the thread
starting at
[ruby-core:2068] http://rubyurl.com/vFI

I had a nagging suspicion that something like that would be involved.
I'll have to read the Ruby source code some time.

Michael
 

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
474,183
Messages
2,570,966
Members
47,513
Latest member
JeremyLabo

Latest Threads

Top