Module: PaperTrailDiff::Support

Defined in:
lib/paper_trail_diff/support.rb,
sig/generated/paper_trail_diff/support.rbs

Overview

Internal helpers for isolating and serializing values held by result objects.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.ambiguous_message(pair) ⇒ Object

Signature:

  • (Array[untyped]) -> String



81
82
83
84
85
86
87
# File 'lib/paper_trail_diff/support.rb', line 81

def ambiguous_message(pair)
  left, right = pair
  "versions #{left.id.inspect} and #{right.id.inspect} share the timestamp " \
    "#{left.created_at.inspect} and have ids that do not order them, so their " \
    'sequence cannot be recovered; record versions at sub-second precision or ' \
    'with sequential ids'
end

.ambiguous_pair(sorted) ⇒ Object

Signature:

  • (Array[untyped]) -> Array[untyped]?



68
69
70
71
72
73
# File 'lib/paper_trail_diff/support.rb', line 68

def ambiguous_pair(sorted)
  sorted.each_cons(2).find do |left, right|
    left.created_at == right.created_at &&
      !(sequential_id?(left.id) && sequential_id?(right.id))
  end
end

.association_path(parent, name) ⇒ Object

Signature:

  • (String, String) -> String



96
97
98
# File 'lib/paper_trail_diff/support.rb', line 96

def association_path(parent, name)
  parent.empty? ? name : "#{parent}.#{name}"
end

.chronological_sort(versions) ⇒ Object

Ordering falls back to the id whenever timestamps tie, which recovers the real order only while ids increase with insertion. An autoincrement id does; a UUID does not, so a tie between UUID-keyed versions is genuinely unorderable and any timeline built from it would be fiction.

Signature:

  • (Array[untyped]) -> Array[untyped]

Raises:



59
60
61
62
63
64
65
# File 'lib/paper_trail_diff/support.rb', line 59

def chronological_sort(versions)
  sorted = versions.sort_by { |version| chronological_version_key(version) }
  ambiguous = ambiguous_pair(sorted)
  return sorted unless ambiguous

  raise AmbiguousVersionOrderError, ambiguous_message(ambiguous)
end

.chronological_version_key(version) ⇒ Object

Signature:

  • (untyped) -> Array[untyped]



50
51
52
# File 'lib/paper_trail_diff/support.rb', line 50

def chronological_version_key(version)
  [version.created_at, version.id.to_s.rjust(32, '0')]
end

.compare_versions(left, right) ⇒ Object

Signature:

  • (untyped, untyped) -> Integer



90
91
92
93
# File 'lib/paper_trail_diff/support.rb', line 90

def compare_versions(left, right)
  chronological_version_key(left) <=> chronological_version_key(right) ||
    raise(ConfigurationError, 'versions have incomparable timestamps')
end

.immutable_copy(value) ⇒ Object

Signature:

  • (untyped) -> untyped



10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/paper_trail_diff/support.rb', line 10

def immutable_copy(value)
  return value if value.frozen?

  case value
  when Hash
    immutable_hash(value)
  when Array
    value.map { |item| immutable_copy(item) }.freeze
  else
    duplicate_and_freeze(value)
  end
end

.merge_record_groups(existing, incoming) ⇒ Object

Signature:

  • (Hash[String, Hash[Symbol, untyped]], Hash[String, Hash[Symbol, untyped]]) -> Hash[String, Hash[Symbol, untyped]]



101
102
103
104
105
106
107
# File 'lib/paper_trail_diff/support.rb', line 101

def merge_record_groups(existing, incoming)
  existing.merge(incoming) do |_name, left, right|
    merged = left.merge(right, ids: (left.fetch(:ids) | right.fetch(:ids)))
    owners = merge_record_owners(left[:owners], right[:owners])
    owners ? merged.merge(owners: owners) : merged
  end
end

.sequential_id?(id) ⇒ Boolean

Signature:

  • (untyped) -> bool

Returns:

  • (Boolean)


76
77
78
# File 'lib/paper_trail_diff/support.rb', line 76

def sequential_id?(id)
  id.is_a?(Integer) || id.to_s.match?(/\A\d+\z/)
end

.serialize(value) ⇒ Object

Signature:

  • (untyped) -> untyped



34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/paper_trail_diff/support.rb', line 34

def serialize(value)
  case value
  when Hash
    serialized = {} #: Hash[untyped, untyped]
    value.each do |key, item|
      serialized[key] = serialize(item)
    end
    serialized
  when Array
    value.map { |item| serialize(item) }
  else
    paper_trail_diff_value?(value) ? value.to_h : value
  end
end

Instance Method Details

#self?.ambiguous_messageString

: (Array) -> String

Parameters:

  • (Array[untyped])

Returns:

  • (String)


32
# File 'sig/generated/paper_trail_diff/support.rbs', line 32

def self?.ambiguous_message: (Array[untyped]) -> String

#self?.ambiguous_pairArray[untyped]?

: (Array) -> Array?

Parameters:

  • (Array[untyped])

Returns:

  • (Array[untyped], nil)


26
# File 'sig/generated/paper_trail_diff/support.rbs', line 26

def self?.ambiguous_pair: (Array[untyped]) -> Array[untyped]?

#self?.association_pathString

: (String, String) -> String

Parameters:

  • (String)
  • (String)

Returns:

  • (String)


38
# File 'sig/generated/paper_trail_diff/support.rbs', line 38

def self?.association_path: (String, String) -> String

#self?.chronological_sortArray[untyped]

Ordering falls back to the id whenever timestamps tie, which recovers the real order only while ids increase with insertion. An autoincrement id does; a UUID does not, so a tie between UUID-keyed versions is genuinely unorderable and any timeline built from it would be fiction. : (Array) -> Array

Parameters:

  • (Array[untyped])

Returns:

  • (Array[untyped])


23
# File 'sig/generated/paper_trail_diff/support.rbs', line 23

def self?.chronological_sort: (Array[untyped]) -> Array[untyped]

#self?.chronological_version_keyArray[untyped]

: (untyped) -> Array

Parameters:

  • (Object)

Returns:

  • (Array[untyped])


16
# File 'sig/generated/paper_trail_diff/support.rbs', line 16

def self?.chronological_version_key: (untyped) -> Array[untyped]

#self?.compare_versionsInteger

: (untyped, untyped) -> Integer

Parameters:

  • (Object)
  • (Object)

Returns:

  • (Integer)


35
# File 'sig/generated/paper_trail_diff/support.rbs', line 35

def self?.compare_versions: (untyped, untyped) -> Integer

#self?.duplicate_and_freezeObject

: (untyped) -> untyped

Parameters:

  • (Object)

Returns:

  • (Object)


44
# File 'sig/generated/paper_trail_diff/support.rbs', line 44

def self?.duplicate_and_freeze: (untyped) -> untyped

#self?.immutable_copyObject

: (untyped) -> untyped

Parameters:

  • (Object)

Returns:

  • (Object)


7
# File 'sig/generated/paper_trail_diff/support.rbs', line 7

def self?.immutable_copy: (untyped) -> untyped

#self?.immutable_hashHash[untyped, untyped]

: (Hash[untyped, untyped]) -> Hash[untyped, untyped]

Parameters:

  • (Hash[untyped, untyped])

Returns:

  • (Hash[untyped, untyped])


10
# File 'sig/generated/paper_trail_diff/support.rbs', line 10

def self?.immutable_hash: (Hash[untyped, untyped]) -> Hash[untyped, untyped]

#self?.merge_record_groupsHash[String, Hash[Symbol, untyped]]

: (Hash[String, Hash[Symbol, untyped]], Hash[String, Hash[Symbol, untyped]]) -> Hash[String, Hash[Symbol, untyped]]

Parameters:

  • (Hash[String, Hash[Symbol, untyped]])
  • (Hash[String, Hash[Symbol, untyped]])

Returns:

  • (Hash[String, Hash[Symbol, untyped]])


41
# File 'sig/generated/paper_trail_diff/support.rbs', line 41

def self?.merge_record_groups: (Hash[String, Hash[Symbol, untyped]], Hash[String, Hash[Symbol, untyped]]) -> Hash[String, Hash[Symbol, untyped]]

#self?.merge_record_ownersHash[String, Array[untyped]]?

: (Hash[String, Array[untyped]]?, Hash[String, Array[untyped]]?) -> Hash[String, Array[untyped]]?

Parameters:

  • (Hash[String, Array[untyped]], nil)
  • (Hash[String, Array[untyped]], nil)

Returns:

  • (Hash[String, Array[untyped]], nil)


47
# File 'sig/generated/paper_trail_diff/support.rbs', line 47

def self?.merge_record_owners: (Hash[String, Array[untyped]]?, Hash[String, Array[untyped]]?) -> Hash[String, Array[untyped]]?

#self?.paper_trail_diff_value?Boolean

: (untyped) -> bool

Parameters:

  • (Object)

Returns:

  • (Boolean)


50
# File 'sig/generated/paper_trail_diff/support.rbs', line 50

def self?.paper_trail_diff_value?: (untyped) -> bool

#self?.sequential_id?Boolean

: (untyped) -> bool

Parameters:

  • (Object)

Returns:

  • (Boolean)


29
# File 'sig/generated/paper_trail_diff/support.rbs', line 29

def self?.sequential_id?: (untyped) -> bool

#self?.serializeObject

: (untyped) -> untyped

Parameters:

  • (Object)

Returns:

  • (Object)


13
# File 'sig/generated/paper_trail_diff/support.rbs', line 13

def self?.serialize: (untyped) -> untyped