Ideas to write this nicer (maybe using duck typing)

I have a class FreezingArray, which behaves like an Array, but provides
3 additional methods. Let’s say that farr is an instance of
FreezingArray. We then can do one of:

farr.set_const_from(a)

This appends all the records in array a to farr, then freezes farr.

farr.set_const_with(e)

This iterates over the enumerator e and puts its outcome into farr, then
freezes farr.

farr.set_const(x)

Calls set_const_with, if x is an enumerator, or set_const_from if it
isn’t.

My implementation is straightforward:

class FreezingArray <Array
def initialize
super
end
def set_const_from(array_of_recs)
concat(array_of_recs)
freeze
end
def set_const_with(enumerator)
enumerator.each { |rec| self << rec }
freeze
end
def set_const(array_or_enumerator)
if array_or_enumerator.is_a?(Enumerator)
set_const_with(array_or_enumerator)
else
set_const_from(array_or_enumerator)
end
end
end

It is just not very elegant, because of the is_a?(Enumerator). Any ideas
how this can be written in a more elegant way?

Hi Ronald,

maybe something like this:

class FreezingArray < Array
def initialize(data)
super(Array(data)) # explicit coercion
freeze
end
end

I was not aware, that the class Array can be used in this way!

It just must not be done in the constructor, but picking up your idea, I
think this one should work

def set_const(array_or_enumerator)
  concat(Array(array_or_enumerator))
  freeze
end

Thanks for the suggestion.

You need to be a bit careful though, because, for example, you can
coerce a String into an Array:
Array(“this”) #=> [“this”]

But semantically this is still the right approach, in my opinion.

I think it depends a bit on how sure you are that the argument is going
to be an array_or_enumerator. In the original version:

[].concat(“this”)

is going to raise because “this” isn’t an Array.

Why don’t you want to use the constructor? It seems like the natural
place to me.

Have you tried simply calling set_const_with with an array as the
argument? It work for both arrays and Enumerators because #each is
defined for both.

Dansei Yuuki wrote in post #1184718:

Have you tried simply calling set_const_with with an array as the
argument? It work for both arrays and Enumerators because #each is
defined for both.

Ah, you are right!!! Of course, this will work. Thanks a lot.

Why don’t you want to use the constructor?

In my application, the array will be created (and initially filled with
some values) in one place, then passed to other routines, which fill the
remaining part and freeze it, so I found it more naturally to separate
it. Of course it can be done in the constructor (and the program logic
could be adapted). It’s just a design choice.