mongoid 5.0.2 → 5.1.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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE +1 -1
  5. data/README.md +1 -1
  6. data/lib/mongoid/changeable.rb +1 -1
  7. data/lib/mongoid/clients.rb +1 -0
  8. data/lib/mongoid/clients/options.rb +119 -7
  9. data/lib/mongoid/config.rb +7 -0
  10. data/lib/mongoid/config/options.rb +15 -0
  11. data/lib/mongoid/contextual/geo_near.rb +12 -0
  12. data/lib/mongoid/contextual/mongo.rb +6 -0
  13. data/lib/mongoid/criteria.rb +2 -56
  14. data/lib/mongoid/criteria/findable.rb +4 -1
  15. data/lib/mongoid/criteria/includable.rb +142 -0
  16. data/lib/mongoid/criteria/modifiable.rb +13 -1
  17. data/lib/mongoid/document.rb +21 -0
  18. data/lib/mongoid/fields/foreign_key.rb +5 -1
  19. data/lib/mongoid/fields/localized.rb +16 -1
  20. data/lib/mongoid/fields/validators/macro.rb +1 -0
  21. data/lib/mongoid/findable.rb +1 -0
  22. data/lib/mongoid/loggable.rb +1 -1
  23. data/lib/mongoid/matchable.rb +6 -1
  24. data/lib/mongoid/persistable.rb +2 -2
  25. data/lib/mongoid/relations/eager.rb +12 -5
  26. data/lib/mongoid/relations/eager/base.rb +3 -1
  27. data/lib/mongoid/relations/referenced/many.rb +39 -6
  28. data/lib/mongoid/relations/targets/enumerable.rb +3 -3
  29. data/lib/mongoid/scopable.rb +5 -2
  30. data/lib/mongoid/version.rb +1 -1
  31. data/spec/app/models/address.rb +2 -0
  32. data/spec/app/models/agent.rb +2 -0
  33. data/spec/app/models/alert.rb +2 -0
  34. data/spec/app/models/post.rb +1 -0
  35. data/spec/config/mongoid.yml +1 -0
  36. data/spec/mongoid/changeable_spec.rb +1 -1
  37. data/spec/mongoid/clients/options_spec.rb +57 -0
  38. data/spec/mongoid/config_spec.rb +38 -0
  39. data/spec/mongoid/contextual/geo_near_spec.rb +19 -0
  40. data/spec/mongoid/criteria/findable_spec.rb +11 -0
  41. data/spec/mongoid/criteria/modifiable_spec.rb +126 -0
  42. data/spec/mongoid/criteria_spec.rb +81 -5
  43. data/spec/mongoid/document_spec.rb +56 -0
  44. data/spec/mongoid/fields/foreign_key_spec.rb +23 -0
  45. data/spec/mongoid/fields/localized_spec.rb +32 -14
  46. data/spec/mongoid/matchable_spec.rb +127 -1
  47. data/spec/mongoid/persistable_spec.rb +24 -0
  48. data/spec/mongoid/relations/embedded/many_spec.rb +16 -0
  49. data/spec/mongoid/relations/referenced/many_spec.rb +60 -0
  50. data/spec/mongoid/relations/referenced/many_to_many_spec.rb +40 -0
  51. data/spec/mongoid/scopable_spec.rb +67 -0
  52. data/spec/spec_helper.rb +4 -0
  53. data/spec/support/authorization.rb +2 -1
  54. metadata +6 -5
  55. metadata.gz.sig +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1aa194434a7d5ebe53a1f05929202d4976201f90
4
- data.tar.gz: 31e2605c59062cb947a510d399a25c173c9ff504
3
+ metadata.gz: 8d93faa3fdc0513cf6d8daafdf1ab90fd4744fd8
4
+ data.tar.gz: 77316de1b6bdd99154eacba90bd510b2ca636fdb
5
5
  SHA512:
6
- metadata.gz: a4d133d4ce0e944995f0a0718ba506ca8ede6cc6f232a14cbe34d371f3db80d5736610ffdc6726bb5de3b658355c4ce818b10c82c172669da82aeafabbb13172
7
- data.tar.gz: bf1274305b4c8165ac084e06246adf841fd17aeefdf017eb442e81d92c2aa75c6ab8183e591ba7e5b8a757d8ea39899c4a6ce41cb966d835f020edf3a45e6e6e
6
+ metadata.gz: 9397767bc7a5cfb49438682bea971816f69a6053beb740cc2ab17fec5add3e59f08d72958464b762d3ad399612479b99810b629969ca184c1bebc65fb661c1b2
7
+ data.tar.gz: 0725f14ab25f26b0bc29e73b721d9bfe84de5a83119a7fd390957ef4c81c90d8a270725bb42d56b7f3e782cf059e9292815ba8c683585d80ba813ed20e20f809
Binary file
data.tar.gz.sig CHANGED
Binary file
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2009-2013 Durran Jordan
1
+ Copyright (c) 2009-2016 Durran Jordan
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -31,7 +31,7 @@ Please see the [MongoDB website](http://docs.mongodb.org/ecosystem/tutorial/ruby
31
31
  License
32
32
  -------
33
33
 
34
- Copyright (c) 2009-2015 Durran Jordan
34
+ Copyright (c) 2009-2016 Durran Jordan
35
35
 
36
36
  Permission is hereby granted, free of charge, to any person obtaining
37
37
  a copy of this software and associated documentation files (the
@@ -69,7 +69,7 @@ module Mongoid
69
69
  change = attribute_change(attr)
70
70
  _changes[attr] = change if change
71
71
  end
72
- _changes
72
+ _changes.with_indifferent_access
73
73
  end
74
74
 
75
75
  # Call this method after save, so the changes can be properly switched.
@@ -111,6 +111,7 @@ module Mongoid
111
111
  #
112
112
  # @since 3.0.0
113
113
  def mongo_client
114
+ return client_with_options if client_with_options
114
115
  client = Clients.with_name(client_name)
115
116
  opts = self.persistence_options ? self.persistence_options.dup : {}
116
117
  if defined?(Mongo::Client::VALID_OPTIONS)
@@ -43,14 +43,15 @@ module Mongoid
43
43
  end
44
44
 
45
45
  def mongo_client
46
- if persistence_options
47
- if persistence_options[:client]
48
- client = Clients.with_name(persistence_options[:client])
46
+ tmp = persistence_options
47
+ if opts = tmp && tmp.dup
48
+ if opts[:client]
49
+ client = Clients.with_name(opts[:client])
49
50
  else
50
51
  client = Clients.with_name(self.class.client_name)
51
52
  client.use(self.class.database_name)
52
53
  end
53
- client.with(persistence_options.reject{ |k, v| k == :collection || k == :client })
54
+ client.with(opts.reject{ |k, v| k == :collection || k == :client })
54
55
  end
55
56
  end
56
57
 
@@ -76,13 +77,27 @@ module Mongoid
76
77
  Thread.current["[mongoid][#{klass}]:persistence-options"]
77
78
  end
78
79
 
80
+ # Get the client with special options for the current thread.
81
+ #
82
+ # @example Get the client with options.
83
+ # Threaded.client_with_options(Band)
84
+ #
85
+ # @param [ Class ] klass The model class.
86
+ #
87
+ # @return [ Mongo::Client ] The client.
88
+ #
89
+ # @since 5.1.0
90
+ def client_with_options(klass = self)
91
+ Thread.current["[mongoid][#{klass}]:mongo-client"]
92
+ end
93
+
79
94
  private
80
95
  # Set the persistence options on the current thread.
81
96
  #
82
97
  # @api private
83
98
  #
84
99
  # @example Set the persistence options.
85
- # Threaded.set_persistence_options(Band, { safe: { fsync: true }})
100
+ # Threaded.set_persistence_options(Band, { write: { w: 3 }})
86
101
  #
87
102
  # @param [ Class ] klass The model class.
88
103
  # @param [ Hash ] options The persistence options.
@@ -93,6 +108,97 @@ module Mongoid
93
108
  def set_persistence_options(klass, options)
94
109
  Thread.current["[mongoid][#{klass}]:persistence-options"] = options
95
110
  end
111
+
112
+ # Unset the persistence options on the current thread.
113
+ #
114
+ # @api private
115
+ #
116
+ # @example Unset the persistence options.
117
+ # Threaded.unset_persistence_options(Band)
118
+ #
119
+ # @param [ Class ] klass The model class.
120
+ #
121
+ # @return [ nil ] nil.
122
+ #
123
+ # @since 5.1.0
124
+ def unset_persistence_options(klass)
125
+ Thread.current["[mongoid][#{klass}]:persistence-options"] = nil
126
+ end
127
+
128
+ # Set the persistence options and client with those options on the current thread.
129
+ # Note that a client will only be set if its cluster differs from the cluster of the
130
+ # original client.
131
+ #
132
+ # @api private
133
+ #
134
+ # @example Set the persistence options and client with those options on the current thread.
135
+ # Threaded.set_options(Band, { write: { w: 3 }})
136
+ #
137
+ # @param [ Class ] klass The model class.
138
+ # @param [ Mongo::Client ] client The client with options.
139
+ #
140
+ # @return [ Mongo::Client, nil ] The client or nil if the cluster does not change.
141
+ #
142
+ # @since 5.1.0
143
+ def set_options(klass, options)
144
+ original_cluster = mongo_client.cluster
145
+ set_persistence_options(klass, options)
146
+ m = mongo_client
147
+ set_client_with_options(klass, m) unless m.cluster.equal?(original_cluster)
148
+ end
149
+
150
+ # Set the client with special options on the current thread.
151
+ #
152
+ # @api private
153
+ #
154
+ # @example Set the client with options.
155
+ # Threaded.set_client_with_options(Band, client)
156
+ #
157
+ # @param [ Class ] klass The model class.
158
+ # @param [ Mongo::Client ] client The client with options.
159
+ #
160
+ # @return [ Mongo::Client ] The client.
161
+ #
162
+ # @since 5.1.0
163
+ def set_client_with_options(klass, client)
164
+ Thread.current["[mongoid][#{klass}]:mongo-client"] = client
165
+ end
166
+
167
+ # Unset the client with special options on the current thread.
168
+ #
169
+ # @api private
170
+ #
171
+ # @example Unset the client with options.
172
+ # Threaded.unset_client_with_options(Band)
173
+ #
174
+ # @param [ Class ] klass The model class.
175
+ #
176
+ # @return [ nil ] nil.
177
+ #
178
+ # @since 5.1.0
179
+ def unset_client_with_options(klass)
180
+ if client = Thread.current["[mongoid][#{klass}]:mongo-client"]
181
+ client.close
182
+ Thread.current["[mongoid][#{klass}]:mongo-client"] = nil
183
+ end
184
+ end
185
+
186
+ # Unset the persistence options and client with special options on the current thread.
187
+ #
188
+ # @api private
189
+ #
190
+ # @example Unset the persistence options and client with options.
191
+ # Threaded.unset_options(Band)
192
+ #
193
+ # @param [ Class ] klass The model class.
194
+ #
195
+ # @return [ nil ] nil.
196
+ #
197
+ # @since 5.1.0
198
+ def unset_options(klass)
199
+ unset_persistence_options(klass)
200
+ unset_client_with_options(klass)
201
+ end
96
202
  end
97
203
 
98
204
  module ClassMethods
@@ -144,7 +250,13 @@ module Mongoid
144
250
  #
145
251
  # @since 3.0.0
146
252
  def with(options)
147
- Proxy.new(self, (persistence_options || {}).merge(options))
253
+ if block_given?
254
+ set_options(self, options)
255
+ yield self
256
+ unset_options(self)
257
+ else
258
+ Proxy.new(self, (persistence_options || {}).merge(options))
259
+ end
148
260
  end
149
261
  end
150
262
 
@@ -174,7 +286,7 @@ module Mongoid
174
286
  end
175
287
  ret
176
288
  ensure
177
- set_persistence_options(@target, nil)
289
+ unset_persistence_options(@target)
178
290
  end
179
291
 
180
292
  def send(symbol, *args)
@@ -24,6 +24,7 @@ module Mongoid
24
24
  option :duplicate_fields_exception, default: false
25
25
  option :use_activesupport_time_zone, default: true
26
26
  option :use_utc, default: false
27
+ option :log_level, default: :info
27
28
 
28
29
  # Has Mongoid been configured? This is checking that at least a valid
29
30
  # client config exists.
@@ -128,6 +129,7 @@ module Mongoid
128
129
  configuration = settings.with_indifferent_access
129
130
  self.options = configuration[:options]
130
131
  self.clients = configuration[:clients]
132
+ set_log_levels
131
133
  end
132
134
 
133
135
  # Override the database to use globally.
@@ -243,6 +245,11 @@ module Mongoid
243
245
 
244
246
  private
245
247
 
248
+ def set_log_levels
249
+ Mongoid.logger.level = Mongoid::Config.log_level
250
+ Mongo::Logger.logger.level = Mongoid.logger.level
251
+ end
252
+
246
253
  def clients=(clients)
247
254
  raise Errors::NoClientsConfig.new unless clients
248
255
  c = clients.with_indifferent_access
@@ -69,6 +69,21 @@ module Mongoid
69
69
  def settings
70
70
  @settings ||= {}
71
71
  end
72
+
73
+ # Get the log level.
74
+ #
75
+ # @example Get the log level.
76
+ # config.log_level
77
+ #
78
+ # @return [ Integer ] The log level.
79
+ #
80
+ # @since 5.1.0
81
+ def log_level
82
+ if settings[:log_level]
83
+ level = settings[:log_level].upcase.to_s
84
+ "Logger::#{level}".constantize
85
+ end
86
+ end
72
87
  end
73
88
  end
74
89
  end
@@ -198,6 +198,18 @@ module Mongoid
198
198
  stats["time"]
199
199
  end
200
200
 
201
+ # Is this context's criteria considered empty?
202
+ #
203
+ # @example Is this context's criteria considered empty?
204
+ # geo_near.empty_and_chainable?
205
+ #
206
+ # @return [ true ] Always true.
207
+ #
208
+ # @since 5.1.0
209
+ def empty_and_chainable?
210
+ true
211
+ end
212
+
201
213
  private
202
214
 
203
215
  # Apply criteria specific options - query, limit.
@@ -543,6 +543,12 @@ module Mongoid
543
543
  if criteria.options[:timeout] == false
544
544
  @view = view.no_cursor_timeout
545
545
  end
546
+ if criteria.options[:cursor_type]
547
+ # @todo: update to use #cursor_type method on view when driver 2.3 is released.
548
+ # See RUBY-1080
549
+ @view = view.clone
550
+ @view.options.merge!(cursor_type: criteria.options[:cursor_type])
551
+ end
546
552
  end
547
553
 
548
554
  # Apply an option.
@@ -1,5 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require "mongoid/criteria/findable"
3
+ require "mongoid/criteria/includable"
3
4
  require "mongoid/criteria/inspectable"
4
5
  require "mongoid/criteria/marshalable"
5
6
  require "mongoid/criteria/modifiable"
@@ -19,6 +20,7 @@ module Mongoid
19
20
  include Origin::Queryable
20
21
  include Findable
21
22
  include Inspectable
23
+ include Includable
22
24
  include Marshalable
23
25
  include Modifiable
24
26
  include Scopable
@@ -194,62 +196,6 @@ module Mongoid
194
196
  klass ? super(klass.aliased_fields, klass.fields) : super({}, {})
195
197
  end
196
198
 
197
- # Eager loads all the provided relations. Will load all the documents
198
- # into the identity map whose ids match based on the extra query for the
199
- # ids.
200
- #
201
- # @note This will work for embedded relations that reference another
202
- # collection via belongs_to as well.
203
- #
204
- # @note Eager loading brings all the documents into memory, so there is a
205
- # sweet spot on the performance gains. Internal benchmarks show that
206
- # eager loading becomes slower around 100k documents, but this will
207
- # naturally depend on the specific application.
208
- #
209
- # @example Eager load the provided relations.
210
- # Person.includes(:posts, :game)
211
- #
212
- # @param [ Array<Symbol> ] relations The names of the relations to eager
213
- # load.
214
- #
215
- # @return [ Criteria ] The cloned criteria.
216
- #
217
- # @since 2.2.0
218
- def includes(*relations)
219
- relations.flatten.each do |name|
220
- metadata = klass.reflect_on_association(name)
221
- raise Errors::InvalidIncludes.new(klass, relations) unless metadata
222
- inclusions.push(metadata) unless inclusions.include?(metadata)
223
- end
224
- clone
225
- end
226
-
227
- # Get a list of criteria that are to be executed for eager loading.
228
- #
229
- # @example Get the eager loading inclusions.
230
- # Person.includes(:game).inclusions
231
- #
232
- # @return [ Array<Metadata> ] The inclusions.
233
- #
234
- # @since 2.2.0
235
- def inclusions
236
- @inclusions ||= []
237
- end
238
-
239
- # Set the inclusions for the criteria.
240
- #
241
- # @example Set the inclusions.
242
- # criteria.inclusions = [ meta ]
243
- #
244
- # @param [ Array<Metadata> ] The inclusions.
245
- #
246
- # @return [ Array<Metadata> ] The new inclusions.
247
- #
248
- # @since 3.0.0
249
- def inclusions=(value)
250
- @inclusions = value
251
- end
252
-
253
199
  # Merges another object with this +Criteria+ and returns a new criteria.
254
200
  # The other object may be a +Criteria+ or a +Hash+. This is used to
255
201
  # combine multiple scopes together, where a chained scope situation
@@ -120,7 +120,10 @@ module Mongoid
120
120
  #
121
121
  # @since 3.0.0
122
122
  def mongoize_ids(ids)
123
- ids.map{ |id| klass.fields["_id"].mongoize(id) }
123
+ ids.map do |id|
124
+ id = id[:id] if id.respond_to?(:keys) && id[:id]
125
+ klass.fields["_id"].mongoize(id)
126
+ end
124
127
  end
125
128
 
126
129
  # Convenience method of raising an invalid options error.
@@ -0,0 +1,142 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ class Criteria
4
+
5
+ # Module providing functionality for parsing (nested) inclusion definitions.
6
+ module Includable
7
+
8
+ # Eager loads all the provided relations. Will load all the documents
9
+ # into the identity map whose ids match based on the extra query for the
10
+ # ids.
11
+ #
12
+ # @note This will work for embedded relations that reference another
13
+ # collection via belongs_to as well.
14
+ #
15
+ # @note Eager loading brings all the documents into memory, so there is a
16
+ # sweet spot on the performance gains. Internal benchmarks show that
17
+ # eager loading becomes slower around 100k documents, but this will
18
+ # naturally depend on the specific application.
19
+ #
20
+ # @example Eager load the provided relations.
21
+ # Person.includes(:posts, :game)
22
+ #
23
+ # @param [ Array<Symbol>, Array<Hash> ] relations The names of the relations to eager
24
+ # load.
25
+ #
26
+ # @return [ Criteria ] The cloned criteria.
27
+ #
28
+ # @since 2.2.0
29
+ def includes(*relations)
30
+ relations.flatten.each do |relation|
31
+ if relation.is_a?(Hash)
32
+ extract_nested_inclusion(klass, relation)
33
+ else
34
+ add_inclusion(klass, relation)
35
+ end
36
+ end
37
+ clone
38
+ end
39
+
40
+ # Get a list of criteria that are to be executed for eager loading.
41
+ #
42
+ # @example Get the eager loading inclusions.
43
+ # Person.includes(:game).inclusions
44
+ #
45
+ # @return [ Array<Metadata> ] The inclusions.
46
+ #
47
+ # @since 2.2.0
48
+ def inclusions
49
+ @inclusions ||= []
50
+ end
51
+
52
+ # Set the inclusions for the criteria.
53
+ #
54
+ # @example Set the inclusions.
55
+ # criteria.inclusions = [ meta ]
56
+ #
57
+ # @param [ Array<Metadata> ] The inclusions.
58
+ #
59
+ # @return [ Array<Metadata> ] The new inclusions.
60
+ #
61
+ # @since 3.0.0
62
+ def inclusions=(value)
63
+ @inclusions = value
64
+ end
65
+
66
+ private
67
+
68
+ # Add an inclusion definition to the list of inclusions for the criteria.
69
+ #
70
+ # @example Add an inclusion.
71
+ # criteria.add_inclusion(Person, :posts)
72
+ #
73
+ # @param [ Class, String, Symbol ] _klass The class or string/symbol of the class name.
74
+ # @param [ Symbol ] relation The relation.
75
+ #
76
+ # @raise [ Errors::InvalidIncludes ] If no relation is found.
77
+ #
78
+ # @since 5.1.0
79
+ def add_inclusion(_klass, relation)
80
+ metadata = get_inclusion_metadata(_klass, relation)
81
+ raise Errors::InvalidIncludes.new(_klass, [ relation ]) unless metadata
82
+ inclusions.push(metadata) unless inclusions.include?(metadata)
83
+ end
84
+
85
+ # Extract inclusion definitions from a list.
86
+ #
87
+ # @example Extract the inclusions from a list.
88
+ # criteria.extract_relations_list(:posts, [{ :alerts => :items }])
89
+ #
90
+ # @param [ Symbol ] association The name of the association.
91
+ # @param [ Array ] relations A list of associations.
92
+ #
93
+ # @since 5.1.0
94
+ def extract_relations_list(association, relations)
95
+ relations.each do |relation|
96
+ if relation.is_a?(Hash)
97
+ extract_nested_inclusion(association, relation)
98
+ else
99
+ add_inclusion(association, relation)
100
+ end
101
+ end
102
+ end
103
+
104
+ # Extract nested inclusion.
105
+ #
106
+ # @example Extract the inclusions from a nested definition.
107
+ # criteria.extract_nested_inclusion(User, { :posts => [:alerts] })
108
+ #
109
+ # @param [ Class, Symbol ] _klass The class for which the inclusion should be added.
110
+ # @param [ Hash ] relation The nested inclusion.
111
+ #
112
+ # @since 5.1.0
113
+ def extract_nested_inclusion(_klass, relation)
114
+ relation.each do |association, _inclusion|
115
+ add_inclusion(_klass, association)
116
+ if _inclusion.is_a?(Array)
117
+ extract_relations_list(association, _inclusion)
118
+ else
119
+ add_inclusion(association, _inclusion)
120
+ end
121
+ end
122
+ end
123
+
124
+ # Get the metadata for an inclusion.
125
+ #
126
+ # @example Get the metadata for an inclusion definition.
127
+ # criteria.get_inclusion_metadata(User, :posts)
128
+ #
129
+ # @param [ Class, Symbol, String ] _klass The class for determining the association metadata
130
+ # @param [ Symbol ] association The name of the association.
131
+ #
132
+ # @since 5.1.0
133
+ def get_inclusion_metadata(_klass, association)
134
+ if _klass.is_a?(Class)
135
+ _klass.reflect_on_association(association)
136
+ else
137
+ _klass.to_s.classify.constantize.reflect_on_association(association)
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end