mongoid-history 0.8.3 → 0.8.5

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  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 -99
  9. data/CHANGELOG.md +173 -163
  10. data/CONTRIBUTING.md +117 -118
  11. data/Dangerfile +1 -1
  12. data/Gemfile +49 -40
  13. data/LICENSE.txt +20 -20
  14. data/README.md +609 -608
  15. data/RELEASING.md +66 -67
  16. data/Rakefile +24 -24
  17. data/UPGRADING.md +53 -53
  18. data/lib/mongoid/history/attributes/base.rb +72 -72
  19. data/lib/mongoid/history/attributes/create.rb +45 -45
  20. data/lib/mongoid/history/attributes/destroy.rb +34 -34
  21. data/lib/mongoid/history/attributes/update.rb +104 -104
  22. data/lib/mongoid/history/options.rb +177 -177
  23. data/lib/mongoid/history/trackable.rb +588 -583
  24. data/lib/mongoid/history/tracker.rb +247 -247
  25. data/lib/mongoid/history/version.rb +5 -5
  26. data/lib/mongoid/history.rb +77 -77
  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 -65
  30. data/perf/gc_suite.rb +21 -21
  31. data/spec/integration/embedded_in_polymorphic_spec.rb +112 -112
  32. data/spec/integration/integration_spec.rb +976 -976
  33. data/spec/integration/multi_relation_spec.rb +47 -47
  34. data/spec/integration/multiple_trackers_spec.rb +68 -68
  35. data/spec/integration/nested_embedded_documents_spec.rb +64 -64
  36. data/spec/integration/nested_embedded_documents_tracked_in_parent_spec.rb +124 -124
  37. data/spec/integration/nested_embedded_polymorphic_documents_spec.rb +115 -115
  38. data/spec/integration/subclasses_spec.rb +47 -47
  39. data/spec/integration/track_history_order_spec.rb +84 -84
  40. data/spec/integration/validation_failure_spec.rb +76 -76
  41. data/spec/spec_helper.rb +32 -30
  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 -12
  45. data/spec/unit/attributes/base_spec.rb +141 -141
  46. data/spec/unit/attributes/create_spec.rb +342 -342
  47. data/spec/unit/attributes/destroy_spec.rb +228 -228
  48. data/spec/unit/attributes/update_spec.rb +342 -342
  49. data/spec/unit/callback_options_spec.rb +165 -165
  50. data/spec/unit/embedded_methods_spec.rb +87 -87
  51. data/spec/unit/history_spec.rb +58 -58
  52. data/spec/unit/my_instance_methods_spec.rb +555 -555
  53. data/spec/unit/options_spec.rb +365 -365
  54. data/spec/unit/singleton_methods_spec.rb +406 -406
  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 -987
  58. data/spec/unit/tracker_spec.rb +190 -190
  59. metadata +9 -7
  60. data/.travis.yml +0 -36
@@ -1,104 +1,104 @@
1
- module Mongoid
2
- module History
3
- module Attributes
4
- class Update < ::Mongoid::History::Attributes::Base
5
- # @example when both an attribute `foo` and a child's attribute `nested_bar.baz` are changed
6
- #
7
- # {
8
- # 'foo' => ['foo_before_changes', 'foo_after_changes']
9
- # 'nested_bar.baz' => ['nested_bar_baz_before_changes', 'nested_bar_baz_after_changes']
10
- # }
11
- # }
12
- #
13
- # @return [Hash<String, Array<(?,?)>>] Hash of changes
14
- def attributes
15
- changes_from_parent.deep_merge(changes_from_children)
16
- end
17
-
18
- private
19
-
20
- def changes_from_parent
21
- parent_changes = {}
22
- changes.each do |k, v|
23
- change_value = begin
24
- if trackable_class.tracked_embeds_one?(k)
25
- embeds_one_changes_from_parent(k, v)
26
- elsif trackable_class.tracked_embeds_many?(k)
27
- embeds_many_changes_from_parent(k, v)
28
- elsif trackable_class.tracked?(k, :update)
29
- { k => format_field(k, v) } unless v.all?(&:blank?)
30
- end
31
- end
32
- parent_changes.merge!(change_value) if change_value.present?
33
- end
34
- parent_changes
35
- end
36
-
37
- def changes_from_children
38
- embeds_one_changes_from_embedded_documents
39
- end
40
-
41
- # Retrieve the list of changes applied directly to the nested documents
42
- #
43
- # @example when a child's name is changed from "todd" to "mario"
44
- #
45
- # child = Child.new(name: 'todd')
46
- # Parent.create(child: child)
47
- # child.name = "Mario"
48
- #
49
- # embeds_one_changes_from_embedded_documents # when called from "Parent"
50
- # # => { "child.name"=>["todd", "mario"] }
51
- #
52
- # @return [Hash<String, Array<(?,?)>] changes of embeds_ones from embedded documents
53
- def embeds_one_changes_from_embedded_documents
54
- embedded_doc_changes = {}
55
- trackable_class.tracked_embeds_one.each do |rel|
56
- rel_class = trackable_class.relation_class_of(rel)
57
- paranoia_field = Mongoid::History.trackable_class_settings(rel_class)[:paranoia_field]
58
- paranoia_field = rel_class.aliased_fields.key(paranoia_field) || paranoia_field
59
- rel = aliased_fields.key(rel) || rel
60
- obj = trackable.send(rel)
61
- next if !obj || (obj.respond_to?(paranoia_field) && obj.public_send(paranoia_field).present?)
62
-
63
- obj.changes.each do |k, v|
64
- embedded_doc_changes["#{rel}.#{k}"] = [v.first, v.last]
65
- end
66
- end
67
- embedded_doc_changes
68
- end
69
-
70
- # @param [String] relation
71
- # @param [String] value
72
- #
73
- # @return [Hash<String, Array<(?,?)>>]
74
- def embeds_one_changes_from_parent(relation, value)
75
- relation = trackable_class.database_field_name(relation)
76
- relation_class = trackable_class.relation_class_of(relation)
77
- paranoia_field = Mongoid::History.trackable_class_settings(relation_class)[:paranoia_field]
78
- original_value = value[0][paranoia_field].present? ? {} : format_embeds_one_relation(relation, value[0])
79
- modified_value = value[1][paranoia_field].present? ? {} : format_embeds_one_relation(relation, value[1])
80
- return if original_value == modified_value
81
-
82
- { relation => [original_value, modified_value] }
83
- end
84
-
85
- # @param [String] relation
86
- # @param [String] value
87
- #
88
- # @return [Hash<Array<(?,?)>>]
89
- def embeds_many_changes_from_parent(relation, value)
90
- relation = trackable_class.database_field_name(relation)
91
- relation_class = trackable_class.relation_class_of(relation)
92
- paranoia_field = Mongoid::History.trackable_class_settings(relation_class)[:paranoia_field]
93
- original_value = value[0].reject { |rel| rel[paranoia_field].present? }
94
- .map { |v_attrs| format_embeds_many_relation(relation, v_attrs) }
95
- modified_value = value[1].reject { |rel| rel[paranoia_field].present? }
96
- .map { |v_attrs| format_embeds_many_relation(relation, v_attrs) }
97
- return if original_value == modified_value
98
-
99
- { relation => [original_value, modified_value] }
100
- end
101
- end
102
- end
103
- end
104
- end
1
+ module Mongoid
2
+ module History
3
+ module Attributes
4
+ class Update < ::Mongoid::History::Attributes::Base
5
+ # @example when both an attribute `foo` and a child's attribute `nested_bar.baz` are changed
6
+ #
7
+ # {
8
+ # 'foo' => ['foo_before_changes', 'foo_after_changes']
9
+ # 'nested_bar.baz' => ['nested_bar_baz_before_changes', 'nested_bar_baz_after_changes']
10
+ # }
11
+ # }
12
+ #
13
+ # @return [Hash<String, Array<(?,?)>>] Hash of changes
14
+ def attributes
15
+ changes_from_parent.deep_merge(changes_from_children)
16
+ end
17
+
18
+ private
19
+
20
+ def changes_from_parent
21
+ parent_changes = {}
22
+ changes.each do |k, v|
23
+ change_value = begin
24
+ if trackable_class.tracked_embeds_one?(k)
25
+ embeds_one_changes_from_parent(k, v)
26
+ elsif trackable_class.tracked_embeds_many?(k)
27
+ embeds_many_changes_from_parent(k, v)
28
+ elsif trackable_class.tracked?(k, :update)
29
+ { k => format_field(k, v) } unless v.all?(&:blank?)
30
+ end
31
+ end
32
+ parent_changes.merge!(change_value) if change_value.present?
33
+ end
34
+ parent_changes
35
+ end
36
+
37
+ def changes_from_children
38
+ embeds_one_changes_from_embedded_documents
39
+ end
40
+
41
+ # Retrieve the list of changes applied directly to the nested documents
42
+ #
43
+ # @example when a child's name is changed from "todd" to "mario"
44
+ #
45
+ # child = Child.new(name: 'todd')
46
+ # Parent.create(child: child)
47
+ # child.name = "Mario"
48
+ #
49
+ # embeds_one_changes_from_embedded_documents # when called from "Parent"
50
+ # # => { "child.name"=>["todd", "mario"] }
51
+ #
52
+ # @return [Hash<String, Array<(?,?)>] changes of embeds_ones from embedded documents
53
+ def embeds_one_changes_from_embedded_documents
54
+ embedded_doc_changes = {}
55
+ trackable_class.tracked_embeds_one.each do |rel|
56
+ rel_class = trackable_class.relation_class_of(rel)
57
+ paranoia_field = Mongoid::History.trackable_class_settings(rel_class)[:paranoia_field]
58
+ paranoia_field = rel_class.aliased_fields.key(paranoia_field) || paranoia_field
59
+ rel = aliased_fields.key(rel) || rel
60
+ obj = trackable.send(rel)
61
+ next if !obj || (obj.respond_to?(paranoia_field) && obj.public_send(paranoia_field).present?)
62
+
63
+ obj.changes.each do |k, v|
64
+ embedded_doc_changes["#{rel}.#{k}"] = [v.first, v.last]
65
+ end
66
+ end
67
+ embedded_doc_changes
68
+ end
69
+
70
+ # @param [String] relation
71
+ # @param [String] value
72
+ #
73
+ # @return [Hash<String, Array<(?,?)>>]
74
+ def embeds_one_changes_from_parent(relation, value)
75
+ relation = trackable_class.database_field_name(relation)
76
+ relation_class = trackable_class.relation_class_of(relation)
77
+ paranoia_field = Mongoid::History.trackable_class_settings(relation_class)[:paranoia_field]
78
+ original_value = value[0][paranoia_field].present? ? {} : format_embeds_one_relation(relation, value[0])
79
+ modified_value = value[1][paranoia_field].present? ? {} : format_embeds_one_relation(relation, value[1])
80
+ return if original_value == modified_value
81
+
82
+ { relation => [original_value, modified_value] }
83
+ end
84
+
85
+ # @param [String] relation
86
+ # @param [String] value
87
+ #
88
+ # @return [Hash<Array<(?,?)>>]
89
+ def embeds_many_changes_from_parent(relation, value)
90
+ relation = trackable_class.database_field_name(relation)
91
+ relation_class = trackable_class.relation_class_of(relation)
92
+ paranoia_field = Mongoid::History.trackable_class_settings(relation_class)[:paranoia_field]
93
+ original_value = value[0].reject { |rel| rel[paranoia_field].present? }
94
+ .map { |v_attrs| format_embeds_many_relation(relation, v_attrs) }
95
+ modified_value = value[1].reject { |rel| rel[paranoia_field].present? }
96
+ .map { |v_attrs| format_embeds_many_relation(relation, v_attrs) }
97
+ return if original_value == modified_value
98
+
99
+ { relation => [original_value, modified_value] }
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -1,177 +1,177 @@
1
- module Mongoid
2
- module History
3
- class Options
4
- attr_reader :trackable, :options
5
-
6
- def initialize(trackable, opts = {})
7
- @trackable = trackable
8
- @options = default_options.merge(opts)
9
- end
10
-
11
- def scope
12
- trackable.collection_name.to_s.singularize.to_sym
13
- end
14
-
15
- def prepared
16
- return @prepared if @prepared
17
- @prepared = options.dup
18
- prepare_skipped_fields
19
- prepare_formatted_fields
20
- parse_tracked_fields_and_relations
21
- @prepared
22
- end
23
-
24
- private
25
-
26
- def default_options
27
- { on: :all,
28
- except: %i[created_at updated_at],
29
- tracker_class_name: nil,
30
- modifier_field: :modifier,
31
- version_field: :version,
32
- changes_method: :changes,
33
- scope: scope,
34
- track_create: true,
35
- track_update: true,
36
- track_destroy: true,
37
- format: nil }
38
- end
39
-
40
- # Sets the :except attributes and relations in `options` to be an [ Array <String> ]
41
- # The attribute names and relations are stored by their `database_field_name`s
42
- # Removes the `nil` and duplicate entries from skipped attributes/relations list
43
- def prepare_skipped_fields
44
- # normalize :except fields to an array of database field strings
45
- @prepared[:except] = Array(@prepared[:except])
46
- @prepared[:except] = @prepared[:except].map { |field| trackable.database_field_name(field) }.compact.uniq
47
- end
48
-
49
- def prepare_formatted_fields
50
- formats = {}
51
-
52
- if @prepared[:format].class == Hash
53
- @prepared[:format].each do |field, format|
54
- next if field.nil?
55
-
56
- field = trackable.database_field_name(field)
57
-
58
- if format.class == Hash && trackable.embeds_many?(field)
59
- relation_class = trackable.relation_class_of(field)
60
- formats[field] = format.inject({}) { |a, e| a.merge(relation_class.database_field_name(e.first) => e.last) }
61
- elsif format.class == Hash && trackable.embeds_one?(field)
62
- relation_class = trackable.relation_class_of(field)
63
- formats[field] = format.inject({}) { |a, e| a.merge(relation_class.database_field_name(e.first) => e.last) }
64
- else
65
- formats[field] = format
66
- end
67
- end
68
- end
69
-
70
- @prepared[:format] = formats
71
- end
72
-
73
- def parse_tracked_fields_and_relations
74
- # case `options[:on]`
75
- # when `posts: [:id, :title]`, then it will convert it to `[[:posts, [:id, :title]]]`
76
- # when `:foo`, then `[:foo]`
77
- # when `[:foo, { posts: [:id, :title] }]`, then return as is
78
- @prepared[:on] = Array(@prepared[:on])
79
-
80
- @prepared[:on] = @prepared[:on].map { |opt| opt == :all ? :fields : opt }
81
-
82
- if @prepared[:on].include?(:fields)
83
- @prepared[:on] = @prepared[:on].reject { |opt| opt == :fields }
84
- @prepared[:on] = @prepared[:on] | trackable.fields.keys.map(&:to_sym) - reserved_fields.map(&:to_sym)
85
- end
86
-
87
- if @prepared[:on].include?(:embedded_relations)
88
- @prepared[:on] = @prepared[:on].reject { |opt| opt == :embedded_relations }
89
- @prepared[:on] = @prepared[:on] | trackable.embedded_relations.keys
90
- end
91
-
92
- @prepared[:fields] = []
93
- @prepared[:dynamic] = []
94
- @prepared[:relations] = { embeds_one: {}, embeds_many: {} }
95
-
96
- @prepared[:on].each do |option|
97
- if option.is_a?(Hash)
98
- option.each { |k, v| split_and_categorize(k => v) }
99
- else
100
- split_and_categorize(option)
101
- end
102
- end
103
- end
104
-
105
- def split_and_categorize(field_and_options)
106
- field = get_database_field_name(field_and_options)
107
- field_options = get_field_options(field_and_options)
108
- categorize_tracked_option(field, field_options)
109
- end
110
-
111
- # Returns the database_field_name key for tracked option
112
- #
113
- # @param [ String | Symbol | Array | Hash ] option The field or relation name to track
114
- #
115
- # @return [ String ] the database field name for tracked option
116
- def get_database_field_name(option)
117
- key = if option.is_a?(Hash)
118
- option.keys.first
119
- elsif option.is_a?(Array)
120
- option.first
121
- end
122
- trackable.database_field_name(key || option)
123
- end
124
-
125
- # Returns the tracked attributes for embedded relations, otherwise `nil`
126
- #
127
- # @param [ String | Symbol | Array | Hash ] option The field or relation name to track
128
- #
129
- # @return [ nil | Array <String | Symbol> ] the list of tracked fields for embedded relation
130
- def get_field_options(option)
131
- if option.is_a?(Hash)
132
- option.values.first
133
- elsif option.is_a?(Array)
134
- option.last
135
- end
136
- end
137
-
138
- # Tracks the passed option under:
139
- # `fields`
140
- # `dynamic`
141
- # `relations -> embeds_one` or
142
- # `relations -> embeds_many`
143
- #
144
- # @param [ String ] field The database field name of field or relation to track
145
- # @param [ nil | Array <String | Symbol> ] field_options The tracked fields for embedded relations
146
- def categorize_tracked_option(field, field_options = nil)
147
- return if @prepared[:except].include?(field)
148
- return if reserved_fields.include?(field)
149
-
150
- field_options = Array(field_options)
151
-
152
- if trackable.embeds_one?(field)
153
- track_relation(field, :embeds_one, field_options)
154
- elsif trackable.embeds_many?(field)
155
- track_relation(field, :embeds_many, field_options)
156
- elsif trackable.fields.keys.include?(field)
157
- @prepared[:fields] << field
158
- else
159
- @prepared[:dynamic] << field
160
- end
161
- end
162
-
163
- def track_relation(field, kind, field_options)
164
- relation_class = trackable.relation_class_of(field)
165
- @prepared[:relations][kind][field] = if field_options.blank?
166
- relation_class.fields.keys
167
- else
168
- %w[_id] | field_options.map { |opt| relation_class.database_field_name(opt) }
169
- end
170
- end
171
-
172
- def reserved_fields
173
- @reserved_fields ||= ['_id', '_type', @prepared[:version_field].to_s, "#{@prepared[:modifier_field]}_id"]
174
- end
175
- end
176
- end
177
- end
1
+ module Mongoid
2
+ module History
3
+ class Options
4
+ attr_reader :trackable, :options
5
+
6
+ def initialize(trackable, opts = {})
7
+ @trackable = trackable
8
+ @options = default_options.merge(opts)
9
+ end
10
+
11
+ def scope
12
+ trackable.collection_name.to_s.singularize.to_sym
13
+ end
14
+
15
+ def prepared
16
+ return @prepared if @prepared
17
+ @prepared = options.dup
18
+ prepare_skipped_fields
19
+ prepare_formatted_fields
20
+ parse_tracked_fields_and_relations
21
+ @prepared
22
+ end
23
+
24
+ private
25
+
26
+ def default_options
27
+ { on: :all,
28
+ except: %i[created_at updated_at],
29
+ tracker_class_name: nil,
30
+ modifier_field: :modifier,
31
+ version_field: :version,
32
+ changes_method: :changes,
33
+ scope: scope,
34
+ track_create: true,
35
+ track_update: true,
36
+ track_destroy: true,
37
+ format: nil }
38
+ end
39
+
40
+ # Sets the :except attributes and relations in `options` to be an [ Array <String> ]
41
+ # The attribute names and relations are stored by their `database_field_name`s
42
+ # Removes the `nil` and duplicate entries from skipped attributes/relations list
43
+ def prepare_skipped_fields
44
+ # normalize :except fields to an array of database field strings
45
+ @prepared[:except] = Array(@prepared[:except])
46
+ @prepared[:except] = @prepared[:except].map { |field| trackable.database_field_name(field) }.compact.uniq
47
+ end
48
+
49
+ def prepare_formatted_fields
50
+ formats = {}
51
+
52
+ if @prepared[:format].class == Hash
53
+ @prepared[:format].each do |field, format|
54
+ next if field.nil?
55
+
56
+ field = trackable.database_field_name(field)
57
+
58
+ if format.class == Hash && trackable.embeds_many?(field)
59
+ relation_class = trackable.relation_class_of(field)
60
+ formats[field] = format.inject({}) { |a, e| a.merge(relation_class.database_field_name(e.first) => e.last) }
61
+ elsif format.class == Hash && trackable.embeds_one?(field)
62
+ relation_class = trackable.relation_class_of(field)
63
+ formats[field] = format.inject({}) { |a, e| a.merge(relation_class.database_field_name(e.first) => e.last) }
64
+ else
65
+ formats[field] = format
66
+ end
67
+ end
68
+ end
69
+
70
+ @prepared[:format] = formats
71
+ end
72
+
73
+ def parse_tracked_fields_and_relations
74
+ # case `options[:on]`
75
+ # when `posts: [:id, :title]`, then it will convert it to `[[:posts, [:id, :title]]]`
76
+ # when `:foo`, then `[:foo]`
77
+ # when `[:foo, { posts: [:id, :title] }]`, then return as is
78
+ @prepared[:on] = Array(@prepared[:on])
79
+
80
+ @prepared[:on] = @prepared[:on].map { |opt| opt == :all ? :fields : opt }
81
+
82
+ if @prepared[:on].include?(:fields)
83
+ @prepared[:on] = @prepared[:on].reject { |opt| opt == :fields }
84
+ @prepared[:on] = @prepared[:on] | trackable.fields.keys.map(&:to_sym) - reserved_fields.map(&:to_sym)
85
+ end
86
+
87
+ if @prepared[:on].include?(:embedded_relations)
88
+ @prepared[:on] = @prepared[:on].reject { |opt| opt == :embedded_relations }
89
+ @prepared[:on] = @prepared[:on] | trackable.embedded_relations.keys
90
+ end
91
+
92
+ @prepared[:fields] = []
93
+ @prepared[:dynamic] = []
94
+ @prepared[:relations] = { embeds_one: {}, embeds_many: {} }
95
+
96
+ @prepared[:on].each do |option|
97
+ if option.is_a?(Hash)
98
+ option.each { |k, v| split_and_categorize(k => v) }
99
+ else
100
+ split_and_categorize(option)
101
+ end
102
+ end
103
+ end
104
+
105
+ def split_and_categorize(field_and_options)
106
+ field = get_database_field_name(field_and_options)
107
+ field_options = get_field_options(field_and_options)
108
+ categorize_tracked_option(field, field_options)
109
+ end
110
+
111
+ # Returns the database_field_name key for tracked option
112
+ #
113
+ # @param [ String | Symbol | Array | Hash ] option The field or relation name to track
114
+ #
115
+ # @return [ String ] the database field name for tracked option
116
+ def get_database_field_name(option)
117
+ key = if option.is_a?(Hash)
118
+ option.keys.first
119
+ elsif option.is_a?(Array)
120
+ option.first
121
+ end
122
+ trackable.database_field_name(key || option)
123
+ end
124
+
125
+ # Returns the tracked attributes for embedded relations, otherwise `nil`
126
+ #
127
+ # @param [ String | Symbol | Array | Hash ] option The field or relation name to track
128
+ #
129
+ # @return [ nil | Array <String | Symbol> ] the list of tracked fields for embedded relation
130
+ def get_field_options(option)
131
+ if option.is_a?(Hash)
132
+ option.values.first
133
+ elsif option.is_a?(Array)
134
+ option.last
135
+ end
136
+ end
137
+
138
+ # Tracks the passed option under:
139
+ # `fields`
140
+ # `dynamic`
141
+ # `relations -> embeds_one` or
142
+ # `relations -> embeds_many`
143
+ #
144
+ # @param [ String ] field The database field name of field or relation to track
145
+ # @param [ nil | Array <String | Symbol> ] field_options The tracked fields for embedded relations
146
+ def categorize_tracked_option(field, field_options = nil)
147
+ return if @prepared[:except].include?(field)
148
+ return if reserved_fields.include?(field)
149
+
150
+ field_options = Array(field_options)
151
+
152
+ if trackable.embeds_one?(field)
153
+ track_relation(field, :embeds_one, field_options)
154
+ elsif trackable.embeds_many?(field)
155
+ track_relation(field, :embeds_many, field_options)
156
+ elsif trackable.fields.keys.include?(field)
157
+ @prepared[:fields] << field
158
+ else
159
+ @prepared[:dynamic] << field
160
+ end
161
+ end
162
+
163
+ def track_relation(field, kind, field_options)
164
+ relation_class = trackable.relation_class_of(field)
165
+ @prepared[:relations][kind][field] = if field_options.blank?
166
+ relation_class.fields.keys
167
+ else
168
+ %w[_id] | field_options.map { |opt| relation_class.database_field_name(opt) }
169
+ end
170
+ end
171
+
172
+ def reserved_fields
173
+ @reserved_fields ||= ['_id', '_type', @prepared[:version_field].to_s, "#{@prepared[:modifier_field]}_id"]
174
+ end
175
+ end
176
+ end
177
+ end