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,3 +1,8 @@
1
+ = PLEASE READ (03 JAN 2011)
2
+
3
+ USERS CURRENTLY POINTED AT master, PLEASE MOVE TO safe_master UNTIL
4
+ FURTHER NOTICE.
5
+
1
6
  = Overview
2
7
 
3
8
  == About Mongoid
@@ -18,6 +23,9 @@ Mongoid is developed against Ruby 1.8.7, 1.9.1, 1.9.2, and REE.
18
23
  Please see the new Mongoid website for up-to-date documentation:
19
24
  {mongoid.org}[http://mongoid.org]
20
25
 
26
+ = Donating
27
+ * {Support Mongoid at Pledgie}[http://www.pledgie.com/campaigns/7757]
28
+
21
29
  = License
22
30
 
23
31
  Copyright (c) 2009, 2010 Durran Jordan
@@ -0,0 +1,51 @@
1
+ require "bundler"
2
+ Bundler.setup
3
+
4
+ require "rake"
5
+ require "rake/rdoctask"
6
+ require "rspec"
7
+ require "rspec/core/rake_task"
8
+
9
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
10
+ require "mongoid/version"
11
+
12
+ task :gem => :build
13
+ task :build do
14
+ system "gem build mongoid.gemspec"
15
+ end
16
+
17
+ task :install => :build do
18
+ system "sudo gem install mongoid-#{Mongoid::VERSION}.gem"
19
+ end
20
+
21
+ task :release => :build do
22
+ system "git tag -a #{Mongoid::VERSION} -m 'Tagging #{Mongoid::VERSION}'"
23
+ system "git push --tags"
24
+ system "gem push mongoid-#{Mongoid::VERSION}.gem"
25
+ end
26
+
27
+ Rspec::Core::RakeTask.new(:spec) do |spec|
28
+ spec.pattern = "spec/**/*_spec.rb"
29
+ end
30
+
31
+ Rspec::Core::RakeTask.new("spec:unit") do |spec|
32
+ spec.pattern = "spec/unit/**/*_spec.rb"
33
+ end
34
+
35
+ Rspec::Core::RakeTask.new("spec:integration") do |spec|
36
+ spec.pattern = "spec/integration/**/*_spec.rb"
37
+ end
38
+
39
+ Rspec::Core::RakeTask.new('spec:progress') do |spec|
40
+ spec.rspec_opts = %w(--format progress)
41
+ spec.pattern = "spec/**/*_spec.rb"
42
+ end
43
+
44
+ Rake::RDocTask.new do |rdoc|
45
+ rdoc.rdoc_dir = "rdoc"
46
+ rdoc.title = "mongoid #{Mongoid::VERSION}"
47
+ rdoc.rdoc_files.include("README*")
48
+ rdoc.rdoc_files.include("lib/**/*.rb")
49
+ end
50
+
51
+ task :default => :spec
@@ -0,0 +1,39 @@
1
+ nl:
2
+ activemodel:
3
+ errors:
4
+ messages:
5
+ taken: al in gebruik
6
+
7
+ mongoid:
8
+ errors:
9
+ messages:
10
+ document_not_found:
11
+ Document niet gevonden voor class %{klass} met de id(s) %{identifiers}.
12
+ invalid_database:
13
+ Database moet een Mongo::DB zijn, niet een %{name}.
14
+ invalid_type:
15
+ Veld was gedefinieerd als een %{klass}, maar ontvangen als %{other} met
16
+ de waarde %{value}.
17
+ unsupported_version:
18
+ MongoDB %{version} wordt niet ondersteund, upgrade naar versie %{mongo_version}.
19
+ validations:
20
+ Gefaalde validatie - %{errors}.
21
+ invalid_collection:
22
+ Toegang tot de collectie voor %{klass} is niet toegestaan aangezien het
23
+ een embedded document is, benader de collectie van een root document.
24
+ invalid_field:
25
+ Het is niet toegestaan om een veld genaamd %{name} te definiëren.
26
+ Definieer geen velden die conflicteren met de Mongoid interne attributen of methode namen.
27
+ Gerbuik Document#instance_methods om welke namen dit gaat.
28
+ too_many_nested_attribute_records:
29
+ Het accepteren van nested attributes voor %{association} is gelimiteerd tot %{limit} records.
30
+ embedded_in_must_have_inverse_of:
31
+ Opties voor embedded_in association moet gebruik maken van de inverse_of optie.
32
+ dependent_only_references_one_or_many:
33
+ De dependent => destroy|delete optie die was meegegeven is alleen geldig
34
+ op references_one en references_many associations.
35
+ association_cant_have_inverse_of:
36
+ Het definieren van een inverse_of op deze association is niet toegestaan. Gebruik
37
+ alleen deze optie met embedded_in en references_many as array.
38
+ calling_document_find_with_nil_is_invalid:
39
+ Het is niet toegestaan om Document#find aan te roepen met de waarde nil.
@@ -1,4 +1,4 @@
1
- en:
1
+ ro:
2
2
  activemodel:
3
3
  errors:
4
4
  messages:
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+
2
3
  # Copyright (c) 2009, 2010 Durran Jordan
3
4
  #
4
5
  # Permission is hereby granted, free of charge, to any person obtaining
@@ -19,9 +20,7 @@
19
20
  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
21
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
22
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
-
23
23
  require "delegate"
24
- require "singleton"
25
24
  require "time"
26
25
  require "ostruct"
27
26
  require "active_support/core_ext"
@@ -43,7 +42,6 @@ require "mongo"
43
42
  require "mongoid/errors"
44
43
  require "mongoid/extensions"
45
44
  require "mongoid/safe"
46
- require "mongoid/associations"
47
45
  require "mongoid/atomicity"
48
46
  require "mongoid/attributes"
49
47
  require "mongoid/callbacks"
@@ -51,9 +49,11 @@ require "mongoid/collection"
51
49
  require "mongoid/collections"
52
50
  require "mongoid/config"
53
51
  require "mongoid/contexts"
52
+ require "mongoid/copyable"
54
53
  require "mongoid/criteria"
55
54
  require "mongoid/cursor"
56
55
  require "mongoid/deprecation"
56
+ require "mongoid/default_scope"
57
57
  require "mongoid/dirty"
58
58
  require "mongoid/extras"
59
59
  require "mongoid/factory"
@@ -63,17 +63,20 @@ require "mongoid/finders"
63
63
  require "mongoid/hierarchy"
64
64
  require "mongoid/identity"
65
65
  require "mongoid/indexes"
66
+ require "mongoid/inspection"
66
67
  require "mongoid/javascript"
67
68
  require "mongoid/json"
68
69
  require "mongoid/keys"
69
70
  require "mongoid/logger"
70
71
  require "mongoid/matchers"
71
- require "mongoid/memoization"
72
72
  require "mongoid/modifiers"
73
73
  require "mongoid/multi_parameter_attributes"
74
+ require "mongoid/multi_database"
74
75
  require "mongoid/named_scope"
76
+ require "mongoid/nested_attributes"
75
77
  require "mongoid/paths"
76
78
  require "mongoid/persistence"
79
+ require "mongoid/relations"
77
80
  require "mongoid/safety"
78
81
  require "mongoid/scope"
79
82
  require "mongoid/state"
@@ -100,7 +103,7 @@ module Mongoid #:nodoc
100
103
 
101
104
  # Sets the Mongoid configuration options. Best used by passing a block.
102
105
  #
103
- # Example:
106
+ # @example Set up configuration options.
104
107
  #
105
108
  # Mongoid.configure do |config|
106
109
  # name = "mongoid_test"
@@ -113,33 +116,30 @@ module Mongoid #:nodoc
113
116
  # ]
114
117
  # end
115
118
  #
116
- # Returns:
117
- #
118
- # The Mongoid +Config+ singleton instance.
119
+ # @return [ Config ] The configuration obejct.
119
120
  def configure
120
- config = Mongoid::Config.instance
121
+ config = Mongoid::Config
121
122
  block_given? ? yield(config) : config
122
123
  end
124
+ alias :config :configure
123
125
 
124
126
  # Easy convenience method for generating an alert from the
125
127
  # deprecation module.
126
128
  #
127
- # Example:
129
+ # @example Alert a deprecation.
130
+ # Mongoid.deprecate("Method no longer used")
128
131
  #
129
- # <tt>Mongoid.deprecate("Method no longer used")</tt>
132
+ # @param [ String ] message The message to print.
130
133
  def deprecate(message)
131
- Mongoid::Deprecation.instance.alert(message)
134
+ Mongoid::Deprecation.alert(message)
132
135
  end
133
-
134
- alias :config :configure
135
136
  end
136
137
 
137
138
  # Take all the public instance methods from the Config singleton and allow
138
139
  # them to be accessed through the Mongoid module directly.
139
140
  #
140
- # Example:
141
- #
142
- # <tt>Mongoid.database = Mongo::Connection.new.db("test")</tt>
141
+ # @example Delegate the configuration methods.
142
+ # Mongoid.database = Mongo::Connection.new.db("test")
143
143
  Mongoid::Config.public_instance_methods(false).each do |name|
144
144
  (class << self; self; end).class_eval <<-EOT
145
145
  def #{name}(*args)
@@ -1,46 +1,52 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
- module Atomicity #:nodoc:
3
+
4
+ # This module contains the logic for supporting atomic operations against the
5
+ # database.
6
+ #
7
+ # @todo Durran: Refactor class out into separate objects for each type of
8
+ # update.
9
+ module Atomicity
4
10
  extend ActiveSupport::Concern
5
11
 
6
12
  # Get all the atomic updates that need to happen for the current
7
13
  # +Document+. This includes all changes that need to happen in the
8
14
  # entire hierarchy that exists below where the save call was made.
9
15
  #
10
- # Example:
11
- #
12
- # <tt>person.save</tt> # Saves entire tree
16
+ # @note
17
+ # MongoDB does not allow "conflicting modifications" to be
18
+ # performed in a single operation. Conflicting modifications are
19
+ # detected by the 'haveConflictingMod' function in MongoDB.
20
+ # Examination of the code suggests that two modifications (a $set
21
+ # and a $pushAll, for example) conflict if:
22
+ # (1) the key paths being modified are equal.
23
+ # (2) one key path is a prefix of the other.
24
+ # So a $set of 'addresses.0.street' will conflict with a $pushAll
25
+ # to 'addresses', and we will need to split our update into two
26
+ # pieces. We do not, however, attempt to match MongoDB's logic
27
+ # exactly. Instead, we assume that two updates conflict if the
28
+ # first component of the two key paths matches.
13
29
  #
14
- # Returns:
30
+ # @example Get the updates that need to occur.
31
+ # person._updates
15
32
  #
16
- # A +Hash+ of all atomic updates that need to occur.
33
+ # @return [ Hash ] The updates and their modifiers.
17
34
  def _updates
18
35
  processed = {}
19
-
36
+
20
37
  _children.inject({ "$set" => _sets, "$pushAll" => {}, :other => {} }) do |updates, child|
21
38
  changes = child._sets
22
39
  updates["$set"].update(changes)
23
40
  unless changes.empty?
24
41
  processed[child._conficting_modification_key] = true
25
42
  end
26
-
27
- # MongoDB does not allow "conflicting modifications" to be
28
- # performed in a single operation. Conflicting modifications are
29
- # detected by the 'haveConflictingMod' function in MongoDB.
30
- # Examination of the code suggests that two modifications (a $set
31
- # and a $pushAll, for example) conflict if (1) the key paths being
32
- # modified are equal or (2) one key path is a prefix of the other.
33
- # So a $set of 'addresses.0.street' will conflict with a $pushAll
34
- # to 'addresses', and we will need to split our update into two
35
- # pieces. We do not, however, attempt to match MongoDB's logic
36
- # exactly. Instead, we assume that two updates conflict if the
37
- # first component of the two key paths matches.
43
+
38
44
  if processed.has_key?(child._conficting_modification_key)
39
45
  target = :other
40
46
  else
41
47
  target = "$pushAll"
42
48
  end
43
-
49
+
44
50
  child._pushes.each do |attr, val|
45
51
  if updates[target].has_key?(attr)
46
52
  updates[target][attr] << val
@@ -55,24 +61,50 @@ module Mongoid #:nodoc:
55
61
  end
56
62
 
57
63
  protected
64
+
58
65
  # Get the key used to check for conflicting modifications. For now, we
59
66
  # just use the first component of _path, and discard the first period
60
67
  # and everything that follows.
68
+ #
69
+ # @example Get the key.
70
+ # person._conflicting_modification_key
71
+ #
72
+ # @return [ String ] The conflicting key.
61
73
  def _conficting_modification_key
62
74
  _path.sub(/\..*/, '')
63
75
  end
64
76
 
65
77
  # Get all the push attributes that need to occur.
78
+ #
79
+ # @example Get the pushes.
80
+ # person._pushes
81
+ #
82
+ # @return [ Hash ] The $pushAll operations.
66
83
  def _pushes
67
- (new_record? && embedded_many? && !_parent.new_record?) ? { _path => raw_attributes } : {}
84
+ pushable? ? { _path => to_hash } : {}
85
+ end
86
+
87
+ # Determine if the document can be pushed.
88
+ #
89
+ # @example Is this pushable?
90
+ # person.pushable?
91
+ #
92
+ # @return [ true, false ] Is the document new and embedded?
93
+ def pushable?
94
+ new_record? && embedded_many? && _parent.persisted?
68
95
  end
69
96
 
70
97
  # Get all the attributes that need to be set.
98
+ #
99
+ # @example Get the sets.
100
+ # person._sets
101
+ #
102
+ # @return [ Hash ] The $set operations.
71
103
  def _sets
72
104
  if changed? && !new_record?
73
105
  setters
74
106
  else
75
- embedded_one? && new_record? ? { _path => raw_attributes } : {}
107
+ embedded_one? && new_record? ? { _path => to_hash } : {}
76
108
  end
77
109
  end
78
110
  end
@@ -1,23 +1,75 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
+
4
+ # This module contains the logic for handling the internal attributes hash,
5
+ # and how to get and set values.
3
6
  module Attributes
4
- extend ActiveSupport::Concern
7
+
8
+ # Returns the object type. This corresponds to the name of the class that
9
+ # this document is, which is used in determining the class to
10
+ # instantiate in various cases.
11
+ #
12
+ # @example Get the type.
13
+ # person._type
14
+ #
15
+ # @return [ String ] The name of the class the document is.
16
+ def _type
17
+ @attributes["_type"]
18
+ end
19
+
20
+ # Set the type of the document. This should be the name of the class.
21
+ #
22
+ # @example Set the type
23
+ # person._type = "Person"
24
+ #
25
+ # @param [ String ] new_type The name of the class.
26
+ #
27
+ # @return [ String ] the new type.
28
+ def _type=(new_type)
29
+ @attributes["_type"] = new_type
30
+ end
31
+
32
+ # Determine if an attribute is present.
33
+ #
34
+ # @example Is the attribute present?
35
+ # person.attribute_present?("title")
36
+ #
37
+ # @param [ String, Symbol ] name The name of the attribute.
38
+ #
39
+ # @return [ true, false ] True if present, false if not.
40
+ def attribute_present?(name)
41
+ !read_attribute(name).blank?
42
+ end
5
43
 
6
44
  # Get the id associated with this object. This will pull the _id value out
7
- # of the attributes +Hash+.
45
+ # of the attributes.
46
+ #
47
+ # @example Get the id.
48
+ # person.id
49
+ #
50
+ # @return [ BSON::ObjectId, String ] The id of the document.
8
51
  def id
9
52
  @attributes["_id"]
10
53
  end
54
+ alias :_id :id
11
55
 
12
- # Set the id of the +Document+ to a new one.
56
+ # Set the id of the document to a new one.
57
+ #
58
+ # @example Set the id.
59
+ # person.id = BSON::ObjectId.new
60
+ #
61
+ # @param [ BSON::ObjectId, String ] new_id The new id.
62
+ #
63
+ # @return [ BSON::ObjectId, String ] The new id.
13
64
  def id=(new_id)
14
65
  @attributes["_id"] = new_id
15
66
  end
16
-
17
- alias :_id :id
18
67
  alias :_id= :id=
19
68
 
20
69
  # Used for allowing accessor methods for dynamic attributes.
70
+ #
71
+ # @param [ String, Symbol ] name The name of the method.
72
+ # @param [ Array ] *args The arguments to the method.
21
73
  def method_missing(name, *args)
22
74
  attr = name.to_s
23
75
  return super unless @attributes.has_key?(attr.reader)
@@ -29,44 +81,42 @@ module Mongoid #:nodoc:
29
81
  end
30
82
  end
31
83
 
32
- # Override respond_to? so it responds properly for dynamic attributes
33
- def respond_to?(*args)
34
- (Mongoid.allow_dynamic_fields && @attributes && @attributes.has_key?(args.first.to_s)) || super
35
- end
36
-
37
84
  # Process the provided attributes casting them to their proper values if a
38
- # field exists for them on the +Document+. This will be limited to only the
85
+ # field exists for them on the document. This will be limited to only the
39
86
  # attributes provided in the suppied +Hash+ so that no extra nil values get
40
87
  # put into the document's attributes.
88
+ #
89
+ # @example Process the attributes.
90
+ # person.process(:title => "sir", :age => 40)
91
+ #
92
+ # @param [ Hash ] attrs The attributes to set.
41
93
  def process(attrs = nil)
94
+ pending = {}
42
95
  sanitize_for_mass_assignment(attrs || {}).each_pair do |key, value|
43
96
  if set_allowed?(key)
44
97
  write_attribute(key, value)
45
98
  else
46
- if associations.include?(key.to_s) and associations[key.to_s].embedded? and value.is_a?(Hash)
47
- if association = send(key)
48
- association.nested_build(value)
49
- else
50
- send("build_#{key}", value)
51
- end
52
- else
53
- send("#{key}=", value)
54
- end
99
+ pending[key.to_s] = value and next if relations.has_key?(key.to_s)
100
+ send("#{key}=", value)
55
101
  end
56
102
  end
103
+ yield self if block_given?
104
+ process_relations(pending)
57
105
  setup_modifications
58
106
  end
59
107
 
60
- # Read a value from the +Document+ attributes. If the value does not exist
108
+ # Read a value from the document attributes. If the value does not exist
61
109
  # it will return nil.
62
110
  #
63
- # Options:
111
+ # @example Read an attribute.
112
+ # person.read_attribute(:title)
64
113
  #
65
- # name: The name of the attribute to get.
114
+ # @example Read an attribute (alternate syntax.)
115
+ # person[:title]
66
116
  #
67
- # Example:
117
+ # @param [ String, Symbol ] name The name of the attribute to get.
68
118
  #
69
- # <tt>person.read_attribute(:title)</tt>
119
+ # @return [ Object ] The value of the attribute.
70
120
  def read_attribute(name)
71
121
  access = name.to_s
72
122
  value = @attributes[access]
@@ -78,93 +128,75 @@ module Mongoid #:nodoc:
78
128
  # Remove a value from the +Document+ attributes. If the value does not exist
79
129
  # it will fail gracefully.
80
130
  #
81
- # Options:
82
- #
83
- # name: The name of the attribute to remove.
131
+ # @example Remove the attribute.
132
+ # person.remove_attribute(:title)
84
133
  #
85
- # Example:
86
- #
87
- # <tt>person.remove_attribute(:title)</tt>
134
+ # @param [ String, Symbol ] name The name of the attribute to remove.
88
135
  def remove_attribute(name)
89
136
  access = name.to_s
90
137
  modify(access, @attributes.delete(name.to_s), nil)
91
138
  end
92
139
 
93
- # Returns true when attribute is present.
140
+ # Override respond_to? so it responds properly for dynamic attributes.
94
141
  #
95
- # Options:
142
+ # @example Does this object respond to the method?
143
+ # person.respond_to?(:title)
96
144
  #
97
- # name: The name of the attribute to request presence on.
98
- def attribute_present?(name)
99
- value = read_attribute(name)
100
- !value.blank?
101
- end
102
-
103
- # Returns the object type. This corresponds to the name of the class that
104
- # this +Document+ is, which is used in determining the class to
105
- # instantiate in various cases.
106
- def _type
107
- @attributes["_type"]
108
- end
109
-
110
- # Set the type of the +Document+. This should be the name of the class.
111
- def _type=(new_type)
112
- @attributes["_type"] = new_type
145
+ # @param [ Array ] *args The name of the method.
146
+ #
147
+ # @return [ true, false ] True if it does, false if not.
148
+ def respond_to?(*args)
149
+ (Mongoid.allow_dynamic_fields &&
150
+ @attributes &&
151
+ @attributes.has_key?(args.first.to_s)
152
+ ) || super
113
153
  end
114
154
 
115
- # Write a single attribute to the +Document+ attribute +Hash+. This will
155
+ # Write a single attribute to the document attribute hash. This will
116
156
  # also fire the before and after update callbacks, and perform any
117
157
  # necessary typecasting.
118
158
  #
119
- # Options:
120
- #
121
- # name: The name of the attribute to update.
122
- # value: The value to set for the attribute.
123
- #
124
- # Example:
159
+ # @example Write the attribute.
160
+ # person.write_attribute(:title, "Mr.")
125
161
  #
126
- # <tt>person.write_attribute(:title, "Mr.")</tt>
162
+ # @example Write the attribute (alternate syntax.)
163
+ # person[:title] = "Mr."
127
164
  #
128
- # This will also cause the observing +Document+ to notify it's parent if
129
- # there is any.
165
+ # @param [ String, Symbol ] name The name of the attribute to update.
166
+ # @param [ Object ] value The value to set for the attribute.
130
167
  def write_attribute(name, value)
131
168
  access = name.to_s
132
169
  modify(access, @attributes[access], typed_value_for(access, value))
133
- notify if !id.blank? && new_record?
134
170
  end
135
171
  alias :[]= :write_attribute
136
172
 
137
- # Writes the supplied attributes +Hash+ to the +Document+. This will only
173
+ # Writes the supplied attributes hash to the document. This will only
138
174
  # overwrite existing attributes if they are present in the new +Hash+, all
139
175
  # others will be preserved.
140
176
  #
141
- # Options:
177
+ # @example Write the attributes.
178
+ # person.write_attributes(:title => "Mr.")
142
179
  #
143
- # attrs: The +Hash+ of new attributes to set on the +Document+
180
+ # @example Write the attributes (alternate syntax.)
181
+ # person.attributes = { :title => "Mr." }
144
182
  #
145
- # Example:
146
- #
147
- # <tt>person.write_attributes(:title => "Mr.")</tt>
148
- #
149
- # This will also cause the observing +Document+ to notify it's parent if
150
- # there is any.
183
+ # @param [ Hash ] attrs The new attributes to set.
151
184
  def write_attributes(attrs = nil)
152
185
  process(attrs || {})
153
- identified = !id.blank?
154
- if new_record? && !identified
155
- identify; notify
186
+ if new_record? && id.blank?
187
+ identify
156
188
  end
157
189
  end
158
190
  alias :attributes= :write_attributes
159
191
 
160
192
  protected
161
193
 
162
- # Return the typecast value for a field.
163
- def typed_value_for(key, value)
164
- fields.has_key?(key) ? fields[key].set(value) : value
165
- end
166
-
167
- # apply default values to attributes - calling procs as required
194
+ # Get the default values for the attributes.
195
+ #
196
+ # @example Get the defaults.
197
+ # person.default_attributes
198
+ #
199
+ # @return [ Hash ] The default values for each field.
168
200
  def default_attributes
169
201
  default_values = defaults
170
202
  default_values.each_pair do |key, val|
@@ -173,59 +205,47 @@ module Mongoid #:nodoc:
173
205
  default_values || {}
174
206
  end
175
207
 
176
- # Return true if dynamic field setting is enabled.
177
- def set_allowed?(key)
178
- Mongoid.allow_dynamic_fields && !respond_to?("#{key}=")
179
- end
180
-
181
- # Used when supplying a :reject_if block as an option to
182
- # accepts_nested_attributes_for
183
- def reject(attributes, options)
184
- rejector = options[:reject_if]
185
- if rejector
186
- attributes.delete_if do |key, value|
187
- rejector.call(value)
208
+ # Process all the pending relations that needed to wait until ids were set
209
+ # to fire off.
210
+ #
211
+ # @example Process the relations.
212
+ # document.process_relations({ "addressable" => person })
213
+ #
214
+ # @param [ Hash ] pending The pending relation values.
215
+ def process_relations(pending)
216
+ pending.each_pair do |name, value|
217
+ metadata = relations[name]
218
+ if value.is_a?(Hash)
219
+ metadata.nested_builder(value, {}).build(self)
220
+ else
221
+ send("#{name}=", value)
188
222
  end
189
223
  end
190
224
  end
191
225
 
192
- # Used when supplying a :limit as an option to accepts_nested_attributes_for
193
- def limit(attributes, name, options)
194
- if options[:limit] && attributes.size > options[:limit]
195
- raise Mongoid::Errors::TooManyNestedAttributeRecords.new(name, options[:limit])
196
- end
226
+ # Return true if dynamic field setting is enabled.
227
+ #
228
+ # @example Is a set allowed for this name?
229
+ # person.set_allowed?(:title)
230
+ #
231
+ # @param [ String, Symbol ] key The name of the field.
232
+ #
233
+ # @return [ true, false ] True if allowed, false if not.
234
+ def set_allowed?(key)
235
+ Mongoid.allow_dynamic_fields && !respond_to?("#{key}=")
197
236
  end
198
237
 
199
- module ClassMethods
200
- # Defines attribute setters for the associations specified by the names.
201
- # This will work for a has one or has many association.
202
- #
203
- # Example:
204
- #
205
- # class Person
206
- # include Mongoid::Document
207
- # embeds_one :name
208
- # embeds_many :addresses
209
- #
210
- # accepts_nested_attributes_for :name, :addresses
211
- # end
212
- def accepts_nested_attributes_for(*args)
213
- associations = args.flatten
214
- options = associations.last.is_a?(Hash) ? associations.pop : {}
215
- associations.each do |name|
216
- define_method("#{name}_attributes=") do |attrs|
217
- reject(attrs, options)
218
- limit(attrs, name, options)
219
- association = send(name)
220
- if association
221
- # observe(association, true)
222
- association.nested_build(attrs, options)
223
- else
224
- send("build_#{name}", attrs, options)
225
- end
226
- end
227
- end
228
- end
238
+ # Return the typecasted value for a field.
239
+ #
240
+ # @example Get the value typecasted.
241
+ # person.typed_value_for(:title, :sir)
242
+ #
243
+ # @param [ String, Symbol ] key The field name.
244
+ # @param [ Object ] value The uncast value.
245
+ #
246
+ # @return [ Object ] The cast value.
247
+ def typed_value_for(key, value)
248
+ fields.has_key?(key) ? fields[key].set(value) : value
229
249
  end
230
250
  end
231
251
  end