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

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