mongoid-audit_log 0.2.0 → 0.5.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
- SHA1:
3
- metadata.gz: 910aee76df538dfa623d329bb7c3434b34db263c
4
- data.tar.gz: 5c8c388d1002e83786d7ba71b7fe2aa0c4ea5a9a
2
+ SHA256:
3
+ metadata.gz: ff3af4d1f95aaf1d746dbaa69f13499aeeddacd82617903a7c39cab41ccb19f1
4
+ data.tar.gz: 8aa212d93cc1797ffa0d00311f61cb529bb7a43b0fcca4398663c1ead4d538a6
5
5
  SHA512:
6
- metadata.gz: 9eafe09cfc8b25a50bb36ab05593dbb34bdf6c52abbe996d885c9d83595c3db61f0a04f4fcc2f6d83f7be2b08712dbd9288d0c19a7704adf82bc83090131c78b
7
- data.tar.gz: 66413e69767f96505f46ce6ec3d9248b3a9b4c75fda2b0e58060c086d79879b9424faec4f6669d791ed60e5b93887e59daf98d1aad89c53473468d55031e3b63
6
+ metadata.gz: b1b9ccdb25f8bd5de48aa18ead6b7fc12f65fe40eea47cdd9ffe7594c86be327a860614452fe663e1613c46a634472ff74a235fae16d35b3e00dbb7ad9e9d0a7
7
+ data.tar.gz: d317c56de028253096e9e97febe256688e6b6ea0f4bea53d7aff41451fc1b66fb48d2cb0415ba9c3aa6e70fa35f6edf3f3bf11ad797adf75cd1765aa871a285f
data/README.md CHANGED
@@ -44,6 +44,11 @@ If you want to log the user who made the change, pass that user to the record me
44
44
  Mongoid::AuditLog.record(current_user) do
45
45
  Model.create!
46
46
  end
47
+
48
+ # or
49
+
50
+ Mongoid::AuditLog.current_modifier = current_user
51
+ Mongoid::AuditLog.enable
47
52
  ```
48
53
 
49
54
  A basic implementation in a Rails app might look something like:
@@ -123,6 +128,23 @@ model.audit_log_entries.first.create? # => true
123
128
  model.audit_log_entries.first.model_attributes # => {"name"=>"foo bar"}
124
129
  ```
125
130
 
131
+ ### Restoring
132
+
133
+ You can restore models for `Mongoid::AuditLog::Entry` instances for deletions.
134
+ This works for both root and embedded documents.
135
+
136
+ Examples:
137
+ ```ruby
138
+ model = Model.create!(:name => 'foo bar')
139
+ Mongoid::AuditLog.record { model.destroy }
140
+
141
+ entry = Mongoid::AuditLog::Entry.first
142
+ entry.restore!
143
+
144
+ model == Model.find_by(name: 'foo bar') # => true
145
+ ```
146
+
147
+ It's possible to end up in a situation where a destroy entry cannot be restored, e.g. an entry deleting an embedded document for a root document that's already been deleted. In these scenarios, `Mongoid::AuditLog::Restore::InvalidRestore` will be raised.
126
148
 
127
149
  ## Contributing
128
150
 
@@ -3,6 +3,7 @@ require "mongoid/audit_log/config"
3
3
  require "mongoid/audit_log/entry"
4
4
  require "mongoid/audit_log/changes"
5
5
  require "mongoid/audit_log/embedded_changes"
6
+ require "mongoid/audit_log/restore"
6
7
 
7
8
  module Mongoid
8
9
  module AuditLog
@@ -24,20 +25,30 @@ module Mongoid
24
25
  end
25
26
 
26
27
  def self.record(modifier = nil)
27
- Thread.current[:mongoid_audit_log_recording] = true
28
- Thread.current[:mongoid_audit_log_modifier] = modifier
28
+ already_recording = recording?
29
+ enable unless already_recording
30
+ self.current_modifier = modifier
29
31
  yield
30
32
  ensure
31
- Thread.current[:mongoid_audit_log_recording] = nil
32
- Thread.current[:mongoid_audit_log_modifier] = nil
33
+ disable unless already_recording
34
+ self.current_modifier = nil
35
+ end
36
+
37
+ def self.enable
38
+ Thread.current[:mongoid_audit_log_recording] = true
33
39
  end
34
40
 
35
41
  def self.disable
36
- tmp = Thread.current[:mongoid_audit_log_recording]
42
+ already_recording = recording?
37
43
  Thread.current[:mongoid_audit_log_recording] = false
38
- yield
39
- ensure
40
- Thread.current[:mongoid_audit_log_recording] = tmp
44
+
45
+ if block_given?
46
+ begin
47
+ yield
48
+ ensure
49
+ Thread.current[:mongoid_audit_log_recording] = already_recording
50
+ end
51
+ end
41
52
  end
42
53
 
43
54
  def self.recording?
@@ -48,6 +59,10 @@ module Mongoid
48
59
  Thread.current[:mongoid_audit_log_modifier]
49
60
  end
50
61
 
62
+ def self.current_modifier=(modifier)
63
+ Thread.current[:mongoid_audit_log_modifier] = modifier
64
+ end
65
+
51
66
  private
52
67
 
53
68
  def set_audit_log_changes
@@ -61,9 +76,17 @@ module Mongoid
61
76
  :audited_type => self.class,
62
77
  :audited_id => id,
63
78
  :tracked_changes => @_audit_log_changes.all,
64
- :model_attributes => attributes.dup
79
+ :model_attributes => attributes.deep_dup,
80
+ :document_path => traverse_association_chain
65
81
  )
66
82
  end
67
83
  end
84
+
85
+ def traverse_association_chain(node = self, current_relation = nil)
86
+ relation = node.embedded? ? node.metadata_name.to_s : nil
87
+ list = node._parent ? traverse_association_chain(node._parent, relation) : []
88
+ list << { class_name: node.class.name, id: node.id, relation: current_relation }
89
+ list
90
+ end
68
91
  end
69
92
  end
@@ -8,8 +8,14 @@ module Mongoid
8
8
  field :tracked_changes, :type => Hash, :default => {}
9
9
  field :modifier_id, :type => String
10
10
  field :model_attributes, :type => Hash
11
+ field :document_path, :type => Array
12
+ field :restored, :type => Boolean, default: false
11
13
 
12
- belongs_to :audited, :polymorphic => true
14
+ if Gem::Version.new(Mongoid::VERSION) < Gem::Version.new('6.0.0.beta')
15
+ belongs_to :audited, :polymorphic => true
16
+ else
17
+ belongs_to :audited, :polymorphic => true, :optional => true
18
+ end
13
19
 
14
20
  index({ :audited_id => 1, :audited_type => 1 })
15
21
  index({ :modifier_id => 1 })
@@ -27,7 +33,9 @@ module Mongoid
27
33
 
28
34
  def valid?(*)
29
35
  result = super
30
- self.modifier = Mongoid::AuditLog.current_modifier if result
36
+ if result && modifier.blank?
37
+ self.modifier = Mongoid::AuditLog.current_modifier
38
+ end
31
39
  result
32
40
  end
33
41
 
@@ -36,7 +44,7 @@ module Mongoid
36
44
  nil
37
45
  else
38
46
  klass = Mongoid::AuditLog.modifier_class_name.constantize
39
- klass.find(modifier_id)
47
+ klass.find(modifier_id) rescue nil
40
48
  end
41
49
  end
42
50
 
@@ -50,6 +58,42 @@ module Mongoid
50
58
  @modifier = modifier
51
59
  end
52
60
 
61
+ def for_embedded_doc?
62
+ document_path.try(:length).to_i > 1
63
+ end
64
+
65
+ def audited
66
+ return nil if audited_type.blank? || audited_id.blank?
67
+
68
+ if for_embedded_doc?
69
+ lookup_from_document_path
70
+ else
71
+ audited_type.constantize.where(id: audited_id).first
72
+ end
73
+ end
74
+
75
+ def root
76
+ root = document_path.first
77
+ return audited if root.blank?
78
+
79
+ if for_embedded_doc?
80
+ root['class_name'].constantize.where(id: root['id']).first
81
+ else
82
+ audited
83
+ end
84
+ end
85
+
86
+ def restore!
87
+ raise Restore::InvalidRestore if restored? || !destroy?
88
+
89
+ Restore.new(self).perform
90
+ update_attributes!(:restored => true)
91
+ end
92
+
93
+ def restorable?
94
+ destroy? && !restored? && Restore.new(self).valid?
95
+ end
96
+
53
97
  def respond_to?(sym, *args)
54
98
  key = sym.to_s
55
99
  (model_attributes.present? && model_attributes.has_key?(key)) || super
@@ -64,6 +108,32 @@ module Mongoid
64
108
  super
65
109
  end
66
110
  end
111
+
112
+ private
113
+
114
+ def lookup_from_document_path
115
+ return nil if document_path.blank?
116
+
117
+ document_path.reduce(root) do |current, path|
118
+ relation_match = if document_path_matches?(path, current)
119
+ current
120
+ elsif current.respond_to?(:detect)
121
+ current.detect do |model|
122
+ document_path_matches?(path, model)
123
+ end
124
+ end
125
+
126
+ if path['relation'].blank? || relation_match.blank?
127
+ return relation_match
128
+ else
129
+ relation_match.send(path['relation'])
130
+ end
131
+ end
132
+ end
133
+
134
+ def document_path_matches?(path, object)
135
+ object.class.name == path['class_name'] && object.id == path['id']
136
+ end
67
137
  end
68
138
  end
69
139
  end
@@ -0,0 +1,94 @@
1
+ module Mongoid
2
+ module AuditLog
3
+ class Restore
4
+ class DuplicateError < StandardError; end
5
+ class InvalidRestore < StandardError; end
6
+
7
+ attr_reader :entry
8
+ delegate :name, to: :restored
9
+ delegate :for_embedded_doc?, to: :entry
10
+
11
+ def initialize(entry)
12
+ @entry = entry
13
+ end
14
+
15
+ def valid?
16
+ !entry.for_embedded_doc? || restored_root.present?
17
+ end
18
+
19
+ def perform
20
+ restored.attributes = attributes
21
+ restored.save!
22
+ end
23
+
24
+ def attributes
25
+ @attributes ||=
26
+ begin
27
+ attrs = entry.model_attributes.deep_dup
28
+ restored.send(:process_localized_attributes, model_class, attrs)
29
+ attrs
30
+ end
31
+ end
32
+
33
+ def model_class
34
+ entry.audited_type.constantize
35
+ end
36
+
37
+ def restored
38
+ @restored ||=
39
+ if entry.for_embedded_doc?
40
+ find_embedded_restored
41
+ else
42
+ find_root_restored
43
+ end
44
+ end
45
+
46
+ def find_root_restored
47
+ model_class.new
48
+ end
49
+
50
+ def find_embedded_restored
51
+ raise InvalidRestore if restored_root.blank?
52
+
53
+ last_path = document_path.last
54
+ metadata = restored_root.class.reflect_on_association(last_path['relation'])
55
+ relation = restored_root.send(last_path['relation'])
56
+
57
+ if metadata.many?
58
+ relation.build
59
+ elsif relation.present?
60
+ raise DuplicateError
61
+ else
62
+ restored_root.send("build_#{metadata.name}")
63
+ end
64
+ end
65
+
66
+ def restored_root
67
+ document_path.reduce(entry.root) do |current, path|
68
+ match = if document_path_matches?(path, current)
69
+ current
70
+ elsif current.respond_to?(:detect)
71
+ current.detect do |model|
72
+ document_path_matches?(path, model)
73
+ end
74
+ end
75
+
76
+ if path == document_path.last
77
+ return match
78
+ else
79
+ match.send(path['relation'])
80
+ end
81
+ end
82
+ end
83
+
84
+ def document_path
85
+ # don't need last because that entry represents the deleted doc
86
+ entry.document_path[0..-2]
87
+ end
88
+
89
+ def document_path_matches?(path, object)
90
+ object.class.name == path['class_name'] && object.id == path['id']
91
+ end
92
+ end
93
+ end
94
+ end
@@ -1,5 +1,5 @@
1
1
  module Mongoid
2
2
  module AuditLog
3
- VERSION = "0.2.0"
3
+ VERSION = "0.5.1"
4
4
  end
5
5
  end
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["bencrouse@gmail.com"]
11
11
  spec.description = %q{Stupid simple audit logging for Mongoid}
12
12
  spec.summary = %q{No fancy versioning, undo, redo, etc. Just saves changes to Mongoid models in a separate collection.}
13
- spec.homepage = "https://github.com/bencrouse/mongoid-audit-log"
13
+ spec.homepage = "https://github.com/bencrouse/mongoid-audit_log"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
@@ -21,8 +21,8 @@ Gem::Specification.new do |spec|
21
21
  spec.required_ruby_version = ">= 1.9.2"
22
22
  spec.required_rubygems_version = ">= 1.3.6"
23
23
 
24
- spec.add_runtime_dependency "mongoid", [">= 4.0"]
24
+ spec.add_runtime_dependency "mongoid", [">= 6.0.x", "< 7.0"]
25
25
 
26
- spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "bundler"
27
27
  spec.add_development_dependency "rake"
28
28
  end
@@ -10,12 +10,30 @@ module Mongoid
10
10
  class ::Product
11
11
  include Mongoid::Document
12
12
  include Mongoid::AuditLog
13
+ field :name, :type => String
14
+ embeds_many :variants
15
+ end
16
+
17
+ class ::Variant
18
+ include Mongoid::Document
19
+ include Mongoid::AuditLog
20
+ field :sku, :type => String
21
+ embedded_in :product
22
+ embeds_many :options
23
+ end
24
+
25
+ class ::Option
26
+ include Mongoid::Document
27
+ include Mongoid::AuditLog
28
+ field :name, :type => String
29
+ embedded_in :variant
13
30
  end
14
31
  end
15
32
 
16
33
  after(:all) do
17
34
  AuditLog.modifier_class_name = @remember_modifier_class_name
18
35
  Object.send(:remove_const, :Product)
36
+ Object.send(:remove_const, :Variant)
19
37
  end
20
38
 
21
39
  let(:user) { User.create! }
@@ -50,11 +68,39 @@ module Mongoid
50
68
  end
51
69
  end
52
70
 
71
+ describe '#valid?' do
72
+ it 'does not override a manually set modifier' do
73
+ entry = Entry.new(:modifier_id => user.id)
74
+ entry.valid?
75
+ entry.modifier.should == user
76
+ end
77
+ end
78
+
53
79
  describe '#modifier' do
54
80
  it 'finds the modifier based on the configured class' do
55
81
  entry = Entry.new(:modifier_id => user.id)
56
82
  entry.modifier.should == user
57
83
  end
84
+
85
+ it 'can handle a missing modifier' do
86
+ entry = Entry.new(:modifier_id => 'foo')
87
+ entry.modifier.should be_nil
88
+ end
89
+ end
90
+
91
+ describe '#audited' do
92
+ it 'uses the document path to find embedded documents' do
93
+ product = Product.create!(:name => 'Foo bar')
94
+ variant = product.variants.create!
95
+ option = AuditLog.record { variant.options.create! }
96
+
97
+ entry = Entry.desc(:created_at).first
98
+ entry.audited.should == option
99
+ end
100
+
101
+ it 'returns nil if the audited info is blank' do
102
+ Entry.new.audited.should be_nil
103
+ end
58
104
  end
59
105
 
60
106
  describe '#modifier_id=' do
@@ -92,6 +138,85 @@ module Mongoid
92
138
  entry.other.should == nil
93
139
  end
94
140
  end
141
+
142
+ describe '#root' do
143
+ it 'returns nil if cannot be found' do
144
+ product = Product.create!(:name => 'Foo bar')
145
+ AuditLog.record { product.variants.create! }
146
+
147
+ entry = Entry.desc(:created_at).first
148
+ product.destroy
149
+ entry.root.should be_nil
150
+ end
151
+ end
152
+
153
+ describe '#restore!' do
154
+ it 'marks the entry as restored' do
155
+ product = Product.create!(:name => 'Foo bar')
156
+ AuditLog.record { product.destroy }
157
+
158
+ entry = Entry.desc(:created_at).first
159
+ entry.restore!
160
+ entry.should be_restored
161
+ end
162
+
163
+ it 'raises InvalidRestore if already restored' do
164
+ product = Product.create!(:name => 'Foo bar')
165
+ AuditLog.record { product.destroy }
166
+
167
+ entry = Entry.desc(:created_at).first
168
+ entry.restore!
169
+
170
+ expect { entry.restore! }.to raise_error(Restore::InvalidRestore)
171
+ end
172
+
173
+ it 'raises InvalidRestore if not a destroy' do
174
+ product = Product.create!(:name => 'Foo bar')
175
+ AuditLog.record { product.variants.create! }
176
+
177
+ entry = Entry.desc(:created_at).first
178
+ expect { entry.restore! }.to raise_error(Restore::InvalidRestore)
179
+ end
180
+
181
+ it 'raises InvalidRestore if the root is blank' do
182
+ product = Product.create!(:name => 'Foo bar')
183
+ product.variants.create!
184
+ AuditLog.record { product.variants.first.destroy }
185
+ product.destroy
186
+
187
+ entry = Entry.desc(:created_at).first
188
+ expect { entry.restore! }.to raise_error(Restore::InvalidRestore)
189
+ end
190
+ end
191
+
192
+ describe '#restorable?' do
193
+ it 'is false if already restored' do
194
+ product = Product.create!(:name => 'Foo bar')
195
+ AuditLog.record { product.destroy }
196
+
197
+ entry = Entry.desc(:created_at).first
198
+ entry.restore!
199
+ entry.should_not be_restorable
200
+ end
201
+
202
+ it 'is false if not a destroy' do
203
+ product = Product.create!(:name => 'Foo bar')
204
+ AuditLog.record { product.variants.create! }
205
+
206
+ entry = Entry.desc(:created_at).first
207
+ entry.should_not be_restorable
208
+ end
209
+
210
+ it 'is false is root is blank' do
211
+ product = Product.create!(:name => 'Foo bar')
212
+ product.variants.create!
213
+ AuditLog.record { product.variants.first.destroy }
214
+ product.destroy
215
+
216
+ entry = Entry.desc(:created_at).first
217
+ entry.should_not be_restorable
218
+ end
219
+ end
95
220
  end
96
221
  end
97
222
  end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ module Mongoid
4
+ module AuditLog
5
+ describe Restore do
6
+ before(:all) do
7
+ class ::First
8
+ include Mongoid::Document
9
+ include Mongoid::AuditLog
10
+
11
+ field :name, type: String, localize: true
12
+ embeds_many :seconds
13
+ end
14
+
15
+ class ::Second
16
+ include Mongoid::Document
17
+ include Mongoid::AuditLog
18
+
19
+ field :name, type: String, localize: true
20
+ embedded_in :first
21
+ embeds_many :thirds
22
+ end
23
+
24
+ class ::Third
25
+ include Mongoid::Document
26
+ include Mongoid::AuditLog
27
+
28
+ field :name, type: String, localize: true
29
+ embedded_in :second
30
+ end
31
+ end
32
+
33
+ after(:all) do
34
+ [:First, :Second, :Third].each { |c| Object.send(:remove_const, c) }
35
+ end
36
+
37
+ describe '.perform' do
38
+ it 'restores a root document' do
39
+ root = First.create!(name: 'Foo')
40
+ Mongoid::AuditLog.record { root.destroy }
41
+ restore = Restore.new(Mongoid::AuditLog::Entry.first)
42
+ restore.perform
43
+
44
+ restore.restored.should be_persisted
45
+ First.count.should == 1
46
+ restore.restored.should == First.first
47
+ restore.restored.name.should == 'Foo'
48
+ end
49
+
50
+ it 'restores an embedded array document' do
51
+ root = First.create!(name: 'Foo', seconds: [{ name: 'Bar' }])
52
+ Mongoid::AuditLog.record { root.seconds.first.destroy }
53
+ restore = Restore.new(Mongoid::AuditLog::Entry.first)
54
+ restore.perform
55
+ root.reload
56
+
57
+ restore.restored.should be_persisted
58
+ root.seconds.length.should == 1
59
+ root.seconds.first.should == restore.restored
60
+ root.seconds.first.name.should == 'Bar'
61
+ end
62
+
63
+ it 'restores a nested array document' do
64
+ root = First.create!(
65
+ name: 'Foo',
66
+ seconds: [{ name: 'Bar', thirds: [{ name: 'Baz' }] }]
67
+ )
68
+ Mongoid::AuditLog.record { root.seconds.first.thirds.first.destroy }
69
+ restore = Restore.new(Mongoid::AuditLog::Entry.first)
70
+ restore.perform
71
+ root.reload
72
+
73
+ restore.restored.should be_persisted
74
+ root.seconds.first.thirds.length.should == 1
75
+ root.seconds.first.thirds.first.should == restore.restored
76
+ root.seconds.first.thirds.first.name.should == 'Baz'
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -12,9 +12,12 @@ module Mongoid
12
12
 
13
13
  class ::Variant
14
14
  include Mongoid::Document
15
+ include Mongoid::AuditLog
15
16
  field :sku, :type => String
16
17
  embedded_in :product
17
18
  end
19
+
20
+ AuditLog.disable
18
21
  end
19
22
 
20
23
  after(:all) do
@@ -94,8 +97,31 @@ module Mongoid
94
97
  end
95
98
  end
96
99
 
100
+ describe '.enable' do
101
+ after(:each) do
102
+ AuditLog.disable
103
+ end
104
+
105
+ it 'starts recording' do
106
+ AuditLog.enable
107
+
108
+ product = Product.create!(:name => 'Foo bar')
109
+ product.audit_log_entries.count.should == 1
110
+ end
111
+ end
112
+
97
113
  describe '.disable' do
98
- it 'can disable recording' do
114
+ it 'stops recording' do
115
+ AuditLog.enable
116
+ AuditLog.disable
117
+
118
+ product = Product.create!(:name => 'Foo bar')
119
+ product.audit_log_entries.should be_empty
120
+ end
121
+
122
+ it 'can disable recording for a block' do
123
+ AuditLog.disable
124
+
99
125
  AuditLog.record do
100
126
  product = Product.create!(:name => 'Foo bar')
101
127
 
@@ -124,6 +150,7 @@ module Mongoid
124
150
  entry = product.audit_log_entries.first
125
151
 
126
152
  entry.create?.should be_true
153
+ entry.root.should == product
127
154
 
128
155
  entry.tracked_changes.should == {
129
156
  'name' => [nil, 'Foo bar']
@@ -141,13 +168,14 @@ module Mongoid
141
168
  product.save!
142
169
 
143
170
  entry = product.audit_log_entries.first
171
+ entry.root.should == product
144
172
 
145
173
  entry.create?.should be_true
146
174
 
147
175
  entry.model_attributes.should == {
148
176
  '_id' => product.id,
149
177
  'name' => 'Foo bar',
150
- 'variants' => [{ '_id' => product.variants.first.id, 'sku'=>'sku' }]
178
+ 'variants' => [{ '_id' => product.variants.first.id, 'sku' => 'sku' }]
151
179
  }
152
180
 
153
181
  entry.tracked_changes.should == {
@@ -155,6 +183,21 @@ module Mongoid
155
183
  'variants' => [{ 'sku' => [nil, 'sku'] }]
156
184
  }
157
185
  end
186
+
187
+ it 'tracks parents on embedded creations' do
188
+ product = Product.create!(:name => 'Foo bar')
189
+ variant = product.variants.create!(sku: 'sku')
190
+
191
+ entry = Mongoid::AuditLog::Entry.desc(:created_at).first
192
+ entry.root.should == product
193
+ entry.document_path.length.should == 2
194
+ entry.document_path.first['class_name'].should == product.class.name
195
+ entry.document_path.first['id'].should == product.id
196
+ entry.document_path.first['relation'].should == 'variants'
197
+ entry.document_path.second['class_name'].should == variant.class.name
198
+ entry.document_path.second['id'].should == variant.id
199
+ entry.document_path.second['relation'].should == nil
200
+ end
158
201
  end
159
202
 
160
203
  context 'update' do
@@ -164,6 +207,7 @@ module Mongoid
164
207
  entry = product.audit_log_entries.desc(:created_at).first
165
208
 
166
209
  entry.update?.should be_true
210
+ entry.root.should == product
167
211
  entry.tracked_changes.should == { 'name' => ['Foo bar', 'Bar baz'] }
168
212
  end
169
213
 
@@ -179,6 +223,7 @@ module Mongoid
179
223
  entry = product.audit_log_entries.desc(:created_at).first
180
224
 
181
225
  entry.update?.should be_true
226
+ entry.root.should == product
182
227
  entry.tracked_changes.should == {
183
228
  'name' => ['Foo bar', 'Bar baz'],
184
229
  'variants' => [{ 'sku' => ['sku', 'newsku'] }]
@@ -190,6 +235,23 @@ module Mongoid
190
235
  product.update_attributes(:name => 'Foo bar')
191
236
  product.audit_log_entries.length.should == 1
192
237
  end
238
+
239
+ it 'tracks parents on embedded updates' do
240
+ product = Product.create!(:name => 'Foo bar')
241
+ variant = product.variants.create!(sku: 'sku')
242
+ variant.sku = 'newsku'
243
+ variant.save!
244
+
245
+ entry = Mongoid::AuditLog::Entry.desc(:created_at).first
246
+ entry.root.should == product
247
+ entry.document_path.length.should == 2
248
+ entry.document_path.first['class_name'].should == product.class.name
249
+ entry.document_path.first['id'].should == product.id
250
+ entry.document_path.first['relation'].should == 'variants'
251
+ entry.document_path.second['class_name'].should == variant.class.name
252
+ entry.document_path.second['id'].should == variant.id
253
+ entry.document_path.second['relation'].should == nil
254
+ end
193
255
  end
194
256
 
195
257
  context 'destroy' do
@@ -199,6 +261,23 @@ module Mongoid
199
261
  entry = product.audit_log_entries.desc(:created_at).first
200
262
 
201
263
  entry.destroy?.should be_true
264
+ entry.root.should == nil
265
+ end
266
+
267
+ it 'tracks parents on embedded destroys' do
268
+ product = Product.create!(:name => 'Foo bar')
269
+ variant = product.variants.create!(sku: 'sku')
270
+ variant.destroy!
271
+
272
+ entry = Mongoid::AuditLog::Entry.desc(:created_at).first
273
+ entry.root.should == product
274
+ entry.document_path.length.should == 2
275
+ entry.document_path.first['class_name'].should == product.class.name
276
+ entry.document_path.first['id'].should == product.id
277
+ entry.document_path.first['relation'].should == 'variants'
278
+ entry.document_path.second['class_name'].should == variant.class.name
279
+ entry.document_path.second['id'].should == variant.id
280
+ entry.document_path.second['relation'].should == nil
202
281
  end
203
282
  end
204
283
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid-audit_log
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Crouse
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-08 00:00:00.000000000 Z
11
+ date: 2020-10-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mongoid
@@ -16,28 +16,34 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '4.0'
19
+ version: 6.0.x
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7.0'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
- version: '4.0'
29
+ version: 6.0.x
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7.0'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: bundler
29
35
  requirement: !ruby/object:Gem::Requirement
30
36
  requirements:
31
- - - "~>"
37
+ - - ">="
32
38
  - !ruby/object:Gem::Version
33
- version: '1.3'
39
+ version: '0'
34
40
  type: :development
35
41
  prerelease: false
36
42
  version_requirements: !ruby/object:Gem::Requirement
37
43
  requirements:
38
- - - "~>"
44
+ - - ">="
39
45
  - !ruby/object:Gem::Version
40
- version: '1.3'
46
+ version: '0'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: rake
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -70,14 +76,16 @@ files:
70
76
  - lib/mongoid/audit_log/config.rb
71
77
  - lib/mongoid/audit_log/embedded_changes.rb
72
78
  - lib/mongoid/audit_log/entry.rb
79
+ - lib/mongoid/audit_log/restore.rb
73
80
  - lib/mongoid/audit_log/version.rb
74
81
  - mongoid-audit_log.gemspec
75
82
  - spec/mongoid/audit_log/changes_spec.rb
76
83
  - spec/mongoid/audit_log/entry_spec.rb
84
+ - spec/mongoid/audit_log/restore_spec.rb
77
85
  - spec/mongoid/audit_log_spec.rb
78
86
  - spec/spec_helper.rb
79
87
  - spec/support/models.rb
80
- homepage: https://github.com/bencrouse/mongoid-audit-log
88
+ homepage: https://github.com/bencrouse/mongoid-audit_log
81
89
  licenses:
82
90
  - MIT
83
91
  metadata: {}
@@ -96,8 +104,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
104
  - !ruby/object:Gem::Version
97
105
  version: 1.3.6
98
106
  requirements: []
99
- rubyforge_project:
100
- rubygems_version: 2.4.5
107
+ rubygems_version: 3.0.3
101
108
  signing_key:
102
109
  specification_version: 4
103
110
  summary: No fancy versioning, undo, redo, etc. Just saves changes to Mongoid models
@@ -105,6 +112,7 @@ summary: No fancy versioning, undo, redo, etc. Just saves changes to Mongoid mod
105
112
  test_files:
106
113
  - spec/mongoid/audit_log/changes_spec.rb
107
114
  - spec/mongoid/audit_log/entry_spec.rb
115
+ - spec/mongoid/audit_log/restore_spec.rb
108
116
  - spec/mongoid/audit_log_spec.rb
109
117
  - spec/spec_helper.rb
110
118
  - spec/support/models.rb