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.
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