Reputation: 238687
I am creating an object that acts like an array, but needs to do some data initialization before items can be added to the array.
class People
def initialize(data_array)
@people_array = data_array.map {|data| Person.new(data) }
end
def <<(data)
@people_array << Person.new(data)
end
# def map(&block) ...
# def each(&block) ...
# etc...
end
I would like to support all the same methods that the array supports and delegate to those methods so that I don't have to rewrite all of them. What is the simplest/cleanest way to achieve this?
Upvotes: 4
Views: 1466
Reputation: 9523
Maybe inheriting the Array
class is a good solution:
class People < Array
def initialize(data_array)
super(data_array.map {|data| Person.new(data) })
end
def <<(item)
super(Person.new(item))
end
end
However, it has some disadvantages as mentioned in the comments and in axel's answer.
An alternative solution is composition and mixins:
class People
include Enumerable
def initialize(data_array)
@collection = data_array.map {|data| Person.new(data) }
end
def each(&block)
@collection.each(&block)
end
def <<(data)
@collection << Person.new(data)
end
end
However, this solution only gives you methods provided by the Enumerable module, along with the <<
operator.
To see what methods the Array
itself defines, easily do:
(Array.instance_methods - (Enumerable.instance_methods + Object.instance_methods)).sort
=> [:&, :*, :+, :-, :<<, :[], :[]=, :assoc, :at, :bsearch, :clear, :collect!,
:combination, :compact, :compact!, :concat, :delete, :delete_at, :delete_if, :each,
:each_index, :empty?, :fetch, :fill, :flatten, :flatten!, :index, :insert, :join,
:keep_if, :last, :length, :map!, :pack, :permutation, :pop, :product, :push, :rassoc,
:reject!, :repeated_combination, :repeated_permutation, :replace, :reverse, :reverse!,
:rindex, :rotate, :rotate!, :sample, :select!, :shift, :shuffle, :shuffle!, :size, :slice,
:slice!, :sort!, :sort_by!, :to_ary, :transpose, :uniq, :uniq!, :unshift, :values_at, :|]
However I would consider this a dirty solution, and would encourage going with Axel's answer.
Upvotes: 3
Reputation: 536
Of course, you can always meta-hack it
class People
def model
@arr ||= []
end
def method_missing(method_sym, *arguments, &block)
define_method method_sym do |*arguments|
model.send method_sym, *arguments
end
send(method_sym, *arguments, &block)
end
#probably should implement respond_to? here
end
I like this approach a little better because you aren't confusing implementing against Array versus extending Array. You can build a custom matcher to only implement particular array methods for example that are available for your People class.
Upvotes: 1
Reputation: 1364
You can use a Delegator for that
class SimpleDelegator < Delegator
def initialize(obj)
super # pass obj to Delegator constructor, required
@delegate_sd_obj = obj # store obj for future use
end
end
I would discourage from extending Array.
Upvotes: 1