module Compony # @api description # This class provides an array-based data structure where elements have symbol names. New elements can be appended or placed at a location using `before:`. # Important: do not mutate this class with any other method call than the natural_-prefixed methods defined below. # Example:
# ```ruby # collection = Compony::NaturalOrdering.new # collection.natural_push(:a, a_payload) # collection.natural_push(:c, c_payload) # collection.natural_push(:b, b_payload, before: :c) # collection.natural_push(:d, d_payload, hidden: true) # collection.natural_push(:a, a_new_payload) # overwrites :a # # collection.reject{|el| el.hidden}.map(&:name) # --> :a, :b, :c # collection.map(&:payload) # --> a_new_payload, b_payload, c_payload, d_payload # ``` class NaturalOrdering < Array def natural_push(name, payload = :missing, before: nil, **kwargs) name = name.to_sym before_name = before&.to_sym old_kwargs = {} old_payload = nil # Fetch existing element if any existing_index = find_index { |el| el.name == name } if existing_index.present? # Copy all non-mentionned kwargs from the element we are about to overwrite old_kwargs = self[existing_index].except(:name, :payload) old_payload = self[existing_index].payload # Replacing an existing element with a before: directive - must delete before calculating indices if before_name.present? delete_at(existing_index) end elsif payload == :missing fail("Cannot insert new element #{name} without a payload (payload can only omitted if overriding another element) in #{inspect}.") end # Fetch before element if before_name.present? before_index = find_index { |el| el.name == before_name } || fail("Element #{before_name.inspect} for :before not found in #{inspect}.") end # Create the element to insert element = MethodAccessibleHash.new(name:, payload: payload == :missing ? old_payload : payload, **old_kwargs.merge(kwargs)) # Insert new element if before_index.present? # Insert before another element insert(before_index, element) elsif existing_index.present? # Override another element self[existing_index] = element else # Append at the end self << element end end end end