mongoid_relations_dirty_tracking 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9bd7befbb1f28738d4b2466c5fee8a4f73f82eb2
4
+ data.tar.gz: 5f86d528912d3a3a90e3306ac8b60069a90cea37
5
+ SHA512:
6
+ metadata.gz: f5f28400a832ca7b5627ce13b4aea1c2c7ba91a885c8b23ab71b76034ba8a59fb4df070f713b6d2430e5abf494c76cb6bd57dfb06f368423dc50bd106306bea2
7
+ data.tar.gz: 3d61e3f9e0a3461996150562c9321545d8cb9c3f9ea8bace95d28c709645126360cb4571ef8f244535b34d16adf7963f3fd2e8c8b1aecd8d3a2c82fd4fba4d42
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in mongoid_track_embedded_changes.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 David Sevcik
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Mongoid::RelationsDirtyTracking
2
+
3
+ Mongoid extension for tracking changes on document relations.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'mongoid_relations_dirty_tracking', github: 'versative/mongoid_relations_dirty_tracking'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ ## Usage
16
+
17
+ ```ruby
18
+ class SampleDocument
19
+ include Mongoid::Document
20
+ include Mongoid::RelationsDirtyTracking
21
+
22
+ embeds_one :foo
23
+ has_many :bars
24
+
25
+ field :title, type: String
26
+ end
27
+
28
+ doc = SampleDocument.create
29
+ doc.foo = Foo.new(title: 'foo')
30
+ doc.bars << Bar.new(title: 'bar')
31
+ doc.title = 'New title'
32
+
33
+ doc.relations_changed? # => true
34
+ doc.relation_changes # => {"foo" => [nil, {"_id"=>"524c35ad1ac1c23084000040", "title" => "foo"}], "bars" => [nil, [{"_id"=>"524c35ad1ac1c23084000083"}]]}
35
+ doc.changed? # => true
36
+ doc.changes # => {"title" => [nil, "New title"], "foo" => [nil, {"_id"=>"524c35ad1ac1c23084000040", "title" => "foo"}], "bars" => [nil, [{"_id"=>"524c35ad1ac1c23084000083"}]]}
37
+
38
+ doc.save
39
+ doc.relations_changed? # => false
40
+ doc.relation_changes # => {}
41
+ doc.changed? # => false
42
+ doc.changes # => {}
43
+ ```
44
+
45
+ ## Contributing
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,5 @@
1
+ module Mongoid
2
+ module RelationsDirtyTracking
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,54 @@
1
+ module Mongoid
2
+ module RelationsDirtyTracking
3
+ module Versioning
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ alias_method_chain :revise, :relations
8
+ alias_method_chain :versioned_changes, :relations
9
+ alias_method_chain :revisable?, :relations
10
+ end
11
+
12
+
13
+ def revise_with_relations
14
+ previous = previous_revision
15
+ if previous && (versioned_attributes_changed? || relations_changed?)
16
+ new_version = versions.build(previous.versioned_attributes, without_protection: true)
17
+
18
+ rel_changes = relation_changes
19
+
20
+ self.class.tracked_relations.each do |rel_name|
21
+ # from some reason previous contaion also newly added relations
22
+ prev_value = rel_changes.include?(rel_name) ? rel_changes[rel_name][0] : previous[rel_name.to_sym]
23
+ new_version.send "#{rel_name}=", preserve_versioned_relation(reflect_on_association(rel_name), prev_value)
24
+ end
25
+
26
+
27
+ if version_max.present? && versions.length > version_max
28
+ deleted = versions.first
29
+ if deleted.paranoid?
30
+ versions.delete_one(deleted)
31
+ collection.find(atomic_selector).
32
+ update({ "$pull" => { "versions" => { "version" => deleted.version }}})
33
+ else
34
+ versions.delete(deleted)
35
+ end
36
+ end
37
+ self.version = (version || 1) + 1
38
+ end
39
+ end
40
+
41
+ def revisable_with_relations?
42
+ (versioned_attributes_changed? || relations_changed?) && !versionless?
43
+ end
44
+
45
+ def versioned_changes_with_relations
46
+ only_versioned_attributes(changes_without_relations.except("updated_at"))
47
+ end
48
+
49
+ def preserve_versioned_relation(rel_meta, value)
50
+ value
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,108 @@
1
+ require 'mongoid'
2
+ require 'active_support/concern'
3
+ require 'active_support/core_ext/module/aliasing'
4
+
5
+
6
+ module Mongoid
7
+ module RelationsDirtyTracking
8
+ extend ActiveSupport::Concern
9
+
10
+ included do
11
+ after_initialize :store_relations_shadow
12
+ after_save :store_relations_shadow
13
+
14
+ alias_method_chain :changes, :relations
15
+ alias_method_chain :changed?, :relations
16
+
17
+ cattr_accessor :relations_dirty_tracking_options
18
+ self.relations_dirty_tracking_options = { only: [], except: ['versions'] }
19
+
20
+ if self.include? Mongoid::Versioning
21
+ include Mongoid::RelationsDirtyTracking::Versioning
22
+ end
23
+ end
24
+
25
+
26
+ def store_relations_shadow
27
+ @relations_shadow = {}
28
+ self.class.tracked_relations.each do |rel_name|
29
+ @relations_shadow[rel_name] = tracked_relation_attributes(rel_name)
30
+ end
31
+ end
32
+
33
+
34
+ def relation_changes
35
+ changes = {}
36
+ @relations_shadow.each_pair do |rel_name, shadow_values|
37
+ current_values = tracked_relation_attributes(rel_name)
38
+ if current_values != shadow_values
39
+ changes[rel_name] = [shadow_values, current_values]
40
+ end
41
+ end
42
+ changes
43
+ end
44
+
45
+
46
+ def relations_changed?
47
+ !relation_changes.empty?
48
+ end
49
+
50
+
51
+ def changed_with_relations?
52
+ changed_without_relations? or relations_changed?
53
+ end
54
+
55
+
56
+ def changes_with_relations
57
+ (changes_without_relations || {}).merge relation_changes
58
+ end
59
+
60
+
61
+ def tracked_relation_attributes(rel_name)
62
+ rel_name = rel_name.to_s
63
+ values = nil
64
+ if meta = relations[rel_name]
65
+ values = if meta.relation == Mongoid::Relations::Embedded::One
66
+ send(rel_name) && send(rel_name).attributes.clone.delete_if {|key, _| key == 'updated_at' }
67
+ elsif meta.relation == Mongoid::Relations::Embedded::Many
68
+ send(rel_name) && send(rel_name).map {|child| child.attributes.clone.delete_if {|key, _| key == 'updated_at' } }
69
+ elsif meta.relation == Mongoid::Relations::Referenced::One
70
+ send(rel_name) && { "#{meta.key}" => send(rel_name)[meta.key] }
71
+ elsif meta.relation == Mongoid::Relations::Referenced::Many
72
+ send("#{rel_name.singularize}_ids").map {|id| { "#{meta.key}" => id } }
73
+ elsif meta.relation == Mongoid::Relations::Referenced::ManyToMany
74
+ send("#{rel_name.singularize}_ids").map {|id| { "#{meta.primary_key}" => id } }
75
+ elsif meta.relation == Mongoid::Relations::Referenced::In
76
+ send(meta.foreign_key) && { "#{meta.foreign_key}" => send(meta.foreign_key)}
77
+ end
78
+ end
79
+ values
80
+ end
81
+
82
+
83
+ module ClassMethods
84
+
85
+ def relations_dirty_tracking(options = {})
86
+ relations_dirty_tracking_options[:only] += [options[:only] || []].flatten.map(&:to_s)
87
+ relations_dirty_tracking_options[:except] += [options[:except] || []].flatten.map(&:to_s)
88
+ end
89
+
90
+
91
+ def track_relation?(rel_name)
92
+ rel_name = rel_name.to_s
93
+ options = relations_dirty_tracking_options
94
+ to_track = (!options[:only].blank? && options[:only].include?(rel_name)) \
95
+ || (options[:only].blank? && !options[:except].include?(rel_name))
96
+
97
+ to_track && [Mongoid::Relations::Embedded::One, Mongoid::Relations::Embedded::Many,
98
+ Mongoid::Relations::Referenced::One, Mongoid::Relations::Referenced::Many,
99
+ Mongoid::Relations::Referenced::ManyToMany, Mongoid::Relations::Referenced::In].include?(relations[rel_name].try(:relation))
100
+ end
101
+
102
+
103
+ def tracked_relations
104
+ @tracked_relations ||= relations.keys.select {|rel_name| track_relation?(rel_name) }
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,2 @@
1
+ require "mongoid/relations_dirty_tracking"
2
+ require 'mongoid/relations_dirty_tracking/versioning'
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ require File.expand_path('../lib/mongoid/relations_dirty_tracking/version', __FILE__)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "mongoid_relations_dirty_tracking"
6
+ spec.version = Mongoid::RelationsDirtyTracking::VERSION
7
+ spec.authors = ["David Sevcik"]
8
+ spec.email = ["david.sevcik@gmail.com"]
9
+ spec.description = "Mongoid extension for tracking changes on document relations"
10
+ spec.summary = "Mongoid extension for tracking changes on document relations"
11
+ spec.homepage = "http://github.com/versative/relations_dirty_tracking"
12
+ spec.license = "MIT"
13
+
14
+ spec.add_runtime_dependency 'activesupport', '~> 3.0'
15
+ spec.add_runtime_dependency 'mongoid', '>= 3.1.0', '< 4.0'
16
+
17
+ spec.add_development_dependency "bundler", "~> 1.3"
18
+ spec.add_development_dependency "rake"
19
+ spec.add_development_dependency "rspec", "~> 2.8"
20
+ spec.add_development_dependency "pry"
21
+
22
+ spec.files = `git ls-files`.split($/)
23
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
24
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
25
+ spec.require_paths = ["lib"]
26
+ end
@@ -0,0 +1,382 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe Mongoid::RelationsDirtyTracking do
5
+ subject { TestDocument.create }
6
+
7
+ its(:changed?) { should be_false }
8
+ its(:children_changed?) { should be_false }
9
+ its(:relations_changed?) { should be_false }
10
+ its(:changed_with_relations?) { should be_false }
11
+
12
+
13
+ context "embeds_one relationship" do
14
+ context "when adding document" do
15
+ before :each do
16
+ @embedded_doc = TestEmbeddedDocument.new
17
+ subject.one_document = @embedded_doc
18
+ end
19
+
20
+ its(:changed?) { should be_true }
21
+ its(:children_changed?) { should be_false }
22
+ its(:relations_changed?) { should be_true }
23
+ its(:changed_with_relations?) { should be_true }
24
+ its(:changes_with_relations) { should include(subject.relation_changes) }
25
+
26
+ describe "#relation_changes" do
27
+ it "returns array with differences" do
28
+ expect(subject.relation_changes['one_document']).to eq([nil, @embedded_doc.attributes])
29
+ end
30
+ end
31
+ end
32
+
33
+
34
+ context "when removing document" do
35
+ before :each do
36
+ @embedded_doc = TestEmbeddedDocument.new
37
+ subject.one_document = @embedded_doc
38
+ subject.save!
39
+ subject.one_document = nil
40
+ end
41
+
42
+ its(:changed?) { should be_true }
43
+ its(:children_changed?) { should be_false }
44
+ its(:relations_changed?) { should be_true }
45
+ its(:changed_with_relations?) { should be_true }
46
+ its(:changes_with_relations) { should include(subject.relation_changes) }
47
+
48
+ describe "#relation_changes" do
49
+ it "returns array with differences" do
50
+ expect(subject.relation_changes['one_document']).to eq([@embedded_doc.attributes, nil])
51
+ end
52
+ end
53
+ end
54
+
55
+
56
+ context "when changing value on embedded document" do
57
+ before :each do
58
+ @embedded_doc = TestEmbeddedDocument.new
59
+ subject.one_document = @embedded_doc
60
+ subject.save!
61
+ subject.one_document.title = 'foobar'
62
+ end
63
+
64
+ its(:changed?) { should be_true }
65
+ its(:children_changed?) { should be_true }
66
+ its(:relations_changed?) { should be_true }
67
+ its(:changed_with_relations?) { should be_true }
68
+ its(:changes_with_relations) { should include(subject.relation_changes) }
69
+
70
+ describe "#relation_changes" do
71
+ it "returns array with differences" do
72
+ old_attributes = @embedded_doc.attributes.clone.delete_if {|key, val| key == "title" }
73
+ expect(subject.relation_changes['one_document']).to eq([old_attributes, @embedded_doc.attributes])
74
+ end
75
+ end
76
+ end
77
+
78
+ context "when just updated_at is changed on embedded document" do
79
+ before :each do
80
+ embedded_doc = Class.new(TestEmbeddedDocument) { include Mongoid::Timestamps }.new
81
+ subject.one_document = @embedded_doc
82
+ subject.save!
83
+ embedded_doc.updated_at = Time.now
84
+ end
85
+ its(:changed?) { should be_false }
86
+ end
87
+ end
88
+
89
+ context "embeds_many relationship" do
90
+ context "when adding document" do
91
+ before :each do
92
+ @embedded_doc = TestEmbeddedDocument.new
93
+ subject.many_documents << @embedded_doc
94
+ end
95
+
96
+ its(:changed?) { should be_true }
97
+ its(:children_changed?) { should be_false }
98
+ its(:relations_changed?) { should be_true }
99
+ its(:changed_with_relations?) { should be_true }
100
+ its(:changes_with_relations) { should include(subject.relation_changes) }
101
+
102
+ describe "#relation_changes" do
103
+ it "returns array with differences" do
104
+ expect(subject.relation_changes['many_documents']).to eq([[], [@embedded_doc.attributes]])
105
+ end
106
+ end
107
+ end
108
+
109
+
110
+ context "when removing document" do
111
+ before :each do
112
+ @embedded_doc = TestEmbeddedDocument.new
113
+ subject.many_documents = [@embedded_doc]
114
+ subject.save!
115
+ subject.many_documents.delete @embedded_doc
116
+ end
117
+
118
+ its(:changed?) { should be_true }
119
+ its(:children_changed?) { should be_false }
120
+ its(:relations_changed?) { should be_true }
121
+ its(:changed_with_relations?) { should be_true }
122
+ its(:changes_with_relations) { should include(subject.relation_changes) }
123
+
124
+ describe "#relation_changes" do
125
+ it "returns array with differences" do
126
+ expect(subject.relation_changes['many_documents']).to eq([[@embedded_doc.attributes], []])
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+
133
+ context "has_one relationship" do
134
+ context "when adding document" do
135
+ before :each do
136
+ @related_doc = TestRelatedDocument.new
137
+ subject.one_related = @related_doc
138
+ end
139
+
140
+ its(:changed?) { should be_true }
141
+ its(:children_changed?) { should be_false }
142
+ its(:relations_changed?) { should be_true }
143
+ its(:changed_with_relations?) { should be_true }
144
+ its(:changes_with_relations) { should include(subject.relation_changes) }
145
+
146
+ describe "#relation_changes" do
147
+ it "returns array with differences" do
148
+ expect(subject.relation_changes['one_related']).to eq([nil, {'_id' => @related_doc._id}])
149
+ end
150
+ end
151
+ end
152
+
153
+ context "when removing document" do
154
+ before :each do
155
+ @related_doc = TestRelatedDocument.new
156
+ subject.one_related = @related_doc
157
+ subject.save!
158
+ subject.one_related = nil
159
+ end
160
+
161
+ its(:changed?) { should be_true }
162
+ its(:children_changed?) { should be_false }
163
+ its(:relations_changed?) { should be_true }
164
+ its(:changed_with_relations?) { should be_true }
165
+ its(:changes_with_relations) { should include(subject.relation_changes) }
166
+
167
+ describe "#relation_changes" do
168
+ it "returns array with differences" do
169
+ expect(subject.relation_changes['one_related']).to eq([{'_id' => @related_doc._id}, nil])
170
+ end
171
+ end
172
+ end
173
+
174
+ context "when changing document" do
175
+ before :each do
176
+ @related_doc = TestRelatedDocument.new
177
+ subject.one_related = @related_doc
178
+ subject.save!
179
+ subject.one_related = @another_related_doc = TestRelatedDocument.new
180
+ end
181
+
182
+ its(:changed?) { should be_true }
183
+ its(:children_changed?) { should be_false }
184
+ its(:relations_changed?) { should be_true }
185
+ its(:changed_with_relations?) { should be_true }
186
+ its(:changes_with_relations) { should include(subject.relation_changes) }
187
+
188
+ describe "#relation_changes" do
189
+ it "returns array with differences" do
190
+ expect(subject.relation_changes['one_related']).to eq([{'_id' => @related_doc._id}, {'_id' => @another_related_doc._id}])
191
+ end
192
+ end
193
+ end
194
+
195
+ context "when changing value on referenced document" do
196
+ before :each do
197
+ @related_doc = TestRelatedDocument.new
198
+ subject.one_related = @related_doc
199
+ subject.save!
200
+ subject.one_related.title = "New title"
201
+ end
202
+
203
+ its(:changed?) { should be_false }
204
+ its(:children_changed?) { should be_false }
205
+ its(:relations_changed?) { should be_false }
206
+ its(:changed_with_relations?) { should be_false }
207
+ its(:relation_changes) { should be_empty }
208
+ end
209
+ end
210
+
211
+
212
+ context "has_many relationship" do
213
+ context "when adding document" do
214
+ before :each do
215
+ @related_doc = TestRelatedDocument.new
216
+ subject.many_related << @related_doc
217
+ end
218
+
219
+ its(:changed?) { should be_true }
220
+ its(:children_changed?) { should be_false }
221
+ its(:relations_changed?) { should be_true }
222
+ its(:changed_with_relations?) { should be_true }
223
+ its(:changes_with_relations) { should include(subject.relation_changes) }
224
+
225
+ describe "#relation_changes" do
226
+ it "returns array with differences" do
227
+ expect(subject.relation_changes['many_related']).to eq([[], [{'_id' => @related_doc._id}]])
228
+ end
229
+ end
230
+ end
231
+
232
+ context "when removing document" do
233
+ before :each do
234
+ @related_doc = TestRelatedDocument.new
235
+ subject.many_related << @related_doc
236
+ subject.save!
237
+ subject.many_related.delete @related_doc
238
+ end
239
+
240
+ its(:changed?) { should be_true }
241
+ its(:children_changed?) { should be_false }
242
+ its(:relations_changed?) { should be_true }
243
+ its(:changed_with_relations?) { should be_true }
244
+ its(:changes_with_relations) { should include(subject.relation_changes) }
245
+
246
+ describe "#relation_changes" do
247
+ it "returns array with differences" do
248
+ expect(subject.relation_changes['many_related']).to eq([[{'_id' => @related_doc._id}], []])
249
+ end
250
+ end
251
+ end
252
+ end
253
+
254
+
255
+ context "has_and_belongs_to_many relationship" do
256
+ context "when adding document" do
257
+ before :each do
258
+ @related_doc = TestRelatedDocument.new
259
+ subject.many_to_many_related << @related_doc
260
+ end
261
+
262
+ its(:changed?) { should be_true }
263
+ its(:children_changed?) { should be_false }
264
+ its(:relations_changed?) { should be_true }
265
+ its(:changed_with_relations?) { should be_true }
266
+ its(:changes_with_relations) { should include(subject.relation_changes) }
267
+
268
+ describe "#relation_changes" do
269
+ it "returns array with differences" do
270
+ expect(subject.relation_changes['many_to_many_related']).to eq([[], [{'_id' => @related_doc._id}]])
271
+ end
272
+ end
273
+ end
274
+
275
+ context "when removing document" do
276
+ before :each do
277
+ @related_doc = TestRelatedDocument.new
278
+ subject.many_to_many_related << @related_doc
279
+ subject.save!
280
+ subject.many_to_many_related.delete @related_doc
281
+ end
282
+
283
+ its(:changed?) { should be_true }
284
+ its(:children_changed?) { should be_false }
285
+ its(:relations_changed?) { should be_true }
286
+ its(:changed_with_relations?) { should be_true }
287
+ its(:changes_with_relations) { should include(subject.relation_changes) }
288
+
289
+ describe "#relation_changes" do
290
+ it "returns array with differences" do
291
+ expect(subject.relation_changes['many_to_many_related']).to eq([[{'_id' => @related_doc._id}], []])
292
+ end
293
+ end
294
+ end
295
+ end
296
+
297
+
298
+ context "belongs_to relationship" do
299
+ subject { TestRelatedDocument.create }
300
+
301
+ context "when adding document" do
302
+ before :each do
303
+ @doc = TestDocument.create
304
+ subject.test_document = @doc
305
+ end
306
+
307
+ its(:changed?) { should be_true }
308
+ its(:children_changed?) { should be_false }
309
+ its(:relations_changed?) { should be_true }
310
+ its(:changed_with_relations?) { should be_true }
311
+ its(:changes_with_relations) { should include(subject.relation_changes) }
312
+
313
+ describe "#relation_changes" do
314
+ it "returns array with differences" do
315
+ expect(subject.relation_changes['test_document']).to eq([nil, {'test_document_id' => @doc._id}])
316
+ end
317
+ end
318
+ end
319
+
320
+ context "when removing document" do
321
+ before :each do
322
+ @doc = TestDocument.create
323
+ subject.test_document = @doc
324
+ subject.save!
325
+ subject.test_document = nil
326
+ end
327
+
328
+ its(:changed?) { should be_true }
329
+ its(:children_changed?) { should be_false }
330
+ its(:relations_changed?) { should be_true }
331
+ its(:changed_with_relations?) { should be_true }
332
+ its(:changes_with_relations) { should include(subject.relation_changes) }
333
+
334
+ describe "#relation_changes" do
335
+ it "returns array with differences" do
336
+ expect(subject.relation_changes['test_document']).to eq([{'test_document_id' => @doc._id}, nil])
337
+ end
338
+ end
339
+ end
340
+ end
341
+
342
+
343
+ describe ".track_relation?" do
344
+ context "with only options" do
345
+ it "do tracks only specified relations" do
346
+ expect(TestDocumentWithOnlyOption.track_relation? :many_documents).to be_true
347
+ expect(TestDocumentWithOnlyOption.track_relation? :one_related).to be_false
348
+ end
349
+ end
350
+
351
+ context "with except options" do
352
+ it "do no track excluded relations" do
353
+ expect(TestDocumentWithExceptOption.track_relation? 'many_documents').to be_false
354
+ expect(TestDocumentWithExceptOption.track_relation? 'one_related').to be_true
355
+ end
356
+ end
357
+ end
358
+
359
+
360
+ describe "by befault the versions relation is not tracked" do
361
+ context "when not called 'relations_dirty_tracking'" do
362
+ it "'versions' is excluded from tracing" do
363
+ expect(Class.new(TestDocument).relations_dirty_tracking_options[:except]).to include('versions')
364
+ end
365
+ end
366
+
367
+ context "when called 'relations_dirty_tracking' with only" do
368
+ it "'versions' is excluded from tracing" do
369
+ klass = Class.new(TestDocument) { relations_dirty_tracking(only: 'foobar') }
370
+ expect(klass.relations_dirty_tracking_options[:except]).to include('versions')
371
+ end
372
+ end
373
+
374
+ context "when called 'relations_dirty_tracking' with except" do
375
+ it "'versions' is excluded from tracing" do
376
+ klass = Class.new(TestDocument) { relations_dirty_tracking(except: 'foobar') }
377
+ expect(klass.relations_dirty_tracking_options[:except]).to include('versions')
378
+ expect(klass.relations_dirty_tracking_options[:except]).to include('foobar')
379
+ end
380
+ end
381
+ end
382
+ end
@@ -0,0 +1,65 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'mongoid'
4
+ require 'mongoid/relations_dirty_tracking'
5
+
6
+ require 'rspec'
7
+
8
+ class TestDocument
9
+ include Mongoid::Document
10
+ include Mongoid::RelationsDirtyTracking
11
+
12
+ embeds_one :one_document, class_name: 'TestEmbeddedDocument'
13
+ embeds_many :many_documents, class_name: 'TestEmbeddedDocument'
14
+
15
+ has_one :one_related, class_name: 'TestRelatedDocument'
16
+ has_many :many_related, class_name: 'TestRelatedDocument'
17
+ has_and_belongs_to_many :many_to_many_related, class_name: 'TestRelatedDocument'
18
+ end
19
+
20
+ class TestEmbeddedDocument
21
+ include Mongoid::Document
22
+
23
+ embedded_in :test_document
24
+
25
+ field :title, type: String
26
+ end
27
+
28
+ class TestRelatedDocument
29
+ include Mongoid::Document
30
+ include Mongoid::RelationsDirtyTracking
31
+
32
+ belongs_to :test_document, inverse_of: :one_related
33
+
34
+ field :title, type: String
35
+ end
36
+
37
+ class TestDocumentWithOnlyOption
38
+ include Mongoid::Document
39
+ include Mongoid::RelationsDirtyTracking
40
+
41
+ embeds_many :many_documents, class_name: 'TestEmbeddedDocument'
42
+ has_one :one_related, class_name: 'TestRelatedDocument'
43
+
44
+ relations_dirty_tracking only: :many_documents
45
+ end
46
+
47
+ class TestDocumentWithExceptOption
48
+ include Mongoid::Document
49
+ include Mongoid::RelationsDirtyTracking
50
+
51
+ embeds_many :many_documents, class_name: 'TestEmbeddedDocument'
52
+ has_one :one_related, class_name: 'TestRelatedDocument'
53
+
54
+ relations_dirty_tracking except: 'many_documents'
55
+ end
56
+
57
+
58
+ Mongoid.configure do |config|
59
+ config.connect_to('mongoid_relations_dirty_tracking_test')
60
+ end
61
+
62
+ RSpec.configure do |config|
63
+ config.mock_with :rspec
64
+ config.after(:each) { Mongoid.purge! }
65
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid_relations_dirty_tracking
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - David Sevcik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: mongoid
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 3.1.0
34
+ - - <
35
+ - !ruby/object:Gem::Version
36
+ version: '4.0'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 3.1.0
44
+ - - <
45
+ - !ruby/object:Gem::Version
46
+ version: '4.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '1.3'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ version: '1.3'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rake
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ~>
80
+ - !ruby/object:Gem::Version
81
+ version: '2.8'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ~>
87
+ - !ruby/object:Gem::Version
88
+ version: '2.8'
89
+ - !ruby/object:Gem::Dependency
90
+ name: pry
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ description: Mongoid extension for tracking changes on document relations
104
+ email:
105
+ - david.sevcik@gmail.com
106
+ executables: []
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - .gitignore
111
+ - Gemfile
112
+ - LICENSE.txt
113
+ - README.md
114
+ - Rakefile
115
+ - lib/mongoid/relations_dirty_tracking.rb
116
+ - lib/mongoid/relations_dirty_tracking/version.rb
117
+ - lib/mongoid/relations_dirty_tracking/versioning.rb
118
+ - lib/mongoid_relations_dirty_tracking.rb
119
+ - mongoid_relations_dirty_tracking.gemspec
120
+ - spec/lib/mongoid/relations_dirty_tracking_spec.rb
121
+ - spec/spec_helper.rb
122
+ homepage: http://github.com/versative/relations_dirty_tracking
123
+ licenses:
124
+ - MIT
125
+ metadata: {}
126
+ post_install_message:
127
+ rdoc_options: []
128
+ require_paths:
129
+ - lib
130
+ required_ruby_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ required_rubygems_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - '>='
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ requirements: []
141
+ rubyforge_project:
142
+ rubygems_version: 2.1.11
143
+ signing_key:
144
+ specification_version: 4
145
+ summary: Mongoid extension for tracking changes on document relations
146
+ test_files:
147
+ - spec/lib/mongoid/relations_dirty_tracking_spec.rb
148
+ - spec/spec_helper.rb