activehistory 0.2 → 0.2.1

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: 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