Class: PaperTrailDiff::ComparisonBatch

Inherits:
Object
  • Object
show all
Defined in:
lib/paper_trail_diff/comparison_batch.rb,
sig/generated/paper_trail_diff/comparison_batch.rbs

Overview

Validates and executes independent comparisons with shared live endpoint loading.

Instance Method Summary collapse

Constructor Details

#initialize(comparisons, live_loader:, preparer:, history_preparer:, historical_snapshotter:, live_normalizer:) ⇒ ComparisonBatch

: (Array, live_loader: untyped, preparer: untyped, history_preparer: untyped, historical_snapshotter: untyped, live_normalizer: untyped) -> void

Parameters:

  • (Array[comparison_input])
  • live_loader: (Object)
  • preparer: (Object)
  • history_preparer: (Object)
  • historical_snapshotter: (Object)
  • live_normalizer: (Object)


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

def initialize( # rubocop:disable Metrics/ParameterLists
  comparisons,
  live_loader:,
  preparer:,
  history_preparer:,
  historical_snapshotter:,
  live_normalizer:
)
  @comparisons = comparisons
  @live_loader = live_loader
  @preparer = preparer
  @history_preparer = history_preparer
  @historical_snapshotter = historical_snapshotter
  @live_normalizer = live_normalizer
end

Instance Method Details

#callcomparison_results

: () -> comparison_results

Returns:

  • (comparison_results)


25
26
27
28
29
30
# File 'lib/paper_trail_diff/comparison_batch.rb', line 25

def call
  resolved = BatchBoundaryResolver.new(comparison_pairs).call
  pairs = resolved.reject { |pair| pair.any? { |endpoint| symbolic?(endpoint) } }
  ensure_unique_identities!(resolved)
  results(resolved, prepared_live_snapshots(pairs)).freeze
end

#compare(from, to, live_snapshots) ⇒ Diff

: (untyped, untyped, Hash[identity, RecordSnapshot]) -> Diff

Parameters:

Returns:



74
75
76
# File 'lib/paper_trail_diff/comparison_batch.rb', line 74

def compare(from, to, live_snapshots)
  Engine.compare(snapshot(from, live_snapshots), snapshot(to, live_snapshots))
end

#comparison_pair(comparison, index) ⇒ [ untyped, untyped ]

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

Parameters:

  • (Object)
  • (Integer)

Returns:

  • ([ untyped, untyped ])


129
130
131
132
# File 'lib/paper_trail_diff/comparison_batch.rb', line 129

def comparison_pair(comparison, index)
  valid_comparison!(comparison, index)
  [endpoint(comparison, :from), endpoint(comparison, :to)]
end

#comparison_pairsArray[[ untyped, untyped ]]

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

Returns:

  • (Array[[ untyped, untyped ]])


118
119
120
121
122
123
124
125
126
# File 'lib/paper_trail_diff/comparison_batch.rb', line 118

def comparison_pairs
  unless @comparisons.is_a?(Array)
    raise ConfigurationError, 'comparisons: must be an array of from:/to: hashes'
  end

  @comparisons.map.with_index do |comparison, index|
    comparison_pair(comparison, index)
  end
end

#endpoint(comparison, name) ⇒ Object

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

Parameters:

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

Returns:

  • (Object)


146
147
148
# File 'lib/paper_trail_diff/comparison_batch.rb', line 146

def endpoint(comparison, name)
  comparison.key?(name) ? comparison.fetch(name) : comparison.fetch(name.to_s)
end

#endpoint_requirements(pairs) ⇒ Hash[untyped, bool]

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

Parameters:

  • (Array[[ untyped, untyped ]])

Returns:

  • (Hash[untyped, bool])


166
167
168
169
170
171
172
173
# File 'lib/paper_trail_diff/comparison_batch.rb', line 166

def endpoint_requirements(pairs)
  requirements = {} #: Hash[untyped, bool]
  pairs.flatten.each_with_object(requirements) do |endpoint, collected|
    historical = Endpoint.version?(endpoint)
    model_class = historical ? Endpoint.model_class(endpoint) : endpoint.class
    collected[model_class] = collected.fetch(model_class, false) || historical
  end
end

#ensure_unique_identities!(pairs) ⇒ void

This method returns an undefined value.

: (Array[[untyped, untyped]]) -> void

Parameters:

  • (Array[[ untyped, untyped ]])


151
152
153
154
155
156
# File 'lib/paper_trail_diff/comparison_batch.rb', line 151

def ensure_unique_identities!(pairs)
  identities = pairs.map { |from, to| entry_identity(from, to) }
  return if identities.uniq.length == identities.length

  raise ConfigurationError, 'comparisons: root identities must be unique'
end

#entry_identity(from, to) ⇒ identity

: (untyped, untyped) -> identity

Parameters:

  • (Object)
  • (Object)

Returns:

  • (identity)


69
70
71
# File 'lib/paper_trail_diff/comparison_batch.rb', line 69

def entry_identity(from, to)
  Endpoint.identity(symbolic?(from) ? to : from)
end

#historical_versions(pairs, live_records) ⇒ Array[untyped]

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

Parameters:

  • (Array[[ untyped, untyped ]])
  • (Hash[identity, untyped])

Returns:

  • (Array[untyped])


110
111
112
113
114
115
# File 'lib/paper_trail_diff/comparison_batch.rb', line 110

def historical_versions(pairs, live_records)
  selected = pairs.flatten.select do |endpoint|
    Endpoint.version?(endpoint) && live_records.key?(Endpoint.identity(endpoint))
  end
  selected.uniq { |version| [version.class.name, version.id] }
end

#load_live_records(endpoints) ⇒ Hash[identity, untyped]

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

Parameters:

  • (Array[untyped])

Returns:

  • (Hash[identity, untyped])


86
87
88
89
90
91
# File 'lib/paper_trail_diff/comparison_batch.rb', line 86

def load_live_records(endpoints)
  records = endpoints.reject { |endpoint| Endpoint.version?(endpoint) }.uniq do |record|
    Endpoint.identity(record)
  end
  @live_loader.call(records)
end

#normalize_live_records(records) ⇒ Hash[identity, RecordSnapshot]

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

Parameters:

  • (Hash[identity, untyped])

Returns:



94
95
96
# File 'lib/paper_trail_diff/comparison_batch.rb', line 94

def normalize_live_records(records)
  records.transform_values { |record| @live_normalizer.call(record) }
end

#prepare_endpoint_classes!(pairs) ⇒ void

This method returns an undefined value.

: (Array[[untyped, untyped]]) -> void

Parameters:

  • (Array[[ untyped, untyped ]])


159
160
161
162
163
# File 'lib/paper_trail_diff/comparison_batch.rb', line 159

def prepare_endpoint_classes!(pairs)
  endpoint_requirements(pairs).each do |model_class, historical|
    @preparer.call(model_class, historical: historical)
  end
end

#prepare_historical_batches!(pairs, live_records) ⇒ void

This method returns an undefined value.

: (Array[[untyped, untyped]], Hash[identity, untyped]) -> void

Parameters:

  • (Array[[ untyped, untyped ]])
  • (Hash[identity, untyped])


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

def prepare_historical_batches!(pairs, live_records)
  grouped = historical_versions(pairs, live_records).group_by do |version|
    Endpoint.model_class(version)
  end
  grouped.each_value do |versions|
    records = versions.map { |version| live_records.fetch(Endpoint.identity(version)) }.uniq
    @history_preparer.call(records, versions)
  end
end

#prepared_live_snapshots(pairs) ⇒ Hash[identity, RecordSnapshot]

: (Array[[untyped, untyped]]) -> Hash[identity, RecordSnapshot]

Parameters:

  • (Array[[ untyped, untyped ]])

Returns:



42
43
44
45
46
47
48
# File 'lib/paper_trail_diff/comparison_batch.rb', line 42

def prepared_live_snapshots(pairs)
  pairs.each { |from, to| Endpoint.validate_pair!(from, to) }
  prepare_endpoint_classes!(pairs)
  live_records = load_live_records(pairs.flatten)
  prepare_historical_batches!(pairs, live_records)
  normalize_live_records(live_records)
end

#results(resolved, live_snapshots) ⇒ comparison_results

A boundary symbol that stayed unresolved means the root has no recorded history. An absent history is an empty result rather than a failed request, matching how the timeline APIs answer the same question. : (Array[[untyped, untyped]], Hash[identity, RecordSnapshot]) -> comparison_results

Parameters:

Returns:

  • (comparison_results)


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

def results(resolved, live_snapshots)
  resolved.to_h do |from, to|
    identity = Support.immutable_copy(entry_identity(from, to))
    next [identity, Diff.new] if symbolic?(from) || symbolic?(to)

    [identity, compare(from, to, live_snapshots)]
  end
end

#snapshot(endpoint, live_snapshots) ⇒ RecordSnapshot?

: (untyped, Hash[identity, RecordSnapshot]) -> RecordSnapshot?

Parameters:

Returns:



79
80
81
82
83
# File 'lib/paper_trail_diff/comparison_batch.rb', line 79

def snapshot(endpoint, live_snapshots)
  return @historical_snapshotter.call(endpoint) if Endpoint.version?(endpoint)

  live_snapshots.fetch(Endpoint.identity(endpoint))
end

#symbolic?(endpoint) ⇒ Boolean

: (untyped) -> bool

Parameters:

  • (Object)

Returns:

  • (Boolean)


51
52
53
# File 'lib/paper_trail_diff/comparison_batch.rb', line 51

def symbolic?(endpoint)
  BatchBoundaryResolver.symbolic?(endpoint)
end

#valid_comparison!(comparison, index) ⇒ void

This method returns an undefined value.

: (untyped, Integer) -> void

Parameters:

  • (Object)
  • (Integer)


135
136
137
138
139
140
141
142
143
# File 'lib/paper_trail_diff/comparison_batch.rb', line 135

def valid_comparison!(comparison, index)
  if comparison.is_a?(Hash)
    keys = comparison.keys.map(&:to_s)
    return if keys.sort == %w[from to] && keys.length == 2
  end

  message = "comparisons[#{index}]: must contain exactly from: and to:"
  raise ConfigurationError, message
end