# # The ArrayFields module implements methods which allow an Array to be indexed # by String or Symbol. It is not required to manually use this module to extend # Array's - they are auto-extended when Array#fields= is called # module ArrayFields #{{{ # # multiton cache of fields - wraps fields and fieldpos map to save memory # class FieldSet #{{{ class << self #{{{ def new fields #{{{ @sets ||= {} obj = @sets[fields] unless obj obj = super @sets[fields] = obj end obj #}}} end #}}} end attr :fields attr :fieldpos def initialize fields #{{{ raise ArgumentError, "<#{ fields.inspect }> not inject-able" unless fields.respond_to? :inject @fieldpos = fields.inject({}) do |h, f| unless String === f or Symbol === f raise ArgumentError, "<#{ f.inspect }> neither String nor Symbol" end h[f] = h.size h end @fields = fields #}}} end def pos field #{{{ @fieldpos[field] #}}} end #}}} end VERSION = '3.2.0' def [](idx, *args) #{{{ if @fieldset and (String === idx or Symbol === idx) pos = @fieldset.pos idx return nil unless pos super(pos, *args) else super end #}}} end alias slice [] def []=(idx, *args) #{{{ if @fieldset and (String === idx or Symbol === idx) pos = @fieldset.pos idx @fieldset.fieldpos[idx] = pos = size unless pos super(pos, *args) else super end #}}} end def at idx #{{{ if @fieldset and (String === idx or Symbol === idx) pos = @fieldset.pos idx return nil unless pos super pos else super end #}}} end def delete_at idx #{{{ if @fieldset and (String === idx or Symbol === idx) pos = @fieldset.pos idx return nil unless pos super pos else super end #}}} end def fill(obj, *args) #{{{ idx = args.first if idx and @fieldset and (String === idx or Symbol === idx) idx = args.shift pos = @fieldset.pos idx super(obj, pos, *args) else super end #}}} end def values_at(*idxs) #{{{ idxs.flatten! if @fieldset idxs.map!{|i| (String === i or Symbol === i) ? @fieldset.pos(i) : i} end super(*idxs) #}}} end alias indices values_at alias indexes values_at def slice!(*args) #{{{ ret = self[*args] self[*args] = nil ret #}}} end def each_with_field #{{{ each_with_index do |elem, i| yield elem, @fieldset.fields[i] end #}}} end #}}} end # # The Array class is extened with a methods to allow keyword access # class Array #{{{ # # sets fields an dynamically extends this Array instance with methods for # keyword access # def fields= fields #{{{ extend ArrayFields unless defined? @fieldset @fieldset = if ArrayFields::FieldSet === fields fields else ArrayFields::FieldSet.new fields end #}}} end # # access to fieldset # attr_reader :fieldset # # access to field list # def fields #{{{ @fieldset and @fieldset.fields #}}} end #}}} end