mongoid 2.0.0.rc.6 → 2.0.0.rc.7

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 (64) hide show
  1. data/Rakefile +2 -2
  2. data/lib/config/locales/bg.yml +3 -6
  3. data/lib/config/locales/de.yml +2 -5
  4. data/lib/config/locales/en.yml +2 -5
  5. data/lib/config/locales/es.yml +2 -5
  6. data/lib/config/locales/fr.yml +2 -5
  7. data/lib/config/locales/hu.yml +2 -5
  8. data/lib/config/locales/it.yml +2 -5
  9. data/lib/config/locales/kr.yml +2 -5
  10. data/lib/config/locales/nl.yml +2 -5
  11. data/lib/config/locales/pl.yml +2 -5
  12. data/lib/config/locales/pt-br.yml +2 -5
  13. data/lib/config/locales/pt.yml +2 -5
  14. data/lib/config/locales/ro.yml +2 -5
  15. data/lib/config/locales/ru.yml +41 -0
  16. data/lib/config/locales/sv.yml +2 -5
  17. data/lib/config/locales/zh-CN.yml +3 -6
  18. data/lib/mongoid/atomicity.rb +2 -2
  19. data/lib/mongoid/attributes.rb +40 -69
  20. data/lib/mongoid/attributes/processing.rb +142 -0
  21. data/lib/mongoid/collections.rb +1 -0
  22. data/lib/mongoid/components.rb +1 -1
  23. data/lib/mongoid/config.rb +0 -1
  24. data/lib/mongoid/contexts/mongo.rb +13 -9
  25. data/lib/mongoid/criteria.rb +15 -1
  26. data/lib/mongoid/criterion/inclusion.rb +36 -11
  27. data/lib/mongoid/criterion/inspection.rb +3 -1
  28. data/lib/mongoid/criterion/optional.rb +2 -2
  29. data/lib/mongoid/dirty.rb +1 -0
  30. data/lib/mongoid/document.rb +11 -5
  31. data/lib/mongoid/extensions/integer/conversions.rb +2 -2
  32. data/lib/mongoid/extensions/object_id/conversions.rb +62 -32
  33. data/lib/mongoid/field.rb +5 -2
  34. data/lib/mongoid/identity.rb +2 -1
  35. data/lib/mongoid/matchers.rb +5 -32
  36. data/lib/mongoid/matchers/default.rb +53 -10
  37. data/lib/mongoid/matchers/or.rb +30 -0
  38. data/lib/mongoid/matchers/strategies.rb +63 -0
  39. data/lib/mongoid/multi_parameter_attributes.rb +4 -2
  40. data/lib/mongoid/nested_attributes.rb +8 -0
  41. data/lib/mongoid/persistence.rb +2 -1
  42. data/lib/mongoid/persistence/insert.rb +3 -3
  43. data/lib/mongoid/persistence/insert_embedded.rb +2 -1
  44. data/lib/mongoid/relations.rb +2 -1
  45. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +4 -2
  46. data/lib/mongoid/relations/builders/nested_attributes/many.rb +2 -0
  47. data/lib/mongoid/relations/cascading.rb +1 -1
  48. data/lib/mongoid/relations/cascading/nullify.rb +1 -1
  49. data/lib/mongoid/relations/constraint.rb +42 -0
  50. data/lib/mongoid/relations/cyclic.rb +6 -0
  51. data/lib/mongoid/relations/embedded/many.rb +4 -4
  52. data/lib/mongoid/relations/macros.rb +1 -1
  53. data/lib/mongoid/relations/many.rb +1 -0
  54. data/lib/mongoid/relations/metadata.rb +12 -4
  55. data/lib/mongoid/relations/nested_builder.rb +1 -3
  56. data/lib/mongoid/relations/referenced/many.rb +11 -18
  57. data/lib/mongoid/relations/referenced/many_to_many.rb +19 -32
  58. data/lib/mongoid/relations/referenced/one.rb +3 -1
  59. data/lib/mongoid/timestamps.rb +1 -1
  60. data/lib/mongoid/validations/associated.rb +11 -9
  61. data/lib/mongoid/validations/uniqueness.rb +1 -1
  62. data/lib/mongoid/version.rb +1 -1
  63. data/lib/mongoid/versioning.rb +2 -2
  64. metadata +9 -4
@@ -51,8 +51,11 @@ module Mongoid #:nodoc:
51
51
  unless options[:identity]
52
52
  type.set(object)
53
53
  else
54
- metadata = options[:metadata]
55
- object.blank? ? type.set(object) : BSON::ObjectId.cast!(metadata.inverse_klass, object)
54
+ if object.blank?
55
+ type.set(object)
56
+ else
57
+ options[:metadata].constraint.convert(object)
58
+ end
56
59
  end
57
60
  end
58
61
 
@@ -12,7 +12,8 @@ module Mongoid #:nodoc:
12
12
  # @example Create the id and set the type.
13
13
  # identity.create
14
14
  def create
15
- identify and type
15
+ identify
16
+ type
16
17
  end
17
18
 
18
19
  # Create the new identity generator - this will be expanded in the future
@@ -1,15 +1,5 @@
1
1
  # encoding: utf-8
2
- require "mongoid/matchers/default"
3
- require "mongoid/matchers/all"
4
- require "mongoid/matchers/exists"
5
- require "mongoid/matchers/gt"
6
- require "mongoid/matchers/gte"
7
- require "mongoid/matchers/in"
8
- require "mongoid/matchers/lt"
9
- require "mongoid/matchers/lte"
10
- require "mongoid/matchers/ne"
11
- require "mongoid/matchers/nin"
12
- require "mongoid/matchers/size"
2
+ require "mongoid/matchers/strategies"
13
3
 
14
4
  module Mongoid #:nodoc:
15
5
 
@@ -28,28 +18,11 @@ module Mongoid #:nodoc:
28
18
  # @return [ true, false ] True if matches, false if not.
29
19
  def matches?(selector)
30
20
  selector.each_pair do |key, value|
31
- return false unless matcher(key, value).matches?(value)
32
- end; true
33
- end
34
-
35
- protected
36
-
37
- # Get the matcher for the supplied key and value. Will determine the class
38
- # name from the key.
39
- #
40
- # @example Get the matcher.
41
- # document.matcher(:title, { "$in" => [ "test" ] })
42
- #
43
- # @param [ Symbol, String ] key The field name.
44
- # @param [ Object, Hash ] The value or selector.
45
- #
46
- # @return [ Matcher ] The matcher.
47
- def matcher(key, value)
48
- if value.is_a?(Hash)
49
- name = "Mongoid::Matchers::#{value.keys.first.gsub("$", "").camelize}"
50
- return name.constantize.new(attributes[key])
21
+ unless Strategies.matcher(self, key, value).matches?(value)
22
+ return false
23
+ end
51
24
  end
52
- Default.new(attributes[key])
25
+ return true
53
26
  end
54
27
  end
55
28
  end
@@ -1,26 +1,69 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
3
  module Matchers #:nodoc:
4
+
5
+ # Contains all the default behavior for checking for matching documents
6
+ # given MongoDB expressions.
4
7
  class Default
8
+
9
+ attr_accessor :attribute, :document
10
+
5
11
  # Creating a new matcher only requires the value.
6
- def initialize(attribute)
7
- @attribute = attribute
12
+ #
13
+ # @example Create a new matcher.
14
+ # Default.new("attribute")
15
+ #
16
+ # @param [ Object ] attribute The current attribute to check against.
17
+ #
18
+ # @since 1.0.0
19
+ def initialize(attribute, document = nil)
20
+ @attribute, @document = attribute, document
8
21
  end
9
- # Return true if the attribute and value are equal.
22
+
23
+ # Return true if the attribute and value are equal, or if it is an array
24
+ # if the value is included.
25
+ #
26
+ # @example Does this value match?
27
+ # default.matches?("value")
28
+ #
29
+ # @param [ Object ] value The value to check if it matches.
30
+ #
31
+ # @return [ true, false ] True if matches, false if not.
32
+ #
33
+ # @since 1.0.0
10
34
  def matches?(value)
11
- @attribute.is_a?(Array) && value.is_a?(String) ?
12
- @attribute.include?(value) : value === @attribute
35
+ attribute.is_a?(Array) ? attribute.include?(value) : value === attribute
13
36
  end
14
37
 
15
38
  protected
16
- # Return the first value in the hash.
17
- def first(value)
18
- value.values.first
39
+
40
+ # Convenience method for getting the first value in a hash.
41
+ #
42
+ # @example Get the first value.
43
+ # matcher.first(:test => "value")
44
+ #
45
+ # @param [ Hash ] hash The has to pull from.
46
+ #
47
+ # @return [ Object ] The first value.
48
+ #
49
+ # @since 1.0.0
50
+ def first(hash)
51
+ hash.values.first
19
52
  end
20
53
 
21
- # If object exists then compare, else return false
54
+ # If object exists then compare the two, otherwise return false
55
+ #
56
+ # @example Determine if we can compare.
57
+ # matcher.determine("test", "$in")
58
+ #
59
+ # @param [ Object ] value The value to compare with.
60
+ # @param [ Symbol, String ] operator The comparison operation.
61
+ #
62
+ # @return [ true, false ] The comparison or false.
63
+ #
64
+ # @since 1.0.0
22
65
  def determine(value, operator)
23
- @attribute ? @attribute.send(operator, first(value)) : false
66
+ attribute ? attribute.send(operator, first(value)) : false
24
67
  end
25
68
  end
26
69
  end
@@ -0,0 +1,30 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Matchers #:nodoc:
4
+
5
+ # Defines behavior for handling $or expressions in embedded documents.
6
+ class Or < Default
7
+
8
+ # Does the supplied query match the attribute?
9
+ #
10
+ # @example Does this match?
11
+ # matcher.matches?("$or" => [ { field => value } ])
12
+ #
13
+ # @param [ Array ] conditions The or expression.
14
+ #
15
+ # @return [ true, false ] True if matches, false if not.
16
+ #
17
+ # @since 2.0.0.rc.7
18
+ def matches?(conditions)
19
+ conditions.each do |condition|
20
+ key = condition.keys.first
21
+ value = condition.values.first
22
+ if Strategies.matcher(document, key, value).matches?(value)
23
+ return true
24
+ end
25
+ end
26
+ return false
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+ require "mongoid/matchers/default"
3
+ require "mongoid/matchers/all"
4
+ require "mongoid/matchers/exists"
5
+ require "mongoid/matchers/gt"
6
+ require "mongoid/matchers/gte"
7
+ require "mongoid/matchers/in"
8
+ require "mongoid/matchers/lt"
9
+ require "mongoid/matchers/lte"
10
+ require "mongoid/matchers/ne"
11
+ require "mongoid/matchers/nin"
12
+ require "mongoid/matchers/or"
13
+ require "mongoid/matchers/size"
14
+
15
+ module Mongoid #:nodoc:
16
+ module Matchers #:nodoc:
17
+
18
+ # This module is responsible for returning the correct matcher given a
19
+ # MongoDB query expression.
20
+ module Strategies
21
+ extend self
22
+
23
+ MATCHERS = {
24
+ "$all" => Matchers::All,
25
+ "$exists" => Matchers::Exists,
26
+ "$gt" => Matchers::Gt,
27
+ "$gte" => Matchers::Gte,
28
+ "$in" => Matchers::In,
29
+ "$lt" => Matchers::Lt,
30
+ "$lte" => Matchers::Lte,
31
+ "$ne" => Matchers::Ne,
32
+ "$nin" => Matchers::Nin,
33
+ "$or" => Matchers::Or,
34
+ "$size" => Matchers::Size
35
+ }
36
+
37
+ # Get the matcher for the supplied key and value. Will determine the class
38
+ # name from the key.
39
+ #
40
+ # @example Get the matcher.
41
+ # document.matcher(:title, { "$in" => [ "test" ] })
42
+ #
43
+ # @param [ Document ] document The document to check.
44
+ # @param [ Symbol, String ] key The field name.
45
+ # @param [ Object, Hash ] The value or selector.
46
+ #
47
+ # @return [ Matcher ] The matcher.
48
+ #
49
+ # @since 2.0.0.rc.7
50
+ def matcher(document, key, value)
51
+ if value.is_a?(Hash)
52
+ MATCHERS[value.keys.first].new(document.attributes[key])
53
+ else
54
+ if key == "$or"
55
+ Matchers::Or.new(value, document)
56
+ else
57
+ Default.new(document.attributes[key])
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,5 +1,7 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
+
4
+ # @todo: Durran: This module needs an overhaul.
3
5
  module MultiParameterAttributes
4
6
  module Errors
5
7
  # Raised when an error occurred while doing a mass assignment to an attribute through the
@@ -64,9 +66,9 @@ module Mongoid #:nodoc:
64
66
 
65
67
  def instantiate_object(klass, values_with_empty_parameters)
66
68
  return nil if values_with_empty_parameters.all? { |v| v.nil? }
67
-
69
+
68
70
  values = values_with_empty_parameters.collect { |v| v.nil? ? 1 : v }
69
-
71
+
70
72
  if klass == DateTime || klass == Date || klass == Time
71
73
  klass.send(:convert_to_time, values)
72
74
  elsif klass
@@ -3,6 +3,13 @@ module Mongoid #:nodoc:
3
3
  module NestedAttributes
4
4
  extend ActiveSupport::Concern
5
5
 
6
+ included do
7
+ class_attribute :nested_attributes
8
+ self.nested_attributes = []
9
+
10
+ delegate :nested_attributes, :to => "self.class"
11
+ end
12
+
6
13
  module ClassMethods
7
14
  REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |_, value| value.blank? } }
8
15
 
@@ -32,6 +39,7 @@ module Mongoid #:nodoc:
32
39
  options = args.extract_options!
33
40
  options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
34
41
  args.each do |name|
42
+ self.nested_attributes += [ "#{name}_attributes=" ]
35
43
  define_method("#{name}_attributes=") do |attrs|
36
44
  relation = relations[name.to_s]
37
45
  relation.nested_builder(attrs, options).build(self)
@@ -55,6 +55,7 @@ module Mongoid #:nodoc:
55
55
  # @return [ TrueClass ] True.
56
56
  def remove(options = {})
57
57
  if Remove.new(self, options).persist
58
+ raw_attributes.freeze
58
59
  self.destroyed = true
59
60
  cascade!
60
61
  end; true
@@ -71,7 +72,7 @@ module Mongoid #:nodoc:
71
72
  #
72
73
  # @return [ true, false ] True if validation passed.
73
74
  def save!(options = {})
74
- self.class.fail_validate!(self) unless upsert; true
75
+ self.class.fail_validate!(self) unless upsert(options); true
75
76
  end
76
77
 
77
78
  # Update the document in the datbase.
@@ -25,8 +25,8 @@ module Mongoid #:nodoc:
25
25
  # The +Document+, whether the insert succeeded or not.
26
26
  def persist
27
27
  return document if validate && document.invalid?(:create)
28
- document.run_callbacks(:create) do
29
- document.run_callbacks(:save) do
28
+ document.run_callbacks(:save) do
29
+ document.run_callbacks(:create) do
30
30
  if insert
31
31
  document.new_record = false
32
32
  document._children.each { |child| child.new_record = false }
@@ -45,7 +45,7 @@ module Mongoid #:nodoc:
45
45
  options.merge(:validate => validate)
46
46
  ).persist
47
47
  else
48
- collection.insert(document.to_hash, options)
48
+ collection.insert(document.as_document, options)
49
49
  end
50
50
  end
51
51
  end
@@ -31,9 +31,10 @@ module Mongoid #:nodoc:
31
31
  if parent.new_record?
32
32
  parent.insert
33
33
  else
34
- update = { document._inserter => { document._position => document.raw_attributes } }
34
+ update = { document._inserter => { document._position => document.as_document } }
35
35
  collection.update(parent._selector, update, options.merge(:multi => false))
36
36
  document.new_record = false
37
+ document.move_changes
37
38
  end
38
39
  document
39
40
  end
@@ -2,6 +2,7 @@
2
2
  require "mongoid/relations/accessors"
3
3
  require "mongoid/relations/auto_save"
4
4
  require "mongoid/relations/cascading"
5
+ require "mongoid/relations/constraint"
5
6
  require "mongoid/relations/cyclic"
6
7
  require "mongoid/relations/proxy"
7
8
  require "mongoid/relations/bindings"
@@ -50,7 +51,7 @@ module Mongoid # :nodoc:
50
51
  #
51
52
  # @since 2.0.0.rc.1
52
53
  def embedded?
53
- _parent.present?
54
+ cyclic ? _parent.present? : self.class.embedded?
54
55
  end
55
56
 
56
57
  # Determine if the document is part of an embeds_many relation.
@@ -50,7 +50,7 @@ module Mongoid # :nodoc:
50
50
  inverse,
51
51
  false,
52
52
  OPTIONS
53
- ).push(base, :continue => false)
53
+ ).push(base, :binding => true, :continue => false)
54
54
  end
55
55
  end
56
56
  end
@@ -88,7 +88,9 @@ module Mongoid # :nodoc:
88
88
  if options[:continue]
89
89
  inverse = metadata.inverse(target)
90
90
  if inverse
91
- doc.do_or_do_not(inverse).delete(base)
91
+ doc.do_or_do_not(
92
+ inverse, false, OPTIONS
93
+ ).delete(base, :binding => true, :continue => false)
92
94
  end
93
95
  end
94
96
  end
@@ -106,6 +106,8 @@ module Mongoid # :nodoc:
106
106
  document = existing.find(convert_id(attrs[:id]))
107
107
  destroyable?(attrs) ? document.destroy : document.update_attributes(attrs)
108
108
  else
109
+ # @todo: Durran: Tell the push not to save the base and call it
110
+ # after all processing is done. This is related to #581.
109
111
  existing.push(metadata.klass.new(attrs)) unless destroyable?(attrs)
110
112
  end
111
113
  end
@@ -47,7 +47,7 @@ module Mongoid # :nodoc:
47
47
  #
48
48
  # @since 2.0.0.rc.1
49
49
  def cascade(metadata)
50
- tap { cascades << metadata.name.to_s if metadata.dependent? }
50
+ tap { self.cascades += [ metadata.name.to_s ] if metadata.dependent? }
51
51
  end
52
52
  end
53
53
  end
@@ -10,7 +10,7 @@ module Mongoid # :nodoc:
10
10
  # @example Nullify the reference.
11
11
  # strategy.cascade
12
12
  def cascade
13
- relation.nullify
13
+ relation.nullify if relation
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # Used for converting foreign key values to the correct type based on the
6
+ # types of ids that the document stores.
7
+ #
8
+ # @note Durran: The name of this class is this way to match the metadata
9
+ # getter, and foreign_key was already taken there.
10
+ class Constraint
11
+ attr_reader :metadata
12
+
13
+ # Create the new constraint with the metadata.
14
+ #
15
+ # @example Instantiate the constraint.
16
+ # Constraint.new(metdata)
17
+ #
18
+ # @param [ Metadata ] metadata The metadata of the relation.
19
+ #
20
+ # @since 2.0.0.rc.7
21
+ def initialize(metadata)
22
+ @metadata = metadata
23
+ end
24
+
25
+ # Convert the supplied object to the appropriate type to set as the
26
+ # foreign key for a relation.
27
+ #
28
+ # @example Convert the object.
29
+ # constraint.convert("12345")
30
+ #
31
+ # @param [ Object ] object The object to convert.
32
+ #
33
+ # @return [ Object ] The object cast to the correct type.
34
+ #
35
+ # @since 2.0.0.rc.7
36
+ def convert(object)
37
+ return object if metadata.polymorphic?
38
+ BSON::ObjectId.convert(metadata.klass, object)
39
+ end
40
+ end
41
+ end
42
+ end