mongoid 4.0.0.alpha2 → 4.0.0.beta1
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/CHANGELOG.md +55 -0
- data/README.md +3 -3
- data/lib/config/locales/en.yml +13 -0
- data/lib/mongoid.rb +3 -1
- data/lib/mongoid/atomic.rb +1 -1
- data/lib/mongoid/atomic/paths/embedded/many.rb +1 -1
- data/lib/mongoid/atomic/paths/embedded/one.rb +1 -1
- data/lib/mongoid/attributes.rb +23 -1
- data/lib/mongoid/attributes/processing.rb +1 -1
- data/lib/mongoid/composable.rb +3 -2
- data/lib/mongoid/contextual/command.rb +0 -26
- data/lib/mongoid/contextual/geo_near.rb +1 -1
- data/lib/mongoid/contextual/mongo.rb +6 -29
- data/lib/mongoid/contextual/text_search.rb +3 -5
- data/lib/mongoid/criteria.rb +1 -1
- data/lib/mongoid/criteria/modifiable.rb +27 -7
- data/lib/mongoid/criteria/permission.rb +70 -0
- data/lib/mongoid/document.rb +5 -6
- data/lib/mongoid/errors.rb +2 -0
- data/lib/mongoid/errors/document_not_destroyed.rb +25 -0
- data/lib/mongoid/errors/readonly_document.rb +24 -0
- data/lib/mongoid/extensions/boolean.rb +1 -0
- data/lib/mongoid/extensions/hash.rb +1 -1
- data/lib/mongoid/factory.rb +5 -3
- data/lib/mongoid/fields.rb +32 -0
- data/lib/mongoid/fields/localized.rb +1 -1
- data/lib/mongoid/fields/standard.rb +1 -1
- data/lib/mongoid/findable.rb +1 -0
- data/lib/mongoid/interceptable.rb +11 -6
- data/lib/mongoid/log_subscriber.rb +34 -1
- data/lib/mongoid/persistable/deletable.rb +1 -0
- data/lib/mongoid/persistable/destroyable.rb +7 -2
- data/lib/mongoid/persistable/updatable.rb +27 -26
- data/lib/mongoid/query_cache.rb +246 -0
- data/lib/mongoid/railties/database.rake +4 -26
- data/lib/mongoid/relations.rb +8 -22
- data/lib/mongoid/relations/accessors.rb +0 -3
- data/lib/mongoid/relations/binding.rb +1 -1
- data/lib/mongoid/relations/bindings/embedded/in.rb +1 -1
- data/lib/mongoid/relations/eager.rb +5 -6
- data/lib/mongoid/relations/eager/base.rb +97 -5
- data/lib/mongoid/relations/eager/belongs_to.rb +1 -0
- data/lib/mongoid/relations/eager/has_and_belongs_to_many.rb +16 -9
- data/lib/mongoid/relations/eager/has_many.rb +1 -0
- data/lib/mongoid/relations/eager/has_one.rb +1 -0
- data/lib/mongoid/relations/embedded/batchable.rb +1 -1
- data/lib/mongoid/relations/embedded/in.rb +4 -4
- data/lib/mongoid/relations/embedded/many.rb +7 -5
- data/lib/mongoid/relations/embedded/one.rb +1 -1
- data/lib/mongoid/relations/macros.rb +1 -0
- data/lib/mongoid/relations/marshalable.rb +3 -3
- data/lib/mongoid/relations/proxy.rb +12 -10
- data/lib/mongoid/relations/referenced/in.rb +2 -2
- data/lib/mongoid/relations/referenced/many.rb +9 -9
- data/lib/mongoid/relations/referenced/many_to_many.rb +7 -7
- data/lib/mongoid/relations/referenced/one.rb +4 -4
- data/lib/mongoid/{state.rb → stateful.rb} +13 -1
- data/lib/mongoid/tasks/database.rake +31 -0
- data/lib/mongoid/tasks/database.rb +107 -0
- data/lib/mongoid/threaded.rb +0 -47
- data/lib/mongoid/validatable/uniqueness.rb +4 -16
- data/lib/mongoid/version.rb +1 -1
- data/lib/rails/generators/mongoid/config/templates/mongoid.yml +0 -3
- data/lib/rails/mongoid.rb +0 -124
- data/spec/app/models/edit.rb +5 -0
- data/spec/app/models/even.rb +7 -0
- data/spec/app/models/line_item.rb +1 -1
- data/spec/app/models/note.rb +2 -0
- data/spec/app/models/odd.rb +7 -0
- data/spec/app/models/record.rb +5 -0
- data/spec/app/models/wiki_page.rb +1 -1
- data/spec/mongoid/attributes_spec.rb +76 -1
- data/spec/mongoid/changeable_spec.rb +6 -2
- data/spec/mongoid/contextual/mongo_spec.rb +3 -1
- data/spec/mongoid/contextual/text_search_spec.rb +3 -1
- data/spec/mongoid/criteria/modifiable_spec.rb +192 -0
- data/spec/mongoid/criteria_spec.rb +6 -2
- data/spec/mongoid/errors/document_not_destroyed_spec.rb +33 -0
- data/spec/mongoid/errors/readonly_document_spec.rb +29 -0
- data/spec/mongoid/fields/localized_spec.rb +15 -0
- data/spec/mongoid/fields_spec.rb +88 -2
- data/spec/mongoid/log_subscriber_spec.rb +3 -3
- data/spec/mongoid/persistable/deletable_spec.rb +14 -1
- data/spec/mongoid/persistable/destroyable_spec.rb +45 -1
- data/spec/mongoid/persistable/savable_spec.rb +34 -5
- data/spec/mongoid/query_cache_spec.rb +197 -0
- data/spec/mongoid/relations/bindings/embedded/in_spec.rb +2 -2
- data/spec/mongoid/relations/builders/referenced/many_spec.rb +1 -1
- data/spec/mongoid/relations/eager/has_and_belongs_to_many_spec.rb +11 -37
- data/spec/mongoid/relations/eager/has_one_spec.rb +1 -1
- data/spec/mongoid/relations/embedded/in_spec.rb +1 -1
- data/spec/mongoid/relations/embedded/many_spec.rb +10 -10
- data/spec/mongoid/relations/embedded/one_spec.rb +10 -2
- data/spec/mongoid/relations/referenced/in_spec.rb +1 -1
- data/spec/mongoid/relations/referenced/many_spec.rb +37 -2
- data/spec/mongoid/relations/touchable_spec.rb +20 -0
- data/spec/mongoid/{state_spec.rb → stateful_spec.rb} +26 -1
- data/spec/mongoid/tasks/database_rake_spec.rb +285 -0
- data/spec/mongoid/tasks/database_spec.rb +148 -0
- data/spec/mongoid/validatable/uniqueness_spec.rb +7 -0
- data/spec/rails/mongoid_spec.rb +0 -316
- data/spec/spec_helper.rb +1 -0
- metadata +30 -8
@@ -0,0 +1,246 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid
|
3
|
+
|
4
|
+
# A cache of database queries on a per-request basis.
|
5
|
+
#
|
6
|
+
# @since 4.0.0
|
7
|
+
module QueryCache
|
8
|
+
class << self
|
9
|
+
|
10
|
+
# Get the cached queries.
|
11
|
+
#
|
12
|
+
# @example Get the cached queries from the current thread.
|
13
|
+
# QueryCache.cache_table
|
14
|
+
#
|
15
|
+
# @return [ Hash ] The hash of cached queries.
|
16
|
+
#
|
17
|
+
# @since 4.0.0
|
18
|
+
def cache_table
|
19
|
+
Thread.current["[mongoid]:query_cache"] ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Clear the query cache.
|
23
|
+
#
|
24
|
+
# @example Clear the cache.
|
25
|
+
# QueryCache.clear_cache
|
26
|
+
#
|
27
|
+
# @return [ nil ] Always nil.
|
28
|
+
#
|
29
|
+
# @since 4.0.0
|
30
|
+
def clear_cache
|
31
|
+
Thread.current["[mongoid]:query_cache"] = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
# Set whether the cache is enabled.
|
35
|
+
#
|
36
|
+
# @example Set if the cache is enabled.
|
37
|
+
# QueryCache.enabled = true
|
38
|
+
#
|
39
|
+
# @param [ true, false ] value The enabled value.
|
40
|
+
#
|
41
|
+
# @since 4.0.0
|
42
|
+
def enabled=(value)
|
43
|
+
Thread.current["[mongoid]:query_cache:enabled"] = value
|
44
|
+
end
|
45
|
+
|
46
|
+
# Is the query cache enabled on the current thread?
|
47
|
+
#
|
48
|
+
# @example Is the query cache enabled?
|
49
|
+
# QueryCache.enabled?
|
50
|
+
#
|
51
|
+
# @return [ true, false ] If the cache is enabled.
|
52
|
+
#
|
53
|
+
# @since 4.0.0
|
54
|
+
def enabled?
|
55
|
+
!!Thread.current["[mongoid]:query_cache:enabled"]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Execute the block while using the query cache.
|
59
|
+
#
|
60
|
+
# @example Execute with the cache.
|
61
|
+
# QueryCache.cache { collection.find }
|
62
|
+
#
|
63
|
+
# @return [ Object ] The result of the block.
|
64
|
+
#
|
65
|
+
# @since 4.0.0
|
66
|
+
def cache
|
67
|
+
enabled = QueryCache.enabled?
|
68
|
+
QueryCache.enabled = true
|
69
|
+
yield
|
70
|
+
ensure
|
71
|
+
QueryCache.enabled = enabled
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# The middleware to be added to a rack application in order to activate the
|
76
|
+
# query cache.
|
77
|
+
#
|
78
|
+
# @since 4.0.0
|
79
|
+
class Middleware
|
80
|
+
|
81
|
+
# Instantiate the middleware.
|
82
|
+
#
|
83
|
+
# @example Create the new middleware.
|
84
|
+
# Middleware.new(app)
|
85
|
+
#
|
86
|
+
# @param [ Object ] app The rack applciation stack.
|
87
|
+
#
|
88
|
+
# @since 4.0.0
|
89
|
+
def initialize(app)
|
90
|
+
@app = app
|
91
|
+
end
|
92
|
+
|
93
|
+
# Execute the request, wrapping in a query cache.
|
94
|
+
#
|
95
|
+
# @example Execute the request.
|
96
|
+
# middleware.call(env)
|
97
|
+
#
|
98
|
+
# @param [ Object ] env The environment.
|
99
|
+
#
|
100
|
+
# @return [ Object ] The result of the call.
|
101
|
+
#
|
102
|
+
# @since 4.0.0
|
103
|
+
def call(env)
|
104
|
+
QueryCache.cache { @app.call(env) }
|
105
|
+
ensure
|
106
|
+
QueryCache.clear_cache
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
module Base # :nodoc:
|
111
|
+
|
112
|
+
def alias_query_cache_clear(*method_names)
|
113
|
+
method_names.each do |method_name|
|
114
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
115
|
+
def #{method_name}_with_clear_cache(*args)
|
116
|
+
QueryCache.clear_cache
|
117
|
+
#{method_name}_without_clear_cache(*args)
|
118
|
+
end
|
119
|
+
CODE
|
120
|
+
|
121
|
+
alias_method_chain method_name, :clear_cache
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Module to include in objects which need to wrap caching behaviour around
|
127
|
+
# them.
|
128
|
+
#
|
129
|
+
# @since 4.0.0
|
130
|
+
module Cacheable
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def with_cache
|
135
|
+
return yield unless QueryCache.enabled?
|
136
|
+
return yield if system_collection?
|
137
|
+
key = cache_key
|
138
|
+
if QueryCache.cache_table.has_key?(key)
|
139
|
+
instrument(key) { QueryCache.cache_table[key] }
|
140
|
+
else
|
141
|
+
QueryCache.cache_table[key] = yield
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def instrument(key, &block)
|
146
|
+
ActiveSupport::Notifications.instrument("query_cache.mongoid", key: key, &block)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Adds behaviour around caching to a Moped Query object.
|
151
|
+
#
|
152
|
+
# @since 4.0.0
|
153
|
+
module Query
|
154
|
+
extend ActiveSupport::Concern
|
155
|
+
include Cacheable
|
156
|
+
|
157
|
+
included do
|
158
|
+
extend QueryCache::Base
|
159
|
+
alias_method_chain :cursor, :cache
|
160
|
+
alias_method_chain :first, :cache
|
161
|
+
alias_query_cache_clear :remove, :remove_all, :update, :update_all, :upsert
|
162
|
+
end
|
163
|
+
|
164
|
+
# Provide a wrapped query cache cursor.
|
165
|
+
#
|
166
|
+
# @example Get the wrapped caching cursor.
|
167
|
+
# query.cursor_with_cache
|
168
|
+
#
|
169
|
+
# @return [ CachedCursor ] The cached cursor.
|
170
|
+
#
|
171
|
+
# @since 4.0.0
|
172
|
+
def cursor_with_cache
|
173
|
+
CachedCursor.new(session, operation)
|
174
|
+
end
|
175
|
+
|
176
|
+
# Override first with caching.
|
177
|
+
#
|
178
|
+
# @example Get the first with a cache.
|
179
|
+
# query.first_with_cache
|
180
|
+
#
|
181
|
+
# @return [ Hash ] The first document.
|
182
|
+
#
|
183
|
+
# @since 4.0.0
|
184
|
+
def first_with_cache
|
185
|
+
with_cache do
|
186
|
+
first_without_cache
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def cache_key
|
193
|
+
[ operation.database, operation.collection, operation.selector ]
|
194
|
+
end
|
195
|
+
|
196
|
+
def system_collection?
|
197
|
+
operation.collection =~ /^system./
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Adds behaviour to the query cache for collections.
|
202
|
+
#
|
203
|
+
# @since 4.0.0
|
204
|
+
module Collection
|
205
|
+
extend ActiveSupport::Concern
|
206
|
+
|
207
|
+
included do
|
208
|
+
extend QueryCache::Base
|
209
|
+
alias_query_cache_clear :insert
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# A Cursor that attempts to load documents from memory first before hitting
|
214
|
+
# the database if the same query has already been executed.
|
215
|
+
#
|
216
|
+
# @since 4.0.0
|
217
|
+
class CachedCursor < Moped::Cursor
|
218
|
+
include Cacheable
|
219
|
+
|
220
|
+
# Override the loading of docs to attempt to fetch from the cache.
|
221
|
+
#
|
222
|
+
# @example Load the documents.
|
223
|
+
# cursor.load_docs
|
224
|
+
#
|
225
|
+
# @return [ Array<Hash> ] The documents.
|
226
|
+
#
|
227
|
+
# @since 4.0.0
|
228
|
+
def load_docs
|
229
|
+
with_cache { super }
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
def cache_key
|
235
|
+
[ @database, @collection, @selector ]
|
236
|
+
end
|
237
|
+
|
238
|
+
def system_collection?
|
239
|
+
@collection =~ /^system./
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
Moped::Query.__send__(:include, Mongoid::QueryCache::Query)
|
246
|
+
Moped::Collection.__send__(:include, Mongoid::QueryCache::Collection)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
load 'mongoid/tasks/database.rake'
|
2
|
+
|
1
3
|
namespace :db do
|
2
4
|
|
3
5
|
unless Rake::Task.task_defined?("db:drop")
|
@@ -21,7 +23,7 @@ namespace :db do
|
|
21
23
|
|
22
24
|
unless Rake::Task.task_defined?("db:setup")
|
23
25
|
desc "Create the database, and initialize with the seed data"
|
24
|
-
task :setup => [ "db:create", "
|
26
|
+
task :setup => [ "db:create", "mongoid:create_indexes", "db:seed" ]
|
25
27
|
end
|
26
28
|
|
27
29
|
unless Rake::Task.task_defined?("db:reset")
|
@@ -64,32 +66,8 @@ namespace :db do
|
|
64
66
|
end
|
65
67
|
|
66
68
|
namespace :mongoid do
|
67
|
-
|
68
|
-
task :create_indexes => :environment do
|
69
|
-
::Rails.application.eager_load!
|
70
|
-
::Rails::Mongoid.create_indexes
|
71
|
-
end
|
72
|
-
|
73
|
-
desc "Remove indexes that exist in the database but aren't specified on the models"
|
74
|
-
task :remove_undefined_indexes => :environment do
|
75
|
-
::Rails.application.eager_load!
|
76
|
-
::Rails::Mongoid.remove_undefined_indexes
|
77
|
-
end
|
78
|
-
|
79
|
-
desc "Remove the indexes defined on your mongoid models without questions!"
|
80
|
-
task :remove_indexes => :environment do
|
69
|
+
task :load_models do
|
81
70
|
::Rails.application.eager_load!
|
82
|
-
::Rails::Mongoid.remove_indexes
|
83
|
-
end
|
84
|
-
|
85
|
-
desc "Drops the database for the current Rails.env"
|
86
|
-
task :drop => :environment do
|
87
|
-
::Mongoid::Sessions.default.drop
|
88
|
-
end
|
89
|
-
|
90
|
-
desc "Drop all collections except the system collections"
|
91
|
-
task :purge => :environment do
|
92
|
-
::Mongoid.purge!
|
93
71
|
end
|
94
72
|
end
|
95
73
|
end
|
data/lib/mongoid/relations.rb
CHANGED
@@ -46,7 +46,8 @@ module Mongoid
|
|
46
46
|
include Touchable
|
47
47
|
include CounterCache
|
48
48
|
|
49
|
-
attr_accessor :
|
49
|
+
attr_accessor :__metadata
|
50
|
+
alias :relation_metadata :__metadata
|
50
51
|
|
51
52
|
# Determine if the document itself is embedded in another document via the
|
52
53
|
# proper channels. (If it has a parent document.)
|
@@ -70,7 +71,7 @@ module Mongoid
|
|
70
71
|
#
|
71
72
|
# @since 2.0.0.rc.1
|
72
73
|
def embedded_many?
|
73
|
-
|
74
|
+
__metadata && __metadata.macro == :embeds_many
|
74
75
|
end
|
75
76
|
|
76
77
|
# Determine if the document is part of an embeds_one relation.
|
@@ -82,7 +83,7 @@ module Mongoid
|
|
82
83
|
#
|
83
84
|
# @since 2.0.0.rc.1
|
84
85
|
def embedded_one?
|
85
|
-
|
86
|
+
__metadata && __metadata.macro == :embeds_one
|
86
87
|
end
|
87
88
|
|
88
89
|
# Get the metadata name for this document. If no metadata was defined
|
@@ -97,8 +98,8 @@ module Mongoid
|
|
97
98
|
#
|
98
99
|
# @since 3.0.0
|
99
100
|
def metadata_name
|
100
|
-
raise Errors::NoMetadata.new(self.class.name) unless
|
101
|
-
|
101
|
+
raise Errors::NoMetadata.new(self.class.name) unless __metadata
|
102
|
+
__metadata.name
|
102
103
|
end
|
103
104
|
|
104
105
|
# Determine if the document is part of an references_many relation.
|
@@ -110,7 +111,7 @@ module Mongoid
|
|
110
111
|
#
|
111
112
|
# @since 2.0.0.rc.1
|
112
113
|
def referenced_many?
|
113
|
-
|
114
|
+
__metadata && __metadata.macro == :has_many
|
114
115
|
end
|
115
116
|
|
116
117
|
# Determine if the document is part of an references_one relation.
|
@@ -122,7 +123,7 @@ module Mongoid
|
|
122
123
|
#
|
123
124
|
# @since 2.0.0.rc.1
|
124
125
|
def referenced_one?
|
125
|
-
|
126
|
+
__metadata && __metadata.macro == :has_one
|
126
127
|
end
|
127
128
|
|
128
129
|
# Convenience method for iterating through the loaded relations and
|
@@ -143,20 +144,5 @@ module Mongoid
|
|
143
144
|
end
|
144
145
|
end
|
145
146
|
end
|
146
|
-
|
147
|
-
module ClassMethods
|
148
|
-
|
149
|
-
# This is convenience for librarys still on the old API.
|
150
|
-
#
|
151
|
-
# @example Get the associations.
|
152
|
-
# Person.associations
|
153
|
-
#
|
154
|
-
# @return [ Hash ] The relations.
|
155
|
-
#
|
156
|
-
# @since 2.3.1
|
157
|
-
def associations
|
158
|
-
self.relations
|
159
|
-
end
|
160
|
-
end
|
161
147
|
end
|
162
148
|
end
|
@@ -155,7 +155,6 @@ module Mongoid
|
|
155
155
|
# person.game?
|
156
156
|
#
|
157
157
|
# @param [ String, Symbol ] name The name of the relation.
|
158
|
-
# @param [ Metadata ] The metadata.
|
159
158
|
#
|
160
159
|
# @return [ Class ] The model being set up.
|
161
160
|
#
|
@@ -201,7 +200,6 @@ module Mongoid
|
|
201
200
|
# Person.ids_getter("addresses", metadata)
|
202
201
|
#
|
203
202
|
# @param [ String, Symbol ] name The name of the relation.
|
204
|
-
# @param [ Metadata] metadata The metadata for the relation.
|
205
203
|
#
|
206
204
|
# @return [ Class ] The class being set up.
|
207
205
|
def ids_getter(name, metadata)
|
@@ -212,7 +210,6 @@ module Mongoid
|
|
212
210
|
self
|
213
211
|
end
|
214
212
|
|
215
|
-
|
216
213
|
# Defines the setter for the relation. This does a few things based on
|
217
214
|
# some conditions. If there is an existing association, a target
|
218
215
|
# substitution will take place, otherwise a new relation will be
|
@@ -193,7 +193,7 @@ module Mongoid
|
|
193
193
|
def set_base_metadata
|
194
194
|
inverse_metadata = metadata.inverse_metadata(target)
|
195
195
|
if inverse_metadata != metadata && !inverse_metadata.nil?
|
196
|
-
base.
|
196
|
+
base.__metadata = inverse_metadata
|
197
197
|
end
|
198
198
|
end
|
199
199
|
|
@@ -24,7 +24,7 @@ module Mongoid
|
|
24
24
|
#
|
25
25
|
# @since 2.0.0.rc.1
|
26
26
|
def bind_one
|
27
|
-
base.
|
27
|
+
base.__metadata = metadata.inverse_metadata(target) unless base.__metadata
|
28
28
|
base.parentize(target)
|
29
29
|
binding do
|
30
30
|
if base.embedded_many?
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require "mongoid/relations/eager/base"
|
2
3
|
require "mongoid/relations/eager/belongs_to"
|
3
4
|
require "mongoid/relations/eager/has_one"
|
@@ -11,12 +12,10 @@ module Mongoid
|
|
11
12
|
attr_accessor :eager_loaded
|
12
13
|
|
13
14
|
def with_eager_loading(document)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
doc
|
19
|
-
end
|
15
|
+
return nil unless document
|
16
|
+
doc = Factory.from_db(klass, document, criteria.options[:fields])
|
17
|
+
eager_load_one(doc)
|
18
|
+
doc
|
20
19
|
end
|
21
20
|
|
22
21
|
def eager_load_one(doc)
|
@@ -1,56 +1,148 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
module Mongoid
|
2
3
|
module Relations
|
3
4
|
module Eager
|
5
|
+
# Base class for eager load preload functions.
|
6
|
+
#
|
7
|
+
# @since 4.0.0
|
4
8
|
class Base
|
9
|
+
|
10
|
+
# Instantiate the eager load class.
|
11
|
+
#
|
12
|
+
# @example Create the new belongs to eager load preloader.
|
13
|
+
# BelongsTo.new(relations_metadata, parent_docs)
|
14
|
+
#
|
15
|
+
# @param [ Array<Metadata> ] Relations to eager load
|
16
|
+
# @param [ Array<Document> ] Documents to preload the relations
|
17
|
+
#
|
18
|
+
# @return [ Base ] The eager load preloader
|
19
|
+
#
|
20
|
+
# @since 4.0.0
|
5
21
|
def initialize(associations, docs)
|
6
22
|
@associations = associations
|
7
23
|
@docs = docs
|
8
24
|
@grouped_docs = {}
|
9
25
|
end
|
10
26
|
|
27
|
+
# Shift the current relation metadata
|
28
|
+
#
|
29
|
+
# @example Shift the current metadata.
|
30
|
+
# loader.shift_metadata
|
31
|
+
#
|
32
|
+
# @return [ Object ] The relation metadata object.
|
33
|
+
#
|
34
|
+
# @since 4.0.0
|
11
35
|
def shift_metadata
|
12
36
|
@metadata = @associations.shift
|
13
37
|
end
|
14
38
|
|
39
|
+
# Run the preloader.
|
40
|
+
#
|
41
|
+
# @example Preload the relations into the documents.
|
42
|
+
# loader.run
|
43
|
+
#
|
44
|
+
# @return [ Array ] The list of documents given.
|
45
|
+
#
|
46
|
+
# @since 4.0.0
|
15
47
|
def run
|
16
48
|
while shift_metadata
|
17
49
|
preload
|
18
50
|
end
|
51
|
+
@docs
|
19
52
|
end
|
20
53
|
|
54
|
+
# Preload the current relation.
|
55
|
+
#
|
56
|
+
# This method should be implemented in the subclass
|
57
|
+
#
|
58
|
+
# @example Preload the current relation into the documents.
|
59
|
+
# loader.preload
|
60
|
+
#
|
61
|
+
# @since 4.0.0
|
21
62
|
def preload
|
22
63
|
raise NotImplementedError
|
23
64
|
end
|
24
65
|
|
66
|
+
# Run the preloader.
|
67
|
+
#
|
68
|
+
# @example Iterate over the documents loadded for the current relation
|
69
|
+
# loader.each_loaded_document { |doc| }
|
70
|
+
#
|
71
|
+
# @since 4.0.0
|
25
72
|
def each_loaded_document
|
26
73
|
@metadata.klass.any_in(key => keys_from_docs).each do |doc|
|
27
74
|
yield doc
|
28
75
|
end
|
29
76
|
end
|
30
77
|
|
78
|
+
# Set the pre-loaded document into its parent.
|
79
|
+
#
|
80
|
+
# @example Set docs into parent with pk = "foo"
|
81
|
+
# loader.set_on_parent("foo", docs)
|
82
|
+
#
|
83
|
+
# @param [ ObjectId ] parent`s id
|
84
|
+
# @param [ Document, Array ] element to push into the parent
|
85
|
+
#
|
86
|
+
# @since 4.0.0
|
31
87
|
def set_on_parent(id, element)
|
32
88
|
grouped_docs[id].each do |d|
|
33
89
|
set_relation(d, element)
|
34
90
|
end
|
35
91
|
end
|
36
92
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
93
|
+
# Return a hash with the current documents grouped by key.
|
94
|
+
#
|
95
|
+
# @example Return a hash with the current documents grouped by key.
|
96
|
+
# loader.grouped_docs
|
97
|
+
#
|
98
|
+
# @return [ Hash ] hash with groupd documents.
|
99
|
+
#
|
100
|
+
# @since 4.0.0
|
41
101
|
def grouped_docs
|
42
102
|
@grouped_docs[@metadata.name] ||= @docs.group_by do |doc|
|
43
103
|
doc.send(group_by_key)
|
44
104
|
end
|
45
105
|
end
|
46
106
|
|
107
|
+
# Group the documents and return the keys
|
108
|
+
#
|
109
|
+
# @example
|
110
|
+
# loader.keys_from_docs
|
111
|
+
#
|
112
|
+
# @return [ Array ] keys, ids
|
113
|
+
#
|
114
|
+
# @since 4.0.0
|
47
115
|
def keys_from_docs
|
48
116
|
grouped_docs.keys
|
49
117
|
end
|
50
118
|
|
51
|
-
|
119
|
+
# Return the key to group the current documents.
|
120
|
+
#
|
121
|
+
# This method should be implemented in the subclass
|
122
|
+
#
|
123
|
+
# @example Return the key for group
|
124
|
+
# loader.group_by_key
|
125
|
+
#
|
126
|
+
# @return [ Symbol ] Key to group by the current documents.
|
127
|
+
#
|
128
|
+
# @since 4.0.0
|
129
|
+
def group_by_key
|
52
130
|
raise NotImplementedError
|
53
131
|
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
# Set the pre-loaded document into its parent.
|
135
|
+
#
|
136
|
+
# @example Set docs into parent using the current relation name.
|
137
|
+
# loader.set_relation(doc, docs)
|
138
|
+
#
|
139
|
+
# @param [ Document ] document
|
140
|
+
# @param [ Document, Array ] element to set into the parent
|
141
|
+
#
|
142
|
+
# @since 4.0.0
|
143
|
+
def set_relation(doc, element)
|
144
|
+
doc.set_relation(@metadata.name, element)
|
145
|
+
end
|
54
146
|
end
|
55
147
|
end
|
56
148
|
end
|