mongoid 2.0.0.beta.20 → 2.0.0.rc.1

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 (134) hide show
  1. data/README.rdoc +8 -0
  2. data/Rakefile +51 -0
  3. data/lib/config/locales/nl.yml +39 -0
  4. data/lib/config/locales/ro.yml +1 -1
  5. data/lib/mongoid.rb +17 -17
  6. data/lib/mongoid/atomicity.rb +54 -22
  7. data/lib/mongoid/attributes.rb +145 -125
  8. data/lib/mongoid/callbacks.rb +7 -2
  9. data/lib/mongoid/collection.rb +49 -32
  10. data/lib/mongoid/collections.rb +0 -1
  11. data/lib/mongoid/components.rb +34 -29
  12. data/lib/mongoid/config.rb +207 -193
  13. data/lib/mongoid/config/database.rb +167 -0
  14. data/lib/mongoid/contexts.rb +2 -5
  15. data/lib/mongoid/contexts/enumerable.rb +30 -4
  16. data/lib/mongoid/contexts/ids.rb +2 -2
  17. data/lib/mongoid/contexts/mongo.rb +30 -5
  18. data/lib/mongoid/copyable.rb +44 -0
  19. data/lib/mongoid/criteria.rb +110 -56
  20. data/lib/mongoid/criterion/creational.rb +34 -0
  21. data/lib/mongoid/criterion/destructive.rb +37 -0
  22. data/lib/mongoid/criterion/exclusion.rb +3 -1
  23. data/lib/mongoid/criterion/inclusion.rb +59 -64
  24. data/lib/mongoid/criterion/inspection.rb +22 -0
  25. data/lib/mongoid/criterion/optional.rb +42 -54
  26. data/lib/mongoid/criterion/selector.rb +9 -0
  27. data/lib/mongoid/default_scope.rb +28 -0
  28. data/lib/mongoid/deprecation.rb +5 -5
  29. data/lib/mongoid/dirty.rb +4 -5
  30. data/lib/mongoid/document.rb +161 -114
  31. data/lib/mongoid/extensions.rb +7 -11
  32. data/lib/mongoid/extensions/array/parentization.rb +2 -2
  33. data/lib/mongoid/extensions/date/conversions.rb +1 -1
  34. data/lib/mongoid/extensions/hash/conversions.rb +0 -23
  35. data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
  36. data/lib/mongoid/extensions/object/reflections.rb +17 -0
  37. data/lib/mongoid/extensions/object/yoda.rb +27 -0
  38. data/lib/mongoid/extensions/string/conversions.rb +23 -4
  39. data/lib/mongoid/extensions/time_conversions.rb +4 -4
  40. data/lib/mongoid/field.rb +30 -19
  41. data/lib/mongoid/fields.rb +15 -5
  42. data/lib/mongoid/finders.rb +19 -11
  43. data/lib/mongoid/hierarchy.rb +34 -28
  44. data/lib/mongoid/identity.rb +62 -20
  45. data/lib/mongoid/inspection.rb +58 -0
  46. data/lib/mongoid/matchers.rb +20 -0
  47. data/lib/mongoid/multi_database.rb +11 -0
  48. data/lib/mongoid/nested_attributes.rb +41 -0
  49. data/lib/mongoid/paranoia.rb +3 -4
  50. data/lib/mongoid/paths.rb +1 -1
  51. data/lib/mongoid/persistence.rb +89 -90
  52. data/lib/mongoid/persistence/command.rb +20 -4
  53. data/lib/mongoid/persistence/insert.rb +13 -11
  54. data/lib/mongoid/persistence/insert_embedded.rb +8 -6
  55. data/lib/mongoid/persistence/remove.rb +6 -4
  56. data/lib/mongoid/persistence/remove_all.rb +6 -4
  57. data/lib/mongoid/persistence/remove_embedded.rb +8 -6
  58. data/lib/mongoid/persistence/update.rb +12 -10
  59. data/lib/mongoid/railtie.rb +2 -2
  60. data/lib/mongoid/railties/database.rake +10 -9
  61. data/lib/mongoid/relations.rb +104 -0
  62. data/lib/mongoid/relations/accessors.rb +154 -0
  63. data/lib/mongoid/relations/auto_save.rb +34 -0
  64. data/lib/mongoid/relations/binding.rb +24 -0
  65. data/lib/mongoid/relations/bindings.rb +9 -0
  66. data/lib/mongoid/relations/bindings/embedded/in.rb +77 -0
  67. data/lib/mongoid/relations/bindings/embedded/many.rb +93 -0
  68. data/lib/mongoid/relations/bindings/embedded/one.rb +65 -0
  69. data/lib/mongoid/relations/bindings/referenced/in.rb +78 -0
  70. data/lib/mongoid/relations/bindings/referenced/many.rb +93 -0
  71. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +94 -0
  72. data/lib/mongoid/relations/bindings/referenced/one.rb +63 -0
  73. data/lib/mongoid/relations/builder.rb +41 -0
  74. data/lib/mongoid/relations/builders.rb +79 -0
  75. data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
  76. data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
  77. data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
  78. data/lib/mongoid/relations/builders/nested_attributes/many.rb +116 -0
  79. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  80. data/lib/mongoid/relations/builders/referenced/in.rb +32 -0
  81. data/lib/mongoid/relations/builders/referenced/many.rb +26 -0
  82. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
  83. data/lib/mongoid/relations/builders/referenced/one.rb +30 -0
  84. data/lib/mongoid/relations/cascading.rb +55 -0
  85. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  86. data/lib/mongoid/relations/cascading/destroy.rb +19 -0
  87. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  88. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  89. data/lib/mongoid/relations/cyclic.rb +97 -0
  90. data/lib/mongoid/relations/embedded/in.rb +172 -0
  91. data/lib/mongoid/relations/embedded/many.rb +450 -0
  92. data/lib/mongoid/relations/embedded/one.rb +169 -0
  93. data/lib/mongoid/relations/macros.rb +302 -0
  94. data/lib/mongoid/relations/many.rb +185 -0
  95. data/lib/mongoid/relations/metadata.rb +529 -0
  96. data/lib/mongoid/relations/nested_builder.rb +52 -0
  97. data/lib/mongoid/relations/one.rb +29 -0
  98. data/lib/mongoid/relations/polymorphic.rb +54 -0
  99. data/lib/mongoid/relations/proxy.rb +122 -0
  100. data/lib/mongoid/relations/referenced/in.rb +214 -0
  101. data/lib/mongoid/relations/referenced/many.rb +358 -0
  102. data/lib/mongoid/relations/referenced/many_to_many.rb +379 -0
  103. data/lib/mongoid/relations/referenced/one.rb +204 -0
  104. data/lib/mongoid/relations/reflections.rb +45 -0
  105. data/lib/mongoid/safe.rb +11 -1
  106. data/lib/mongoid/safety.rb +122 -97
  107. data/lib/mongoid/scope.rb +14 -9
  108. data/lib/mongoid/state.rb +37 -3
  109. data/lib/mongoid/timestamps.rb +11 -0
  110. data/lib/mongoid/validations.rb +42 -3
  111. data/lib/mongoid/validations/associated.rb +8 -5
  112. data/lib/mongoid/validations/uniqueness.rb +23 -2
  113. data/lib/mongoid/version.rb +1 -1
  114. data/lib/mongoid/versioning.rb +25 -16
  115. data/lib/rails/generators/mongoid/model/templates/model.rb +3 -1
  116. metadata +95 -80
  117. data/lib/mongoid/associations.rb +0 -364
  118. data/lib/mongoid/associations/embedded_in.rb +0 -74
  119. data/lib/mongoid/associations/embeds_many.rb +0 -299
  120. data/lib/mongoid/associations/embeds_one.rb +0 -111
  121. data/lib/mongoid/associations/foreign_key.rb +0 -35
  122. data/lib/mongoid/associations/meta_data.rb +0 -38
  123. data/lib/mongoid/associations/options.rb +0 -78
  124. data/lib/mongoid/associations/proxy.rb +0 -60
  125. data/lib/mongoid/associations/referenced_in.rb +0 -70
  126. data/lib/mongoid/associations/references_many.rb +0 -254
  127. data/lib/mongoid/associations/references_many_as_array.rb +0 -128
  128. data/lib/mongoid/associations/references_one.rb +0 -104
  129. data/lib/mongoid/extensions/array/accessors.rb +0 -17
  130. data/lib/mongoid/extensions/array/assimilation.rb +0 -26
  131. data/lib/mongoid/extensions/hash/accessors.rb +0 -42
  132. data/lib/mongoid/extensions/hash/assimilation.rb +0 -40
  133. data/lib/mongoid/extensions/nil/assimilation.rb +0 -17
  134. data/lib/mongoid/memoization.rb +0 -33
@@ -1,111 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- # Represents an association that is embedded in a parent document as a
5
- # one-to-one relationship.
6
- class EmbedsOne < Proxy
7
-
8
- # Build a new object for the association.
9
- def build(attrs = {}, type = nil)
10
- @target = attrs.assimilate(@parent, @options, type); self
11
- end
12
-
13
- # Replaces the target with a new object
14
- #
15
- # Returns the association proxy
16
- def replace(obj)
17
- @target = obj
18
- self
19
- end
20
-
21
- # Creates the new association by finding the attributes in
22
- # the parent document with its name, and instantiating a
23
- # new document for it.
24
- #
25
- # All method calls on this object will then be delegated
26
- # to the internal document itself.
27
- #
28
- # Options:
29
- #
30
- # document: The parent +Document+
31
- # attributes: The attributes of the target object.
32
- # options: The association options.
33
- #
34
- # Returns:
35
- #
36
- # A new +HashOne+ association proxy.
37
- def initialize(document, options, target = nil)
38
- @parent, @options = document, options
39
-
40
- if target
41
- replace(target)
42
- else
43
- attributes = document.raw_attributes[options.name]
44
- build(attributes) unless attributes.blank?
45
- end
46
-
47
- extends(options)
48
- end
49
-
50
- # Used for setting the association via a nested attributes setter on the
51
- # parent +Document+. Called when using accepts_nested_attributes_for.
52
- #
53
- # Options:
54
- #
55
- # attributes: The attributes for the new association
56
- #
57
- # Returns:
58
- #
59
- # A new target document.
60
- def nested_build(attributes, options = nil)
61
- options ||= {}
62
- _destroy = Boolean.set(attributes.delete('_destroy'))
63
- if options[:allow_destroy] && _destroy
64
- @target = nil
65
- elsif @target.present? || !options[:update_only]
66
- @target.write_attributes(attributes)
67
- end
68
- target
69
- end
70
-
71
- class << self
72
- # Returns the macro used to create the association.
73
- def macro
74
- :embeds_one
75
- end
76
-
77
- # Perform an update of the relationship of the parent and child. This
78
- # will assimilate the child +Document+ into the parent's object graph.
79
- #
80
- # Options:
81
- #
82
- # child: The child +Document+ or +Hash+.
83
- # parent: The parent +Document+ to update.
84
- # options: The association +Options+
85
- #
86
- # Example:
87
- #
88
- # <tt>EmbedsOne.update({:first_name => "Hank"}, person, options)</tt>
89
- #
90
- # Returns:
91
- #
92
- # A new +EmbedsOne+ association proxy.
93
- def update(child, parent, options)
94
- child.assimilate(parent, options)
95
- new(parent, options, child.is_a?(Hash) ? nil : child)
96
- end
97
-
98
- # Validate the options passed to the embeds one macro, to encapsulate
99
- # the behavior in this class instead of the associations module.
100
- #
101
- # Options:
102
- #
103
- # options: Thank you captain obvious.
104
- def validate_options(options = {})
105
- check_dependent_not_allowed!(options)
106
- check_inverse_not_allowed!(options)
107
- end
108
- end
109
- end
110
- end
111
- end
@@ -1,35 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- module ForeignKey #:nodoc:
5
- extend ActiveSupport::Concern
6
-
7
- module ClassMethods #:nodoc:
8
- # Determine the value for the foreign key constriant field in the
9
- # database, based on the type of association or if the actual value was
10
- # supplied as an option.
11
- #
12
- # Example:
13
- #
14
- # <tt>contraint(:posts, {}, :references_one)</tt>
15
- #
16
- # Returns
17
- #
18
- # A +String+ for the foreign key field.
19
- def constraint(name, options, association)
20
- key = options[:foreign_key]
21
-
22
- # Always return the supplied foreign_key option if it was supplied -
23
- # the user should always be ble to override.
24
- return key.to_s if key
25
-
26
- case association
27
- when :one, :many then self.name.foreign_key
28
- when :many_as_array then "#{name.to_s.singularize}_ids"
29
- else name.to_s.foreign_key
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
@@ -1,38 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- # This class contains metadata about association proxies.
5
- class MetaData
6
-
7
- attr_reader :association, :options
8
-
9
- delegate :macro, :to => :association
10
-
11
- # Delegate all methods on +Options+ to the options instance.
12
- Associations::Options.public_instance_methods(false).each do |name|
13
- define_method(name) { |*args| @options.send(name) }
14
- end
15
-
16
- # Return true if this meta data is for an embedded association.
17
- #
18
- # Example:
19
- #
20
- # <tt>metadata.embedded?</tt>
21
- def embedded?
22
- [ EmbedsOne, EmbedsMany ].include?(association)
23
- end
24
-
25
- # Create the new associations MetaData object, which holds the type of
26
- # the association and its options, with convenience methods for getting
27
- # that information.
28
- #
29
- # Options:
30
- #
31
- # association: The association type as a class instance.
32
- # options: The association options
33
- def initialize(association, options)
34
- @association, @options = association, options
35
- end
36
- end
37
- end
38
- end
@@ -1,78 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- class Options < Hash #:nodoc:
5
-
6
- # Create the new +Options+ object, which provides convenience methods for
7
- # accessing values out of an options +Hash+.
8
- def initialize(attributes = {})
9
- self.merge!(attributes)
10
- end
11
-
12
- # For relational associations we want to know if we cascade deletes or
13
- # destroys to associations.
14
- def dependent
15
- self[:dependent]
16
- end
17
-
18
- # Returns the extension if it exists, nil if not.
19
- def extension
20
- self[:extend]
21
- end
22
-
23
- # Returns true is the options have extensions.
24
- def extension?
25
- !extension.nil?
26
- end
27
-
28
- # Return the foreign key if it exists, otherwise inflect it from the
29
- # associated class name.
30
- def foreign_key
31
- key = self[:foreign_key] || klass.name.to_s.foreign_key
32
- key.to_s
33
- end
34
-
35
- # Returns whether the foreign key column is indexed.
36
- def index
37
- self[:index] || false
38
- end
39
-
40
- # Returns the name of the inverse_of association
41
- def inverse_of
42
- self[:inverse_of]
43
- end
44
-
45
- # Return a +Class+ for the options. See #class_name
46
- def klass
47
- class_name.constantize
48
- end
49
-
50
- # Return a +String+ representing the associated class_name. If a class_name
51
- # was provided, then the constantized class_name will be returned. If not,
52
- # a constant based on the association name will be returned.
53
- def class_name
54
- self[:class_name] || name.to_s.classify
55
- end
56
-
57
- # Returns the association name of the options.
58
- def name
59
- self[:name].to_s
60
- end
61
-
62
- # Returns whether or not this association is polymorphic.
63
- def polymorphic
64
- self[:polymorphic] == true
65
- end
66
-
67
- # Used with references_many to save as array of ids.
68
- def stored_as
69
- self[:stored_as]
70
- end
71
-
72
- # Used with references_many to define a default sorting order
73
- def default_order
74
- self[:default_order]
75
- end
76
- end
77
- end
78
- end
@@ -1,60 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc
3
- module Associations #:nodoc
4
- class Proxy #:nodoc
5
- instance_methods.each do |method|
6
- undef_method(method) unless method =~ /(^__|^send$|^object_id$|^extend$)/
7
- end
8
- attr_reader \
9
- :options,
10
- :target
11
-
12
- # Default behavior of method missing should be to delegate all calls
13
- # to the target of the proxy. This can be overridden in special cases.
14
- def method_missing(name, *args, &block)
15
- @target.send(name, *args, &block)
16
- end
17
-
18
- # If anonymous extensions are added this will take care of them.
19
- def extends(options)
20
- extend Module.new(&options.extension) if options.extension?
21
- end
22
-
23
- # Sets up the parent, klass, foreign_key, options
24
- def setup(document, options)
25
- @parent = document
26
- @klass = options.klass
27
- @options = options
28
- @foreign_key = options.foreign_key
29
- extends(options)
30
- end
31
-
32
- protected
33
- class << self
34
- def check_dependent_not_allowed!(options)
35
- if options.has_key?(:dependent)
36
- raise Errors::InvalidOptions.new(
37
- "dependent_only_references_one_or_many", {}
38
- )
39
- end
40
- end
41
-
42
- def check_inverse_not_allowed!(options)
43
- if options.has_key?(:inverse_of)
44
- raise Errors::InvalidOptions.new(
45
- "association_cant_have_inverse_of", {}
46
- )
47
- end
48
- end
49
-
50
- def check_inverse_must_be_defined!(options)
51
- unless options.has_key?(:inverse_of)
52
- raise Errors::InvalidOptions.new(
53
- "embedded_in_must_have_inverse_of", {}
54
- )
55
- end
56
- end
57
- end
58
- end
59
- end
60
- end
@@ -1,70 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- # Represents a relational association to a "parent" object.
5
- class ReferencedIn < Proxy
6
-
7
- # Initializing a related association only requires looking up the object
8
- # by its id.
9
- #
10
- # Options:
11
- #
12
- # document: The +Document+ that contains the relationship.
13
- # options: The association +Options+.
14
- def initialize(document, options, target = nil)
15
- @options = options
16
-
17
- if target
18
- replace(target)
19
- else
20
- foreign_key = document.send(options.foreign_key)
21
- replace(options.klass.find(foreign_key)) unless foreign_key.blank?
22
- end
23
-
24
- extends(options)
25
- end
26
-
27
- # Replaces the target with a new object
28
- #
29
- # Returns the association proxy
30
- def replace(obj)
31
- @target = obj
32
- self
33
- end
34
-
35
- class << self
36
- # Returns the macro used to create the association.
37
- def macro
38
- :referenced_in
39
- end
40
-
41
- # Perform an update of the relationship of the parent and child. This
42
- # will assimilate the child +Document+ into the parent's object graph.
43
- #
44
- # Options:
45
- #
46
- # target: The target(parent) object
47
- # document: The +Document+ to update.
48
- # options: The association +Options+
49
- #
50
- # Example:
51
- #
52
- # <tt>ReferencedIn.update(person, game, options)</tt>
53
- def update(target, document, options)
54
- document.send("#{options.foreign_key}=", target ? target.id : nil)
55
- new(document, options, target)
56
- end
57
-
58
- # Validate the options passed to the referenced in macro, to encapsulate
59
- # the behavior in this class instead of the associations module.
60
- #
61
- # Options:
62
- #
63
- # options: Thank you captain obvious.
64
- def validate_options(options = {})
65
- check_dependent_not_allowed!(options)
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,254 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Associations #:nodoc:
4
- # Represents an relational one-to-many association with an object in a
5
- # separate collection or database.
6
- class ReferencesMany < Proxy
7
-
8
- # Appends the object to the +Array+, setting its parent in
9
- # the process.
10
- def <<(*objects)
11
- load_target
12
- objects.flatten.each do |object|
13
- object.write_attribute(@foreign_key, @parent.id)
14
- @target << object
15
- object.save unless @parent.new_record?
16
- end
17
- end
18
-
19
- alias :concat :<<
20
- alias :push :<<
21
-
22
- # Builds a new Document and adds it to the association collection. The
23
- # document created will be of the same class as the others in the
24
- # association, and the attributes will be passed into the constructor.
25
- #
26
- # Returns the newly created object.
27
- def build(attributes = {},type = nil)
28
- load_target
29
- name = determine_name
30
- object = (type || @klass).instantiate(attributes)
31
- object.send("#{name}=", @parent)
32
- @target << object
33
- object
34
- end
35
-
36
- # Creates a new Document and adds it to the association collection. The
37
- # document created will be of the same class as the others in the
38
- # association, and the attributes will be passed into the constructor and
39
- # the new object will then be saved.
40
- #
41
- # Returns the newly created object.
42
- def create(attributes = nil,type = nil)
43
- build(attributes,type).tap(&:save)
44
- end
45
-
46
- # Creates a new Document and adds it to the association collection. If
47
- # validation fails an error is raised.
48
- #
49
- # Returns the newly created object.
50
- def create!(attributes = nil,type = nil)
51
- build(attributes,type).tap(&:save!)
52
- end
53
-
54
- # Delete all the associated objects.
55
- #
56
- # Example:
57
- #
58
- # <tt>person.posts.delete_all</tt>
59
- #
60
- # Returns:
61
- #
62
- # The number of objects deleted.
63
- def delete_all(conditions = {})
64
- remove(:delete_all, conditions[:conditions])
65
- end
66
-
67
- # Destroy all the associated objects.
68
- #
69
- # Example:
70
- #
71
- # <tt>person.posts.destroy_all</tt>
72
- #
73
- # Returns:
74
- #
75
- # The number of objects destroyed.
76
- def destroy_all(conditions = {})
77
- remove(:destroy_all, conditions[:conditions])
78
- end
79
-
80
- # Finds a document in this association.
81
- # If an id is passed, will return the document for that id.
82
- def find(id_or_type, options = {})
83
- return self.id_criteria(id_or_type) unless id_or_type.is_a?(Symbol)
84
- options[:conditions] = (options[:conditions] || {}).merge(@foreign_key.to_sym => @parent.id)
85
- @klass.find(id_or_type, options)
86
- end
87
-
88
- # Initializing a related association only requires looking up the objects
89
- # by their ids.
90
- #
91
- # Options:
92
- #
93
- # document: The +Document+ that contains the relationship.
94
- # options: The association +Options+.
95
- def initialize(document, options, target = nil)
96
- setup(document, options)
97
- @target = target || query.call
98
- end
99
-
100
- # Override the default behavior to allow the criteria to get reset on
101
- # each call into the association.
102
- #
103
- # Example:
104
- #
105
- # person.posts.where(:title => "New")
106
- # person.posts # resets the criteria
107
- #
108
- # Returns:
109
- #
110
- # A Criteria object or Array.
111
- def method_missing(name, *args, &block)
112
- @target = query.call unless @target.is_a?(Array)
113
- @target.send(name, *args, &block)
114
- end
115
-
116
- # Used for setting associations via a nested attributes setter from the
117
- # parent +Document+.
118
- #
119
- # Options:
120
- #
121
- # attributes: A +Hash+ of integer keys and +Hash+ values.
122
- #
123
- # Returns:
124
- #
125
- # The newly build target Document.
126
- def nested_build(attributes, options = {})
127
- attributes.each do |index, attrs|
128
- begin
129
- _destroy = Boolean.set(attrs.delete('_destroy'))
130
- document = find(attrs.delete("id"))
131
- if options && options[:allow_destroy] && _destroy
132
- @target.delete(document)
133
- document.destroy
134
- else
135
- document.update_attributes(attrs)
136
- end
137
- rescue Errors::DocumentNotFound
138
- create(attrs)
139
- end
140
- end
141
- end
142
-
143
- protected
144
- # Load the target entries if the parent document is new.
145
- def load_target
146
- @target = @target.entries if @parent.new_record?
147
- end
148
-
149
- def determine_name
150
- @proxy ||= class << self; self; end
151
- @proxy.send(:determine_name, @parent, @options)
152
- end
153
-
154
- # The default query used for retrieving the documents from the database.
155
- # In this case we use the common API between Mongoid, ActiveRecord, and
156
- # DataMapper so we can do one-to-many relationships with data in other
157
- # databases.
158
- #
159
- # Example:
160
- #
161
- # <tt>association.query</tt>
162
- #
163
- # Returns:
164
- #
165
- # A +Criteria+ if a Mongoid association.
166
- # An +Array+ of objects if an ActiveRecord association
167
- # A +Collection+ if a DataMapper association.
168
- def query
169
- @query ||= lambda {
170
- @klass.all(:conditions => { @foreign_key => @parent.id }).tap do |crit|
171
- crit.set_order_by(@options.default_order) if @options.default_order
172
- end
173
- }
174
- end
175
-
176
- # Remove the objects based on conditions.
177
- def remove(method, conditions)
178
- selector = { @foreign_key => @parent.id }.merge(conditions || {})
179
- removed = @klass.send(method, :conditions => selector)
180
- reset; removed
181
- end
182
-
183
- # Reset the memoized association on the parent. This will execute the
184
- # database query again.
185
- #
186
- # Example:
187
- #
188
- # <tt>association.reset</tt>
189
- #
190
- # Returns:
191
- #
192
- # See #query rdoc for return values.
193
- def reset
194
- @parent.send(:reset, @options.name) { query.call }
195
- end
196
-
197
- class << self
198
- # Preferred method for creating the new +ReferencesMany+ association.
199
- #
200
- # Options:
201
- #
202
- # document: The +Document+ that contains the relationship.
203
- # options: The association +Options+.
204
- def instantiate(document, options, target = nil)
205
- new(document, options, target)
206
- end
207
-
208
- # Returns the macro used to create the association.
209
- def macro
210
- :references_many
211
- end
212
-
213
- # Perform an update of the relationship of the parent and child. This
214
- # will assimilate the child +Document+ into the parent's object graph.
215
- #
216
- # Options:
217
- #
218
- # related: The related object
219
- # parent: The parent +Document+ to update.
220
- # options: The association +Options+
221
- #
222
- # Example:
223
- #
224
- # <tt>RelatesToOne.update(game, person, options)</tt>
225
- def update(target, document, options)
226
- name = determine_name(document, options)
227
- target.each { |child| child.send("#{name}=", document) }
228
- instantiate(document, options, target)
229
- end
230
-
231
- protected
232
- def determine_name(document, options)
233
- target = document.class
234
- if (inverse = options.inverse_of) && inverse.is_a?(Array)
235
- inverse = [*inverse].detect { |name| target.respond_to?(name) }
236
- end
237
- if !inverse
238
- association = detect_association(target, options, false)
239
- association = detect_association(target, options, true) if association.blank?
240
- inferred = association.name if association
241
- end
242
- inverse || inferred || target.to_s.underscore
243
- end
244
-
245
- def detect_association(target, options, with_class_name = false)
246
- association = options.klass.associations.values.detect do |metadata|
247
- metadata.options.klass == target &&
248
- (with_class_name ? true : metadata.options[:class_name].nil?)
249
- end
250
- end
251
- end
252
- end
253
- end
254
- end