Class: Compony::NaturalOrdering

Inherits:
Array
  • Object
show all
Defined in:
lib/compony/natural_ordering.rb

Overview

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:

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

Instance Method Summary collapse

Instance Method Details

#natural_push(name, payload = :missing, before: nil, **kwargs) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/compony/natural_ordering.rb', line 18

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