mongoid 3.0.15 → 3.0.16

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.
@@ -1,7 +1,41 @@
1
1
  # Overview
2
2
 
3
3
  For instructions on upgrading to newer versions, visit
4
- [mongoid.org](http://mongoid.org/docs/upgrading.html).
4
+ [mongoid.org](http://mongoid.org/en/mongoid/docs/upgrading.html).
5
+
6
+ ## 3.0.16
7
+
8
+ ### Resolved Issues
9
+
10
+ * \#2661 Implement instance level `model_name` for documents.
11
+
12
+ * \#2651 Ensure `Criteria#type` works properly with both symbol and string
13
+ keys in the selector.
14
+
15
+ * \#2647 Ensure `deleted?` and `destroyed?` on paranoid documents return the
16
+ same value.
17
+
18
+ * \#2646 Set unloaded doc in memory on enumerable targets before yielding to
19
+ the block.
20
+
21
+ * \#2645 Take caching into consideration when asking for counts.
22
+ (Arthur Nogueira Neves)
23
+
24
+ * \#2642 Don't batch push empty arrays on embedded documents. (Laszlo Bacsi)
25
+
26
+ * \#2639 Avoid extra unnecesary queries on new records when building relations
27
+ off of them.
28
+
29
+ * \#2638 When a criteria is eager loading, calling `first` or `last` then
30
+ iterating the entire results properly eager loads the full request.
31
+
32
+ * \#2618 Validating uniqueness now always uses string consistency by default.
33
+
34
+ * \#2564 Fixed infinite recursion for cases where a relation getter was
35
+ overridden and called the setter from that method.
36
+
37
+ * \#2554 Ensure `unscoped` on an `embeds_many` does not include documents
38
+ flagged for destruction.
5
39
 
6
40
  ## 3.0.15
7
41
 
@@ -0,0 +1,158 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Contextual
4
+ module Eager
5
+
6
+ # @attribute [rw] eager_loaded Has the context been eager loaded?
7
+ attr_accessor :eager_loaded
8
+
9
+ private
10
+
11
+ # Eager load the inclusions for the provided documents.
12
+ #
13
+ # @api private
14
+ #
15
+ # @example Eager load the inclusions.
16
+ # context.eager_load(docs)
17
+ #
18
+ # @param [ Array<Document> ] docs The docs returning from the db.
19
+ #
20
+ # @return [ true ] Always true.
21
+ #
22
+ # @since 3.0.0
23
+ def eager_load(docs)
24
+ load_inclusions(docs)
25
+ self.eager_loaded = true
26
+ end
27
+
28
+ # Eager load the inclusions for the provided document.
29
+ #
30
+ # @api private
31
+ #
32
+ # @example Eager load the inclusions.
33
+ # context.eager_load(doc)
34
+ #
35
+ # @param [ Document ] doc The doc returning from the db.
36
+ #
37
+ # @return [ true ] Always true.
38
+ #
39
+ # @since 3.0.16
40
+ def eager_load_one(doc)
41
+ load_inclusions([ doc ])
42
+ inclusions_loaded[doc.id] = true
43
+ end
44
+
45
+ # Get the ids that to be used to eager load documents.
46
+ #
47
+ # @api private
48
+ #
49
+ # @example Get the ids.
50
+ # context.eager_load(docs, metadata)
51
+ #
52
+ # @param [ Array<Document> ] docs The pre-loaded documents.
53
+ # @param [ Metadata ] metadata The relation metadata.
54
+ #
55
+ # @return [ Array<Object> ] The ids.
56
+ #
57
+ # @since 3.0.0
58
+ def eager_loaded_ids(docs, metadata)
59
+ if metadata.stores_foreign_key?
60
+ docs.flat_map{ |doc| doc.send(metadata.foreign_key) }
61
+ else
62
+ docs.map(&:id)
63
+ end
64
+ end
65
+
66
+ # Is this context able to be eager loaded?
67
+ #
68
+ # @api private
69
+ #
70
+ # @example Is the context eager loadable?
71
+ # context.eager_loadable?
72
+ #
73
+ # @example Is the single document eager loadable?
74
+ # context.eager_loadable?(document)
75
+ #
76
+ # @param [ Document ] document The single document to load for.
77
+ #
78
+ # @return [ true, false ] If the context is able to be eager loaded.
79
+ #
80
+ # @since 3.0.0
81
+ def eager_loadable?(document = nil)
82
+ return false if criteria.inclusions.empty?
83
+ document ? !inclusions_loaded?(document) : !eager_loaded
84
+ end
85
+
86
+ # Has a hash of individual documents that have had their relations reager
87
+ # loaded.
88
+ #
89
+ # @api private
90
+ #
91
+ # @example Get the documents with relations eager loaded.
92
+ # context.inclusions_loaded
93
+ #
94
+ # @return [ Hash ] The documents that have had eager loaded inclusions.
95
+ #
96
+ # @since 3.0.16
97
+ def inclusions_loaded
98
+ @inclusions_loaded ||= {}
99
+ end
100
+
101
+ # Has the document had its inclusions loaded?
102
+ #
103
+ # @api private
104
+ #
105
+ # @example Has the document had its inclusions loaded?
106
+ # context.inclusions_loaded?(document)
107
+ #
108
+ # @param [ Document ] document The document to check.
109
+ #
110
+ # @return [ true, false ] If the document had it's inclusions loaded.
111
+ #
112
+ # @since 3.0.16
113
+ def inclusions_loaded?(document)
114
+ inclusions_loaded.has_key?(document.id)
115
+ end
116
+
117
+ # Eager load the inclusions for the provided documents.
118
+ #
119
+ # @api private
120
+ #
121
+ # @example Eager load the inclusions.
122
+ # context.load_inclusions(docs)
123
+ #
124
+ # @param [ Array<Document> ] docs The docs returning from the db.
125
+ #
126
+ # @return [ true ] Always true.
127
+ #
128
+ # @since 3.0.16
129
+ def load_inclusions(docs)
130
+ criteria.inclusions.each do |metadata|
131
+ metadata.eager_load(eager_loaded_ids(docs, metadata)) if !docs.empty?
132
+ end
133
+ end
134
+
135
+ # If the provided document exists, eager load its dependencies or return
136
+ # nil.
137
+ #
138
+ # @api private
139
+ #
140
+ # @example Eager load if the document is not nil.
141
+ # context.with_eager_loading(document)
142
+ #
143
+ # @param [ Hash ] document The document from the database.
144
+ #
145
+ # @return [ Document, nil ] The instantiated model document.
146
+ #
147
+ # @since 3.0.0
148
+ def with_eager_loading(document)
149
+ selecting do
150
+ return nil unless document
151
+ doc = Factory.from_db(klass, document, criteria.object_id)
152
+ eager_load_one(doc) if eager_loadable?(doc)
153
+ doc
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -2,6 +2,7 @@
2
2
  require "mongoid/contextual/atomic"
3
3
  require "mongoid/contextual/aggregable/mongo"
4
4
  require "mongoid/contextual/command"
5
+ require "mongoid/contextual/eager"
5
6
  require "mongoid/contextual/find_and_modify"
6
7
  require "mongoid/contextual/map_reduce"
7
8
 
@@ -11,6 +12,7 @@ module Mongoid
11
12
  include Enumerable
12
13
  include Aggregable::Mongo
13
14
  include Atomic
15
+ include Eager
14
16
 
15
17
  # @attribute [r] collection The collection to query against.
16
18
  # @attribute [r] criteria The criteria for the context.
@@ -18,9 +20,6 @@ module Mongoid
18
20
  # @attribute [r] query The Moped query.
19
21
  attr_reader :collection, :criteria, :klass, :query
20
22
 
21
- # @attribute [rw] eager_loaded Has the context been eager loaded?
22
- attr_accessor :eager_loaded
23
-
24
23
  # Is the enumerable of matching documents empty?
25
24
  #
26
25
  # @example Is the context empty?
@@ -66,7 +65,7 @@ module Mongoid
66
65
  # @since 3.0.0
67
66
  def count(document = nil, &block)
68
67
  return super(&block) if block_given?
69
- return query.count unless document
68
+ return (cached? ? @count ||= query.count : query.count) unless document
70
69
  collection.find(criteria.and(_id: document.id).selector).count
71
70
  end
72
71
 
@@ -79,7 +78,7 @@ module Mongoid
79
78
  #
80
79
  # @since 3.0.0
81
80
  def delete
82
- query.count.tap do
81
+ self.count.tap do
83
82
  query.remove_all
84
83
  end
85
84
  end
@@ -94,7 +93,7 @@ module Mongoid
94
93
  #
95
94
  # @since 3.0.0
96
95
  def destroy
97
- destroyed = query.count
96
+ destroyed = self.count
98
97
  each do |doc|
99
98
  doc.destroy
100
99
  end
@@ -242,7 +241,7 @@ module Mongoid
242
241
  #
243
242
  # @since 3.0.0
244
243
  def length
245
- @length ||= query.count
244
+ @length ||= self.count
246
245
  end
247
246
  alias :size :length
248
247
 
@@ -523,56 +522,6 @@ module Mongoid
523
522
  end
524
523
  end
525
524
 
526
- # Eager load the inclusions for the provided documents.
527
- #
528
- # @example Eager load the inclusions.
529
- # context.eager_load(docs)
530
- #
531
- # @param [ Array<Document> ] docs The docs returning from the db.
532
- #
533
- # @return [ true ] Always true.
534
- #
535
- # @since 3.0.0
536
- def eager_load(docs)
537
- criteria.inclusions.reject! do |metadata|
538
- metadata.eager_load(eager_loaded_ids(docs, metadata)) if !docs.empty?
539
- end
540
- self.eager_loaded = true
541
- end
542
-
543
- # Get the ids that to be used to eager load documents.
544
- #
545
- # @api private
546
- #
547
- # @example Get the ids.
548
- # context.eager_load(docs, metadata)
549
- #
550
- # @param [ Array<Document> ] docs The pre-loaded documents.
551
- # @param [ Metadata ] metadata The relation metadata.
552
- #
553
- # @return [ Array<Object> ] The ids.
554
- #
555
- # @since 3.0.0
556
- def eager_loaded_ids(docs, metadata)
557
- if metadata.stores_foreign_key?
558
- docs.flat_map{ |doc| doc.send(metadata.foreign_key) }
559
- else
560
- docs.map(&:id)
561
- end
562
- end
563
-
564
- # Is this context able to be eager loaded?
565
- #
566
- # @example Is the context eager loadable?
567
- # context.eager_loadable?
568
- #
569
- # @return [ true, false ] If the context is able to be eager loaded.
570
- #
571
- # @since 3.0.0
572
- def eager_loadable?
573
- !eager_loaded && !criteria.inclusions.empty?
574
- end
575
-
576
525
  # Apply all the optional criterion.
577
526
  #
578
527
  # @example Apply the options.
@@ -608,26 +557,6 @@ module Mongoid
608
557
  end
609
558
  end
610
559
 
611
- # If the provided document exists, eager load it's dependencies or return
612
- # nil.
613
- #
614
- # @example Eager load if the document is not nil.
615
- # context.with_eager_loading(document)
616
- #
617
- # @param [ Hash ] document The document from the database.
618
- #
619
- # @return [ Document, nil ] The instantiated model document.
620
- #
621
- # @since 3.0.0
622
- def with_eager_loading(document)
623
- selecting do
624
- return nil unless document
625
- doc = Factory.from_db(klass, document, criteria.object_id)
626
- eager_load([ doc ]) if eager_loadable?
627
- doc
628
- end
629
- end
630
-
631
560
  # Yield to the document.
632
561
  #
633
562
  # @api private
@@ -747,7 +747,9 @@ module Mongoid
747
747
  #
748
748
  # @since 3.0.3
749
749
  def type_selectable?
750
- klass.hereditary? && !selector.keys.include?(:_type)
750
+ klass.hereditary? &&
751
+ !selector.keys.include?("_type") &&
752
+ !selector.keys.include?(:_type)
751
753
  end
752
754
 
753
755
  # Get the selector for type selection.
@@ -153,6 +153,18 @@ module Mongoid
153
153
  end
154
154
  end
155
155
 
156
+ # Return the model name of the document.
157
+ #
158
+ # @example Return the model name.
159
+ # document.model_name
160
+ #
161
+ # @return [ String ] The model name.
162
+ #
163
+ # @since 3.0.16
164
+ def model_name
165
+ self.class.model_name
166
+ end
167
+
156
168
  # Return the key value for the document.
157
169
  #
158
170
  # @example Return the key.
@@ -99,6 +99,9 @@ module Mongoid
99
99
  opts = options || {}
100
100
  opts[:dropDups] = opts.delete(:drop_dups) if opts.has_key?(:drop_dups)
101
101
  opts[:bucketSize] = opts.delete(:bucket_size) if opts.has_key?(:bucket_size)
102
+ if opts.has_key?(:expire_after_seconds)
103
+ opts[:expireAfterSeconds] = opts.delete(:expire_after_seconds)
104
+ end
102
105
  opts
103
106
  end
104
107
 
@@ -7,7 +7,19 @@ module Mongoid
7
7
  module Options
8
8
  extend self
9
9
 
10
- VALID_OPTIONS = [ :background, :drop_dups, :name, :sparse, :unique, :max, :min, :bits, :bucket_size ]
10
+ VALID_OPTIONS = [
11
+ :background,
12
+ :drop_dups,
13
+ :name,
14
+ :sparse,
15
+ :unique,
16
+ :max,
17
+ :min,
18
+ :bits,
19
+ :bucket_size,
20
+ :expire_after_seconds
21
+ ]
22
+
11
23
  VALID_TYPES = [ 1, -1, "2d", "geoHaystack" ]
12
24
 
13
25
  # Validate the index specification.
@@ -80,6 +80,7 @@ module Mongoid
80
80
  def destroyed?
81
81
  (@destroyed ||= false) || !!deleted_at
82
82
  end
83
+ alias :deleted? :destroyed?
83
84
 
84
85
  # Restores a previously soft-deleted document. Handles this by removing the
85
86
  # deleted_at flag.
@@ -91,6 +91,43 @@ module Mongoid
91
91
 
92
92
  private
93
93
 
94
+ # Get the relation. Extracted out from the getter method to avoid
95
+ # infinite recursion when overriding the getter.
96
+ #
97
+ # @api private
98
+ #
99
+ # @example Get the relation.
100
+ # document.get_relation(:name, metadata)
101
+ #
102
+ # @param [ Symbol ] name The name of the relation.
103
+ # @param [ Metadata ] metadata The relation metadata.
104
+ # @param [ true, false ] reload If the relation is to be reloaded.
105
+ #
106
+ # @return [ Proxy ] The relation.
107
+ #
108
+ # @since 3.0.16
109
+ def get_relation(name, metadata, reload = false)
110
+ variable = "@#{name}"
111
+ value = if instance_variable_defined?(variable) && !reload
112
+ instance_variable_get(variable)
113
+ else
114
+ _building do
115
+ _loading do
116
+ # @note In the case where the record is new and we are binding,
117
+ # we want to avoid an extra database query when we know it is not
118
+ # necessary.
119
+ key = (new_record? && _binding?) ? nil : attributes[metadata.key]
120
+ __build__(name, key, metadata)
121
+ end
122
+ end
123
+ end
124
+ if value.nil? && metadata.autobuilding? && !without_autobuild?
125
+ send("build_#{name}")
126
+ else
127
+ value
128
+ end
129
+ end
130
+
94
131
  # Is the current code executing without autobuild functionality?
95
132
  #
96
133
  # @example Is autobuild disabled?
@@ -162,20 +199,8 @@ module Mongoid
162
199
  #
163
200
  # @since 2.0.0.rc.1
164
201
  def getter(name, metadata)
165
- re_define_method(name) do |*args|
166
- reload, variable = args.first, "@#{name}"
167
- value = if instance_variable_defined?(variable) && !reload
168
- instance_variable_get(variable)
169
- else
170
- _building do
171
- _loading { __build__(name, attributes[metadata.key], metadata) }
172
- end
173
- end
174
- if value.nil? && metadata.autobuilding? && !without_autobuild?
175
- send("build_#{name}")
176
- else
177
- value
178
- end
202
+ re_define_method(name) do |reload = false|
203
+ get_relation(name, metadata, reload)
179
204
  end
180
205
  self
181
206
  end
@@ -216,8 +241,8 @@ module Mongoid
216
241
  def setter(name, metadata)
217
242
  re_define_method("#{name}=") do |object|
218
243
  without_autobuild do
219
- if metadata.many? || send(name)
220
- set_relation(name, send(name).substitute(object.substitutable))
244
+ if metadata.many? || get_relation(name, metadata)
245
+ set_relation(name, get_relation(name, metadata).substitute(object.substitutable))
221
246
  else
222
247
  __build__(name, object.substitutable, metadata)
223
248
  end
@@ -48,8 +48,7 @@ module Mongoid
48
48
 
49
49
  # Set up the autosave behaviour for references many and references one
50
50
  # relations. When the option is set to true, these relations will get
51
- # saved automatically when the parent is first saved, but not if the
52
- # parent already exists in the database.
51
+ # saved automatically when the parent saved, if they are dirty.
53
52
  #
54
53
  # @example Set up autosave options.
55
54
  # Person.autosave(metadata)
@@ -59,7 +59,7 @@ module Mongoid
59
59
  #
60
60
  # @since 2.4.0
61
61
  def concat(docs)
62
- batch_insert(docs)
62
+ batch_insert(docs) unless docs.empty?
63
63
  self
64
64
  end
65
65
 
@@ -302,7 +302,7 @@ module Mongoid
302
302
  def unscoped
303
303
  criterion = klass.unscoped
304
304
  criterion.embedded = true
305
- criterion.documents = _unscoped
305
+ criterion.documents = _unscoped.delete_if(&:marked_for_destruction?)
306
306
  criterion
307
307
  end
308
308
 
@@ -165,8 +165,8 @@ module Mongoid
165
165
  else
166
166
  _unloaded.each do |doc|
167
167
  document = _added.delete(doc.id) || _loaded.delete(doc.id) || doc
168
- yield(document)
169
168
  _loaded[document.id] = document
169
+ yield(document)
170
170
  end
171
171
  end
172
172
  _added.each_pair do |id, doc|
@@ -267,7 +267,9 @@ module Mongoid
267
267
  # @since 2.4.10
268
268
  def validate_root(document, attribute, value)
269
269
  criteria = create_criteria(klass, document, attribute, value)
270
- add_error(document, attribute, value) if criteria.exists?
270
+ if criteria.with(consistency: :strong).exists?
271
+ add_error(document, attribute, value)
272
+ end
271
273
  end
272
274
 
273
275
  # Are we required to validate the document?
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid
3
- VERSION = "3.0.15"
3
+ VERSION = "3.0.16"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.15
4
+ version: 3.0.16
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-17 00:00:00.000000000 Z
12
+ date: 2012-12-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -107,6 +107,7 @@ files:
107
107
  - lib/mongoid/contextual/aggregable/mongo.rb
108
108
  - lib/mongoid/contextual/atomic.rb
109
109
  - lib/mongoid/contextual/command.rb
110
+ - lib/mongoid/contextual/eager.rb
110
111
  - lib/mongoid/contextual/find_and_modify.rb
111
112
  - lib/mongoid/contextual/map_reduce.rb
112
113
  - lib/mongoid/contextual/memory.rb