mongoid 5.0.2 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
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