mongoid-audit_log 0.2.0 → 0.5.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
- 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