Class: PaperTrailDiff::PreparedHistory

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

Overview

Batched record states and relationship candidates for one activity range.

Constant Summary collapse

DIRECT_RESOLVERS =

Returns:

  • (Object)
{
  belongs_to: :resolve_belongs_to,
  has_one: :resolve_children,
  has_many: :resolve_children,
  has_and_belongs_to_many: :resolve_habtm
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(records) ⇒ PreparedHistory

: (PreparedRecordIndex) -> void

Parameters:



16
17
18
19
20
21
# File 'lib/paper_trail_diff/prepared_history.rb', line 16

def initialize(records)
  @records = records
  @edges = {} #: Hash[Array[String], Hash[String, Hash[Symbol, untyped]]]
  @unsupported = {} #: Hash[Array[String], bool]
  @habtm = {} #: Hash[Array[String], Hash[String, Array[untyped]]]
end

Instance Method Details

#belongs_to_class(record, reflection) ⇒ Object

: (untyped, untyped) -> untyped

Parameters:

  • (Object)
  • (Object)

Returns:

  • (Object)


193
194
195
196
197
198
199
# File 'lib/paper_trail_diff/prepared_history.rb', line 193

def belongs_to_class(record, reflection)
  return reflection.klass unless reflection.polymorphic?

  Object.const_get(record.public_send(reflection.foreign_type).to_s)
rescue NameError
  nil
end

#belongs_to_records(record, reflection, boundary) ⇒ Array[untyped]?

: (untyped, untyped, untyped) -> Array?

Parameters:

  • (Object)
  • (Object)
  • (Object)

Returns:

  • (Array[untyped], nil)


103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/paper_trail_diff/prepared_history.rb', line 103

def belongs_to_records(record, reflection, boundary)
  foreign_key = scalar_foreign_key(reflection)
  return unless foreign_key

  id = record.public_send(foreign_key)
  return [] if id.nil?

  model_class = belongs_to_class(record, reflection)
  return unless model_class && @records.loaded?(model_class, id)

  Array(@records.record_before(model_class, id, boundary)).compact
end

#child_ids_for(group, owner) ⇒ Array[untyped]

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

Parameters:

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

Returns:

  • (Array[untyped])


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

def child_ids_for(group, owner)
  owners = group[:owners]
  empty = [] #: Array[untyped]
  owners ? owners.fetch(owner.id.to_s, empty) : group.fetch(:ids)
end

#child_records(record, reflection, boundary) ⇒ Array[untyped]

: (untyped, untyped, untyped) -> Array

Parameters:

  • (Object)
  • (Object)
  • (Object)

Returns:

  • (Array[untyped])


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

def child_records(record, reflection, boundary)
  edge(record.class, reflection).values.flat_map do |group|
    model_class = group.fetch(:model)
    child_ids_for(group, record).filter_map do |id|
      child = @records.record_before(model_class, id, boundary)
      child if child && member_of?(child, reflection, record)
    end
  end
end

#default_scoped?(model_class) ⇒ Boolean

: (untyped) -> bool

Parameters:

  • (Object)

Returns:

  • (Boolean)


77
78
79
# File 'lib/paper_trail_diff/prepared_history.rb', line 77

def default_scoped?(model_class)
  model_class.default_scopes?
end

#direct_records(record, reflection, boundary, habtm_boundary) ⇒ Array[untyped]?

: (untyped, untyped, untyped, untyped) -> Array?

Parameters:

  • (Object)
  • (Object)
  • (Object)
  • (Object)

Returns:

  • (Array[untyped], nil)


82
83
84
85
# File 'lib/paper_trail_diff/prepared_history.rb', line 82

def direct_records(record, reflection, boundary, habtm_boundary)
  resolver = DIRECT_RESOLVERS.fetch(reflection.macro)
  send(resolver, record, reflection, boundary, habtm_boundary)
end

#edge(owner_class, reflection) ⇒ Hash[String, Hash[Symbol, untyped]]

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

Parameters:

  • (Object)
  • (Object)

Returns:

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


31
32
33
34
# File 'lib/paper_trail_diff/prepared_history.rb', line 31

def edge(owner_class, reflection)
  empty = {} #: Hash[String, Hash[Symbol, untyped]]
  @edges.fetch(edge_key(owner_class, reflection), empty)
end

#edge_key(owner_class, reflection) ⇒ Array[String]

: (untyped, untyped) -> Array

Parameters:

  • (Object)
  • (Object)

Returns:

  • (Array[String])


218
219
220
# File 'lib/paper_trail_diff/prepared_history.rb', line 218

def edge_key(owner_class, reflection)
  [owner_class.base_class.name.to_s, reflection.name.to_s]
end

#habtm_records(record, reflection, boundary, habtm_boundary) ⇒ Array[untyped]?

: (untyped, untyped, untyped, untyped) -> Array?

Parameters:

  • (Object)
  • (Object)
  • (Object)
  • (Object)

Returns:

  • (Array[untyped], nil)


135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/paper_trail_diff/prepared_history.rb', line 135

def habtm_records(record, reflection, boundary, habtm_boundary)
  transaction_id = transaction_id_for(habtm_boundary)
  return unless transaction_id

  empty = {} #: Hash[String, Array[untyped]]
  ids = @habtm.fetch(edge_key(record.class, reflection), empty)[transaction_id.to_s]
  return unless ids

  ids.filter_map do |id|
    @records.record_before(reflection.klass, id, boundary)
  end
end

#mark_unsupported(owner_class, reflection) ⇒ void

This method returns an undefined value.

: (untyped, untyped) -> void

Parameters:

  • (Object)
  • (Object)


37
38
39
# File 'lib/paper_trail_diff/prepared_history.rb', line 37

def mark_unsupported(owner_class, reflection)
  @unsupported[edge_key(owner_class, reflection)] = true
end

#member_of?(child, reflection, owner) ⇒ Boolean

: (untyped, untyped, untyped) -> bool

Parameters:

  • (Object)
  • (Object)
  • (Object)

Returns:

  • (Boolean)


177
178
179
180
181
182
183
184
# File 'lib/paper_trail_diff/prepared_history.rb', line 177

def member_of?(child, reflection, owner)
  foreign_key = scalar_foreign_key(reflection)
  return false unless foreign_key
  return false unless child.public_send(foreign_key).to_s == owner.id.to_s
  return true unless reflection.options[:as]

  owner_types(reflection, owner).include?(child.public_send(reflection.type).to_s)
end

#merge_edge(owner_class, reflection, groups) ⇒ void

This method returns an undefined value.

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

Parameters:

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


24
25
26
27
28
# File 'lib/paper_trail_diff/prepared_history.rb', line 24

def merge_edge(owner_class, reflection, groups)
  key = edge_key(owner_class, reflection)
  empty = {} #: Hash[String, Hash[Symbol, untyped]]
  @edges[key] = merge_groups(@edges.fetch(key, empty), groups)
end

#merge_groups(existing, incoming) ⇒ Hash[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]])


223
224
225
# File 'lib/paper_trail_diff/prepared_history.rb', line 223

def merge_groups(existing, incoming)
  Support.merge_record_groups(existing, incoming)
end

#owner_types(reflection, owner) ⇒ Array[String]

: (untyped, untyped) -> Array

Parameters:

  • (Object)
  • (Object)

Returns:

  • (Array[String])


202
203
204
205
# File 'lib/paper_trail_diff/prepared_history.rb', line 202

def owner_types(reflection, owner)
  types = [owner.class.name, owner.class.base_class.name, reflection.active_record.name]
  types.map(&:to_s).uniq
end

#record_transition(model_class, id, version) ⇒ [ Hash[untyped, untyped], Hash[untyped, untyped] ]?

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

Parameters:

  • (Object)
  • (Object)
  • (Object)

Returns:

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


47
48
49
# File 'lib/paper_trail_diff/prepared_history.rb', line 47

def record_transition(model_class, id, version)
  @records.transition(model_class, id, version)
end

#register_habtm(owner_class, reflection, memberships) ⇒ void

This method returns an undefined value.

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

Parameters:

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


42
43
44
# File 'lib/paper_trail_diff/prepared_history.rb', line 42

def register_habtm(owner_class, reflection, memberships)
  @habtm[edge_key(owner_class, reflection)] = memberships.transform_keys(&:to_s)
end

#resolve(record, reflection, boundary, habtm_boundary:) ⇒ [ bool, Array[untyped] ]

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

Parameters:

  • (Object)
  • (Object)
  • (Object)
  • habtm_boundary: (Object)

Returns:

  • ([ bool, Array[untyped] ])


52
53
54
55
56
57
58
59
60
61
# File 'lib/paper_trail_diff/prepared_history.rb', line 52

def resolve(record, reflection, boundary, habtm_boundary:)
  return [false, []] unless supported?(record.class, reflection)

  records = if reflection.options[:through]
              through_records(record, reflection, boundary, habtm_boundary)
            else
              direct_records(record, reflection, boundary, habtm_boundary)
            end
  records ? [true, records] : [false, []]
end

#resolve_belongs_to(record, reflection, boundary, _habtm_boundary) ⇒ Array[untyped]?

: (untyped, untyped, untyped, untyped) -> Array?

Parameters:

  • (Object)
  • (Object)
  • (Object)
  • (Object)

Returns:

  • (Array[untyped], nil)


88
89
90
# File 'lib/paper_trail_diff/prepared_history.rb', line 88

def resolve_belongs_to(record, reflection, boundary, _habtm_boundary)
  belongs_to_records(record, reflection, boundary)
end

#resolve_children(record, reflection, boundary, _habtm_boundary) ⇒ Array[untyped]

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

Parameters:

  • (Object)
  • (Object)
  • (Object)
  • (Object)

Returns:

  • (Array[untyped])


93
94
95
# File 'lib/paper_trail_diff/prepared_history.rb', line 93

def resolve_children(record, reflection, boundary, _habtm_boundary)
  child_records(record, reflection, boundary)
end

#resolve_habtm(record, reflection, boundary, habtm_boundary) ⇒ Array[untyped]?

: (untyped, untyped, untyped, untyped) -> Array?

Parameters:

  • (Object)
  • (Object)
  • (Object)
  • (Object)

Returns:

  • (Array[untyped], nil)


98
99
100
# File 'lib/paper_trail_diff/prepared_history.rb', line 98

def resolve_habtm(record, reflection, boundary, habtm_boundary)
  habtm_records(record, reflection, boundary, habtm_boundary)
end

#resolve_sources(intermediates, reflection, boundary, habtm_boundary) ⇒ Array[[ bool, Array[untyped] ]]

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

Parameters:

  • (Array[untyped])
  • (Object)
  • (Object)
  • (Object)

Returns:

  • (Array[[ bool, Array[untyped] ]])


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

def resolve_sources(intermediates, reflection, boundary, habtm_boundary)
  intermediates.map do |intermediate|
    resolve(
      intermediate,
      reflection.source_reflection,
      boundary,
      habtm_boundary: habtm_boundary
    )
  end
end

#scalar_foreign_key(reflection) ⇒ String?

: (untyped) -> String?

Parameters:

  • (Object)

Returns:

  • (String, nil)


187
188
189
190
# File 'lib/paper_trail_diff/prepared_history.rb', line 187

def scalar_foreign_key(reflection)
  key = reflection.foreign_key
  key.to_s unless key.is_a?(Array)
end

#supported?(owner_class, reflection) ⇒ Boolean

: (untyped, untyped) -> bool

Parameters:

  • (Object)
  • (Object)

Returns:

  • (Boolean)


71
72
73
74
# File 'lib/paper_trail_diff/prepared_history.rb', line 71

def supported?(owner_class, reflection)
  !@unsupported[edge_key(owner_class, reflection)] &&
    reflection.scope.nil? && !default_scoped?(reflection.klass)
end

#through_records(record, reflection, boundary, habtm_boundary) ⇒ Array[untyped]?

: (untyped, untyped, untyped, untyped) -> Array?

Parameters:

  • (Object)
  • (Object)
  • (Object)
  • (Object)

Returns:

  • (Array[untyped], nil)


149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/paper_trail_diff/prepared_history.rb', line 149

def through_records(record, reflection, boundary, habtm_boundary)
  handled, intermediates = resolve(
    record,
    reflection.through_reflection,
    boundary,
    habtm_boundary: habtm_boundary
  )
  return unless handled

  resolved = resolve_sources(intermediates, reflection, boundary, habtm_boundary)
  return unless resolved.all?(&:first)

  unique_records(resolved.flat_map(&:last))
end

#transaction_id_for(boundary) ⇒ Object

: (untyped) -> untyped

Parameters:

  • (Object)

Returns:

  • (Object)


208
209
210
# File 'lib/paper_trail_diff/prepared_history.rb', line 208

def transaction_id_for(boundary)
  boundary.transaction_id
end

#unique_records(records) ⇒ Array[untyped]

: (Array) -> Array

Parameters:

  • (Array[untyped])

Returns:

  • (Array[untyped])


213
214
215
# File 'lib/paper_trail_diff/prepared_history.rb', line 213

def unique_records(records)
  records.uniq { |record| [record.class.base_class.name, record.id.to_s] }
end