activehistory 0.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 94d976170424ce9995a019e74e9475ce1036ee85
4
- data.tar.gz: 3ed68d22d9444cab4a7d1536b5a6514d822a677d
3
+ metadata.gz: 738938e4c7303437ed611ad74d5b48736f5590a6
4
+ data.tar.gz: 5e0a178f69fd5cff99025256a819bccb30e35445
5
5
  SHA512:
6
- metadata.gz: 448baea6c2bc3e7d3d284491bfcaebf807ef3ac61fe021151065a88a4b6d38b02ef594a6ffe2b115b57fcef420032c4bfc74b3f0a08cbc6a76e7afba4e51b0b6
7
- data.tar.gz: 202e03ba0ed75936b7ac9f66406f2b1e559449d2eeaa78299793cb65af0d6c18ff06b11da6b1e16931cfb74897dfbac628e416871b0c89cc172c80590653fffe
6
+ metadata.gz: aa67c5b5797e87d86122853469a87b25af576baed17e7672cb81290ee68707038d3368afc09a9b7bcefc2be43605a3ac206fedbb9626dd21d5c46c5f66345849
7
+ data.tar.gz: f535f87bceff3329c4850d584c78201decc0ba7af419fcd9e3f288d1f9d84ea9607b39eaaf13fb7765970d1e41435d2c077918e3898d27a7f5763f3c6bcb9359
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activehistory (0.1)
5
- activerecord (= 5.0.0.1)
4
+ activehistory (0.2.1)
5
+ activerecord (~> 5.0)
6
6
  arel (~> 7.0)
7
7
  globalid (~> 0.3.7)
8
8
 
@@ -36,6 +36,6 @@ Gem::Specification.new do |s|
36
36
  # s.add_runtime_dependency 'msgpack'
37
37
  # s.add_runtime_dependency 'cookie_store'
38
38
  s.add_runtime_dependency 'arel', '~> 7.0'
39
- s.add_runtime_dependency 'activerecord', '5.0.0.1'
39
+ s.add_runtime_dependency 'activerecord', '~> 5.0'
40
40
  s.add_runtime_dependency 'globalid', '~> 0.3.7'
41
41
  end
@@ -1,10 +1,13 @@
1
+ require 'logger'
2
+
1
3
  module ActiveHistory
2
4
 
3
- mattr_accessor :connection
5
+ mattr_accessor :connection, :logger
4
6
 
5
7
  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
8
 
7
9
  def self.configure(settings)
10
+ self.logger = settings&.delete(:logger) || Logger.new(STDOUT)
8
11
  @@connection = ActiveHistory::Connection.new(settings)
9
12
  end
10
13
 
@@ -27,7 +30,7 @@ module ActiveHistory
27
30
 
28
31
  yield
29
32
  ensure
30
- if Thread.current[:activehistory_event] && !Thread.current[:activehistory_event].actions.empty?
33
+ if configured? && Thread.current[:activehistory_event] && !Thread.current[:activehistory_event].actions.empty?
31
34
  Thread.current[:activehistory_event].save!
32
35
  end
33
36
  Thread.current[:activehistory_event] = nil
@@ -45,7 +45,6 @@ module ActiveHistory::Adapter
45
45
  else
46
46
  reflection_or_relation_name
47
47
  end
48
- puts "#{self}##{id}.#{reflection.name} +#{added.inspect} -#{removed.inspect}"
49
48
 
50
49
  action = ActiveHistory.current_event(timestamp: timestamp).action_for(self, id, { type: type, timestamp: timestamp })
51
50
 
@@ -54,9 +53,18 @@ module ActiveHistory::Adapter
54
53
  action.diff[diff_key] ||= [[], []]
55
54
  action.diff[diff_key][0] |= removed
56
55
  action.diff[diff_key][1] |= added
56
+ in_common = (action.diff[diff_key][0] & action.diff[diff_key][1])
57
+ if !in_common.empty?
58
+ action.diff[diff_key][0] = action.diff[diff_key][0] - in_common
59
+ action.diff[diff_key][1] = action.diff[diff_key][1] - in_common
60
+ end
57
61
  else
58
62
  diff_key = "#{reflection.name.to_s.singularize}_id"
59
- action.diff[diff_key] ||= [removed.first, added.first]
63
+ if action.diff.has_key?(diff_key) && action.diff[diff_key][0] == added.first
64
+ action.diff.delete(diff_key)
65
+ else
66
+ action.diff[diff_key] ||= [removed.first, added.first]
67
+ end
60
68
  end
61
69
 
62
70
 
@@ -220,8 +228,6 @@ module ActiveHistory::Adapter
220
228
  timestamp: timestamp,
221
229
  type: type
222
230
  })
223
-
224
-
225
231
  end
226
232
 
227
233
  def activehistory_association_udpated(reflection, id, added: [], removed: [], timestamp: nil, type: :update)
@@ -394,10 +400,11 @@ module ActiveRecord
394
400
 
395
401
  def activehistory_encapsulate
396
402
  @activehistory_timestamp = Time.now.utc
403
+
397
404
  if !Thread.current[:activehistory_save_lock]
398
405
  run_save = true
399
406
  Thread.current[:activehistory_save_lock] = true
400
- if !Thread.current[:activehistory_event]
407
+ if Thread.current[:activehistory_event].nil?
401
408
  destroy_current_event = true
402
409
  Thread.current[:activehistory_event] = ActiveHistory::Event.new(timestamp: @activehistory_timestamp)
403
410
  end
@@ -418,7 +425,6 @@ module ActiveRecord
418
425
  if destroy_current_event
419
426
  Thread.current[:activehistory_event] = nil
420
427
  end
421
-
422
428
  end
423
429
 
424
430
  end
@@ -41,7 +41,13 @@ class ActiveHistory::Event
41
41
  action = @actions.find { |a| a.subject_type.to_s == type.to_s && a.subject_id.to_s == id.to_s }
42
42
 
43
43
  if new_options
44
- action || action!({ subject_type: type, subject_id: id, type: :update }.merge(new_options))
44
+ if action
45
+ action.diff.merge!(new_options[:diff]) if new_options.has_key?(:diff)
46
+ action
47
+ else
48
+ action!({ subject_type: type, subject_id: id, type: :update }.merge(new_options))
49
+ end
50
+
45
51
  else
46
52
  action
47
53
  end
@@ -59,14 +65,18 @@ class ActiveHistory::Event
59
65
 
60
66
  def _update
61
67
  return if actions.empty?
62
- ActiveHistory.connection.post('/actions', {
63
- actions: actions.as_json.map{|json| json[:event_id] = id; json}
64
- })
68
+ actions.delete_if { |a| a.diff.empty? }
69
+ payload = JSON.generate({actions: actions.as_json.map{ |json| json[:event_id] = id; json }})
70
+ ActiveHistory.logger.debug("[ActiveHistory] POST /actions WITH #{payload}")
71
+ ActiveHistory.connection.post('/actions', payload)
65
72
  @actions = []
66
73
  end
67
74
 
68
75
  def _create
69
- ActiveHistory.connection.post('/events', self.as_json)
76
+ actions.delete_if { |a| a.diff.empty? }
77
+ payload = JSON.generate(self.as_json)
78
+ ActiveHistory.logger.debug("[ActiveHistory] POST /events WITH #{payload}")
79
+ ActiveHistory.connection.post('/events', payload)
70
80
  @actions = []
71
81
  @persisted = true
72
82
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveHistory
2
- VERSION = '0.2'
2
+ VERSION = '0.2.1'
3
3
  end
@@ -12,7 +12,7 @@ class HasManyAssociationTest < ActiveSupport::TestCase
12
12
  WebMock::RequestRegistry.instance.reset!
13
13
 
14
14
  @photo = travel_to(@time) { create(:photo, property: @property) }
15
-
15
+
16
16
  assert_posted("/events") do
17
17
  assert_action_for @photo, {
18
18
  diff: {
@@ -71,13 +71,13 @@ class HasManyAssociationTest < ActiveSupport::TestCase
71
71
  test 'has_many <<'
72
72
  test 'has_many.delete'
73
73
  test 'has_many.destroy'
74
-
74
+
75
75
  test 'has_many=' do
76
76
  @property = create(:property)
77
77
  @photo1 = create(:photo)
78
78
  @photo2 = create(:photo)
79
79
  WebMock::RequestRegistry.instance.reset!
80
-
80
+
81
81
  travel_to(@time) { @property.photos = [@photo1] }
82
82
  assert_posted("/events") do
83
83
  assert_action_for @photo1, {
@@ -96,10 +96,10 @@ class HasManyAssociationTest < ActiveSupport::TestCase
96
96
  type: 'update'
97
97
  }
98
98
  end
99
-
99
+
100
100
  WebMock::RequestRegistry.instance.reset!
101
101
  travel_to(@time) { @property.photos = [@photo2] }
102
- assert_posted("/events") do
102
+ assert_posted("/events") do
103
103
  assert_action_for @photo2, {
104
104
  diff: { property_id: [nil, @property.id] },
105
105
  subject_type: "Photo",
@@ -107,7 +107,7 @@ class HasManyAssociationTest < ActiveSupport::TestCase
107
107
  timestamp: @time.iso8601(3),
108
108
  type: 'update'
109
109
  }
110
-
110
+
111
111
  assert_action_for @property, {
112
112
  diff: { photo_ids: [[@photo1.id], [@photo2.id]] },
113
113
  subject_type: "Property",
@@ -115,7 +115,7 @@ class HasManyAssociationTest < ActiveSupport::TestCase
115
115
  timestamp: @time.iso8601(3),
116
116
  type: 'update'
117
117
  }
118
-
118
+
119
119
  assert_action_for @photo1, {
120
120
  diff: { property_id: [@property.id, nil] },
121
121
  subject_type: "Photo",
@@ -125,13 +125,13 @@ class HasManyAssociationTest < ActiveSupport::TestCase
125
125
  }
126
126
  end
127
127
  end
128
-
128
+
129
129
  test 'has_many_ids=' do
130
130
  @property = create(:property)
131
131
  @photo1 = create(:photo)
132
132
  @photo2 = create(:photo)
133
133
  WebMock::RequestRegistry.instance.reset!
134
-
134
+
135
135
  travel_to(@time) { @property.photo_ids = [@photo1].map(&:id) }
136
136
  assert_posted("/events") do
137
137
  assert_action_for @property, {
@@ -141,7 +141,7 @@ class HasManyAssociationTest < ActiveSupport::TestCase
141
141
  timestamp: @time.iso8601(3),
142
142
  type: 'update'
143
143
  }
144
-
144
+
145
145
  assert_action_for @photo1, {
146
146
  diff: { property_id: [nil, @property.id] },
147
147
  subject_type: "Photo",
@@ -150,10 +150,10 @@ class HasManyAssociationTest < ActiveSupport::TestCase
150
150
  type: 'update'
151
151
  }
152
152
  end
153
-
153
+
154
154
  WebMock::RequestRegistry.instance.reset!
155
155
  travel_to(@time) { @property.photo_ids = [@photo2].map(&:id) }
156
- assert_posted("/events") do
156
+ assert_posted("/events") do
157
157
  assert_action_for @photo2, {
158
158
  diff: { property_id: [nil, @property.id] },
159
159
  subject_type: "Photo",
@@ -161,7 +161,7 @@ class HasManyAssociationTest < ActiveSupport::TestCase
161
161
  timestamp: @time.iso8601(3),
162
162
  type: 'update'
163
163
  }
164
-
164
+
165
165
  assert_action_for @property, {
166
166
  diff: { photo_ids: [[@photo1.id], [@photo2.id]] },
167
167
  subject_type: "Property",
@@ -169,7 +169,7 @@ class HasManyAssociationTest < ActiveSupport::TestCase
169
169
  timestamp: @time.iso8601(3),
170
170
  type: 'update'
171
171
  }
172
-
172
+
173
173
  assert_action_for @photo1, {
174
174
  diff: { property_id: [@property.id, nil] },
175
175
  subject_type: "Photo",
@@ -179,16 +179,16 @@ class HasManyAssociationTest < ActiveSupport::TestCase
179
179
  }
180
180
  end
181
181
  end
182
-
182
+
183
183
  test 'has_many.clear' do
184
184
  @photo1 = create(:photo)
185
185
  @photo2 = create(:photo)
186
186
  @property = create(:property, photos: [@photo1, @photo2])
187
187
  WebMock::RequestRegistry.instance.reset!
188
-
188
+
189
189
  travel_to(@time) { @property.photos.clear }
190
190
  assert_posted("/events") do |req|
191
-
191
+
192
192
  assert_action_for @property, {
193
193
  diff: { photo_ids: [[@photo1, @photo2].map(&:id), []] },
194
194
  subject_type: "Property",
@@ -196,7 +196,7 @@ class HasManyAssociationTest < ActiveSupport::TestCase
196
196
  timestamp: @time.iso8601(3),
197
197
  type: 'update'
198
198
  }
199
-
199
+
200
200
  assert_action_for @photo1, {
201
201
  diff: { property_id: [@property.id, nil] },
202
202
  subject_type: "Photo",
@@ -204,7 +204,7 @@ class HasManyAssociationTest < ActiveSupport::TestCase
204
204
  timestamp: @time.iso8601(3),
205
205
  type: 'update'
206
206
  }
207
-
207
+
208
208
  assert_action_for @photo2, {
209
209
  diff: { property_id: [@property.id, nil] },
210
210
  subject_type: "Photo",
@@ -215,6 +215,52 @@ class HasManyAssociationTest < ActiveSupport::TestCase
215
215
  end
216
216
  end
217
217
 
218
+ test 'has_many.clear, has_many=, while updating model; a werid example but could happen in an after_save callback etc..' do
219
+ @photo1 = create(:photo)
220
+ @photo2 = create(:photo)
221
+ @property = create(:property, name: 'old name', photos: [@photo1])
222
+ WebMock::RequestRegistry.instance.reset!
223
+
224
+ travel_to(@time) {
225
+ begin
226
+ Thread.current[:activehistory_save_lock] = true
227
+ Thread.current[:activehistory_event] = ActiveHistory::Event.new()
228
+ @property.name = 'new name'
229
+ @property.photos.clear
230
+ @property.photos = [@photo2, @photo1]
231
+ @property.save
232
+ ActiveHistory.current_event.save!
233
+ ensure
234
+ Thread.current[:activehistory_save_lock] = false
235
+ Thread.current[:activehistory_event] = nil
236
+ end
237
+ }
238
+
239
+ assert_posted("/events") do |req|
240
+
241
+ assert_action_for @property, {
242
+ diff: {
243
+ photo_ids: [[], [@photo2].map(&:id)],
244
+ name: ['old name', 'new name']
245
+ },
246
+ subject_type: "Property",
247
+ subject_id: @property.id,
248
+ timestamp: @time.iso8601(3),
249
+ type: 'update'
250
+ }
251
+
252
+ assert_no_action_for @photo1
253
+
254
+ assert_action_for @photo2, {
255
+ diff: { property_id: [nil, @property.id] },
256
+ subject_type: "Photo",
257
+ subject_id: @photo2.id,
258
+ timestamp: @time.iso8601(3),
259
+ type: 'update'
260
+ }
261
+ end
262
+ end
263
+
218
264
  test 'has_many.create'
219
265
 
220
266
  end
@@ -17,7 +17,8 @@ require 'active_record'
17
17
  require 'activehistory'
18
18
 
19
19
  ActiveHistory.configure({
20
- url: 'http://activehistory.com'
20
+ url: 'http://activehistory.com',
21
+ logger: Logger.new("/dev/null")
21
22
  })
22
23
  ActiveRecord::Base.establish_connection({
23
24
  adapter: 'postgresql',
@@ -40,6 +41,12 @@ class ActiveSupport::TestCase
40
41
  WebMock.stub_request(:any, /^http:\/\/activehistory.com\/.*/)
41
42
  end
42
43
 
44
+ set_callback(:teardown, :after) do
45
+ if !Thread.current[:activehistory_event].nil?
46
+ raise 'no nil'
47
+ end
48
+ end
49
+
43
50
  def assert_posted(path, &block)
44
51
  assert_requested(:post, "#{ActiveHistory.url}#{path}", times: 1) do |req|
45
52
  @req = JSON.parse(req.body)
@@ -48,13 +55,21 @@ class ActiveSupport::TestCase
48
55
  end
49
56
 
50
57
  def assert_action_for(model, expected)
51
- action = @req['actions'].find do |action|
52
- action['subject_type'] == model.class.base_class.model_name.name && action['subject_id'] == model.id
58
+ action = @req['actions'].find do |a|
59
+ a['subject_type'] == model.class.base_class.model_name.name && a['subject_id'] == model.id
53
60
  end
54
61
 
55
62
  assert_equal(expected.as_json, action)
56
63
  end
57
64
 
65
+ def assert_no_action_for(model)
66
+ action = @req['actions'].find do |a|
67
+ a['subject_type'] == model.class.base_class.model_name.name && a['subject_id'] == model.id
68
+ end
69
+
70
+ assert_nil action
71
+ end
72
+
58
73
  def assert_not_posted(path)
59
74
  assert_not_requested(:post, "#{ActiveHistory.url}#{path}")
60
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activehistory
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.2'
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Bracy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-11-07 00:00:00.000000000 Z
11
+ date: 2016-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -210,16 +210,16 @@ dependencies:
210
210
  name: activerecord
211
211
  requirement: !ruby/object:Gem::Requirement
212
212
  requirements:
213
- - - '='
213
+ - - "~>"
214
214
  - !ruby/object:Gem::Version
215
- version: 5.0.0.1
215
+ version: '5.0'
216
216
  type: :runtime
217
217
  prerelease: false
218
218
  version_requirements: !ruby/object:Gem::Requirement
219
219
  requirements:
220
- - - '='
220
+ - - "~>"
221
221
  - !ruby/object:Gem::Version
222
- version: 5.0.0.1
222
+ version: '5.0'
223
223
  - !ruby/object:Gem::Dependency
224
224
  name: globalid
225
225
  requirement: !ruby/object:Gem::Requirement
@@ -290,7 +290,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
290
290
  version: '0'
291
291
  requirements: []
292
292
  rubyforge_project:
293
- rubygems_version: 2.6.4
293
+ rubygems_version: 2.5.1
294
294
  signing_key:
295
295
  specification_version: 4
296
296
  summary: Track changes to ActiveRecord models