mongoid 4.0.0.alpha2 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/README.md +3 -3
  4. data/lib/config/locales/en.yml +13 -0
  5. data/lib/mongoid.rb +3 -1
  6. data/lib/mongoid/atomic.rb +1 -1
  7. data/lib/mongoid/atomic/paths/embedded/many.rb +1 -1
  8. data/lib/mongoid/atomic/paths/embedded/one.rb +1 -1
  9. data/lib/mongoid/attributes.rb +23 -1
  10. data/lib/mongoid/attributes/processing.rb +1 -1
  11. data/lib/mongoid/composable.rb +3 -2
  12. data/lib/mongoid/contextual/command.rb +0 -26
  13. data/lib/mongoid/contextual/geo_near.rb +1 -1
  14. data/lib/mongoid/contextual/mongo.rb +6 -29
  15. data/lib/mongoid/contextual/text_search.rb +3 -5
  16. data/lib/mongoid/criteria.rb +1 -1
  17. data/lib/mongoid/criteria/modifiable.rb +27 -7
  18. data/lib/mongoid/criteria/permission.rb +70 -0
  19. data/lib/mongoid/document.rb +5 -6
  20. data/lib/mongoid/errors.rb +2 -0
  21. data/lib/mongoid/errors/document_not_destroyed.rb +25 -0
  22. data/lib/mongoid/errors/readonly_document.rb +24 -0
  23. data/lib/mongoid/extensions/boolean.rb +1 -0
  24. data/lib/mongoid/extensions/hash.rb +1 -1
  25. data/lib/mongoid/factory.rb +5 -3
  26. data/lib/mongoid/fields.rb +32 -0
  27. data/lib/mongoid/fields/localized.rb +1 -1
  28. data/lib/mongoid/fields/standard.rb +1 -1
  29. data/lib/mongoid/findable.rb +1 -0
  30. data/lib/mongoid/interceptable.rb +11 -6
  31. data/lib/mongoid/log_subscriber.rb +34 -1
  32. data/lib/mongoid/persistable/deletable.rb +1 -0
  33. data/lib/mongoid/persistable/destroyable.rb +7 -2
  34. data/lib/mongoid/persistable/updatable.rb +27 -26
  35. data/lib/mongoid/query_cache.rb +246 -0
  36. data/lib/mongoid/railties/database.rake +4 -26
  37. data/lib/mongoid/relations.rb +8 -22
  38. data/lib/mongoid/relations/accessors.rb +0 -3
  39. data/lib/mongoid/relations/binding.rb +1 -1
  40. data/lib/mongoid/relations/bindings/embedded/in.rb +1 -1
  41. data/lib/mongoid/relations/eager.rb +5 -6
  42. data/lib/mongoid/relations/eager/base.rb +97 -5
  43. data/lib/mongoid/relations/eager/belongs_to.rb +1 -0
  44. data/lib/mongoid/relations/eager/has_and_belongs_to_many.rb +16 -9
  45. data/lib/mongoid/relations/eager/has_many.rb +1 -0
  46. data/lib/mongoid/relations/eager/has_one.rb +1 -0
  47. data/lib/mongoid/relations/embedded/batchable.rb +1 -1
  48. data/lib/mongoid/relations/embedded/in.rb +4 -4
  49. data/lib/mongoid/relations/embedded/many.rb +7 -5
  50. data/lib/mongoid/relations/embedded/one.rb +1 -1
  51. data/lib/mongoid/relations/macros.rb +1 -0
  52. data/lib/mongoid/relations/marshalable.rb +3 -3
  53. data/lib/mongoid/relations/proxy.rb +12 -10
  54. data/lib/mongoid/relations/referenced/in.rb +2 -2
  55. data/lib/mongoid/relations/referenced/many.rb +9 -9
  56. data/lib/mongoid/relations/referenced/many_to_many.rb +7 -7
  57. data/lib/mongoid/relations/referenced/one.rb +4 -4
  58. data/lib/mongoid/{state.rb → stateful.rb} +13 -1
  59. data/lib/mongoid/tasks/database.rake +31 -0
  60. data/lib/mongoid/tasks/database.rb +107 -0
  61. data/lib/mongoid/threaded.rb +0 -47
  62. data/lib/mongoid/validatable/uniqueness.rb +4 -16
  63. data/lib/mongoid/version.rb +1 -1
  64. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +0 -3
  65. data/lib/rails/mongoid.rb +0 -124
  66. data/spec/app/models/edit.rb +5 -0
  67. data/spec/app/models/even.rb +7 -0
  68. data/spec/app/models/line_item.rb +1 -1
  69. data/spec/app/models/note.rb +2 -0
  70. data/spec/app/models/odd.rb +7 -0
  71. data/spec/app/models/record.rb +5 -0
  72. data/spec/app/models/wiki_page.rb +1 -1
  73. data/spec/mongoid/attributes_spec.rb +76 -1
  74. data/spec/mongoid/changeable_spec.rb +6 -2
  75. data/spec/mongoid/contextual/mongo_spec.rb +3 -1
  76. data/spec/mongoid/contextual/text_search_spec.rb +3 -1
  77. data/spec/mongoid/criteria/modifiable_spec.rb +192 -0
  78. data/spec/mongoid/criteria_spec.rb +6 -2
  79. data/spec/mongoid/errors/document_not_destroyed_spec.rb +33 -0
  80. data/spec/mongoid/errors/readonly_document_spec.rb +29 -0
  81. data/spec/mongoid/fields/localized_spec.rb +15 -0
  82. data/spec/mongoid/fields_spec.rb +88 -2
  83. data/spec/mongoid/log_subscriber_spec.rb +3 -3
  84. data/spec/mongoid/persistable/deletable_spec.rb +14 -1
  85. data/spec/mongoid/persistable/destroyable_spec.rb +45 -1
  86. data/spec/mongoid/persistable/savable_spec.rb +34 -5
  87. data/spec/mongoid/query_cache_spec.rb +197 -0
  88. data/spec/mongoid/relations/bindings/embedded/in_spec.rb +2 -2
  89. data/spec/mongoid/relations/builders/referenced/many_spec.rb +1 -1
  90. data/spec/mongoid/relations/eager/has_and_belongs_to_many_spec.rb +11 -37
  91. data/spec/mongoid/relations/eager/has_one_spec.rb +1 -1
  92. data/spec/mongoid/relations/embedded/in_spec.rb +1 -1
  93. data/spec/mongoid/relations/embedded/many_spec.rb +10 -10
  94. data/spec/mongoid/relations/embedded/one_spec.rb +10 -2
  95. data/spec/mongoid/relations/referenced/in_spec.rb +1 -1
  96. data/spec/mongoid/relations/referenced/many_spec.rb +37 -2
  97. data/spec/mongoid/relations/touchable_spec.rb +20 -0
  98. data/spec/mongoid/{state_spec.rb → stateful_spec.rb} +26 -1
  99. data/spec/mongoid/tasks/database_rake_spec.rb +285 -0
  100. data/spec/mongoid/tasks/database_spec.rb +148 -0
  101. data/spec/mongoid/validatable/uniqueness_spec.rb +7 -0
  102. data/spec/rails/mongoid_spec.rb +0 -316
  103. data/spec/spec_helper.rb +1 -0
  104. 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", "db:mongoid:create_indexes", "db:seed" ]
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
- desc "Create the indexes defined on your mongoid models"
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
@@ -46,7 +46,8 @@ module Mongoid
46
46
  include Touchable
47
47
  include CounterCache
48
48
 
49
- attr_accessor :metadata
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
- metadata && metadata.macro == :embeds_many
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
- metadata && metadata.macro == :embeds_one
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 metadata
101
- metadata.name
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
- metadata && metadata.macro == :has_many
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
- metadata && metadata.macro == :has_one
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.metadata = inverse_metadata
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.metadata = metadata.inverse_metadata(target) unless base.metadata
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
- selecting do
15
- return nil unless document
16
- doc = Factory.from_db(klass, document, criteria.object_id)
17
- eager_load_one(doc)
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
- def set_relation(doc, element)
38
- doc.set_relation(@metadata.name, element)
39
- end
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
- def group_by_key(doc)
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