activehistory 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 643c618d011f3af9b72ba7a7fadf3a713aeb0cf4
4
- data.tar.gz: bc2d64e5b59d78deb335733d46eef644defc2656
3
+ metadata.gz: 94d976170424ce9995a019e74e9475ce1036ee85
4
+ data.tar.gz: 3ed68d22d9444cab4a7d1536b5a6514d822a677d
5
5
  SHA512:
6
- metadata.gz: 53870be2424fa9b20e5bea593fb7691f9cd9e92214d558039ec25122f20ef5eb399cdd406c655e71bc978a5fc62289302d8b8be402f3767c210af78245d332bd
7
- data.tar.gz: 4d8b2f3dea6b2aac7922bc8d28868a4b7b6d4f52917d920559f3041ab10fee6e7a5c6fe1aaa661d34b215f5c7f7d8ae6d6690dba93b1bc92ea6b50f0338fed33
6
+ metadata.gz: 448baea6c2bc3e7d3d284491bfcaebf807ef3ac61fe021151065a88a4b6d38b02ef594a6ffe2b115b57fcef420032c4bfc74b3f0a08cbc6a76e7afba4e51b0b6
7
+ data.tar.gz: 202e03ba0ed75936b7ac9f66406f2b1e559449d2eeaa78299793cb65af0d6c18ff06b11da6b1e16931cfb74897dfbac628e416871b0c89cc172c80590653fffe
@@ -4,6 +4,7 @@ PATH
4
4
  activehistory (0.1)
5
5
  activerecord (= 5.0.0.1)
6
6
  arel (~> 7.0)
7
+ globalid (~> 0.3.7)
7
8
 
8
9
  GEM
9
10
  remote: http://rubygems.org/
@@ -21,7 +22,7 @@ GEM
21
22
  tzinfo (~> 1.1)
22
23
  addressable (2.4.0)
23
24
  ansi (1.5.0)
24
- arel (7.1.2)
25
+ arel (7.1.4)
25
26
  builder (3.2.2)
26
27
  concurrent-ruby (1.0.2)
27
28
  crack (0.4.3)
@@ -31,6 +32,8 @@ GEM
31
32
  activesupport (>= 3.0.0)
32
33
  faker (1.6.6)
33
34
  i18n (~> 0.5)
35
+ globalid (0.3.7)
36
+ activesupport (>= 4.1.0)
34
37
  hashdiff (0.3.0)
35
38
  i18n (0.7.0)
36
39
  json (1.8.3)
@@ -87,4 +90,4 @@ DEPENDENCIES
87
90
  webmock
88
91
 
89
92
  BUNDLED WITH
90
- 1.12.5
93
+ 1.13.2
@@ -37,4 +37,5 @@ Gem::Specification.new do |s|
37
37
  # s.add_runtime_dependency 'cookie_store'
38
38
  s.add_runtime_dependency 'arel', '~> 7.0'
39
39
  s.add_runtime_dependency 'activerecord', '5.0.0.1'
40
- end
40
+ s.add_runtime_dependency 'globalid', '~> 0.3.7'
41
+ end
@@ -2,28 +2,41 @@ module ActiveHistory
2
2
 
3
3
  mattr_accessor :connection
4
4
 
5
+ UUIDV4 = /^[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i
6
+
5
7
  def self.configure(settings)
6
8
  @@connection = ActiveHistory::Connection.new(settings)
7
9
  end
8
10
 
9
11
  def self.configured?
10
- class_variable_defined?(:@@connection)
12
+ class_variable_defined?(:@@connection) && !@@connection.nil?
11
13
  end
12
14
 
13
15
  def self.url
14
16
  @@connection.url
15
17
  end
16
18
 
17
- def self.encapsulate(id_or_options=nil)
18
- Thread.current[:activehistory_event] = id_or_options
19
+ def self.encapsulate(attributes_or_event={}, &block)
20
+ if attributes_or_event.is_a?(ActiveHistory::Event)
21
+ event = attributes_or_event
22
+ else
23
+ event = ActiveHistory::Event.new(attributes_or_event)
24
+ end
25
+
26
+ Thread.current[:activehistory_event] = event
27
+
19
28
  yield
20
29
  ensure
21
- if Thread.current[:activehistory_event].is_a?(ActiveHistory::Event)
30
+ if Thread.current[:activehistory_event] && !Thread.current[:activehistory_event].actions.empty?
22
31
  Thread.current[:activehistory_event].save!
23
32
  end
24
33
  Thread.current[:activehistory_event] = nil
25
34
  end
26
35
 
36
+ def self.current_event(timestamp: nil)
37
+ Thread.current[:activehistory_event]
38
+ end
39
+
27
40
  end
28
41
 
29
42
  require 'activehistory/connection'
@@ -1,17 +1,19 @@
1
1
  class ActiveHistory::Action
2
2
 
3
- attr_accessor :type, :timestamp, :subject, :diff
3
+ attr_accessor :type, :timestamp, :subject_type, :subject_id, :diff
4
4
 
5
5
  def initialize(attrs)
6
6
  attrs.each do |k,v|
7
7
  self.send("#{k}=", v)
8
8
  end
9
+ self.diff ||= {}
9
10
  end
10
11
 
11
12
  def as_json
12
13
  {
13
- diff: diff.as_json,
14
- subject: @subject,
14
+ diff: diff.as_json,
15
+ subject_type: @subject_type,
16
+ subject_id: @subject_id,
15
17
  timestamp: @timestamp.iso8601(3),
16
18
  type: @type
17
19
  }
@@ -1,78 +1,135 @@
1
1
  module ActiveHistory::Adapter
2
2
  module ActiveRecord
3
3
  extend ActiveSupport::Concern
4
-
4
+
5
5
  class_methods do
6
6
 
7
7
  def self.extended(other)
8
- other.before_save :activehistory_start
9
- other.before_destroy :activehistory_start
10
-
11
- other.after_create { activehistory_track(:create) }
12
- other.after_update { activehistory_track(:update) }
13
- other.before_destroy { activehistory_track(:destroy) }
14
-
15
- other.after_commit { activehistory_complete }
8
+ other.after_create { activehistory_track(:create) }
9
+ other.after_update { activehistory_track(:update) }
10
+ other.before_destroy { activehistory_track(:destroy) }
16
11
  end
17
12
 
18
13
  def inherited(subclass)
19
14
  super
20
-
21
15
  subclass.instance_variable_set('@activehistory', @activehistory.clone) if defined?(@activehistory)
22
16
  end
23
17
 
24
18
  def track(exclude: [], habtm_model: nil)
25
- @activehistory = {exclude: Array(exclude), habtm_model: habtm_model}
19
+ options = { exclude: Array(exclude) }
20
+ options[:habtm_model] = habtm_model if habtm_model
21
+ @activehistory = options
26
22
  end
27
23
 
28
24
  def has_and_belongs_to_many(name, scope = nil, options = {}, &extension)
29
25
  super
26
+ name = name.to_s
30
27
  habtm_model = self.const_get("HABTM_#{name.to_s.camelize}")
31
28
 
32
29
  habtm_model.track habtm_model: {
33
30
  :left_side => { foreign_key: "#{base_class.name.underscore}_id", inverse_of: name.to_s },
34
- name.to_s.singularize.to_sym => {inverse_of: self.name.underscore.pluralize.to_s}
31
+ name.to_s.singularize.to_sym => { inverse_of: self.name.underscore.pluralize.to_s }
35
32
  }
36
33
 
34
+
37
35
  callback = ->(method, owner, record) {
38
- owner.activehistory_association_udpated(
39
- record.class.reflect_on_association(owner.class.reflect_on_association(name.to_s).options[:inverse_of].to_s),
40
- owner.id,
41
- removed: [record.id],
42
- timestamp: owner.activehistory_timestamp
43
- )
44
- record.activehistory_association_udpated(
45
- owner.class.reflect_on_association(name.to_s),
46
- record.id,
47
- removed: [owner.id],
48
- timestamp: owner.activehistory_timestamp
49
- )
36
+ owner.activehistory_association_changed(name, removed: [record.id])
50
37
  }
51
38
  self.send("after_remove_for_#{name}=", Array(self.send("after_remove_for_#{name}")).compact + [callback])
52
39
  end
53
- end
54
-
55
- def activehistory_id
56
- "#{self.class.name}/#{id}"
40
+
41
+ def activehistory_association_changed(id, reflection_or_relation_name, added: [], removed: [], timestamp: nil, type: :update, propagate: true)
42
+ return if removed.empty? && added.empty?
43
+ reflection = if reflection_or_relation_name.is_a?(String) || reflection_or_relation_name.is_a?(Symbol)
44
+ reflect_on_association(reflection_or_relation_name)
45
+ else
46
+ reflection_or_relation_name
47
+ end
48
+ puts "#{self}##{id}.#{reflection.name} +#{added.inspect} -#{removed.inspect}"
49
+
50
+ action = ActiveHistory.current_event(timestamp: timestamp).action_for(self, id, { type: type, timestamp: timestamp })
51
+
52
+ if reflection.collection?
53
+ diff_key = "#{reflection.name.to_s.singularize}_ids"
54
+ action.diff[diff_key] ||= [[], []]
55
+ action.diff[diff_key][0] |= removed
56
+ action.diff[diff_key][1] |= added
57
+ else
58
+ diff_key = "#{reflection.name.to_s.singularize}_id"
59
+ action.diff[diff_key] ||= [removed.first, added.first]
60
+ end
61
+
62
+
63
+ if propagate && inverse_reflection = reflection.inverse_of
64
+ inverse_klass = inverse_reflection.active_record
65
+
66
+ added.each do |added_id|
67
+ inverse_klass.activehistory_association_changed(added_id, inverse_reflection, {
68
+ added: [id],
69
+ timestamp: timestamp,
70
+ type: type,
71
+ propagate: false
72
+ })
73
+ end
74
+
75
+ removed.each do |removed_id|
76
+ inverse_klass.activehistory_association_changed(removed_id, inverse_reflection, {
77
+ removed: [id],
78
+ timestamp: timestamp,
79
+ type: type,
80
+ propagate: false
81
+ })
82
+ end
83
+ end
84
+ end
85
+
57
86
  end
58
87
 
59
88
  def activehistory_timestamp
60
89
  @activehistory_timestamp ||= Time.now.utc
61
90
  end
62
91
 
63
- def activehistory_start
64
- if activehistory_tracking && !instance_variable_defined?(:@activehistory_finish)
65
- @activehistory_finish = !Thread.current[:activehistory_event]
66
- end
92
+ def with_transaction_returning_status
67
93
  @activehistory_timestamp = Time.now.utc
68
- end
69
-
70
- def activehistory_complete
94
+ if !Thread.current[:activehistory_save_lock]
95
+ run_save = true
96
+ Thread.current[:activehistory_save_lock] = true
97
+ if !Thread.current[:activehistory_event]
98
+ destroy_current_event = true
99
+ Thread.current[:activehistory_event] = ActiveHistory::Event.new(timestamp: @activehistory_timestamp)
100
+ end
101
+ end
102
+
103
+ status = nil
104
+ self.class.transaction do
105
+ add_to_transaction
106
+ begin
107
+ status = yield
108
+ rescue ::ActiveRecord::Rollback
109
+ clear_transaction_record_state
110
+ status = nil
111
+ end
112
+
113
+ if status
114
+ if run_save && ActiveHistory.configured? && !activehistory_event.actions.empty?
115
+ activehistory_event&.save!
116
+ end
117
+ else
118
+ raise ::ActiveRecord::Rollback
119
+ end
120
+ end
121
+ status
122
+ ensure
71
123
  @activehistory_timestamp = nil
72
- if instance_variable_defined?(:@activehistory_finish) && @activehistory_finish && activehistory_tracking
73
- activehistory_event.save! if activehistory_event
124
+ if run_save
125
+ Thread.current[:activehistory_save_lock] = false
126
+ end
127
+ if destroy_current_event
74
128
  Thread.current[:activehistory_event] = nil
75
- @activehistory_timestamp = nil
129
+ end
130
+
131
+ if @transaction_state && @transaction_state.committed?
132
+ clear_transaction_record_state
76
133
  end
77
134
  end
78
135
 
@@ -83,16 +140,7 @@ module ActiveHistory::Adapter
83
140
  end
84
141
 
85
142
  def activehistory_event
86
- case Thread.current[:activehistory_event]
87
- when ActiveHistory::Event
88
- Thread.current[:activehistory_event]
89
- when Hash
90
- Thread.current[:activehistory_event][:timestamp] ||= @activehistory_timestamp
91
- Thread.current[:activehistory_event] = ActiveHistory::Event.new(Thread.current[:activehistory_event])
92
- when Fixnum
93
- else
94
- Thread.current[:activehistory_event] = ActiveHistory::Event.new(timestamp: @activehistory_timestamp)
95
- end
143
+ ActiveHistory.current_event(timestamp: activehistory_timestamp)
96
144
  end
97
145
 
98
146
  def activehistory_track(type)
@@ -111,92 +159,268 @@ module ActiveHistory::Adapter
111
159
  diff = self.attributes.select { |k| !activehistory_tracking[:exclude].include?(k.to_sym) }.map { |k, i| [k, [i, nil]] }.to_h
112
160
  end
113
161
 
114
- return if type == :update && diff.size == 0
162
+ return if type == :update && (diff.keys - (self.send(:timestamp_attributes_for_update) + self.send(:timestamp_attributes_for_create)).map(&:to_s)).empty?
115
163
 
116
- if !activehistory_tracking[:habtm_model]
117
- activehistory_event.action!({
164
+ if activehistory_tracking[:habtm_model]
165
+ if type == :create
166
+ self.class.reflect_on_association(:left_side).klass.activehistory_association_changed(
167
+ self.send(activehistory_tracking[:habtm_model][:left_side][:foreign_key]),
168
+ activehistory_tracking[:habtm_model][:left_side][:inverse_of],
169
+ added: [self.send((self.class.column_names - [activehistory_tracking[:habtm_model][:left_side][:foreign_key]]).first)],
170
+ timestamp: activehistory_timestamp
171
+ )
172
+ end
173
+ else
174
+ activehistory_event.action_for(self.class, id, {
118
175
  type: type,
119
- subject: self.activehistory_id,
120
176
  diff: diff,
121
- timestamp: @activehistory_timestamp
177
+ timestamp: activehistory_timestamp
122
178
  })
123
- end
124
-
125
- self._reflections.each do |key, reflection|
126
- foreign_key = activehistory_tracking.dig(:habtm_model, reflection.name, :foreign_key) || reflection.foreign_key
127
-
128
- if areflection = self.class.reflect_on_association(reflection.name)
129
- if areflection.macro == :has_and_belongs_to_many && type == :create
130
- self.send("#{areflection.name.to_s.singularize}_ids").each do |fid|
131
- next unless fid
132
- activehistory_association_udpated(areflection, fid, added: [diff['id'][1]], timestamp: activehistory_timestamp)
133
- activehistory_association_udpated(areflection.klass.reflect_on_association(areflection.options[:inverse_of]), diff['id'][1], added: [fid], timestamp: activehistory_timestamp, type: :create)
179
+
180
+
181
+ self.class.reflect_on_all_associations.each do |reflection|
182
+ next if activehistory_tracking[:habtm_model]
183
+
184
+ if reflection.macro == :has_and_belongs_to_many && type == :destroy
185
+ activehistory_association_changed(reflection, removed: self.send("#{reflection.name.to_s.singularize}_ids"))
186
+ elsif reflection.macro == :belongs_to && diff.has_key?(reflection.foreign_key)
187
+ case type
188
+ when :create
189
+ old_id = nil
190
+ new_id = diff[reflection.foreign_key][1]
191
+ when :destroy
192
+ old_id = diff[reflection.foreign_key][0]
193
+ new_id = nil
194
+ else
195
+ old_id = diff[reflection.foreign_key][0]
196
+ new_id = diff[reflection.foreign_key][1]
134
197
  end
135
- elsif areflection.macro == :has_and_belongs_to_many && type == :destroy
136
- self.send("#{areflection.name.to_s.singularize}_ids").each do |fid|
137
- activehistory_association_udpated(areflection, fid, removed: [diff['id'][0]], timestamp: activehistory_timestamp, type: :update)
138
- activehistory_association_udpated(areflection.klass.reflect_on_association(areflection.options[:inverse_of]), diff['id'][0], removed: [fid], timestamp: activehistory_timestamp, type: :update)
198
+
199
+ relation_id = self.id || diff.find { |k, v| k != foreign_key }[1][1]
200
+
201
+ if reflection.polymorphic?
202
+ else
203
+ activehistory_association_changed(reflection, removed: [old_id]) if old_id
204
+ activehistory_association_changed(reflection, added: [new_id]) if new_id
139
205
  end
206
+
140
207
  end
141
208
  end
142
-
143
- next unless reflection.macro == :belongs_to && (type == :destroy || diff.has_key?(foreign_key))
144
-
145
- case type
146
- when :create
147
- old_id = nil
148
- new_id = diff[foreign_key][1]
149
- when :destroy
150
- old_id = diff[foreign_key][0]
151
- new_id = nil
152
- else
153
- old_id = diff[foreign_key][0]
154
- new_id = diff[foreign_key][1]
155
- end
156
-
157
- relation_id = self.id || diff.find { |k, v| k != foreign_key }[1][1]
158
-
159
- if reflection.polymorphic?
160
- else
161
- activehistory_association_udpated(reflection, old_id, removed: [relation_id], timestamp: activehistory_timestamp) if old_id
162
- activehistory_association_udpated(reflection, new_id, added: [relation_id], timestamp: activehistory_timestamp) if new_id
163
- end
164
-
165
209
  end
210
+
166
211
 
167
212
  end
168
213
 
214
+ def activehistory_association_changed(relation_name, added: [], removed: [], timestamp: nil, type: :update)
215
+ timestamp ||= activehistory_timestamp
216
+
217
+ self.class.activehistory_association_changed(id, relation_name, {
218
+ added: added,
219
+ removed: removed,
220
+ timestamp: timestamp,
221
+ type: type
222
+ })
223
+
224
+
225
+ end
226
+
169
227
  def activehistory_association_udpated(reflection, id, added: [], removed: [], timestamp: nil, type: :update)
170
- if inverse_of = activehistory_tracking.dig(:habtm_model, reflection.name, :inverse_of)
171
- inverse_of = reflection.klass.reflect_on_association(inverse_of)
228
+ return if !activehistory_tracking || (removed.empty? && added.empty?)
229
+ klass = reflection.active_record
230
+ inverse_klass = reflection.klass
231
+
232
+ inverse_association = if activehistory_tracking.has_key?(:habtm_model)
233
+ inverse_klass.reflect_on_association(activehistory_tracking.dig(:habtm_model, reflection.name.to_s.singularize.to_sym, :inverse_of))
172
234
  else
173
- inverse_of = reflection.inverse_of
235
+ reflection.inverse_of
174
236
  end
175
-
176
- if inverse_of.nil?
237
+
238
+ if inverse_association.nil?
177
239
  puts "NO INVERSE for #{self.class}.#{reflection.name}!!!"
178
240
  return
179
241
  end
180
242
 
181
- model_name = reflection.klass.base_class.model_name.name
182
-
183
- action = activehistory_event.action_for("#{model_name}/#{id}") || activehistory_event.action!({
243
+ action = activehistory_event.action_for(klass, id, {
184
244
  type: type,
185
- subject: "#{model_name}/#{id}",
186
- timestamp: timestamp# || Time.now
245
+ timestamp: timestamp
187
246
  })
188
247
 
189
248
  action.diff ||= {}
190
- if inverse_of.collection? || activehistory_tracking[:habtm_model]
191
- diff_key = "#{inverse_of.name.to_s.singularize}_ids"
249
+ if (reflection.collection? || activehistory_tracking[:habtm_model])
250
+ diff_key = "#{reflection.name.to_s.singularize}_ids"
192
251
  action.diff[diff_key] ||= [[], []]
193
252
  action.diff[diff_key][0] |= removed
194
253
  action.diff[diff_key][1] |= added
195
254
  else
196
- diff_key = "#{inverse_of.name.to_s.singularize}_id"
255
+ diff_key = "#{reflection.name.to_s.singularize}_id"
197
256
  action.diff[diff_key] ||= [removed.first, added.first]
198
257
  end
258
+
259
+ removed.each do |removed_id|
260
+ action = activehistory_event.action_for(inverse_klass, removed_id, {
261
+ type: type,
262
+ timestamp: timestamp
263
+ })
264
+
265
+ action.diff ||= {}
266
+ if inverse_association.collection? || activehistory_tracking[:habtm_model]
267
+ diff_key = "#{inverse_association.name.to_s.singularize}_ids"
268
+ action.diff[diff_key] ||= [[], []]
269
+ action.diff[diff_key][0] |= [id]
270
+ else
271
+ diff_key = "#{inverse_association.name.to_s.singularize}_id"
272
+ action.diff[diff_key] ||= [id, nil]
273
+ end
274
+ end
275
+
276
+ added.each do |added_id|
277
+ action = activehistory_event.action_for(inverse_klass, added_id, {
278
+ type: type,
279
+ timestamp: timestamp
280
+ })
281
+
282
+ action.diff ||= {}
283
+ if inverse_association.collection? || activehistory_tracking[:habtm_model]
284
+ diff_key = "#{inverse_association.name.to_s.singularize}_ids"
285
+ action.diff[diff_key] ||= [[], []]
286
+ action.diff[diff_key][1] |= [id]
287
+ else
288
+ diff_key = "#{inverse_association.name.to_s.singularize}_id"
289
+ action.diff[diff_key] ||= [nil, id]
290
+ end
291
+ end
199
292
  end
200
293
 
201
294
  end
202
- end
295
+ end
296
+
297
+
298
+ module ActiveRecord
299
+ module Associations
300
+ class CollectionAssociation
301
+
302
+ def delete_all(dependent = nil)
303
+ activehistory_encapsulate do
304
+ if dependent && ![:nullify, :delete_all].include?(dependent)
305
+ raise ArgumentError, "Valid values are :nullify or :delete_all"
306
+ end
307
+
308
+ dependent = if dependent
309
+ dependent
310
+ elsif options[:dependent] == :destroy
311
+ :delete_all
312
+ else
313
+ options[:dependent]
314
+ end
315
+
316
+ if dependent == :delete_all
317
+
318
+ else
319
+ removed_ids = self.scope.pluck(:id)
320
+
321
+ action = owner.activehistory_event.action_for(self.reflection.active_record, owner.id, {
322
+ type: :update,
323
+ timestamp: owner.activehistory_timestamp
324
+ })
325
+
326
+ diff_key = "#{self.reflection.name.to_s.singularize}_ids"
327
+ action.diff ||= {}
328
+ action.diff[diff_key] ||= [[], []]
329
+ action.diff[diff_key][0] |= removed_ids
330
+
331
+ ainverse_of = self.klass.reflect_on_association(self.options[:inverse_of])
332
+ if ainverse_of
333
+ removed_ids.each do |removed_id|
334
+ action = owner.activehistory_event.action_for(ainverse_of.active_record, removed_id, {
335
+ type: :update,
336
+ timestamp: owner.activehistory_timestamp
337
+ })
338
+ action.diff ||= {}
339
+ if ainverse_of.collection?
340
+ diff_key = "#{ainverse_of.name.to_s.singularize}_ids"
341
+ action.diff[diff_key] ||= [[], []]
342
+ action.diff[diff_key][0] |= [owner.id]
343
+ else
344
+ diff_key = "#{ainverse_of.name}_id"
345
+ action.diff[diff_key] ||= [owner.id, nil]
346
+ end
347
+ end
348
+ end
349
+ end
350
+
351
+ delete_or_nullify_all_records(dependent).tap do
352
+ reset
353
+ loaded!
354
+ end
355
+ end
356
+ end
357
+
358
+ private
359
+
360
+ def replace_records(new_target, original_target)
361
+ activehistory_encapsulate do
362
+ removed_records = target - new_target
363
+ added_records = new_target - target
364
+
365
+ delete(removed_records)
366
+
367
+ unless concat(added_records)
368
+ @target = original_target
369
+ raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
370
+ "new records could not be saved."
371
+ end
372
+
373
+ if !owner.new_record?
374
+ owner.activehistory_association_changed(self.reflection, added: added_records.map(&:id), removed: removed_records.map(&:id))
375
+ end
376
+ end
377
+
378
+ target
379
+ end
380
+
381
+ def delete_or_destroy(records, method)
382
+ activehistory_encapsulate do
383
+ records = records.flatten
384
+ records.each { |record| raise_on_type_mismatch!(record) }
385
+ existing_records = records.reject(&:new_record?)
386
+
387
+ result = if existing_records.empty?
388
+ remove_records(existing_records, records, method)
389
+ else
390
+ transaction { remove_records(existing_records, records, method) }
391
+ end
392
+ end
393
+ end
394
+
395
+ def activehistory_encapsulate
396
+ @activehistory_timestamp = Time.now.utc
397
+ if !Thread.current[:activehistory_save_lock]
398
+ run_save = true
399
+ Thread.current[:activehistory_save_lock] = true
400
+ if !Thread.current[:activehistory_event]
401
+ destroy_current_event = true
402
+ Thread.current[:activehistory_event] = ActiveHistory::Event.new(timestamp: @activehistory_timestamp)
403
+ end
404
+ end
405
+
406
+ result = yield
407
+
408
+ if run_save && ActiveHistory.configured? && !owner.activehistory_event.actions.empty?
409
+ owner.activehistory_event&.save!
410
+ end
411
+
412
+ result
413
+ ensure
414
+ @activehistory_timestamp = nil
415
+ if run_save
416
+ Thread.current[:activehistory_save_lock] = false
417
+ end
418
+ if destroy_current_event
419
+ Thread.current[:activehistory_event] = nil
420
+ end
421
+
422
+ end
423
+
424
+ end
425
+ end
426
+ end