mongoid-history 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,338 +1,337 @@
1
- module Mongoid::History
2
- module Trackable
3
- extend ActiveSupport::Concern
4
-
5
- module ClassMethods
6
- def track_history(options={})
7
- scope_name = self.collection_name.to_s.singularize.to_sym
8
- default_options = {
9
- :on => :all,
10
- :except => [:created_at, :updated_at],
11
- :modifier_field => :modifier,
12
- :version_field => :version,
13
- :changes_method => :changes,
14
- :scope => scope_name,
15
- :track_create => false,
16
- :track_update => true,
17
- :track_destroy => false,
18
- }
19
-
20
- options = default_options.merge(options)
21
-
22
- # normalize :except fields to an array of database field strings
23
- options[:except] = [options[:except]] unless options[:except].is_a? Array
24
- options[:except] = options[:except].map{|field| database_field_name(field)}.compact.uniq
25
-
26
- # normalize :on fields to either :all or an array of database field strings
27
- if options[:on] != :all
28
- options[:on] = [options[:on]] unless options[:on].is_a? Array
29
- options[:on] = options[:on].map{|field| database_field_name(field)}.compact.uniq
30
- end
31
-
32
- field options[:version_field].to_sym, :type => Integer
33
-
34
- belongs_to_modifier_options = { :class_name => Mongoid::History.modifier_class_name }
35
- belongs_to_modifier_options[:inverse_of] = options[:modifier_field_inverse_of] if options.has_key?(:modifier_field_inverse_of)
36
- belongs_to options[:modifier_field].to_sym, belongs_to_modifier_options
37
-
38
- include MyInstanceMethods
39
- extend SingletonMethods
40
-
41
- delegate :history_trackable_options, :to => 'self.class'
42
- delegate :track_history?, :to => 'self.class'
43
-
44
- before_update :track_update if options[:track_update]
45
- before_create :track_create if options[:track_create]
46
- before_destroy :track_destroy if options[:track_destroy]
47
-
48
- Mongoid::History.trackable_class_options ||= {}
49
- Mongoid::History.trackable_class_options[scope_name] = options
50
- end
51
-
52
- def track_history?
53
- Mongoid::History.enabled? && Thread.current[track_history_flag] != false
54
- end
55
-
56
- def disable_tracking(&block)
57
- begin
58
- Thread.current[track_history_flag] = false
59
- yield
60
- ensure
61
- Thread.current[track_history_flag] = true
62
- end
63
- end
64
-
65
- def track_history_flag
66
- "mongoid_history_#{self.name.underscore}_trackable_enabled".to_sym
67
- end
68
- end
69
-
70
- module MyInstanceMethods
71
- def history_tracks
72
- @history_tracks ||= Mongoid::History.tracker_class.where(:scope => history_trackable_options[:scope], :association_chain => association_hash)
73
- end
74
-
75
- # undo :from => 1, :to => 5
76
- # undo 4
77
- # undo :last => 10
78
- def undo!(modifier, options_or_version=nil)
79
- versions = get_versions_criteria(options_or_version).to_a
80
- versions.sort!{|v1, v2| v2.version <=> v1.version}
81
-
82
- versions.each do |v|
83
- self.attributes = v.undo_attr(modifier)
84
- end
85
- save!
86
- end
87
-
88
- def redo!(modifier, options_or_version=nil)
89
- versions = get_versions_criteria(options_or_version).to_a
90
- versions.sort!{|v1, v2| v1.version <=> v2.version}
91
-
92
- versions.each do |v|
93
- redo_attr = v.redo_attr(modifier)
94
- self.attributes = redo_attr
95
- end
96
- save!
97
- end
98
-
99
- def get_embedded(name)
100
- self.send(self.class.embedded_alias(name))
101
- end
102
-
103
- def create_embedded(name, value)
104
- self.send("create_#{self.class.embedded_alias(name)}!", value)
105
- end
106
-
107
- private
108
- def get_versions_criteria(options_or_version)
109
- if options_or_version.is_a? Hash
110
- options = options_or_version
111
- if options[:from] && options[:to]
112
- lower = options[:from] >= options[:to] ? options[:to] : options[:from]
113
- upper = options[:from] < options[:to] ? options[:to] : options[:from]
114
- versions = history_tracks.where( :version.in => (lower .. upper).to_a )
115
- elsif options[:last]
116
- versions = history_tracks.limit( options[:last] )
117
- else
118
- raise "Invalid options, please specify (:from / :to) keys or :last key."
119
- end
120
- else
121
- options_or_version = options_or_version.to_a if options_or_version.is_a?(Range)
122
- version_field_name = history_trackable_options[:version_field]
123
- version = options_or_version || self.attributes[version_field_name] || self.attributes[version_field_name.to_s]
124
- version = [ version ].flatten
125
- versions = history_tracks.where(:version.in => version)
126
- end
127
- versions.desc(:version)
128
- end
129
-
130
- def traverse_association_chain(node=self)
131
- list = node._parent ? traverse_association_chain(node._parent) : []
132
- list << association_hash(node)
133
- list
134
- end
135
-
136
- def association_hash(node=self)
137
-
138
- # We prefer to look up associations through the parent record because
139
- # we're assured, through the object creation, it'll exist. Whereas we're not guarenteed
140
- # the child to parent (embedded_in, belongs_to) relation will be defined
141
- if node._parent
142
- meta = node._parent.relations.values.select do |relation|
143
- relation.class_name == node.metadata.class_name.to_s
144
- end.first
145
- end
146
-
147
- # if root node has no meta, and should use class name instead
148
- name = meta ? meta.key.to_s : node.class.name
149
-
150
- ActiveSupport::OrderedHash['name', name, 'id', node.id]
151
- end
152
-
153
- # Returns a Hash of field name to pairs of original and modified values
154
- # for each tracked field for a given action.
155
- #
156
- # @param [ String | Symbol ] action The modification action (:create, :update, :destroy)
157
- #
158
- # @return [ Hash<String, Array<Object>> ] the pairs of original and modified
159
- # values for each field
160
- def modified_attributes_for_action(action)
161
- case action.to_sym
162
- when :destroy then modified_attributes_for_destroy
163
- when :create then modified_attributes_for_create
164
- else modified_attributes_for_update
165
- end
166
- end
167
-
168
- def modified_attributes_for_update
169
- @modified_attributes_for_update ||= self.send(history_trackable_options[:changes_method]).select{|k, v| self.class.tracked_field?(k, :update)}
170
- end
171
-
172
- def modified_attributes_for_create
173
- @modified_attributes_for_create ||= attributes.inject({}) do |h,(k,v)|
174
- h[k] = [nil, v]
175
- h
176
- end.select{|k, v| self.class.tracked_field?(k, :create)}
177
- end
178
-
179
- def modified_attributes_for_destroy
180
- @modified_attributes_for_destroy ||= attributes.inject({}) do |h,(k,v)|
181
- h[k] = [v, nil]
182
- h
183
- end.select{|k, v| self.class.tracked_field?(k, :destroy)}
184
- end
185
-
186
- def history_tracker_attributes(action)
187
- return @history_tracker_attributes if @history_tracker_attributes
188
-
189
- @history_tracker_attributes = {
190
- :association_chain => traverse_association_chain,
191
- :scope => history_trackable_options[:scope],
192
- :modifier => send(history_trackable_options[:modifier_field])
193
- }
194
-
195
- original, modified = transform_changes(modified_attributes_for_action(action))
196
-
197
- @history_tracker_attributes[:original] = original
198
- @history_tracker_attributes[:modified] = modified
199
- @history_tracker_attributes
200
- end
201
-
202
- def track_create
203
- track_history_for_action(:create)
204
- end
205
-
206
- def track_update
207
- track_history_for_action(:update)
208
- end
209
-
210
- def track_destroy
211
- track_history_for_action(:destroy)
212
- end
213
-
214
- def clear_trackable_memoization
215
- @history_tracker_attributes = nil
216
- @modified_attributes_for_create = nil
217
- @modified_attributes_for_update = nil
218
- @history_tracks = nil
219
- end
220
-
221
- def transform_changes(changes)
222
- original = {}
223
- modified = {}
224
- changes.each_pair do |k, v|
225
- o, m = v
226
- original[k] = o unless o.nil?
227
- modified[k] = m unless m.nil?
228
- end
229
-
230
- [ original, modified ]
231
- end
232
-
233
- protected
234
-
235
- def track_history_for_action?(action)
236
- track_history? && !(action.to_sym == :update && modified_attributes_for_update.blank?)
237
- end
238
-
239
- def track_history_for_action(action)
240
- if track_history_for_action?(action)
241
- current_version = (self.send(history_trackable_options[:version_field]) || 0 ) + 1
242
- self.send("#{history_trackable_options[:version_field]}=", current_version)
243
- Mongoid::History.tracker_class.create!(history_tracker_attributes(action.to_sym).merge(version: current_version, action: action.to_s, trackable: self))
244
- end
245
- clear_trackable_memoization
246
- end
247
- end
248
-
249
- module SingletonMethods
250
-
251
- # Whether or not the field should be tracked.
252
- #
253
- # @param [ String | Symbol ] field The name or alias of the field
254
- # @param [ String | Symbol ] action The optional action name (:create, :update, or :destroy)
255
- #
256
- # @return [ Boolean ] whether or not the field is tracked for the given action
257
- def tracked_field?(field, action = :update)
258
- tracked_fields_for_action(action).include? database_field_name(field)
259
- end
260
-
261
- # Retrieves the list of tracked fields for a given action.
262
- #
263
- # @param [ String | Symbol ] action The action name (:create, :update, or :destroy)
264
- #
265
- # @return [ Array < String > ] the list of tracked fields for the given action
266
- def tracked_fields_for_action(action)
267
- case action.to_sym
268
- when :destroy then tracked_fields + reserved_tracked_fields
269
- else tracked_fields
270
- end
271
- end
272
-
273
- # Retrieves the memoized base list of tracked fields, excluding reserved fields.
274
- #
275
- # @return [ Array < String > ] the base list of tracked database field names
276
- def tracked_fields
277
- @tracked_fields ||= self.fields.keys.select do |field|
278
- h = history_trackable_options
279
- (h[:on]==:all || h[:on].include?(field)) && !h[:except].include?(field)
280
- end - reserved_tracked_fields
281
- end
282
-
283
- # Retrieves the memoized list of reserved tracked fields, which are only included for certain actions.
284
- #
285
- # @return [ Array < String > ] the list of reserved database field names
286
- def reserved_tracked_fields
287
- @reserved_tracked_fields ||= ["_id", history_trackable_options[:version_field].to_s, "#{history_trackable_options[:modifier_field]}_id"]
288
- end
289
-
290
- def history_trackable_options
291
- @history_trackable_options ||= Mongoid::History.trackable_class_options[self.collection_name.to_s.singularize.to_sym]
292
- end
293
-
294
- # Indicates whether there is an Embedded::One relation for the given embedded field.
295
- #
296
- # @param [ String | Symbol ] embed The name of the embedded field
297
- #
298
- # @return [ Boolean ] true if there is an Embedded::One relation for the given embedded field
299
- def embeds_one?(embed)
300
- relation_of(embed) == Mongoid::Relations::Embedded::One
301
- end
302
-
303
- # Indicates whether there is an Embedded::Many relation for the given embedded field.
304
- #
305
- # @param [ String | Symbol ] embed The name of the embedded field
306
- #
307
- # @return [ Boolean ] true if there is an Embedded::Many relation for the given embedded field
308
- def embeds_many?(embed)
309
- relation_of(embed) == Mongoid::Relations::Embedded::Many
310
- end
311
-
312
- # Retrieves the database representation of an embedded field name, in case the :store_as option is used.
313
- #
314
- # @param [ String | Symbol ] embed The name or alias of the embedded field
315
- #
316
- # @return [ String ] the database name of the embedded field
317
- def embedded_alias(embed)
318
- embedded_aliases[embed]
319
- end
320
-
321
- protected
322
-
323
- # Retrieves the memoized hash of embedded aliases and their associated database representations.
324
- #
325
- # @return [ Hash < String, String > ] hash of embedded aliases (keys) to database representations (values)
326
- def embedded_aliases
327
- @embedded_aliases ||= relations.inject(HashWithIndifferentAccess.new) do |h,(k,v)|
328
- h[v[:store_as]||k]=k; h
329
- end
330
- end
331
-
332
- def relation_of(embed)
333
- meta = reflect_on_association(embedded_alias(embed))
334
- meta ? meta.relation : nil
335
- end
336
- end
337
- end
338
- end
1
+ module Mongoid::History
2
+ module Trackable
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def track_history(options = {})
7
+ scope_name = collection_name.to_s.singularize.to_sym
8
+ default_options = {
9
+ on: :all,
10
+ except: [:created_at, :updated_at],
11
+ modifier_field: :modifier,
12
+ version_field: :version,
13
+ changes_method: :changes,
14
+ scope: scope_name,
15
+ track_create: false,
16
+ track_update: true,
17
+ track_destroy: false,
18
+ }
19
+
20
+ options = default_options.merge(options)
21
+
22
+ # normalize :except fields to an array of database field strings
23
+ options[:except] = [options[:except]] unless options[:except].is_a? Array
24
+ options[:except] = options[:except].map { |field| database_field_name(field) }.compact.uniq
25
+
26
+ # normalize :on fields to either :all or an array of database field strings
27
+ if options[:on] != :all
28
+ options[:on] = [options[:on]] unless options[:on].is_a? Array
29
+ options[:on] = options[:on].map { |field| database_field_name(field) }.compact.uniq
30
+ end
31
+
32
+ field options[:version_field].to_sym, type: Integer
33
+
34
+ belongs_to_modifier_options = { class_name: Mongoid::History.modifier_class_name }
35
+ belongs_to_modifier_options[:inverse_of] = options[:modifier_field_inverse_of] if options.has_key?(:modifier_field_inverse_of)
36
+ belongs_to options[:modifier_field].to_sym, belongs_to_modifier_options
37
+
38
+ include MyInstanceMethods
39
+ extend SingletonMethods
40
+
41
+ delegate :history_trackable_options, to: 'self.class'
42
+ delegate :track_history?, to: 'self.class'
43
+
44
+ before_update :track_update if options[:track_update]
45
+ before_create :track_create if options[:track_create]
46
+ before_destroy :track_destroy if options[:track_destroy]
47
+
48
+ Mongoid::History.trackable_class_options ||= {}
49
+ Mongoid::History.trackable_class_options[scope_name] = options
50
+ end
51
+
52
+ def track_history?
53
+ Mongoid::History.enabled? && Thread.current[track_history_flag] != false
54
+ end
55
+
56
+ def disable_tracking(&block)
57
+ Thread.current[track_history_flag] = false
58
+ yield
59
+ ensure
60
+ Thread.current[track_history_flag] = true
61
+ end
62
+
63
+ def track_history_flag
64
+ "mongoid_history_#{name.underscore}_trackable_enabled".to_sym
65
+ end
66
+ end
67
+
68
+ module MyInstanceMethods
69
+ def history_tracks
70
+ @history_tracks ||= Mongoid::History.tracker_class.where(scope: history_trackable_options[:scope], association_chain: association_hash)
71
+ end
72
+
73
+ # undo :from => 1, :to => 5
74
+ # undo 4
75
+ # undo :last => 10
76
+ def undo!(modifier = nil, options_or_version = nil)
77
+ versions = get_versions_criteria(options_or_version).to_a
78
+ versions.sort! { |v1, v2| v2.version <=> v1.version }
79
+
80
+ versions.each do |v|
81
+ undo_attr = v.undo_attr(modifier)
82
+ attributes.merge!(undo_attr)
83
+ end
84
+ save!
85
+ end
86
+
87
+ def redo!(modifier = nil, options_or_version = nil)
88
+ versions = get_versions_criteria(options_or_version).to_a
89
+ versions.sort! { |v1, v2| v1.version <=> v2.version }
90
+
91
+ versions.each do |v|
92
+ redo_attr = v.redo_attr(modifier)
93
+ attributes.merge!(redo_attr)
94
+ end
95
+ save!
96
+ end
97
+
98
+ def get_embedded(name)
99
+ send(self.class.embedded_alias(name))
100
+ end
101
+
102
+ def create_embedded(name, value)
103
+ send("create_#{self.class.embedded_alias(name)}!", value)
104
+ end
105
+
106
+ private
107
+
108
+ def get_versions_criteria(options_or_version)
109
+ if options_or_version.is_a? Hash
110
+ options = options_or_version
111
+ if options[:from] && options[:to]
112
+ lower = options[:from] >= options[:to] ? options[:to] : options[:from]
113
+ upper = options[:from] < options[:to] ? options[:to] : options[:from]
114
+ versions = history_tracks.where(:version.in => (lower .. upper).to_a)
115
+ elsif options[:last]
116
+ versions = history_tracks.limit(options[:last])
117
+ else
118
+ raise "Invalid options, please specify (:from / :to) keys or :last key."
119
+ end
120
+ else
121
+ options_or_version = options_or_version.to_a if options_or_version.is_a?(Range)
122
+ version_field_name = history_trackable_options[:version_field]
123
+ version = options_or_version || attributes[version_field_name] || attributes[version_field_name.to_s]
124
+ version = [version].flatten
125
+ versions = history_tracks.where(:version.in => version)
126
+ end
127
+ versions.desc(:version)
128
+ end
129
+
130
+ def traverse_association_chain(node = self)
131
+ list = node._parent ? traverse_association_chain(node._parent) : []
132
+ list << association_hash(node)
133
+ list
134
+ end
135
+
136
+ def association_hash(node = self)
137
+ # We prefer to look up associations through the parent record because
138
+ # we're assured, through the object creation, it'll exist. Whereas we're not guarenteed
139
+ # the child to parent (embedded_in, belongs_to) relation will be defined
140
+ if node._parent
141
+ meta = node._parent.relations.values.select do |relation|
142
+ relation.class_name == node.metadata.class_name.to_s
143
+ end.first
144
+ end
145
+
146
+ # if root node has no meta, and should use class name instead
147
+ name = meta ? meta.key.to_s : node.class.name
148
+
149
+ ActiveSupport::OrderedHash['name', name, 'id', node.id]
150
+ end
151
+
152
+ # Returns a Hash of field name to pairs of original and modified values
153
+ # for each tracked field for a given action.
154
+ #
155
+ # @param [ String | Symbol ] action The modification action (:create, :update, :destroy)
156
+ #
157
+ # @return [ Hash<String, Array<Object>> ] the pairs of original and modified
158
+ # values for each field
159
+ def modified_attributes_for_action(action)
160
+ case action.to_sym
161
+ when :destroy then modified_attributes_for_destroy
162
+ when :create then modified_attributes_for_create
163
+ else modified_attributes_for_update
164
+ end
165
+ end
166
+
167
+ def modified_attributes_for_update
168
+ @modified_attributes_for_update ||= send(history_trackable_options[:changes_method]).select { |k, v| self.class.tracked_field?(k, :update) }
169
+ end
170
+
171
+ def modified_attributes_for_create
172
+ @modified_attributes_for_create ||= attributes.inject({}) do |h, (k, v)|
173
+ h[k] = [nil, v]
174
+ h
175
+ end.select { |k, v| self.class.tracked_field?(k, :create) }
176
+ end
177
+
178
+ def modified_attributes_for_destroy
179
+ @modified_attributes_for_destroy ||= attributes.inject({}) do |h, (k, v)|
180
+ h[k] = [v, nil]
181
+ h
182
+ end.select { |k, v| self.class.tracked_field?(k, :destroy) }
183
+ end
184
+
185
+ def history_tracker_attributes(action)
186
+ return @history_tracker_attributes if @history_tracker_attributes
187
+
188
+ @history_tracker_attributes = {
189
+ association_chain: traverse_association_chain,
190
+ scope: history_trackable_options[:scope],
191
+ modifier: send(history_trackable_options[:modifier_field])
192
+ }
193
+
194
+ original, modified = transform_changes(modified_attributes_for_action(action))
195
+
196
+ @history_tracker_attributes[:original] = original
197
+ @history_tracker_attributes[:modified] = modified
198
+ @history_tracker_attributes
199
+ end
200
+
201
+ def track_create
202
+ track_history_for_action(:create)
203
+ end
204
+
205
+ def track_update
206
+ track_history_for_action(:update)
207
+ end
208
+
209
+ def track_destroy
210
+ track_history_for_action(:destroy)
211
+ end
212
+
213
+ def clear_trackable_memoization
214
+ @history_tracker_attributes = nil
215
+ @modified_attributes_for_create = nil
216
+ @modified_attributes_for_update = nil
217
+ @history_tracks = nil
218
+ end
219
+
220
+ def transform_changes(changes)
221
+ original = {}
222
+ modified = {}
223
+ changes.each_pair do |k, v|
224
+ o, m = v
225
+ original[k] = o unless o.nil?
226
+ modified[k] = m unless m.nil?
227
+ end
228
+
229
+ [original, modified]
230
+ end
231
+
232
+ protected
233
+
234
+ def track_history_for_action?(action)
235
+ track_history? && !(action.to_sym == :update && modified_attributes_for_update.blank?)
236
+ end
237
+
238
+ def track_history_for_action(action)
239
+ if track_history_for_action?(action)
240
+ current_version = (send(history_trackable_options[:version_field]) || 0) + 1
241
+ send("#{history_trackable_options[:version_field]}=", current_version)
242
+ Mongoid::History.tracker_class.create!(history_tracker_attributes(action.to_sym).merge(version: current_version, action: action.to_s, trackable: self))
243
+ end
244
+ clear_trackable_memoization
245
+ end
246
+ end
247
+
248
+ module SingletonMethods
249
+ # Whether or not the field should be tracked.
250
+ #
251
+ # @param [ String | Symbol ] field The name or alias of the field
252
+ # @param [ String | Symbol ] action The optional action name (:create, :update, or :destroy)
253
+ #
254
+ # @return [ Boolean ] whether or not the field is tracked for the given action
255
+ def tracked_field?(field, action = :update)
256
+ tracked_fields_for_action(action).include? database_field_name(field)
257
+ end
258
+
259
+ # Retrieves the list of tracked fields for a given action.
260
+ #
261
+ # @param [ String | Symbol ] action The action name (:create, :update, or :destroy)
262
+ #
263
+ # @return [ Array < String > ] the list of tracked fields for the given action
264
+ def tracked_fields_for_action(action)
265
+ case action.to_sym
266
+ when :destroy then tracked_fields + reserved_tracked_fields
267
+ else tracked_fields
268
+ end
269
+ end
270
+
271
+ # Retrieves the memoized base list of tracked fields, excluding reserved fields.
272
+ #
273
+ # @return [ Array < String > ] the base list of tracked database field names
274
+ def tracked_fields
275
+ @tracked_fields ||= fields.keys.select do |field|
276
+ h = history_trackable_options
277
+ (h[:on] == :all || h[:on].include?(field)) && !h[:except].include?(field)
278
+ end - reserved_tracked_fields
279
+ end
280
+
281
+ # Retrieves the memoized list of reserved tracked fields, which are only included for certain actions.
282
+ #
283
+ # @return [ Array < String > ] the list of reserved database field names
284
+ def reserved_tracked_fields
285
+ @reserved_tracked_fields ||= ["_id", history_trackable_options[:version_field].to_s, "#{history_trackable_options[:modifier_field]}_id"]
286
+ end
287
+
288
+ def history_trackable_options
289
+ @history_trackable_options ||= Mongoid::History.trackable_class_options[collection_name.to_s.singularize.to_sym]
290
+ end
291
+
292
+ # Indicates whether there is an Embedded::One relation for the given embedded field.
293
+ #
294
+ # @param [ String | Symbol ] embed The name of the embedded field
295
+ #
296
+ # @return [ Boolean ] true if there is an Embedded::One relation for the given embedded field
297
+ def embeds_one?(embed)
298
+ relation_of(embed) == Mongoid::Relations::Embedded::One
299
+ end
300
+
301
+ # Indicates whether there is an Embedded::Many relation for the given embedded field.
302
+ #
303
+ # @param [ String | Symbol ] embed The name of the embedded field
304
+ #
305
+ # @return [ Boolean ] true if there is an Embedded::Many relation for the given embedded field
306
+ def embeds_many?(embed)
307
+ relation_of(embed) == Mongoid::Relations::Embedded::Many
308
+ end
309
+
310
+ # Retrieves the database representation of an embedded field name, in case the :store_as option is used.
311
+ #
312
+ # @param [ String | Symbol ] embed The name or alias of the embedded field
313
+ #
314
+ # @return [ String ] the database name of the embedded field
315
+ def embedded_alias(embed)
316
+ embedded_aliases[embed]
317
+ end
318
+
319
+ protected
320
+
321
+ # Retrieves the memoized hash of embedded aliases and their associated database representations.
322
+ #
323
+ # @return [ Hash < String, String > ] hash of embedded aliases (keys) to database representations (values)
324
+ def embedded_aliases
325
+ @embedded_aliases ||= relations.inject(HashWithIndifferentAccess.new) do |h, (k, v)|
326
+ h[v[:store_as] || k] = k
327
+ h
328
+ end
329
+ end
330
+
331
+ def relation_of(embed)
332
+ meta = reflect_on_association(embedded_alias(embed))
333
+ meta ? meta.relation : nil
334
+ end
335
+ end
336
+ end
337
+ end