mongoid-history 0.5.0 → 0.6.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 +4 -4
- data/.rubocop_todo.yml +16 -3
- data/.travis.yml +23 -8
- data/CHANGELOG.md +20 -6
- data/Dangerfile +1 -0
- data/Gemfile +22 -1
- data/LICENSE.txt +1 -1
- data/README.md +130 -18
- data/lib/mongoid/history.rb +32 -15
- data/lib/mongoid/history/attributes/base.rb +31 -0
- data/lib/mongoid/history/attributes/create.rb +52 -0
- data/lib/mongoid/history/attributes/destroy.rb +36 -0
- data/lib/mongoid/history/attributes/update.rb +43 -0
- data/lib/mongoid/history/options.rb +153 -0
- data/lib/mongoid/history/trackable.rb +170 -72
- data/lib/mongoid/history/tracker.rb +39 -23
- data/lib/mongoid/history/version.rb +1 -1
- data/mongoid-history.gemspec +0 -8
- data/spec/integration/embedded_in_polymorphic_spec.rb +8 -0
- data/spec/integration/integration_spec.rb +4 -0
- data/spec/integration/nested_embedded_documents_spec.rb +13 -6
- data/spec/integration/nested_embedded_polymorphic_documents_spec.rb +24 -24
- data/spec/spec_helper.rb +6 -0
- data/spec/unit/attributes/base_spec.rb +54 -0
- data/spec/unit/attributes/create_spec.rb +315 -0
- data/spec/unit/attributes/destroy_spec.rb +218 -0
- data/spec/unit/attributes/update_spec.rb +210 -0
- data/spec/unit/embedded_methods_spec.rb +69 -0
- data/spec/unit/history_spec.rb +35 -0
- data/spec/unit/my_instance_methods_spec.rb +485 -0
- data/spec/unit/options_spec.rb +294 -0
- data/spec/unit/singleton_methods_spec.rb +338 -0
- data/spec/unit/store/default_store_spec.rb +11 -0
- data/spec/unit/store/request_store_spec.rb +13 -0
- data/spec/unit/trackable_spec.rb +335 -68
- data/spec/unit/tracker_spec.rb +153 -0
- metadata +31 -102
@@ -0,0 +1,31 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module History
|
3
|
+
module Attributes
|
4
|
+
class Base
|
5
|
+
attr_reader :trackable
|
6
|
+
|
7
|
+
def initialize(trackable)
|
8
|
+
@trackable = trackable
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def trackable_class
|
14
|
+
@trackable_class ||= trackable.class
|
15
|
+
end
|
16
|
+
|
17
|
+
def aliased_fields
|
18
|
+
@aliased_fields ||= trackable_class.aliased_fields
|
19
|
+
end
|
20
|
+
|
21
|
+
def changes_method
|
22
|
+
trackable_class.history_trackable_options[:changes_method]
|
23
|
+
end
|
24
|
+
|
25
|
+
def changes
|
26
|
+
trackable.send(changes_method)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module History
|
3
|
+
module Attributes
|
4
|
+
class Create < ::Mongoid::History::Attributes::Base
|
5
|
+
def attributes
|
6
|
+
@attributes = {}
|
7
|
+
trackable.attributes.each do |k, v|
|
8
|
+
next unless trackable_class.tracked_field?(k, :create)
|
9
|
+
modified = if changes[k]
|
10
|
+
changes[k].class == Array ? changes[k].last : changes[k]
|
11
|
+
else
|
12
|
+
v
|
13
|
+
end
|
14
|
+
@attributes[k] = [nil, modified]
|
15
|
+
end
|
16
|
+
insert_embeds_one_changes
|
17
|
+
insert_embeds_many_changes
|
18
|
+
@attributes
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def insert_embeds_one_changes
|
24
|
+
trackable_class.tracked_embeds_one.each do |rel|
|
25
|
+
rel_class = trackable_class.embeds_one_class(rel)
|
26
|
+
paranoia_field = Mongoid::History.trackable_class_settings(rel_class)[:paranoia_field]
|
27
|
+
paranoia_field = rel_class.aliased_fields.key(paranoia_field) || paranoia_field
|
28
|
+
permitted_attrs = trackable_class.tracked_embeds_one_attributes(rel)
|
29
|
+
rel = aliased_fields.key(rel) || rel
|
30
|
+
obj = trackable.send(rel)
|
31
|
+
next if !obj || (obj.respond_to?(paranoia_field) && obj.public_send(paranoia_field).present?)
|
32
|
+
@attributes[rel] = [nil, obj.attributes.slice(*permitted_attrs)]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def insert_embeds_many_changes
|
37
|
+
trackable_class.tracked_embeds_many.each do |rel|
|
38
|
+
rel_class = trackable_class.embeds_many_class(rel)
|
39
|
+
paranoia_field = Mongoid::History.trackable_class_settings(rel_class)[:paranoia_field]
|
40
|
+
paranoia_field = rel_class.aliased_fields.key(paranoia_field) || paranoia_field
|
41
|
+
permitted_attrs = trackable_class.tracked_embeds_many_attributes(rel)
|
42
|
+
rel = aliased_fields.key(rel) || rel
|
43
|
+
@attributes[rel] = [nil,
|
44
|
+
trackable.send(rel)
|
45
|
+
.reject { |obj| obj.respond_to?(paranoia_field) && obj.public_send(paranoia_field).present? }
|
46
|
+
.map { |obj| obj.attributes.slice(*permitted_attrs) }]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module History
|
3
|
+
module Attributes
|
4
|
+
class Destroy < ::Mongoid::History::Attributes::Base
|
5
|
+
def attributes
|
6
|
+
@attributes = {}
|
7
|
+
trackable.attributes.each { |k, v| @attributes[k] = [v, nil] if trackable_class.tracked_field?(k, :destroy) }
|
8
|
+
insert_embeds_one_changes
|
9
|
+
insert_embeds_many_changes
|
10
|
+
@attributes
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def insert_embeds_one_changes
|
16
|
+
trackable_class.tracked_embeds_one
|
17
|
+
.map { |rel| aliased_fields.key(rel) || rel }
|
18
|
+
.each do |rel|
|
19
|
+
permitted_attrs = trackable_class.tracked_embeds_one_attributes(rel)
|
20
|
+
obj = trackable.send(rel)
|
21
|
+
@attributes[rel] = [obj.attributes.slice(*permitted_attrs), nil] if obj
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def insert_embeds_many_changes
|
26
|
+
trackable_class.tracked_embeds_many
|
27
|
+
.map { |rel| aliased_fields.key(rel) || rel }
|
28
|
+
.each do |rel|
|
29
|
+
permitted_attrs = trackable_class.tracked_embeds_many_attributes(rel)
|
30
|
+
@attributes[rel] = [trackable.send(rel).map { |obj| obj.attributes.slice(*permitted_attrs) }, nil]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module History
|
3
|
+
module Attributes
|
4
|
+
class Update < ::Mongoid::History::Attributes::Base
|
5
|
+
def attributes
|
6
|
+
@attributes = {}
|
7
|
+
changes.each do |k, v|
|
8
|
+
if trackable_class.tracked_embeds_one?(k)
|
9
|
+
insert_embeds_one_changes(k, v)
|
10
|
+
elsif trackable_class.tracked_embeds_many?(k)
|
11
|
+
insert_embeds_many_changes(k, v)
|
12
|
+
elsif trackable_class.tracked?(k, :update)
|
13
|
+
@attributes[k] = v
|
14
|
+
end
|
15
|
+
end
|
16
|
+
@attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def insert_embeds_one_changes(relation, value)
|
22
|
+
relation = trackable_class.database_field_name(relation)
|
23
|
+
permitted_attrs = trackable_class.tracked_embeds_one_attributes(relation)
|
24
|
+
relation_class = trackable_class.embeds_one_class(relation)
|
25
|
+
paranoia_field = Mongoid::History.trackable_class_settings(relation_class)[:paranoia_field]
|
26
|
+
@attributes[relation] = []
|
27
|
+
@attributes[relation][0] = value[0][paranoia_field].present? ? {} : value[0].slice(*permitted_attrs)
|
28
|
+
@attributes[relation][1] = value[1][paranoia_field].present? ? {} : value[1].slice(*permitted_attrs)
|
29
|
+
end
|
30
|
+
|
31
|
+
def insert_embeds_many_changes(relation, value)
|
32
|
+
relation = trackable_class.database_field_name(relation)
|
33
|
+
permitted_attrs = trackable_class.tracked_embeds_many_attributes(relation)
|
34
|
+
relation_class = trackable_class.embeds_many_class(relation)
|
35
|
+
paranoia_field = Mongoid::History.trackable_class_settings(relation_class)[:paranoia_field]
|
36
|
+
@attributes[relation] = []
|
37
|
+
@attributes[relation][0] = value[0].reject { |rel| rel[paranoia_field].present? }.map { |v_attrs| v_attrs.slice(*permitted_attrs) }
|
38
|
+
@attributes[relation][1] = value[1].reject { |rel| rel[paranoia_field].present? }.map { |v_attrs| v_attrs.slice(*permitted_attrs) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module History
|
3
|
+
class Options
|
4
|
+
attr_reader :trackable, :options
|
5
|
+
|
6
|
+
def initialize(trackable)
|
7
|
+
@trackable = trackable
|
8
|
+
end
|
9
|
+
|
10
|
+
def scope
|
11
|
+
trackable.collection_name.to_s.singularize.to_sym
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse(options = {})
|
15
|
+
@options = default_options.merge(options)
|
16
|
+
prepare_skipped_fields
|
17
|
+
parse_tracked_fields_and_relations
|
18
|
+
@options
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def default_options
|
24
|
+
@default_options ||=
|
25
|
+
{ on: :all,
|
26
|
+
except: [:created_at, :updated_at],
|
27
|
+
tracker_class_name: nil,
|
28
|
+
modifier_field: :modifier,
|
29
|
+
version_field: :version,
|
30
|
+
changes_method: :changes,
|
31
|
+
scope: scope,
|
32
|
+
track_create: false,
|
33
|
+
track_update: true,
|
34
|
+
track_destroy: false }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Sets the :except attributes and relations in `options` to be an [ Array <String> ]
|
38
|
+
# The attribute names and relations are stored by their `database_field_name`s
|
39
|
+
# Removes the `nil` and duplicate entries from skipped attributes/relations list
|
40
|
+
def prepare_skipped_fields
|
41
|
+
# normalize :except fields to an array of database field strings
|
42
|
+
@options[:except] = Array(options[:except])
|
43
|
+
@options[:except] = options[:except].map { |field| trackable.database_field_name(field) }.compact.uniq
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse_tracked_fields_and_relations
|
47
|
+
# case `options[:on]`
|
48
|
+
# when `posts: [:id, :title]`, then it will convert it to `[[:posts, [:id, :title]]]`
|
49
|
+
# when `:foo`, then `[:foo]`
|
50
|
+
# when `[:foo, { posts: [:id, :title] }]`, then return as is
|
51
|
+
@options[:on] = Array(options[:on])
|
52
|
+
|
53
|
+
# # :all is just an alias to :fields for now, to support existing users of `mongoid-history`
|
54
|
+
# # In future, :all will track all the fields and relations of trackable class
|
55
|
+
if options[:on].include?(:all)
|
56
|
+
warn "[DEPRECATION] Use :fields instead of :all to track all fields in class #{trackable}.\n\
|
57
|
+
Going forward, :all will track all the fields and relations for the class"
|
58
|
+
end
|
59
|
+
|
60
|
+
@options[:on] = options[:on].map { |opt| (opt == :all) ? :fields : opt }
|
61
|
+
|
62
|
+
if options[:on].include?(:fields)
|
63
|
+
@options[:on] = options[:on].reject { |opt| opt == :fields }
|
64
|
+
@options[:on] = options[:on] | trackable.fields.keys.map(&:to_sym) - reserved_fields.map(&:to_sym)
|
65
|
+
end
|
66
|
+
|
67
|
+
@options[:fields] = []
|
68
|
+
@options[:dynamic] = []
|
69
|
+
@options[:relations] = { embeds_one: {}, embeds_many: {} }
|
70
|
+
|
71
|
+
options[:on].each do |option|
|
72
|
+
field = get_database_field_name(option)
|
73
|
+
field_options = get_field_options(option)
|
74
|
+
categorize_tracked_option(field, field_options)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the database_field_name key for tracked option
|
79
|
+
#
|
80
|
+
# @param [ String | Symbol | Array | Hash ] option The field or relation name to track
|
81
|
+
#
|
82
|
+
# @return [ String ] the database field name for tracked option
|
83
|
+
def get_database_field_name(option)
|
84
|
+
key = if option.is_a?(Hash)
|
85
|
+
option.keys.first
|
86
|
+
elsif option.is_a?(Array)
|
87
|
+
option.first
|
88
|
+
end
|
89
|
+
trackable.database_field_name(key || option)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the tracked attributes for embedded relations, otherwise `nil`
|
93
|
+
#
|
94
|
+
# @param [ String | Symbol | Array | Hash ] option The field or relation name to track
|
95
|
+
#
|
96
|
+
# @return [ nil | Array <String | Symbol> ] the list of tracked fields for embedded relation
|
97
|
+
def get_field_options(option)
|
98
|
+
if option.is_a?(Hash)
|
99
|
+
option.values.first
|
100
|
+
elsif option.is_a?(Array)
|
101
|
+
option.last
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Tracks the passed option under:
|
106
|
+
# `fields`
|
107
|
+
# `dynamic`
|
108
|
+
# `relations -> embeds_one` or
|
109
|
+
# `relations -> embeds_many`
|
110
|
+
#
|
111
|
+
# @param [ String ] field The database field name of field or relation to track
|
112
|
+
# @param [ nil | Array <String | Symbol> ] field_options The tracked fields for embedded relations
|
113
|
+
def categorize_tracked_option(field, field_options = nil)
|
114
|
+
return if options[:except].include?(field)
|
115
|
+
return if reserved_fields.include?(field)
|
116
|
+
|
117
|
+
field_options = Array(field_options)
|
118
|
+
|
119
|
+
if trackable.embeds_one?(field)
|
120
|
+
track_embeds_one(field, field_options)
|
121
|
+
elsif trackable.embeds_many?(field)
|
122
|
+
track_embeds_many(field, field_options)
|
123
|
+
elsif trackable.fields.keys.include?(field)
|
124
|
+
@options[:fields] << field
|
125
|
+
else
|
126
|
+
@options[:dynamic] << field
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def track_embeds_one(field, field_options)
|
131
|
+
relation_class = trackable.embeds_one_class(field)
|
132
|
+
@options[:relations][:embeds_one][field] = if field_options.blank?
|
133
|
+
relation_class.fields.keys
|
134
|
+
else
|
135
|
+
%w(_id) | field_options.map { |opt| relation_class.database_field_name(opt) }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def track_embeds_many(field, field_options)
|
140
|
+
relation_class = trackable.embeds_many_class(field)
|
141
|
+
@options[:relations][:embeds_many][field] = if field_options.blank?
|
142
|
+
relation_class.fields.keys
|
143
|
+
else
|
144
|
+
%w(_id) | field_options.map { |opt| relation_class.database_field_name(opt) }
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def reserved_fields
|
149
|
+
@reserved_fields ||= ['_id', '_type', options[:version_field].to_s, "#{options[:modifier_field]}_id"]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -5,30 +5,10 @@ module Mongoid
|
|
5
5
|
|
6
6
|
module ClassMethods
|
7
7
|
def track_history(options = {})
|
8
|
-
|
9
|
-
default_options = {
|
10
|
-
on: :all,
|
11
|
-
except: [:created_at, :updated_at],
|
12
|
-
modifier_field: :modifier,
|
13
|
-
version_field: :version,
|
14
|
-
changes_method: :changes,
|
15
|
-
scope: scope_name,
|
16
|
-
track_create: false,
|
17
|
-
track_update: true,
|
18
|
-
track_destroy: false
|
19
|
-
}
|
20
|
-
|
21
|
-
options = default_options.merge(options)
|
8
|
+
extend EmbeddedMethods
|
22
9
|
|
23
|
-
|
24
|
-
options
|
25
|
-
options[:except] = options[:except].map { |field| database_field_name(field) }.compact.uniq
|
26
|
-
|
27
|
-
# normalize :on fields to either :all or an array of database field strings
|
28
|
-
if options[:on] != :all
|
29
|
-
options[:on] = [options[:on]] unless options[:on].is_a? Array
|
30
|
-
options[:on] = options[:on].map { |field| database_field_name(field) }.compact.uniq
|
31
|
-
end
|
10
|
+
options_parser = Mongoid::History::Options.new(self)
|
11
|
+
options = options_parser.parse(options)
|
32
12
|
|
33
13
|
field options[:version_field].to_sym, type: Integer
|
34
14
|
|
@@ -47,11 +27,19 @@ module Mongoid
|
|
47
27
|
before_destroy :track_destroy if options[:track_destroy]
|
48
28
|
|
49
29
|
Mongoid::History.trackable_class_options ||= {}
|
50
|
-
Mongoid::History.trackable_class_options[
|
30
|
+
Mongoid::History.trackable_class_options[options_parser.scope] = options
|
31
|
+
end
|
32
|
+
|
33
|
+
def history_settings(options = {})
|
34
|
+
options = Mongoid::History.default_settings.merge(options.symbolize_keys)
|
35
|
+
options = options.slice(*Mongoid::History.default_settings.keys)
|
36
|
+
options[:paranoia_field] = aliased_fields[options[:paranoia_field].to_s] || options[:paranoia_field].to_s
|
37
|
+
Mongoid::History.trackable_settings ||= {}
|
38
|
+
Mongoid::History.trackable_settings[name.to_sym] = options
|
51
39
|
end
|
52
40
|
|
53
41
|
def track_history?
|
54
|
-
Mongoid::History.enabled? &&
|
42
|
+
Mongoid::History.enabled? && Mongoid::History.store[track_history_flag] != false
|
55
43
|
end
|
56
44
|
|
57
45
|
def dynamic_enabled?
|
@@ -59,20 +47,25 @@ module Mongoid
|
|
59
47
|
end
|
60
48
|
|
61
49
|
def disable_tracking(&_block)
|
62
|
-
|
50
|
+
Mongoid::History.store[track_history_flag] = false
|
63
51
|
yield
|
64
52
|
ensure
|
65
|
-
|
53
|
+
Mongoid::History.store[track_history_flag] = true
|
66
54
|
end
|
67
55
|
|
68
56
|
def track_history_flag
|
69
57
|
"mongoid_history_#{name.underscore}_trackable_enabled".to_sym
|
70
58
|
end
|
59
|
+
|
60
|
+
def tracker_class
|
61
|
+
klass = history_trackable_options[:tracker_class_name] || Mongoid::History.tracker_class_name
|
62
|
+
klass.is_a?(Class) ? klass : klass.to_s.camelize.constantize
|
63
|
+
end
|
71
64
|
end
|
72
65
|
|
73
66
|
module MyInstanceMethods
|
74
67
|
def history_tracks
|
75
|
-
@history_tracks ||=
|
68
|
+
@history_tracks ||= self.class.tracker_class.where(
|
76
69
|
scope: related_scope,
|
77
70
|
association_chain: association_hash
|
78
71
|
).asc(:version)
|
@@ -212,21 +205,15 @@ module Mongoid
|
|
212
205
|
end
|
213
206
|
|
214
207
|
def modified_attributes_for_update
|
215
|
-
@modified_attributes_for_update ||=
|
208
|
+
@modified_attributes_for_update ||= Mongoid::History::Attributes::Update.new(self).attributes
|
216
209
|
end
|
217
210
|
|
218
211
|
def modified_attributes_for_create
|
219
|
-
@modified_attributes_for_create ||=
|
220
|
-
h[k] = [nil, v]
|
221
|
-
h
|
222
|
-
end.select { |k, _| self.class.tracked_field?(k, :create) }
|
212
|
+
@modified_attributes_for_create ||= Mongoid::History::Attributes::Create.new(self).attributes
|
223
213
|
end
|
224
214
|
|
225
215
|
def modified_attributes_for_destroy
|
226
|
-
@modified_attributes_for_destroy ||=
|
227
|
-
h[k] = [v, nil]
|
228
|
-
h
|
229
|
-
end.select { |k, _| self.class.tracked_field?(k, :destroy) }
|
216
|
+
@modified_attributes_for_destroy ||= Mongoid::History::Attributes::Destroy.new(self).attributes
|
230
217
|
end
|
231
218
|
|
232
219
|
def history_tracker_attributes(action)
|
@@ -286,13 +273,99 @@ module Mongoid
|
|
286
273
|
if track_history_for_action?(action)
|
287
274
|
current_version = (send(history_trackable_options[:version_field]) || 0) + 1
|
288
275
|
send("#{history_trackable_options[:version_field]}=", current_version)
|
289
|
-
|
276
|
+
self.class.tracker_class.create!(history_tracker_attributes(action.to_sym).merge(version: current_version, action: action.to_s, trackable: self))
|
290
277
|
end
|
291
278
|
clear_trackable_memoization
|
292
279
|
end
|
293
280
|
end
|
294
281
|
|
282
|
+
module EmbeddedMethods
|
283
|
+
# Indicates whether there is an Embedded::One relation for the given embedded field.
|
284
|
+
#
|
285
|
+
# @param [ String | Symbol ] embed The name of the embedded field
|
286
|
+
#
|
287
|
+
# @return [ Boolean ] true if there is an Embedded::One relation for the given embedded field
|
288
|
+
def embeds_one?(embed)
|
289
|
+
relation_of(embed) == Mongoid::Relations::Embedded::One
|
290
|
+
end
|
291
|
+
|
292
|
+
# Return the class for embeds_one relation
|
293
|
+
#
|
294
|
+
# @param [ String ] field The database field name for embedded relation
|
295
|
+
#
|
296
|
+
# @return [ nil | Constant ]
|
297
|
+
def embeds_one_class(field)
|
298
|
+
@embeds_one_class ||= {}
|
299
|
+
return @embeds_one_class[field] if @embeds_one_class.key?(field)
|
300
|
+
field_alias = aliased_fields.key(field)
|
301
|
+
relation = relations
|
302
|
+
.select { |_, v| v.relation == Mongoid::Relations::Embedded::One }
|
303
|
+
.detect { |rel_k, _| rel_k == field_alias }
|
304
|
+
@embeds_one_class[field] = relation && relation.last.class_name.constantize
|
305
|
+
end
|
306
|
+
|
307
|
+
# Indicates whether there is an Embedded::Many relation for the given embedded field.
|
308
|
+
#
|
309
|
+
# @param [ String | Symbol ] embed The name of the embedded field
|
310
|
+
#
|
311
|
+
# @return [ Boolean ] true if there is an Embedded::Many relation for the given embedded field
|
312
|
+
def embeds_many?(embed)
|
313
|
+
relation_of(embed) == Mongoid::Relations::Embedded::Many
|
314
|
+
end
|
315
|
+
|
316
|
+
# Return the class for embeds_many relation
|
317
|
+
#
|
318
|
+
# @param [ String ] field The database field name for embedded relation
|
319
|
+
#
|
320
|
+
# @return [ nil | Constant ]
|
321
|
+
def embeds_many_class(field)
|
322
|
+
@embeds_many_class ||= {}
|
323
|
+
return @embeds_many_class[field] if @embeds_many_class.key?(field)
|
324
|
+
field_alias = aliased_fields.key(field)
|
325
|
+
relation = relations
|
326
|
+
.select { |_, v| v.relation == Mongoid::Relations::Embedded::Many }
|
327
|
+
.detect { |rel_k, _| rel_k == field_alias }
|
328
|
+
@embeds_many_class[field] = relation && relation.last.class_name.constantize
|
329
|
+
end
|
330
|
+
|
331
|
+
# Retrieves the database representation of an embedded field name, in case the :store_as option is used.
|
332
|
+
#
|
333
|
+
# @param [ String | Symbol ] embed The name or alias of the embedded field
|
334
|
+
#
|
335
|
+
# @return [ String ] the database name of the embedded field
|
336
|
+
def embedded_alias(embed)
|
337
|
+
embedded_aliases[embed]
|
338
|
+
end
|
339
|
+
|
340
|
+
protected
|
341
|
+
|
342
|
+
# Retrieves the memoized hash of embedded aliases and their associated database representations.
|
343
|
+
#
|
344
|
+
# @return [ Hash < String, String > ] hash of embedded aliases (keys) to database representations (values)
|
345
|
+
def embedded_aliases
|
346
|
+
@embedded_aliases ||= relations.inject(HashWithIndifferentAccess.new) do |h, (k, v)|
|
347
|
+
h[v[:store_as] || k] = k
|
348
|
+
h
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
def relation_of(embed)
|
353
|
+
meta = reflect_on_association(embedded_alias(embed))
|
354
|
+
meta ? meta.relation : nil
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
295
358
|
module SingletonMethods
|
359
|
+
# Whether or not the field or embedded relation should be tracked.
|
360
|
+
#
|
361
|
+
# @param [ String | Symbol ] field_or_relation The name or alias of the field OR the name of embedded relation
|
362
|
+
# @param [ String | Symbol ] action The optional action name (:create, :update, or :destroy)
|
363
|
+
#
|
364
|
+
# @return [ Boolean ] whether or not the field or embedded relation is tracked for the given action
|
365
|
+
def tracked?(field_or_relation, action = :update)
|
366
|
+
tracked_field?(field_or_relation, action) || tracked_relation?(field_or_relation)
|
367
|
+
end
|
368
|
+
|
296
369
|
# Whether or not the field should be tracked.
|
297
370
|
#
|
298
371
|
# @param [ String | Symbol ] field The name or alias of the field
|
@@ -309,7 +382,9 @@ module Mongoid
|
|
309
382
|
#
|
310
383
|
# @return [ Boolean ] whether or not the field is dynamic
|
311
384
|
def dynamic_field?(field)
|
312
|
-
dynamic_enabled? &&
|
385
|
+
dynamic_enabled? &&
|
386
|
+
!fields.keys.include?(database_field_name(field)) &&
|
387
|
+
!embedded_relations.map { |_, v| v.key }.include?(database_field_name(field))
|
313
388
|
end
|
314
389
|
|
315
390
|
# Retrieves the list of tracked fields for a given action.
|
@@ -328,10 +403,7 @@ module Mongoid
|
|
328
403
|
#
|
329
404
|
# @return [ Array < String > ] the base list of tracked database field names
|
330
405
|
def tracked_fields
|
331
|
-
@tracked_fields ||=
|
332
|
-
h = history_trackable_options
|
333
|
-
(h[:on] == :all ? fields.keys : h[:on]) - h[:except] - reserved_tracked_fields
|
334
|
-
end
|
406
|
+
@tracked_fields ||= history_trackable_options[:fields] + history_trackable_options[:dynamic]
|
335
407
|
end
|
336
408
|
|
337
409
|
# Retrieves the memoized list of reserved tracked fields, which are only included for certain actions.
|
@@ -341,52 +413,78 @@ module Mongoid
|
|
341
413
|
@reserved_tracked_fields ||= ['_id', history_trackable_options[:version_field].to_s, "#{history_trackable_options[:modifier_field]}_id"]
|
342
414
|
end
|
343
415
|
|
344
|
-
|
345
|
-
|
416
|
+
# Whether or not the relation should be tracked.
|
417
|
+
#
|
418
|
+
# @param [ String | Symbol ] relation The name of the relation
|
419
|
+
#
|
420
|
+
# @return [ Boolean ] whether or not the relation is tracked
|
421
|
+
def tracked_relation?(relation)
|
422
|
+
tracked_embeds_one?(relation) || tracked_embeds_many?(relation)
|
346
423
|
end
|
347
424
|
|
348
|
-
#
|
425
|
+
# Whether or not the embeds_one relation should be tracked.
|
349
426
|
#
|
350
|
-
# @param [ String | Symbol ]
|
427
|
+
# @param [ String | Symbol ] relation The name of the embeds_one relation
|
351
428
|
#
|
352
|
-
# @return [ Boolean ]
|
353
|
-
def
|
354
|
-
|
429
|
+
# @return [ Boolean ] whether or not the embeds_one relation is tracked
|
430
|
+
def tracked_embeds_one?(relation)
|
431
|
+
tracked_embeds_one.include?(database_field_name(relation))
|
355
432
|
end
|
356
433
|
|
357
|
-
#
|
358
|
-
#
|
359
|
-
# @param [ String | Symbol ] embed The name of the embedded field
|
434
|
+
# Retrieves the memoized list of tracked embeds_one relations
|
360
435
|
#
|
361
|
-
# @return [
|
362
|
-
def
|
363
|
-
|
436
|
+
# @return [ Array < String > ] the list of tracked embeds_one relations
|
437
|
+
def tracked_embeds_one
|
438
|
+
@tracked_embeds_one ||= begin
|
439
|
+
reflect_on_all_associations(:embeds_one)
|
440
|
+
.map(&:key)
|
441
|
+
.select { |rel| history_trackable_options[:relations][:embeds_one].include? rel }
|
442
|
+
end
|
364
443
|
end
|
365
444
|
|
366
|
-
|
445
|
+
def tracked_embeds_one_attributes(relation)
|
446
|
+
history_trackable_options[:relations][:embeds_one][database_field_name(relation)]
|
447
|
+
end
|
448
|
+
|
449
|
+
# Whether or not the embeds_many relation should be tracked.
|
367
450
|
#
|
368
|
-
# @param [ String | Symbol ]
|
451
|
+
# @param [ String | Symbol ] relation The name of the embeds_many relation
|
369
452
|
#
|
370
|
-
# @return [
|
371
|
-
def
|
372
|
-
|
453
|
+
# @return [ Boolean ] whether or not the embeds_many relation is tracked
|
454
|
+
def tracked_embeds_many?(relation)
|
455
|
+
tracked_embeds_many.include?(database_field_name(relation))
|
373
456
|
end
|
374
457
|
|
375
|
-
|
376
|
-
|
377
|
-
# Retrieves the memoized hash of embedded aliases and their associated database representations.
|
458
|
+
# Retrieves the memoized list of tracked embeds_many relations
|
378
459
|
#
|
379
|
-
# @return [
|
380
|
-
def
|
381
|
-
@
|
382
|
-
|
383
|
-
|
460
|
+
# @return [ Array < String > ] the list of tracked embeds_many relations
|
461
|
+
def tracked_embeds_many
|
462
|
+
@tracked_embeds_many ||= begin
|
463
|
+
reflect_on_all_associations(:embeds_many)
|
464
|
+
.map(&:key)
|
465
|
+
.select { |rel| history_trackable_options[:relations][:embeds_many].include? rel }
|
384
466
|
end
|
385
467
|
end
|
386
468
|
|
387
|
-
def
|
388
|
-
|
389
|
-
|
469
|
+
def tracked_embeds_many_attributes(relation)
|
470
|
+
history_trackable_options[:relations][:embeds_many][database_field_name(relation)]
|
471
|
+
end
|
472
|
+
|
473
|
+
def trackable_scope
|
474
|
+
collection_name.to_s.singularize.to_sym
|
475
|
+
end
|
476
|
+
|
477
|
+
def history_trackable_options
|
478
|
+
@history_trackable_options ||= Mongoid::History.trackable_class_options[trackable_scope]
|
479
|
+
end
|
480
|
+
|
481
|
+
def clear_trackable_memoization
|
482
|
+
@reserved_tracked_fields = nil
|
483
|
+
@history_trackable_options = nil
|
484
|
+
@trackable_settings = nil
|
485
|
+
@tracked_fields = nil
|
486
|
+
@tracked_embeds_one = nil
|
487
|
+
@tracked_embeds_many = nil
|
390
488
|
end
|
391
489
|
end
|
392
490
|
end
|