mongoid 2.0.0.rc.7 → 2.0.0.rc.8

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 (90) hide show
  1. data/lib/config/locales/en.yml +3 -0
  2. data/lib/config/locales/id.yml +46 -0
  3. data/lib/config/locales/ja.yml +40 -0
  4. data/lib/config/locales/vi.yml +45 -0
  5. data/lib/mongoid.rb +5 -3
  6. data/lib/mongoid/attributes.rb +24 -63
  7. data/lib/mongoid/attributes/processing.rb +5 -2
  8. data/lib/mongoid/callbacks.rb +10 -0
  9. data/lib/mongoid/collection.rb +24 -0
  10. data/lib/mongoid/collections/master.rb +14 -6
  11. data/lib/mongoid/collections/operations.rb +1 -1
  12. data/lib/mongoid/collections/retry.rb +39 -0
  13. data/lib/mongoid/collections/slaves.rb +26 -10
  14. data/lib/mongoid/components.rb +4 -4
  15. data/lib/mongoid/config.rb +6 -3
  16. data/lib/mongoid/contexts.rb +0 -1
  17. data/lib/mongoid/contexts/enumerable.rb +19 -7
  18. data/lib/mongoid/contexts/mongo.rb +9 -5
  19. data/lib/mongoid/copyable.rb +10 -8
  20. data/lib/mongoid/criteria.rb +83 -61
  21. data/lib/mongoid/criterion/builder.rb +34 -0
  22. data/lib/mongoid/criterion/creational.rb +2 -2
  23. data/lib/mongoid/criterion/exclusion.rb +58 -32
  24. data/lib/mongoid/criterion/inclusion.rb +49 -10
  25. data/lib/mongoid/criterion/optional.rb +1 -1
  26. data/lib/mongoid/criterion/selector.rb +80 -11
  27. data/lib/mongoid/cursor.rb +6 -1
  28. data/lib/mongoid/default_scope.rb +27 -19
  29. data/lib/mongoid/document.rb +26 -1
  30. data/lib/mongoid/errors.rb +1 -0
  31. data/lib/mongoid/errors/mixed_relations.rb +37 -0
  32. data/lib/mongoid/extensions/object_id/conversions.rb +7 -4
  33. data/lib/mongoid/factory.rb +1 -1
  34. data/lib/mongoid/field.rb +47 -30
  35. data/lib/mongoid/fields.rb +9 -2
  36. data/lib/mongoid/finders.rb +15 -49
  37. data/lib/mongoid/identity.rb +6 -4
  38. data/lib/mongoid/keys.rb +85 -31
  39. data/lib/mongoid/multi_parameter_attributes.rb +2 -2
  40. data/lib/mongoid/named_scope.rb +129 -28
  41. data/lib/mongoid/observer.rb +36 -0
  42. data/lib/mongoid/paranoia.rb +3 -3
  43. data/lib/mongoid/paths.rb +1 -1
  44. data/lib/mongoid/persistence.rb +2 -0
  45. data/lib/mongoid/persistence/atomic.rb +88 -0
  46. data/lib/mongoid/persistence/atomic/add_to_set.rb +30 -0
  47. data/lib/mongoid/persistence/atomic/inc.rb +28 -0
  48. data/lib/mongoid/persistence/atomic/operation.rb +44 -0
  49. data/lib/mongoid/persistence/atomic/pull_all.rb +33 -0
  50. data/lib/mongoid/persistence/atomic/push.rb +28 -0
  51. data/lib/mongoid/railtie.rb +13 -1
  52. data/lib/mongoid/relations.rb +1 -0
  53. data/lib/mongoid/relations/accessors.rb +20 -2
  54. data/lib/mongoid/relations/builders/embedded/one.rb +1 -0
  55. data/lib/mongoid/relations/builders/nested_attributes/many.rb +17 -6
  56. data/lib/mongoid/relations/builders/referenced/many.rb +2 -1
  57. data/lib/mongoid/relations/builders/referenced/one.rb +1 -0
  58. data/lib/mongoid/relations/embedded/atomic.rb +86 -0
  59. data/lib/mongoid/relations/embedded/atomic/operation.rb +63 -0
  60. data/lib/mongoid/relations/embedded/atomic/pull.rb +65 -0
  61. data/lib/mongoid/relations/embedded/atomic/push_all.rb +59 -0
  62. data/lib/mongoid/relations/embedded/atomic/set.rb +61 -0
  63. data/lib/mongoid/relations/embedded/atomic/unset.rb +41 -0
  64. data/lib/mongoid/relations/embedded/many.rb +57 -25
  65. data/lib/mongoid/relations/macros.rb +6 -4
  66. data/lib/mongoid/relations/many.rb +51 -10
  67. data/lib/mongoid/relations/metadata.rb +4 -2
  68. data/lib/mongoid/relations/proxy.rb +39 -24
  69. data/lib/mongoid/relations/referenced/many.rb +47 -26
  70. data/lib/mongoid/relations/referenced/many_to_many.rb +47 -14
  71. data/lib/mongoid/relations/referenced/one.rb +14 -0
  72. data/lib/mongoid/sharding.rb +51 -0
  73. data/lib/mongoid/state.rb +3 -2
  74. data/lib/mongoid/timestamps.rb +5 -29
  75. data/lib/mongoid/timestamps/created.rb +31 -0
  76. data/lib/mongoid/timestamps/updated.rb +33 -0
  77. data/lib/mongoid/validations.rb +10 -3
  78. data/lib/mongoid/validations/referenced.rb +58 -0
  79. data/lib/mongoid/version.rb +1 -1
  80. data/lib/mongoid/versioning.rb +67 -5
  81. data/lib/rails/generators/mongoid/model/templates/model.rb +2 -0
  82. data/lib/rails/generators/mongoid/observer/observer_generator.rb +17 -0
  83. data/lib/rails/generators/mongoid/observer/templates/observer.rb +4 -0
  84. data/lib/rails/generators/mongoid_generator.rb +10 -1
  85. data/lib/rails/mongoid.rb +1 -0
  86. metadata +29 -8
  87. data/lib/mongoid/contexts/ids.rb +0 -25
  88. data/lib/mongoid/modifiers.rb +0 -24
  89. data/lib/mongoid/modifiers/command.rb +0 -18
  90. data/lib/mongoid/modifiers/inc.rb +0 -24
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Persistence #:nodoc:
4
+ module Atomic #:nodoc:
5
+
6
+ # This class provides the ability to perform an explicit $addToSet
7
+ # modification on a specific field.
8
+ class AddToSet < Operation
9
+
10
+ # Sends the atomic $addToSet operation to the database.
11
+ #
12
+ # @example Persist the new values.
13
+ # addToSet.persist
14
+ #
15
+ # @return [ Object ] The new array value.
16
+ #
17
+ # @since 2.0.0
18
+ def persist
19
+ document[field] = [] unless document[field]
20
+ values = document.send(field)
21
+ values.push(value) unless values.include?(value)
22
+ values.tap do
23
+ document.collection.update(document._selector, operation("$addToSet"), options)
24
+ document.changes.delete(field.to_s) if document.persisted?
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Persistence #:nodoc:
4
+ module Atomic #:nodoc:
5
+
6
+ # This class provides atomic $inc behaviour.
7
+ class Inc < Operation
8
+
9
+ # Sends the atomic $inc operation to the database.
10
+ #
11
+ # @example Persist the new values.
12
+ # inc.persist
13
+ #
14
+ # @return [ Object ] The new integer value.
15
+ #
16
+ # @since 2.0.0
17
+ def persist
18
+ current = document[field] || 0
19
+ document[field] = current + value
20
+ document[field].tap do
21
+ document.collection.update(document._selector, operation("$inc"), options)
22
+ document.changes.delete(field.to_s)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Persistence #:nodoc:
4
+ module Atomic #:nodoc:
5
+
6
+ # This is the superclass for all atomic operation objects.
7
+ class Operation
8
+ include Mongoid::Safe
9
+
10
+ attr_reader :document, :field, :value, :options
11
+
12
+ # Initialize the new pullAll operation.
13
+ #
14
+ # @example Create a new pullAll operation.
15
+ # PullAll.new(document, :aliases, [ "Bond" ])
16
+ #
17
+ # @param [ Document ] document The document to pullAll onto.
18
+ # @param [ Symbol ] field The name of the array field.
19
+ # @param [ Object ] value The value to pullAll.
20
+ # @param [ Hash ] options The persistence options.
21
+ #
22
+ # @since 2.0.0
23
+ def initialize(document, field, value, options = {})
24
+ @document, @field, @value = document, field, value
25
+ @options = { :safe => safe_mode?(options) }
26
+ end
27
+
28
+ # Get the atomic operation to perform.
29
+ #
30
+ # @example Get the operation.
31
+ # inc.operation
32
+ #
33
+ # @param [ String ] modifier The modifier to use.
34
+ #
35
+ # @return [ Hash ] The atomic operation for the field and addition.
36
+ #
37
+ # @since 2.0.0
38
+ def operation(modifier)
39
+ { modifier => { field => value } }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Persistence #:nodoc:
4
+ module Atomic #:nodoc:
5
+
6
+ # This class provides the ability to perform an explicit $pullAll
7
+ # modification on a specific field.
8
+ class PullAll < Operation
9
+
10
+ # Sends the atomic $pullAll operation to the database.
11
+ #
12
+ # @example Persist the new values.
13
+ # pull_all.persist
14
+ #
15
+ # @return [ Object ] The new array value.
16
+ #
17
+ # @since 2.0.0
18
+ def persist
19
+ if document[field]
20
+ values = document.send(field)
21
+ values.delete_if { |val| value.include?(val) }
22
+ values.tap do
23
+ document.collection.update(document._selector, operation("$pullAll"), options)
24
+ document.changes.delete(field.to_s) if document.persisted?
25
+ end
26
+ else
27
+ return nil
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Persistence #:nodoc:
4
+ module Atomic #:nodoc:
5
+
6
+ # This class provides the ability to perform an explicit $push modification
7
+ # on a specific field.
8
+ class Push < Operation
9
+
10
+ # Sends the atomic $push operation to the database.
11
+ #
12
+ # @example Persist the new values.
13
+ # push.persist
14
+ #
15
+ # @return [ Object ] The new array value.
16
+ #
17
+ # @since 2.0.0
18
+ def persist
19
+ document[field] = [] unless document[field]
20
+ document.send(field).push(value).tap do |value|
21
+ document.collection.update(document._selector, operation("$push"), options)
22
+ document.changes.delete(field.to_s)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -35,7 +35,7 @@ module Rails #:nodoc:
35
35
  # module MyApplication
36
36
  # class Application < Rails::Application
37
37
  # config.mongoid.logger = Logger.new($stdout, :warn)
38
- # config.mongoid.reconnect_time = 10
38
+ # config.mongoid.persist_in_safe_mode = true
39
39
  # end
40
40
  # end
41
41
  config.mongoid = ::Mongoid::Config
@@ -124,6 +124,18 @@ module Rails #:nodoc:
124
124
  end
125
125
  end
126
126
  end
127
+
128
+ # Instantitate any registered observers after Rails initialization and
129
+ # instantiate them after being reloaded in the development environment
130
+ initializer "instantiate observers" do
131
+ config.after_initialize do
132
+ ::Mongoid.instantiate_observers
133
+
134
+ ActionDispatch::Callbacks.to_prepare(:mongoid_instantiate_observers) do
135
+ ::Mongoid.instantiate_observers
136
+ end
137
+ end
138
+ end
127
139
  end
128
140
  end
129
141
  end
@@ -10,6 +10,7 @@ require "mongoid/relations/builders"
10
10
  require "mongoid/relations/many"
11
11
  require "mongoid/relations/one"
12
12
  require "mongoid/relations/polymorphic"
13
+ require "mongoid/relations/embedded/atomic"
13
14
  require "mongoid/relations/embedded/in"
14
15
  require "mongoid/relations/embedded/many"
15
16
  require "mongoid/relations/embedded/one"
@@ -90,6 +90,20 @@ module Mongoid # :nodoc:
90
90
  instance_variable_set("@#{name}", relation)
91
91
  end
92
92
 
93
+ # Replace an existing relation with a new one.
94
+ #
95
+ # @example Replace the relation.
96
+ # document.substitute("addresses", Address.new)
97
+ #
98
+ # @param [ String ] name The name of the relation.
99
+ # @param [ Document ] object The document to replace with.
100
+ # @options [ Hash ] options The options.
101
+ #
102
+ # @since 2.0.0
103
+ def substitute(name, object, options)
104
+ set(name, ivar(name).substitute(object, options))
105
+ end
106
+
93
107
  module ClassMethods #:nodoc:
94
108
 
95
109
  # Defines the getter for the relation. Nothing too special here: just
@@ -144,9 +158,13 @@ module Mongoid # :nodoc:
144
158
  object, options = args.first, options(args)
145
159
  variable = "@#{name}"
146
160
  if relation_exists?(name) && !object.is_a?(Hash)
147
- set(name, ivar(name).substitute(object, options))
161
+ substitute(name, object, options)
148
162
  else
149
- build(name, object, metadata, options.merge(:eager => true))
163
+ if metadata.embedded? && object.blank? && send(name)
164
+ substitute(name, object, options)
165
+ else
166
+ build(name, object, metadata, options.merge(:eager => true))
167
+ end
150
168
  end
151
169
  end
152
170
  end
@@ -17,6 +17,7 @@ module Mongoid # :nodoc:
17
17
  # @return [ Document ] A single document.
18
18
  def build(type = nil)
19
19
  return object unless object.is_a?(Hash)
20
+ object.delete(:binding)
20
21
  Mongoid::Factory.build(metadata.klass, object)
21
22
  end
22
23
  end
@@ -24,10 +24,16 @@ module Mongoid # :nodoc:
24
24
  if over_limit?(attributes)
25
25
  raise Errors::TooManyNestedAttributeRecords.new(existing, options[:limit])
26
26
  end
27
- attributes.each { |attrs| process(attrs[1]) }
27
+ attributes.each do |attrs|
28
+ if attrs.respond_to?(:with_indifferent_access)
29
+ process(attrs)
30
+ else
31
+ process(attrs[1])
32
+ end
33
+ end
28
34
  end
29
35
 
30
- # Create the new builder for nested attributes on one-to-one
36
+ # Create the new builder for nested attributes on one-to-many
31
37
  # relations.
32
38
  #
33
39
  # Example:
@@ -44,8 +50,12 @@ module Mongoid # :nodoc:
44
50
  #
45
51
  # A new builder.
46
52
  def initialize(metadata, attributes, options = {})
47
- @attributes = attributes.with_indifferent_access.sort do |a, b|
48
- a[0] <=> b[0]
53
+ if attributes.respond_to?(:with_indifferent_access)
54
+ @attributes = attributes.with_indifferent_access.sort do |a, b|
55
+ a[0].to_i <=> b[0].to_i
56
+ end
57
+ else
58
+ @attributes = attributes
49
59
  end
50
60
  @metadata = metadata
51
61
  @options = options
@@ -102,8 +112,9 @@ module Mongoid # :nodoc:
102
112
  # attrs: The single document attributes to process.
103
113
  def process(attrs)
104
114
  return if reject?(attrs)
105
- if attrs[:id]
106
- document = existing.find(convert_id(attrs[:id]))
115
+ if attrs[:id] or attrs['id'] or attrs['_id']
116
+ id = attrs[:id] || attrs['id'] || attrs['_id']
117
+ document = existing.find(convert_id(id))
107
118
  destroyable?(attrs) ? document.destroy : document.update_attributes(attrs)
108
119
  else
109
120
  # @todo: Durran: Tell the push not to save the base and call it
@@ -16,8 +16,9 @@ module Mongoid # :nodoc:
16
16
  # @return [ Array<Document> ] The documents.
17
17
  def build(type = nil)
18
18
  return object unless query?
19
+ return [] if object.is_a?(Array)
19
20
  key = metadata.foreign_key
20
- metadata.klass.find(:conditions => { key => object })
21
+ metadata.klass.all(:conditions => { key => object })
21
22
  end
22
23
  end
23
24
  end
@@ -17,6 +17,7 @@ module Mongoid # :nodoc:
17
17
  def build(type = nil)
18
18
  return object unless query?
19
19
  if object.is_a?(Hash)
20
+ object.delete(:binding)
20
21
  return Mongoid::Factory.build(metadata.klass, object)
21
22
  end
22
23
  metadata.klass.first(
@@ -0,0 +1,86 @@
1
+ # encoding: utf-8
2
+ require "mongoid/relations/embedded/atomic/operation"
3
+ require "mongoid/relations/embedded/atomic/pull"
4
+ require "mongoid/relations/embedded/atomic/push_all"
5
+ require "mongoid/relations/embedded/atomic/set"
6
+ require "mongoid/relations/embedded/atomic/unset"
7
+
8
+ module Mongoid #:nodoc:
9
+ module Relations #:nodoc:
10
+ module Embedded #:nodoc:
11
+
12
+ # This module provides the ability for calls to be declared atomic.
13
+ module Atomic
14
+
15
+ MODIFIERS = {
16
+ :$pull => Pull,
17
+ :$pushAll => PushAll,
18
+ :$set => Set,
19
+ :$unset => Unset
20
+ }
21
+
22
+ private
23
+
24
+ # Executes a block of commands in an atomic fashion. Mongoid will
25
+ # intercept all database upserts while in this block and combine them
26
+ # into a single database call. When the block concludes the atomic
27
+ # update will occur.
28
+ #
29
+ # Since the collection is accessed through the class it would not be
30
+ # thread safe to give it state so we access the atomic updater via the
31
+ # current thread.
32
+ #
33
+ # @note This operation is not safe when attemping to do illegal updates
34
+ # for different objects or collections, since the updator is not
35
+ # scoped on the thread. This is meant for Mongoid internal use only
36
+ # to keep existing design clean.
37
+ #
38
+ # @example Atomically $set multiple saves.
39
+ # atomically(:$set) do
40
+ # address_one.save!
41
+ # address_two.save!
42
+ # end
43
+ #
44
+ # @example Atomically $pushAll multiple new docs.
45
+ # atomically(:$pushAll) do
46
+ # person.addresses.push([ address_one, address_two ])
47
+ # end
48
+ #
49
+ # @param [ Symbol ] modifier The atomic modifier to perform.
50
+ # @param [ Proc ] block The block to execute.
51
+ #
52
+ # @return [ Object ] The result of the operation.
53
+ #
54
+ # @since 2.0.0
55
+ def atomically(modifier, &block)
56
+ updater = Thread.current[:mongoid_atomic_update] ||= MODIFIERS[modifier].new
57
+ count_executions do
58
+ block.call if block
59
+ end.tap do
60
+ if @executions.zero?
61
+ Thread.current[:mongoid_atomic_update] = nil
62
+ updater.execute(collection)
63
+ end
64
+ end
65
+ end
66
+
67
+ # Execute the block, incrementing the executions before the call and
68
+ # decrementing them after in order to be able to nest blocks within
69
+ # each other.
70
+ #
71
+ # @example Execute and increment.
72
+ # execute { block.call }
73
+ #
74
+ # @param [ Proc ] block The block to call.
75
+ #
76
+ # @since 2.0.0
77
+ def count_executions(&block)
78
+ @executions ||= 0 and @executions += 1
79
+ block.call.tap do
80
+ @executions -=1
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Relations #:nodoc:
4
+ module Embedded #:nodoc:
5
+ module Atomic #:nodoc:
6
+
7
+ class Operation
8
+ attr_accessor :documents, :options, :path, :selector
9
+
10
+ # Consumes an execution that was supposed to hit the database, but is
11
+ # now being deferred to later in favor of a single update.
12
+ #
13
+ # @example Consume the operation.
14
+ # set.consume(
15
+ # { "_id" => BSON::ObjectId.new },
16
+ # { "$push" => { "addresses" => { "_id" => "street" } } },
17
+ # { :multi => false, :safe => true }
18
+ # )
19
+ #
20
+ # @param [ Hash ] selector The document selector.
21
+ # @param [ Hash ] operations The ops to set in the db.
22
+ # @param [ Hash ] options The persistence options.
23
+ #
24
+ # @option options [ true, false ] :multi Persist multiple at once.
25
+ # @option options [ true, false ] :safe Persist in safe mode.
26
+ #
27
+ # @since 2.0.0
28
+ def consume(selector, operations, options = {})
29
+ @consumed, @selector, @options = true, selector, options
30
+ @documents ||= []
31
+ parse(operations)
32
+ end
33
+
34
+ # Has this operation consumed any executions?
35
+ #
36
+ # @example Is this consumed?
37
+ # unset.consumed?
38
+ #
39
+ # @return [ true, false ] If the operation has consumed anything.
40
+ #
41
+ # @since 2.0.0
42
+ def consumed?
43
+ !!@consumed
44
+ end
45
+
46
+ # Execute the $pushAll operation on the collection.
47
+ #
48
+ # @example Execute the operation.
49
+ # unset.execute(collection)
50
+ #
51
+ # @param [ Collection ] collection The root collection.
52
+ #
53
+ # @since 2.0.0
54
+ def execute(collection)
55
+ if collection && consumed?
56
+ collection.update(selector, operations, options)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end