Module: PaperTrailDiff

Defined in:
lib/paper_trail_diff.rb,
lib/paper_trail_diff/step.rb,
lib/paper_trail_diff/engine.rb,
lib/paper_trail_diff/errors.rb,
lib/paper_trail_diff/support.rb,
lib/paper_trail_diff/version.rb,
lib/paper_trail_diff/analysis.rb,
lib/paper_trail_diff/endpoint.rb,
lib/paper_trail_diff/snapshot.rb,
lib/paper_trail_diff/traversal.rb,
lib/paper_trail_diff/time_range.rb,
lib/paper_trail_diff/diagnostics.rb,
lib/paper_trail_diff/configuration.rb,
lib/paper_trail_diff/value_objects.rb,
lib/paper_trail_diff/version_range.rb,
lib/paper_trail_diff/activity_event.rb,
lib/paper_trail_diff/activity_range.rb,
lib/paper_trail_diff/analysis_batch.rb,
lib/paper_trail_diff/timeline_range.rb,
lib/paper_trail_diff/instrumentation.rb,
lib/paper_trail_diff/scoped_analysis.rb,
lib/paper_trail_diff/traversal_entry.rb,
lib/paper_trail_diff/activity_history.rb,
lib/paper_trail_diff/comparison_batch.rb,
lib/paper_trail_diff/prepared_history.rb,
lib/paper_trail_diff/timeline_builder.rb,
lib/paper_trail_diff/activity_boundary.rb,
lib/paper_trail_diff/activity_grouping.rb,
lib/paper_trail_diff/nested_comparator.rb,
lib/paper_trail_diff/root_version_plan.rb,
lib/paper_trail_diff/traversal_emitter.rb,
lib/paper_trail_diff/snapshot_traversal.rb,
lib/paper_trail_diff/time_version_range.rb,
lib/paper_trail_diff/traversal_preparer.rb,
lib/paper_trail_diff/activity_root_steps.rb,
lib/paper_trail_diff/paper_trail_adapter.rb,
lib/paper_trail_diff/snapshot_normalizer.rb,
lib/paper_trail_diff/live_graph_collector.rb,
lib/paper_trail_diff/prepared_edge_loader.rb,
lib/paper_trail_diff/version_scope_filter.rb,
lib/paper_trail_diff/activity_relationship.rb,
lib/paper_trail_diff/association_discovery.rb,
lib/paper_trail_diff/association_traversal.rb,
lib/paper_trail_diff/batched_root_analyzer.rb,
lib/paper_trail_diff/batched_root_versions.rb,
lib/paper_trail_diff/collection_comparator.rb,
lib/paper_trail_diff/collection_transition.rb,
lib/paper_trail_diff/prepared_record_index.rb,
lib/paper_trail_diff/scoped_root_selection.rb,
lib/paper_trail_diff/live_endpoint_provider.rb,
lib/paper_trail_diff/root_version_selection.rb,
lib/paper_trail_diff/activity_snapshot_delta.rb,
lib/paper_trail_diff/batch_boundary_resolver.rb,
lib/paper_trail_diff/live_association_reader.rb,
lib/paper_trail_diff/prepared_history_loader.rb,
lib/paper_trail_diff/activity_timeline_builder.rb,
lib/paper_trail_diff/branch_snapshot_refresher.rb,
lib/paper_trail_diff/collection_identity_index.rb,
lib/paper_trail_diff/historical_snapshot_store.rb,
lib/paper_trail_diff/activity_snapshot_sequence.rb,
lib/paper_trail_diff/activity_version_collector.rb,
lib/paper_trail_diff/association_diff_traversal.rb,
lib/paper_trail_diff/live_endpoint_batch_loader.rb,
lib/paper_trail_diff/timeline_snapshot_provider.rb,
lib/paper_trail_diff/activity_event_route_finder.rb,
lib/paper_trail_diff/activity_transaction_grouper.rb,
lib/paper_trail_diff/prepared_association_reifier.rb,
lib/paper_trail_diff/version_sequence_diagnostics.rb,
lib/paper_trail_diff/activity_event_record_resolver.rb,
lib/paper_trail_diff/historical_association_reifier.rb,
lib/paper_trail_diff/time_activity_timeline_builder.rb,
lib/paper_trail_diff/activity_child_candidate_loader.rb,
lib/paper_trail_diff/preloaded_endpoint_batch_loader.rb,
lib/paper_trail_diff/activity_collection_route_change.rb,
lib/paper_trail_diff/activity_event_record_normalizer.rb,
lib/paper_trail_diff/activity_root_snapshot_refresher.rb,
lib/paper_trail_diff/activity_belongs_to_event_applier.rb,
lib/paper_trail_diff/activity_collection_event_applier.rb,
lib/paper_trail_diff/activity_collection_route_updater.rb,
lib/paper_trail_diff/activity_event_snapshot_refresher.rb,
lib/paper_trail_diff/activity_collection_record_updater.rb,
lib/paper_trail_diff/version_association_candidate_scope.rb,
sig/generated/paper_trail_diff.rbs,
sig/generated/paper_trail_diff.rbs,
sig/generated/paper_trail_diff/step.rbs,
sig/generated/paper_trail_diff/engine.rbs,
sig/generated/paper_trail_diff/errors.rbs,
sig/generated/paper_trail_diff/support.rbs,
sig/generated/paper_trail_diff/version.rbs,
sig/generated/paper_trail_diff/analysis.rbs,
sig/generated/paper_trail_diff/endpoint.rbs,
sig/generated/paper_trail_diff/snapshot.rbs,
sig/generated/paper_trail_diff/traversal.rbs,
sig/generated/paper_trail_diff/time_range.rbs,
sig/generated/paper_trail_diff/diagnostics.rbs,
sig/generated/paper_trail_diff/configuration.rbs,
sig/generated/paper_trail_diff/value_objects.rbs,
sig/generated/paper_trail_diff/version_range.rbs,
sig/generated/paper_trail_diff/activity_event.rbs,
sig/generated/paper_trail_diff/activity_range.rbs,
sig/generated/paper_trail_diff/analysis_batch.rbs,
sig/generated/paper_trail_diff/timeline_range.rbs,
sig/generated/paper_trail_diff/instrumentation.rbs,
sig/generated/paper_trail_diff/scoped_analysis.rbs,
sig/generated/paper_trail_diff/traversal_entry.rbs,
sig/generated/paper_trail_diff/activity_history.rbs,
sig/generated/paper_trail_diff/comparison_batch.rbs,
sig/generated/paper_trail_diff/prepared_history.rbs,
sig/generated/paper_trail_diff/timeline_builder.rbs,
sig/generated/paper_trail_diff/activity_boundary.rbs,
sig/generated/paper_trail_diff/activity_grouping.rbs,
sig/generated/paper_trail_diff/nested_comparator.rbs,
sig/generated/paper_trail_diff/root_version_plan.rbs,
sig/generated/paper_trail_diff/traversal_emitter.rbs,
sig/generated/paper_trail_diff/snapshot_traversal.rbs,
sig/generated/paper_trail_diff/time_version_range.rbs,
sig/generated/paper_trail_diff/traversal_preparer.rbs,
sig/generated/paper_trail_diff/activity_root_steps.rbs,
sig/generated/paper_trail_diff/paper_trail_adapter.rbs,
sig/generated/paper_trail_diff/snapshot_normalizer.rbs,
sig/generated/paper_trail_diff/live_graph_collector.rbs,
sig/generated/paper_trail_diff/prepared_edge_loader.rbs,
sig/generated/paper_trail_diff/version_scope_filter.rbs,
sig/generated/paper_trail_diff/activity_relationship.rbs,
sig/generated/paper_trail_diff/association_discovery.rbs,
sig/generated/paper_trail_diff/association_traversal.rbs,
sig/generated/paper_trail_diff/batched_root_analyzer.rbs,
sig/generated/paper_trail_diff/batched_root_versions.rbs,
sig/generated/paper_trail_diff/collection_comparator.rbs,
sig/generated/paper_trail_diff/collection_transition.rbs,
sig/generated/paper_trail_diff/prepared_record_index.rbs,
sig/generated/paper_trail_diff/scoped_root_selection.rbs,
sig/generated/paper_trail_diff/live_endpoint_provider.rbs,
sig/generated/paper_trail_diff/root_version_selection.rbs,
sig/generated/paper_trail_diff/activity_snapshot_delta.rbs,
sig/generated/paper_trail_diff/batch_boundary_resolver.rbs,
sig/generated/paper_trail_diff/live_association_reader.rbs,
sig/generated/paper_trail_diff/prepared_history_loader.rbs,
sig/generated/paper_trail_diff/activity_timeline_builder.rbs,
sig/generated/paper_trail_diff/branch_snapshot_refresher.rbs,
sig/generated/paper_trail_diff/collection_identity_index.rbs,
sig/generated/paper_trail_diff/historical_snapshot_store.rbs,
sig/generated/paper_trail_diff/activity_snapshot_sequence.rbs,
sig/generated/paper_trail_diff/activity_version_collector.rbs,
sig/generated/paper_trail_diff/association_diff_traversal.rbs,
sig/generated/paper_trail_diff/live_endpoint_batch_loader.rbs,
sig/generated/paper_trail_diff/timeline_snapshot_provider.rbs,
sig/generated/paper_trail_diff/activity_event_route_finder.rbs,
sig/generated/paper_trail_diff/activity_transaction_grouper.rbs,
sig/generated/paper_trail_diff/prepared_association_reifier.rbs,
sig/generated/paper_trail_diff/version_sequence_diagnostics.rbs,
sig/generated/paper_trail_diff/activity_event_record_resolver.rbs,
sig/generated/paper_trail_diff/historical_association_reifier.rbs,
sig/generated/paper_trail_diff/time_activity_timeline_builder.rbs,
sig/generated/paper_trail_diff/activity_child_candidate_loader.rbs,
sig/generated/paper_trail_diff/preloaded_endpoint_batch_loader.rbs,
sig/generated/paper_trail_diff/activity_collection_route_change.rbs,
sig/generated/paper_trail_diff/activity_event_record_normalizer.rbs,
sig/generated/paper_trail_diff/activity_root_snapshot_refresher.rbs,
sig/generated/paper_trail_diff/activity_belongs_to_event_applier.rbs,
sig/generated/paper_trail_diff/activity_collection_event_applier.rbs,
sig/generated/paper_trail_diff/activity_collection_route_updater.rbs,
sig/generated/paper_trail_diff/activity_event_snapshot_refresher.rbs,
sig/generated/paper_trail_diff/activity_collection_record_updater.rbs,
sig/generated/paper_trail_diff/version_association_candidate_scope.rbs

Overview

Structured version comparison for PaperTrail. Each method is a thin, documented entry point, so this reads as an API listing rather than logic.

Defined Under Namespace

Modules: ActivityGrouping, ActivityRootSteps, Endpoint, Instrumentation, Support Classes: ActivityBelongsToEventApplier, ActivityBoundary, ActivityChildCandidateLoader, ActivityCollectionEventApplier, ActivityCollectionRecordUpdater, ActivityCollectionRouteChange, ActivityCollectionRouteUpdater, ActivityEvent, ActivityEventRecordNormalizer, ActivityEventRecordResolver, ActivityEventRegistry, ActivityEventRouteFinder, ActivityEventSnapshotRefresher, ActivityHistory, ActivityHistoryBuilder, ActivityRange, ActivityRelationship, ActivityRootSnapshotRefresher, ActivitySnapshotDelta, ActivitySnapshotProvider, ActivitySnapshotSequence, ActivityStep, ActivityTimelineBuilder, ActivityTransactionGrouper, ActivityTransactionGroups, ActivityVersionCollector, AmbiguousVersionOrderError, Analysis, AnalysisBatch, ArrayChange, AssociationDescriptor, AssociationDiscovery, AssociationSnapshot, AssociationTrackingUnavailableError, AssociationTraversal, AssociationTree, BatchBoundaryResolver, BatchLimitExceededError, BatchedRootAnalyzer, BatchedRootVersions, BranchSnapshotRefresher, CollectionAssociationDiff, CollectionComparator, CollectionIdentityIndex, CollectionTransition, ComparisonBatch, ConfigurationError, DiagnosticIssue, DiagnosticReport, Diff, Engine, Error, HistoricalAssociationReifier, HistoricalSnapshotStore, HistoryDiagnostics, IgnorePolicy, IncompleteAssociationHistoryError, IncompleteTimeRangeError, InvalidEndpointError, InvalidTimeRangeError, InvalidTimelineRangeError, LiveAssociationReader, LiveEndpointBatchLoader, LiveEndpointProvider, LiveGraphCollector, NestedComparator, PaperTrailAdapter, PreloadedEndpointBatchLoader, PreparedAssociationReifier, PreparedEdgeLoader, PreparedHistory, PreparedHistoryLoader, PreparedRecordIndex, PreparedRecordSeries, PreparedRecordState, PreparedVersionStateLoader, RecordChange, RecordReference, RecordSnapshot, ReversedEndpointsError, RootVersionPlan, RootVersionSelection, ScopedAnalysis, ScopedRootSelection, SnapshotNormalizer, SnapshotPool, Step, TimeActivityTimelineBuilder, TimeRange, TimeVersionRange, TimelineBuilder, TimelineRange, TimelineSnapshotProvider, ToOneAssociationDiff, TraversalEntry, TraversalPreparer, UnknownAssociationError, UnloadedAssociationError, UnsupportedAssociationError, UnsupportedLiveActivityError, UnversionedAssociationError, ValueChange, VersionAssociationCandidateScope, VersionMismatchError, VersionRange, VersionScopeFilter, VersionSequenceDiagnostics

Constant Summary collapse

DEFAULT_IGNORED_ATTRIBUTES =

rubocop:disable Metrics/ModuleLength

Returns:

  • (Object)
['updated_at'].freeze
SUPPORTED_ASSOCIATION_MACROS =

Returns:

  • (Object)
%i[
  belongs_to
  has_one
  has_many
  has_and_belongs_to_many
].freeze
VERSION =

Returns:

  • (::String)
'0.12.0'

Class Method Summary collapse

Class Method Details

.activity_timeline(record, from: nil, to: nil, within: nil, associations: [], ignore: DEFAULT_IGNORED_ATTRIBUTES, reload_live_endpoints: true, version_scope: nil, close_on: nil, snapshots: false, group: nil) ⇒ Array[ActivityStep]

Compares adjacent root and selected-descendant activity boundaries. reload_live_endpoints: applies only when to: is a current record; the other range forms never read live state. : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool) -> Array

Parameters:

  • (Object)
  • from: (Object) (defaults to: nil)
  • to: (Object) (defaults to: nil)
  • within: (Object) (defaults to: nil)
  • associations: (Array[String | Symbol]) (defaults to: [])
  • ignore: (ignore_option) (defaults to: DEFAULT_IGNORED_ATTRIBUTES)
  • reload_live_endpoints: (Boolean) (defaults to: true)
  • version_scope: (Object) (defaults to: nil)
  • close_on: (Symbol, nil) (defaults to: nil)
  • snapshots: (Boolean) (defaults to: false)

Returns:



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/paper_trail_diff.rb', line 181

def activity_timeline( # rubocop:disable Metrics/ParameterLists
  record,
  from: nil,
  to: nil,
  within: nil,
  associations: [],
  ignore: DEFAULT_IGNORED_ATTRIBUTES,
  reload_live_endpoints: true,
  version_scope: nil,
  close_on: nil,
  snapshots: false,
  group: nil
)
  PaperTrailAdapter.new(
    associations: associations,
    ignore: ignore,
    reload_live_endpoints: reload_live_endpoints
  ).activity_timeline(
    record,
    from: from,
    to: to,
    within: within,
    version_scope: version_scope,
    close_on: close_on,
    group: group,
    snapshots: snapshots
  )
end

.analyze(record, from: nil, to: nil, within: nil, associations: [], ignore: DEFAULT_IGNORED_ATTRIBUTES, activity: false, version_scope: nil, close_on: nil, snapshots: false, group: nil) ⇒ Analysis

Builds an endpoint diff and root-checkpoint timeline while normalizing each version once. : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?, ?snapshots: bool, ?group: Symbol?) -> Analysis

Parameters:

  • (Object)
  • from: (Object) (defaults to: nil)
  • to: (Object) (defaults to: nil)
  • within: (Object) (defaults to: nil)
  • associations: (Array[String | Symbol]) (defaults to: [])
  • ignore: (ignore_option) (defaults to: DEFAULT_IGNORED_ATTRIBUTES)
  • activity: (Boolean) (defaults to: false)
  • version_scope: (Object) (defaults to: nil)
  • close_on: (Symbol, nil) (defaults to: nil)
  • snapshots: (Boolean) (defaults to: false)
  • group: (Symbol, nil) (defaults to: nil)

Returns:



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/paper_trail_diff.rb', line 212

def analyze( # rubocop:disable Metrics/ParameterLists
  record,
  from: nil,
  to: nil,
  within: nil,
  associations: [],
  ignore: DEFAULT_IGNORED_ATTRIBUTES,
  activity: false,
  version_scope: nil,
  close_on: nil,
  snapshots: false,
  group: nil
)
  PaperTrailAdapter.new(associations: associations, ignore: ignore).analyze(
    record,
    from: from,
    to: to,
    within: within,
    activity: activity,
    version_scope: version_scope,
    close_on: close_on,
    snapshots: snapshots,
    group: group
  )
end

.analyze_many(records = nil, scope: nil, limit: nil, within: nil, associations: [], ignore: DEFAULT_IGNORED_ATTRIBUTES, activity: false, version_scope: nil, close_on: nil) ⇒ Hash[identity, Analysis], ScopedAnalysis

Analyzes many roots over one shared time window, preparing their selected history once for the batch instead of once per record. Roots with no versions in the window return an empty Analysis.

Pass records to analyze a list you assembled, which returns a Hash keyed by identity. Pass scope: with a limit: to have the roots selected for you from a relation, which returns a ScopedAnalysis -- the same Hash, plus the roots the relation could not reach. See analyze_scope for why that second collection exists. : (?Array?, ?scope: untyped, ?limit: Integer?, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> (Hash[identity, Analysis] | ScopedAnalysis)

Parameters:

  • (Array[untyped], nil)
  • scope: (Object) (defaults to: nil)
  • limit: (Integer, nil) (defaults to: nil)
  • within: (Object) (defaults to: nil)
  • associations: (Array[String | Symbol]) (defaults to: [])
  • ignore: (ignore_option) (defaults to: DEFAULT_IGNORED_ATTRIBUTES)
  • activity: (Boolean) (defaults to: false)
  • version_scope: (Object) (defaults to: nil)
  • close_on: (Symbol, nil) (defaults to: nil)

Returns:



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/paper_trail_diff.rb', line 248

def analyze_many( # rubocop:disable Metrics/ParameterLists
  records = nil,
  scope: nil,
  limit: nil,
  within: nil,
  associations: [],
  ignore: DEFAULT_IGNORED_ATTRIBUTES,
  activity: false,
  version_scope: nil,
  close_on: nil
)
  adapter = PaperTrailAdapter.new(associations: associations, ignore: ignore)
  if scope
    raise ConfigurationError, 'pass either records or scope:, not both' unless records.nil?

    return adapter.analyze_scope(scope, limit: limit, within: within, activity: activity,
                                        version_scope: version_scope, close_on: close_on)
  end
  raise ConfigurationError, 'pass records or scope:' if records.nil?

  adapter.analyze_many(records, within: within, activity: activity,
                                version_scope: version_scope, close_on: close_on)
end

.analyze_scope(scope, limit:, within: nil, associations: [], ignore: DEFAULT_IGNORED_ATTRIBUTES, activity: false, version_scope: nil, close_on: nil) ⇒ ScopedAnalysis

Analyzes every root the relation reaches whose history moved inside the window, selecting them in a fixed number of queries rather than making the caller rediscover them.

limit: is required and exceeding it raises. Selection moves into the gem here, so the bound on how much work a page can ask for has to move with it, and a truncated audit report is worse than a refused one.

Returns a ScopedAnalysis, which destructures:

analyses, unreachable = PaperTrailDiff.analyze_scope(
Article.where(status: 'published'), within: july, limit: 500
)

unreachable names roots that changed in the window but have no live row left. A relation's conditions are evaluated against the live table, so a destroyed root cannot be tested against them at all -- its history is intact and the state it held at destruction may well have matched. Those roots are reported rather than dropped so that a page auditing deletions is told where to look instead of quietly coming up short.

Note also that a relation selects on current state, not on state during the window: where(status: 'published') means published now, which is a different set from what was published while the window was open. : (untyped, limit: Integer?, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?activity: bool, ?version_scope: untyped, ?close_on: Symbol?) -> ScopedAnalysis

Parameters:

  • (Object)
  • limit: (Integer, nil)
  • within: (Object) (defaults to: nil)
  • associations: (Array[String | Symbol]) (defaults to: [])
  • ignore: (ignore_option) (defaults to: DEFAULT_IGNORED_ATTRIBUTES)
  • activity: (Boolean) (defaults to: false)
  • version_scope: (Object) (defaults to: nil)
  • close_on: (Symbol, nil) (defaults to: nil)

Returns:



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
# File 'lib/paper_trail_diff.rb', line 297

def analyze_scope( # rubocop:disable Metrics/ParameterLists
  scope,
  limit:,
  within: nil,
  associations: [],
  ignore: DEFAULT_IGNORED_ATTRIBUTES,
  activity: false,
  version_scope: nil,
  close_on: nil
)
  PaperTrailAdapter.new(associations: associations, ignore: ignore).analyze_scope(
    scope, limit: limit, within: within, activity: activity,
           version_scope: version_scope, close_on: close_on
  )
end

.association_paths(model_or_record, max_depth: 1) ⇒ Array[AssociationDescriptor]

Discovers supported, finite association paths for a model or record. : (untyped, ?max_depth: Integer) -> Array

Parameters:

  • (Object)
  • max_depth: (Integer) (defaults to: 1)

Returns:



321
322
323
# File 'lib/paper_trail_diff.rb', line 321

def association_paths(model_or_record, max_depth: 1)
  AssociationDiscovery.new(model_or_record, max_depth: max_depth).call
end

.compare(from_version, to_version, associations: [], ignore: DEFAULT_IGNORED_ATTRIBUTES, reload_live_endpoints: true) ⇒ Diff

Compares net state between explicit PaperTrail-version or current-record endpoints. : (untyped, untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool) -> Diff

Parameters:

  • (Object)
  • (Object)
  • associations: (Array[String | Symbol]) (defaults to: [])
  • ignore: (ignore_option) (defaults to: DEFAULT_IGNORED_ATTRIBUTES)
  • reload_live_endpoints: (Boolean) (defaults to: true)

Returns:



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/paper_trail_diff.rb', line 123

def compare(
  from_version,
  to_version,
  associations: [],
  ignore: DEFAULT_IGNORED_ATTRIBUTES,
  reload_live_endpoints: true
)
  PaperTrailAdapter.new(
    associations: associations,
    ignore: ignore,
    reload_live_endpoints: reload_live_endpoints
  ).compare(
    from_version,
    to_version
  )
end

.compare_many(comparisons, associations: [], ignore: DEFAULT_IGNORED_ATTRIBUTES, reload_live_endpoints: true) ⇒ comparison_results

Compares many independent endpoint pairs while batching current-record loading. : (Array, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?reload_live_endpoints: bool) -> comparison_results

Parameters:

  • (Array[comparison_input])
  • associations: (Array[String | Symbol]) (defaults to: [])
  • ignore: (ignore_option) (defaults to: DEFAULT_IGNORED_ATTRIBUTES)
  • reload_live_endpoints: (Boolean) (defaults to: true)

Returns:

  • (comparison_results)


142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/paper_trail_diff.rb', line 142

def compare_many(
  comparisons,
  associations: [],
  ignore: DEFAULT_IGNORED_ATTRIBUTES,
  reload_live_endpoints: true
)
  PaperTrailAdapter.new(
    associations: associations,
    ignore: ignore,
    reload_live_endpoints: reload_live_endpoints
  ).compare_many(comparisons)
end

.diagnose(from_version, to_version, associations: []) ⇒ DiagnosticReport

Reports known reconstruction hazards without mutating application state. : (untyped, untyped, ?associations: Array[String | Symbol]) -> DiagnosticReport

Parameters:

  • (Object)
  • (Object)
  • associations: (Array[String | Symbol]) (defaults to: [])

Returns:



327
328
329
# File 'lib/paper_trail_diff.rb', line 327

def diagnose(from_version, to_version, associations: [])
  HistoryDiagnostics.new(from_version, to_version, associations: associations).call
end

.nested_changes(change, to_value = nil) ⇒ nested_changes

Looks inside an attribute the database stores whole, such as a JSON or jsonb column, and reports which keys changed.

change = diff.attributes.fetch('config')
PaperTrailDiff.nested_changes(change)
# => { ['theme'] => <from "dark" to "light">,
#      ['limits', 'max'] => <from 10 to 20> }

Accepts the ValueChange an attribute diff already produced, or a bare pair. Returns an empty hash when the pair is not two readable structures, which is the honest answer: a column that held text on one side and JSON on the other changed wholesale, and the caller still has that change.

Paths are arrays because a JSON key may contain a dot. Arrays are reported whole rather than by index, since their elements carry no identity and a list that merely shifted would otherwise look changed throughout. A key that was absent reads as NestedComparator::ABSENT rather than nil, which JSON uses for a present null. : (untyped, ?untyped) -> nested_changes

Parameters:

  • (Object)
  • (Object)

Returns:



350
351
352
353
354
355
356
# File 'lib/paper_trail_diff.rb', line 350

def nested_changes(change, to_value = nil)
  # Tested by type rather than by responding to `from`: ActiveSupport gives
  # String#from, so duck-typing here quietly reads a plain string as a pair.
  return NestedComparator.call(change.from, change.to) if change.is_a?(ValueChange)

  NestedComparator.call(change, to_value)
end

.supported_association_macrosArray[Symbol]

Returns the association macros this release can normalize. : () -> Array

Returns:

  • (Array[Symbol])


315
316
317
# File 'lib/paper_trail_diff.rb', line 315

def supported_association_macros
  SUPPORTED_ASSOCIATION_MACROS
end

.timeline(record, from: nil, to: nil, within: nil, associations: [], ignore: DEFAULT_IGNORED_ATTRIBUTES, version_scope: nil, close_on: nil) ⇒ Array[Step]

Compares every adjacent reconstructed state in an inclusive version range. : (untyped, ?from: untyped, ?to: untyped, ?within: untyped, ?associations: Array[String | Symbol], ?ignore: ignore_option, ?version_scope: untyped, ?close_on: Symbol?) -> Array

Parameters:

  • (Object)
  • from: (Object) (defaults to: nil)
  • to: (Object) (defaults to: nil)
  • within: (Object) (defaults to: nil)
  • associations: (Array[String | Symbol]) (defaults to: [])
  • ignore: (ignore_option) (defaults to: DEFAULT_IGNORED_ATTRIBUTES)
  • version_scope: (Object) (defaults to: nil)
  • close_on: (Symbol, nil) (defaults to: nil)

Returns:



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/paper_trail_diff.rb', line 157

def timeline( # rubocop:disable Metrics/ParameterLists
  record,
  from: nil,
  to: nil,
  within: nil,
  associations: [],
  ignore: DEFAULT_IGNORED_ATTRIBUTES,
  version_scope: nil,
  close_on: nil
)
  PaperTrailAdapter.new(associations: associations, ignore: ignore).timeline(
    record,
    from: from,
    to: to,
    within: within,
    version_scope: version_scope,
    close_on: close_on
  )
end