mongoid-history 0.8.0 → 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/.coveralls.yml +1 -1
  3. data/.document +5 -5
  4. data/.github/workflows/test.yml +72 -0
  5. data/.gitignore +46 -46
  6. data/.rspec +2 -2
  7. data/.rubocop.yml +6 -6
  8. data/.rubocop_todo.yml +99 -101
  9. data/CHANGELOG.md +173 -144
  10. data/CONTRIBUTING.md +117 -118
  11. data/Dangerfile +1 -1
  12. data/Gemfile +49 -37
  13. data/LICENSE.txt +20 -20
  14. data/README.md +609 -595
  15. data/RELEASING.md +66 -67
  16. data/Rakefile +24 -24
  17. data/UPGRADING.md +53 -34
  18. data/lib/mongoid/history/attributes/base.rb +72 -72
  19. data/lib/mongoid/history/attributes/create.rb +45 -50
  20. data/lib/mongoid/history/attributes/destroy.rb +34 -34
  21. data/lib/mongoid/history/attributes/update.rb +104 -45
  22. data/lib/mongoid/history/options.rb +177 -179
  23. data/lib/mongoid/history/trackable.rb +588 -521
  24. data/lib/mongoid/history/tracker.rb +247 -244
  25. data/lib/mongoid/history/version.rb +5 -5
  26. data/lib/mongoid/history.rb +77 -52
  27. data/lib/mongoid-history.rb +1 -1
  28. data/mongoid-history.gemspec +25 -25
  29. data/perf/benchmark_modified_attributes_for_create.rb +65 -0
  30. data/perf/gc_suite.rb +21 -0
  31. data/spec/integration/embedded_in_polymorphic_spec.rb +112 -135
  32. data/spec/integration/integration_spec.rb +976 -942
  33. data/spec/integration/multi_relation_spec.rb +47 -53
  34. data/spec/integration/multiple_trackers_spec.rb +68 -71
  35. data/spec/integration/nested_embedded_documents_spec.rb +64 -84
  36. data/spec/integration/nested_embedded_documents_tracked_in_parent_spec.rb +124 -0
  37. data/spec/integration/nested_embedded_polymorphic_documents_spec.rb +115 -127
  38. data/spec/integration/subclasses_spec.rb +47 -29
  39. data/spec/integration/track_history_order_spec.rb +84 -52
  40. data/spec/integration/validation_failure_spec.rb +76 -63
  41. data/spec/spec_helper.rb +32 -25
  42. data/spec/support/error_helpers.rb +7 -0
  43. data/spec/support/mongoid.rb +11 -11
  44. data/spec/support/mongoid_history.rb +12 -13
  45. data/spec/unit/attributes/base_spec.rb +141 -150
  46. data/spec/unit/attributes/create_spec.rb +342 -315
  47. data/spec/unit/attributes/destroy_spec.rb +228 -218
  48. data/spec/unit/attributes/update_spec.rb +342 -321
  49. data/spec/unit/callback_options_spec.rb +165 -159
  50. data/spec/unit/embedded_methods_spec.rb +87 -69
  51. data/spec/unit/history_spec.rb +58 -35
  52. data/spec/unit/my_instance_methods_spec.rb +555 -485
  53. data/spec/unit/options_spec.rb +365 -327
  54. data/spec/unit/singleton_methods_spec.rb +406 -338
  55. data/spec/unit/store/default_store_spec.rb +11 -11
  56. data/spec/unit/store/request_store_spec.rb +13 -13
  57. data/spec/unit/trackable_spec.rb +1057 -689
  58. data/spec/unit/tracker_spec.rb +190 -163
  59. metadata +13 -8
  60. data/.travis.yml +0 -35
@@ -1,244 +1,247 @@
1
- module Mongoid
2
- module History
3
- module Tracker
4
- extend ActiveSupport::Concern
5
-
6
- included do
7
- include Mongoid::Document
8
- include Mongoid::Timestamps
9
- attr_writer :trackable
10
-
11
- field :association_chain, type: Array, default: []
12
- field :modified, type: Hash, default: {}
13
- field :original, type: Hash, default: {}
14
- field :version, type: Integer
15
- field :action, type: String
16
- field :scope, type: String
17
- modifier_options = {
18
- class_name: Mongoid::History.modifier_class_name
19
- }
20
- modifier_options[:optional] = true if Mongoid::Compatibility::Version.mongoid6_or_newer?
21
- belongs_to :modifier, modifier_options
22
-
23
- index(scope: 1)
24
- index(association_chain: 1)
25
-
26
- Mongoid::History.tracker_class_name ||= name.tableize.singularize.to_sym
27
- end
28
-
29
- def undo!(modifier = nil)
30
- if action.to_sym == :destroy
31
- re_create
32
- elsif action.to_sym == :create
33
- re_destroy
34
- elsif Mongoid::Compatibility::Version.mongoid3?
35
- trackable.update_attributes!(undo_attr(modifier), without_protection: true)
36
- else
37
- trackable.update_attributes!(undo_attr(modifier))
38
- end
39
- end
40
-
41
- def redo!(modifier = nil)
42
- if action.to_sym == :destroy
43
- re_destroy
44
- elsif action.to_sym == :create
45
- re_create
46
- elsif Mongoid::Compatibility::Version.mongoid3?
47
- trackable.update_attributes!(redo_attr(modifier), without_protection: true)
48
- else
49
- trackable.update_attributes!(redo_attr(modifier))
50
- end
51
- end
52
-
53
- def undo_attr(modifier)
54
- undo_hash = affected.easy_unmerge(modified)
55
- undo_hash.easy_merge!(original)
56
- modifier_field = trackable.history_trackable_options[:modifier_field]
57
- undo_hash[modifier_field] = modifier if modifier_field
58
- (modified.keys - undo_hash.keys).each do |k|
59
- undo_hash[k] = nil
60
- end
61
- localize_keys(undo_hash)
62
- end
63
-
64
- def redo_attr(modifier)
65
- redo_hash = affected.easy_unmerge(original)
66
- redo_hash.easy_merge!(modified)
67
- modifier_field = trackable.history_trackable_options[:modifier_field]
68
- redo_hash[modifier_field] = modifier if modifier_field
69
- localize_keys(redo_hash)
70
- end
71
-
72
- def trackable_root
73
- @trackable_root ||= trackable_parents_and_trackable.first
74
- end
75
-
76
- def trackable
77
- @trackable ||= trackable_parents_and_trackable.last
78
- end
79
-
80
- def trackable_parents
81
- @trackable_parents ||= trackable_parents_and_trackable[0, -1]
82
- end
83
-
84
- def trackable_parent
85
- @trackable_parent ||= trackable_parents_and_trackable[-2]
86
- end
87
-
88
- # Outputs a :from, :to hash for each affected field. Intentionally excludes fields
89
- # which are not tracked, even if there are tracked values for such fields
90
- # present in the database.
91
- #
92
- # @return [ HashWithIndifferentAccess ] a change set in the format:
93
- # { field_1: {to: new_val}, field_2: {from: old_val, to: new_val} }
94
- def tracked_changes
95
- @tracked_changes ||= (modified.keys | original.keys).inject(HashWithIndifferentAccess.new) do |h, k|
96
- h[k] = { from: original[k], to: modified[k] }.delete_if { |_, vv| vv.nil? }
97
- h
98
- end.delete_if { |k, v| v.blank? || !trackable_parent_class.tracked?(k) }
99
- end
100
-
101
- # Outputs summary of edit actions performed: :add, :modify, :remove, or :array.
102
- # Does deep comparison of arrays. Useful for creating human-readable representations
103
- # of the history tracker. Considers changing a value to 'blank' to be a removal.
104
- #
105
- # @return [ HashWithIndifferentAccess ] a change set in the format:
106
- # { add: { field_1: new_val, ... },
107
- # modify: { field_2: {from: old_val, to: new_val}, ... },
108
- # remove: { field_3: old_val },
109
- # array: { field_4: {add: ['foo', 'bar'], remove: ['baz']} } }
110
- def tracked_edits
111
- return @tracked_edits if @tracked_edits
112
- @tracked_edits = HashWithIndifferentAccess.new
113
-
114
- tracked_changes.each do |k, v|
115
- next if v[:from].blank? && v[:to].blank?
116
-
117
- if trackable_parent_class.tracked_embeds_many?(k)
118
- prepare_tracked_edits_for_embeds_many(k, v)
119
- elsif v[:from].blank?
120
- @tracked_edits[:add] ||= {}
121
- @tracked_edits[:add][k] = v[:to]
122
- elsif v[:to].blank?
123
- @tracked_edits[:remove] ||= {}
124
- @tracked_edits[:remove][k] = v[:from]
125
- elsif v[:from].is_a?(Array) && v[:to].is_a?(Array)
126
- @tracked_edits[:array] ||= {}
127
- old_values = v[:from] - v[:to]
128
- new_values = v[:to] - v[:from]
129
- @tracked_edits[:array][k] = { add: new_values, remove: old_values }.delete_if { |_, vv| vv.blank? }
130
- else
131
- @tracked_edits[:modify] ||= {}
132
- @tracked_edits[:modify][k] = v
133
- end
134
- end
135
- @tracked_edits
136
- end
137
-
138
- # Similar to #tracked_changes, but contains only a single value for each
139
- # affected field:
140
- # - :create and :update return the modified values
141
- # - :destroy returns original values
142
- # Included for legacy compatibility.
143
- #
144
- # @deprecated
145
- #
146
- # @return [ HashWithIndifferentAccess ] a change set in the format:
147
- # { field_1: value, field_2: value }
148
- def affected
149
- target = action.to_sym == :destroy ? :from : :to
150
- @affected ||= tracked_changes.inject(HashWithIndifferentAccess.new) do |h, (k, v)|
151
- h[k] = v[target]
152
- h
153
- end
154
- end
155
-
156
- # Returns the class of the trackable, irrespective of whether the trackable object
157
- # has been destroyed.
158
- #
159
- # @return [ Class ] the class of the trackable
160
- def trackable_parent_class
161
- association_chain.first['name'].constantize
162
- end
163
-
164
- private
165
-
166
- def re_create
167
- association_chain.length > 1 ? create_on_parent : create_standalone
168
- end
169
-
170
- def re_destroy
171
- trackable.destroy
172
- end
173
-
174
- def create_standalone
175
- restored = trackable_parent_class.new(localize_keys(original))
176
- restored.id = original['_id']
177
- restored.save!
178
- end
179
-
180
- def create_on_parent
181
- name = association_chain.last['name']
182
- if trackable_parent.class.embeds_one?(name)
183
- trackable_parent.create_embedded(name, localize_keys(original))
184
- elsif trackable_parent.class.embeds_many?(name)
185
- trackable_parent.get_embedded(name).create!(localize_keys(original))
186
- else
187
- raise 'This should never happen. Please report bug!'
188
- end
189
- end
190
-
191
- def trackable_parents_and_trackable
192
- @trackable_parents_and_trackable ||= traverse_association_chain
193
- end
194
-
195
- def traverse_association_chain
196
- chain = association_chain.dup
197
- doc = nil
198
- documents = []
199
- loop do
200
- node = chain.shift
201
- name = node['name']
202
- doc = if doc.nil?
203
- # root association. First element of the association chain
204
- # unscoped is added to remove any default_scope defined in model
205
- klass = name.classify.constantize
206
- klass.unscoped.where(_id: node['id']).first
207
- elsif doc.class.embeds_one?(name)
208
- doc.get_embedded(name)
209
- elsif doc.class.embeds_many?(name)
210
- doc.get_embedded(name).unscoped.where(_id: node['id']).first
211
- else
212
- raise 'This should never happen. Please report bug.'
213
- end
214
- documents << doc
215
- break if chain.empty?
216
- end
217
- documents
218
- end
219
-
220
- def localize_keys(hash)
221
- klass = association_chain.first['name'].constantize
222
- if klass.respond_to?(:localized_fields)
223
- klass.localized_fields.keys.each do |name|
224
- hash["#{name}_translations"] = hash.delete(name) if hash[name].present?
225
- end
226
- end
227
- hash
228
- end
229
-
230
- def prepare_tracked_edits_for_embeds_many(key, value)
231
- @tracked_edits[:embeds_many] ||= {}
232
- value[:from] ||= []
233
- value[:to] ||= []
234
- modify_ids = value[:from].map { |vv| vv['_id'] }.compact & value[:to].map { |vv| vv['_id'] }.compact
235
- modify_values = modify_ids.map { |id| { from: value[:from].detect { |vv| vv['_id'] == id }, to: value[:to].detect { |vv| vv['_id'] == id } } }
236
- modify_values.delete_if { |vv| vv[:from] == vv[:to] }
237
- ignore_values = modify_values.map { |vv| [vv[:from], vv[:to]] }.flatten
238
- old_values = value[:from] - value[:to] - ignore_values
239
- new_values = value[:to] - value[:from] - ignore_values
240
- @tracked_edits[:embeds_many][key] = { add: new_values, remove: old_values, modify: modify_values }.delete_if { |_, vv| vv.blank? }
241
- end
242
- end
243
- end
244
- end
1
+ module Mongoid
2
+ module History
3
+ module Tracker
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include Mongoid::Document
8
+ include Mongoid::Timestamps
9
+ attr_writer :trackable
10
+
11
+ field :association_chain, type: Array, default: []
12
+ field :modified, type: Hash, default: {}
13
+ field :original, type: Hash, default: {}
14
+ field :version, type: Integer
15
+ field :action, type: String
16
+ field :scope, type: String
17
+ modifier_options = {
18
+ class_name: Mongoid::History.modifier_class_name
19
+ }
20
+ modifier_options[:optional] = true if Mongoid::Compatibility::Version.mongoid6_or_newer?
21
+ belongs_to :modifier, modifier_options
22
+
23
+ index(scope: 1)
24
+ index(association_chain: 1)
25
+
26
+ Mongoid::History.tracker_class_name ||= name.tableize.singularize.to_sym
27
+ end
28
+
29
+ def undo!(modifier = nil)
30
+ if action.to_sym == :destroy
31
+ re_create
32
+ elsif action.to_sym == :create
33
+ re_destroy
34
+ elsif Mongoid::Compatibility::Version.mongoid3?
35
+ trackable.update_attributes!(undo_attr(modifier), without_protection: true)
36
+ else
37
+ trackable.update_attributes!(undo_attr(modifier))
38
+ end
39
+ end
40
+
41
+ def redo!(modifier = nil)
42
+ if action.to_sym == :destroy
43
+ re_destroy
44
+ elsif action.to_sym == :create
45
+ re_create
46
+ elsif Mongoid::Compatibility::Version.mongoid3?
47
+ trackable.update_attributes!(redo_attr(modifier), without_protection: true)
48
+ else
49
+ trackable.update_attributes!(redo_attr(modifier))
50
+ end
51
+ end
52
+
53
+ def undo_attr(modifier)
54
+ undo_hash = affected.easy_unmerge(modified)
55
+ undo_hash.easy_merge!(original)
56
+ modifier_field = trackable.history_trackable_options[:modifier_field]
57
+ undo_hash[modifier_field] = modifier if modifier_field
58
+ (modified.keys - undo_hash.keys).each do |k|
59
+ undo_hash[k] = nil
60
+ end
61
+ localize_keys(undo_hash)
62
+ end
63
+
64
+ def redo_attr(modifier)
65
+ redo_hash = affected.easy_unmerge(original)
66
+ redo_hash.easy_merge!(modified)
67
+ modifier_field = trackable.history_trackable_options[:modifier_field]
68
+ redo_hash[modifier_field] = modifier if modifier_field
69
+ localize_keys(redo_hash)
70
+ end
71
+
72
+ def trackable_root
73
+ @trackable_root ||= trackable_parents_and_trackable.first
74
+ end
75
+
76
+ def trackable
77
+ @trackable ||= trackable_parents_and_trackable.last
78
+ end
79
+
80
+ def trackable_parents
81
+ @trackable_parents ||= trackable_parents_and_trackable[0, -1]
82
+ end
83
+
84
+ def trackable_parent
85
+ @trackable_parent ||= trackable_parents_and_trackable[-2]
86
+ end
87
+
88
+ # Outputs a :from, :to hash for each affected field. Intentionally excludes fields
89
+ # which are not tracked, even if there are tracked values for such fields
90
+ # present in the database.
91
+ #
92
+ # @return [ HashWithIndifferentAccess ] a change set in the format:
93
+ # { field_1: {to: new_val}, field_2: {from: old_val, to: new_val} }
94
+ def tracked_changes
95
+ @tracked_changes ||= (modified.keys | original.keys).inject(HashWithIndifferentAccess.new) do |h, k|
96
+ h[k] = { from: original[k], to: modified[k] }.delete_if { |_, vv| vv.nil? }
97
+ h
98
+ end.delete_if { |k, v| v.blank? || !trackable_parent_class.tracked?(k) }
99
+ end
100
+
101
+ # Outputs summary of edit actions performed: :add, :modify, :remove, or :array.
102
+ # Does deep comparison of arrays. Useful for creating human-readable representations
103
+ # of the history tracker. Considers changing a value to 'blank' to be a removal.
104
+ #
105
+ # @return [ HashWithIndifferentAccess ] a change set in the format:
106
+ # { add: { field_1: new_val, ... },
107
+ # modify: { field_2: {from: old_val, to: new_val}, ... },
108
+ # remove: { field_3: old_val },
109
+ # array: { field_4: {add: ['foo', 'bar'], remove: ['baz']} } }
110
+ def tracked_edits
111
+ return @tracked_edits if @tracked_edits
112
+ @tracked_edits = HashWithIndifferentAccess.new
113
+
114
+ tracked_changes.each do |k, v|
115
+ next if v[:from].blank? && v[:to].blank?
116
+
117
+ if trackable_parent_class.tracked_embeds_many?(k)
118
+ prepare_tracked_edits_for_embeds_many(k, v)
119
+ elsif v[:from].blank?
120
+ @tracked_edits[:add] ||= {}
121
+ @tracked_edits[:add][k] = v[:to]
122
+ elsif v[:to].blank?
123
+ @tracked_edits[:remove] ||= {}
124
+ @tracked_edits[:remove][k] = v[:from]
125
+ elsif v[:from].is_a?(Array) && v[:to].is_a?(Array)
126
+ @tracked_edits[:array] ||= {}
127
+ old_values = v[:from] - v[:to]
128
+ new_values = v[:to] - v[:from]
129
+ @tracked_edits[:array][k] = { add: new_values, remove: old_values }.delete_if { |_, vv| vv.blank? }
130
+ else
131
+ @tracked_edits[:modify] ||= {}
132
+ @tracked_edits[:modify][k] = v
133
+ end
134
+ end
135
+ @tracked_edits
136
+ end
137
+
138
+ # Similar to #tracked_changes, but contains only a single value for each
139
+ # affected field:
140
+ # - :create and :update return the modified values
141
+ # - :destroy returns original values
142
+ # Included for legacy compatibility.
143
+ #
144
+ # @deprecated
145
+ #
146
+ # @return [ HashWithIndifferentAccess ] a change set in the format:
147
+ # { field_1: value, field_2: value }
148
+ def affected
149
+ target = action.to_sym == :destroy ? :from : :to
150
+ @affected ||= tracked_changes.inject(HashWithIndifferentAccess.new) do |h, (k, v)|
151
+ h[k] = v[target]
152
+ h
153
+ end
154
+ end
155
+
156
+ # Returns the class of the trackable, irrespective of whether the trackable object
157
+ # has been destroyed.
158
+ #
159
+ # @return [ Class ] the class of the trackable
160
+ def trackable_parent_class
161
+ association_chain.first['name'].constantize
162
+ end
163
+
164
+ private
165
+
166
+ def re_create
167
+ association_chain.length > 1 ? create_on_parent : create_standalone
168
+ end
169
+
170
+ def re_destroy
171
+ trackable.destroy
172
+ end
173
+
174
+ def create_standalone
175
+ restored = trackable_parent_class.new(localize_keys(original))
176
+ restored.id = original['_id']
177
+ restored.save!
178
+ end
179
+
180
+ def create_on_parent
181
+ name = association_chain.last['name']
182
+
183
+ if trackable_parent.class.embeds_one?(name)
184
+ trackable_parent._create_relation(name, localize_keys(original))
185
+ elsif trackable_parent.class.embeds_many?(name)
186
+ trackable_parent._get_relation(name).create!(localize_keys(original))
187
+ else
188
+ raise 'This should never happen. Please report bug!'
189
+ end
190
+ end
191
+
192
+ def trackable_parents_and_trackable
193
+ @trackable_parents_and_trackable ||= traverse_association_chain
194
+ end
195
+
196
+ def traverse_association_chain
197
+ chain = association_chain.dup
198
+ doc = nil
199
+ documents = []
200
+ loop do
201
+ node = chain.shift
202
+ name = node['name']
203
+ doc = if doc.nil?
204
+ # root association. First element of the association chain
205
+ # unscoped is added to remove any default_scope defined in model
206
+ klass = name.classify.constantize
207
+ klass.unscoped.where(_id: node['id']).first
208
+ elsif doc.class.embeds_one?(name)
209
+ doc._get_relation(name)
210
+ elsif doc.class.embeds_many?(name)
211
+ doc._get_relation(name).unscoped.where(_id: node['id']).first
212
+ else
213
+ relation_klass = doc.class.relation_class_of(name) if doc
214
+ relation_klass ||= 'nil'
215
+ raise "Unexpected relation for field '#{name}': #{relation_klass}. This should never happen. Please report bug."
216
+ end
217
+ documents << doc
218
+ break if chain.empty?
219
+ end
220
+ documents
221
+ end
222
+
223
+ def localize_keys(hash)
224
+ klass = association_chain.first['name'].constantize
225
+ if klass.respond_to?(:localized_fields)
226
+ klass.localized_fields.keys.each do |name|
227
+ hash["#{name}_translations"] = hash.delete(name) if hash[name].present?
228
+ end
229
+ end
230
+ hash
231
+ end
232
+
233
+ def prepare_tracked_edits_for_embeds_many(key, value)
234
+ @tracked_edits[:embeds_many] ||= {}
235
+ value[:from] ||= []
236
+ value[:to] ||= []
237
+ modify_ids = value[:from].map { |vv| vv['_id'] }.compact & value[:to].map { |vv| vv['_id'] }.compact
238
+ modify_values = modify_ids.map { |id| { from: value[:from].detect { |vv| vv['_id'] == id }, to: value[:to].detect { |vv| vv['_id'] == id } } }
239
+ modify_values.delete_if { |vv| vv[:from] == vv[:to] }
240
+ ignore_values = modify_values.map { |vv| [vv[:from], vv[:to]] }.flatten
241
+ old_values = value[:from] - value[:to] - ignore_values
242
+ new_values = value[:to] - value[:from] - ignore_values
243
+ @tracked_edits[:embeds_many][key] = { add: new_values, remove: old_values, modify: modify_values }.delete_if { |_, vv| vv.blank? }
244
+ end
245
+ end
246
+ end
247
+ end
@@ -1,5 +1,5 @@
1
- module Mongoid
2
- module History
3
- VERSION = '0.8.0'.freeze
4
- end
5
- end
1
+ module Mongoid
2
+ module History
3
+ VERSION = '0.8.5'.freeze
4
+ end
5
+ end
@@ -1,52 +1,77 @@
1
- require 'easy_diff'
2
- require 'mongoid/compatibility'
3
- require 'mongoid/history/attributes/base'
4
- require 'mongoid/history/attributes/create'
5
- require 'mongoid/history/attributes/update'
6
- require 'mongoid/history/attributes/destroy'
7
- require 'mongoid/history/options'
8
- require 'mongoid/history/version'
9
- require 'mongoid/history/tracker'
10
- require 'mongoid/history/trackable'
11
-
12
- module Mongoid
13
- module History
14
- GLOBAL_TRACK_HISTORY_FLAG = 'mongoid_history_trackable_enabled'.freeze
15
-
16
- class << self
17
- attr_accessor :tracker_class_name
18
- attr_accessor :trackable_class_options
19
- attr_accessor :trackable_settings
20
- attr_accessor :modifier_class_name
21
- attr_accessor :current_user_method
22
-
23
- def disable(&_block)
24
- store[GLOBAL_TRACK_HISTORY_FLAG] = false
25
- yield
26
- ensure
27
- store[GLOBAL_TRACK_HISTORY_FLAG] = true
28
- end
29
-
30
- def enabled?
31
- store[GLOBAL_TRACK_HISTORY_FLAG] != false
32
- end
33
-
34
- def store
35
- defined?(RequestStore) ? RequestStore.store : Thread.current
36
- end
37
-
38
- def default_settings
39
- @default_settings ||= { paranoia_field: 'deleted_at' }
40
- end
41
-
42
- def trackable_class_settings(trackable_class)
43
- trackable_settings[trackable_class.name.to_sym] || default_settings
44
- end
45
- end
46
- end
47
- end
48
-
49
- Mongoid::History.modifier_class_name = 'User'
50
- Mongoid::History.trackable_class_options = {}
51
- Mongoid::History.trackable_settings = {}
52
- Mongoid::History.current_user_method ||= :current_user
1
+ require 'easy_diff'
2
+ require 'mongoid/compatibility'
3
+ require 'mongoid/history/attributes/base'
4
+ require 'mongoid/history/attributes/create'
5
+ require 'mongoid/history/attributes/update'
6
+ require 'mongoid/history/attributes/destroy'
7
+ require 'mongoid/history/options'
8
+ require 'mongoid/history/version'
9
+ require 'mongoid/history/tracker'
10
+ require 'mongoid/history/trackable'
11
+
12
+ module Mongoid
13
+ module History
14
+ GLOBAL_TRACK_HISTORY_FLAG = 'mongoid_history_trackable_enabled'.freeze
15
+
16
+ class << self
17
+ attr_accessor :tracker_class_name
18
+ attr_accessor :trackable_settings
19
+ attr_accessor :modifier_class_name
20
+ attr_accessor :current_user_method
21
+
22
+ def disable
23
+ original_flag = store[GLOBAL_TRACK_HISTORY_FLAG]
24
+ store[GLOBAL_TRACK_HISTORY_FLAG] = false
25
+ yield if block_given?
26
+ ensure
27
+ store[GLOBAL_TRACK_HISTORY_FLAG] = original_flag if block_given?
28
+ end
29
+
30
+ def enable
31
+ original_flag = store[GLOBAL_TRACK_HISTORY_FLAG]
32
+ store[GLOBAL_TRACK_HISTORY_FLAG] = true
33
+ yield if block_given?
34
+ ensure
35
+ store[GLOBAL_TRACK_HISTORY_FLAG] = original_flag if block_given?
36
+ end
37
+
38
+ alias disable! disable
39
+ alias enable! enable
40
+
41
+ def enabled?
42
+ store[GLOBAL_TRACK_HISTORY_FLAG] != false
43
+ end
44
+
45
+ def store
46
+ defined?(RequestStore) ? RequestStore.store : Thread.current
47
+ end
48
+
49
+ def default_settings
50
+ @default_settings ||= { paranoia_field: 'deleted_at' }
51
+ end
52
+
53
+ def trackable_class_settings(trackable_class)
54
+ trackable_settings[trackable_class.name.to_sym] || default_settings
55
+ end
56
+
57
+ def reset!
58
+ Mongoid::History.modifier_class_name = 'User'
59
+ Mongoid::History.trackable_settings = {}
60
+ Mongoid::History.current_user_method ||= :current_user
61
+
62
+ Mongoid.models.each do |model|
63
+ next unless model.included_modules.include? Mongoid::History::Trackable
64
+
65
+ model.singleton_class.class_eval do
66
+ # Inverse of class_attribute
67
+ %i[mongoid_history_options
68
+ mongoid_history_options=
69
+ mongoid_history_options?].each { |m| remove_possible_method(m) }
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ Mongoid::History.reset!
@@ -1 +1 @@
1
- require 'mongoid/history'
1
+ require 'mongoid/history'