mongoid 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/README.textile +5 -4
  2. data/VERSION +1 -1
  3. data/lib/mongoid.rb +3 -28
  4. data/lib/mongoid/associations.rb +63 -44
  5. data/lib/mongoid/associations/{relates_to_one.rb → belongs_to_related.rb} +4 -4
  6. data/lib/mongoid/associations/has_many.rb +26 -12
  7. data/lib/mongoid/associations/has_many_related.rb +100 -0
  8. data/lib/mongoid/associations/has_one.rb +13 -2
  9. data/lib/mongoid/associations/{relates_to_many.rb → has_one_related.rb} +34 -9
  10. data/lib/mongoid/attributes.rb +108 -8
  11. data/lib/mongoid/commands.rb +3 -11
  12. data/lib/mongoid/commands/save.rb +2 -6
  13. data/lib/mongoid/document.rb +6 -70
  14. data/lib/mongoid/errors.rb +34 -0
  15. data/mongoid.gemspec +14 -11
  16. data/spec/integration/mongoid/associations_spec.rb +19 -14
  17. data/spec/spec_helper.rb +6 -3
  18. data/spec/unit/mongoid/associations/belongs_to_related_spec.rb +112 -0
  19. data/spec/unit/mongoid/associations/has_many_related_spec.rb +292 -0
  20. data/spec/unit/mongoid/associations/has_many_spec.rb +17 -0
  21. data/spec/unit/mongoid/associations/has_one_related_spec.rb +130 -0
  22. data/spec/unit/mongoid/associations/has_one_spec.rb +53 -0
  23. data/spec/unit/mongoid/associations_spec.rb +36 -8
  24. data/spec/unit/mongoid/attributes_spec.rb +218 -0
  25. data/spec/unit/mongoid/commands/save_spec.rb +4 -2
  26. data/spec/unit/mongoid/commands_spec.rb +4 -17
  27. data/spec/unit/mongoid/criteria_spec.rb +0 -5
  28. data/spec/unit/mongoid/document_spec.rb +26 -156
  29. data/spec/unit/mongoid/errors_spec.rb +87 -0
  30. metadata +14 -11
  31. data/lib/mongoid/commands/quick_save.rb +0 -19
  32. data/spec/unit/mongoid/associations/relates_to_many_spec.rb +0 -76
  33. data/spec/unit/mongoid/associations/relates_to_one_spec.rb +0 -112
  34. data/spec/unit/mongoid/commands/quick_save_spec.rb +0 -24
@@ -4,7 +4,7 @@ module Mongoid #:nodoc:
4
4
  class HasOne #:nodoc:
5
5
 
6
6
  delegate :==, :to => :document
7
- attr_reader :document, :parent, :options
7
+ attr_reader :association_name, :document, :parent, :options
8
8
 
9
9
  # Build a new object for the association.
10
10
  def build(attributes)
@@ -32,7 +32,7 @@ module Mongoid #:nodoc:
32
32
  # attributes: The attributes of the decorated object.
33
33
  # options: The association options.
34
34
  def initialize(document, attributes, options)
35
- @parent, @options = document, options
35
+ @parent, @options, @association_name = document, options, options.name
36
36
  unless attributes.nil?
37
37
  @document = attributes.assimilate(@parent, @options)
38
38
  end
@@ -43,6 +43,17 @@ module Mongoid #:nodoc:
43
43
  @document.send(name, *args)
44
44
  end
45
45
 
46
+ # Used for setting the association via a nested attributes setter on the
47
+ # parent +Document+.
48
+ def nested_build(attributes)
49
+ build(attributes)
50
+ end
51
+
52
+ # Need to override here for when the underlying document is nil.
53
+ def valid?
54
+ @document ? @document.valid? : false
55
+ end
56
+
46
57
  class << self
47
58
  # Preferred method of instantiating a new +HasOne+, since nil values
48
59
  # will be handled properly.
@@ -1,7 +1,26 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
3
  module Associations #:nodoc:
4
- class RelatesToMany < DelegateClass(Array) #:nodoc:
4
+ class HasOneRelated #:nodoc:
5
+
6
+ delegate :==, :nil?, :to => :document
7
+ attr_reader :klass, :document
8
+
9
+ # Builds a new Document and sets it as the association.
10
+ #
11
+ # Returns the newly created object.
12
+ def build(attributes)
13
+ @document = @klass.instantiate(attributes)
14
+ @document.send("#{@foreign_key}=", @parent.id)
15
+ end
16
+
17
+ # Builds a new Document and sets it as the association, then saves the
18
+ # newly created document.
19
+ #
20
+ # Returns the newly created object.
21
+ def create(attributes)
22
+ build(attributes); @document.save; @document
23
+ end
5
24
 
6
25
  # Initializing a related association only requires looking up the objects
7
26
  # by their ids.
@@ -11,9 +30,14 @@ module Mongoid #:nodoc:
11
30
  # document: The +Document+ that contains the relationship.
12
31
  # options: The association +Options+.
13
32
  def initialize(document, options)
14
- name = document.class.to_s.foreign_key
15
- @documents = options.klass.all(:conditions => { name => document.id })
16
- super(@documents)
33
+ @parent, @klass = document, options.klass
34
+ @foreign_key = document.class.to_s.foreign_key
35
+ @document = @klass.first(:conditions => { @foreign_key => @parent.id })
36
+ end
37
+
38
+ # Delegate all missing methods over to the +Document+.
39
+ def method_missing(name, *args)
40
+ @document.send(name, *args)
17
41
  end
18
42
 
19
43
  class << self
@@ -29,7 +53,7 @@ module Mongoid #:nodoc:
29
53
 
30
54
  # Returns the macro used to create the association.
31
55
  def macro
32
- :relates_to_many
56
+ :has_one_related
33
57
  end
34
58
 
35
59
  # Perform an update of the relationship of the parent and child. This
@@ -37,16 +61,17 @@ module Mongoid #:nodoc:
37
61
  #
38
62
  # Options:
39
63
  #
40
- # related: The related object
41
- # parent: The parent +Document+ to update.
64
+ # related: The related object to update.
65
+ # document: The parent +Document+.
42
66
  # options: The association +Options+
43
67
  #
44
68
  # Example:
45
69
  #
46
- # <tt>RelatesToOne.update(game, person, options)</tt>
70
+ # <tt>HasManyToRelated.update(game, person, options)</tt>
47
71
  def update(related, document, options)
48
72
  name = document.class.to_s.underscore
49
- related.each { |child| child.send("#{name}=", document) }
73
+ related.send("#{name}=", document)
74
+ related
50
75
  end
51
76
  end
52
77
 
@@ -1,13 +1,113 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
- module Attributes #:nodoc:
4
- # Process the provided attributes casting them to their proper values if a
5
- # field exists for them on the +Document+. This will be limited to only the
6
- # attributes provided in the suppied +Hash+ so that no extra nil values get
7
- # put into the document's attributes.
8
- def process(attrs = {})
9
- attrs.each_pair do |key, value|
10
- send("#{key}=", value)
3
+ module Attributes
4
+ def self.included(base)
5
+ base.class_eval do
6
+ include InstanceMethods
7
+ extend ClassMethods
8
+ end
9
+ end
10
+ module InstanceMethods
11
+ # Process the provided attributes casting them to their proper values if a
12
+ # field exists for them on the +Document+. This will be limited to only the
13
+ # attributes provided in the suppied +Hash+ so that no extra nil values get
14
+ # put into the document's attributes.
15
+ def process(attrs = {})
16
+ attrs.each_pair do |key, value|
17
+ send("#{key}=", value)
18
+ end
19
+ end
20
+
21
+ # Read a value from the +Document+ attributes. If the value does not exist
22
+ # it will return nil.
23
+ #
24
+ # Options:
25
+ #
26
+ # name: The name of the attribute to get.
27
+ #
28
+ # Example:
29
+ #
30
+ # <tt>person.read_attribute(:title)</tt>
31
+ def read_attribute(name)
32
+ fields[name].get(@attributes[name])
33
+ end
34
+
35
+ # Remove a value from the +Document+ attributes. If the value does not exist
36
+ # it will fail gracefully.
37
+ #
38
+ # Options:
39
+ #
40
+ # name: The name of the attribute to remove.
41
+ #
42
+ # Example:
43
+ #
44
+ # <tt>person.remove_attribute(:title)</tt>
45
+ def remove_attribute(name)
46
+ @attributes.delete(name)
47
+ end
48
+
49
+ # Write a single attribute to the +Document+ attribute +Hash+. This will
50
+ # also fire the before and after update callbacks, and perform any
51
+ # necessary typecasting.
52
+ #
53
+ # Options:
54
+ #
55
+ # name: The name of the attribute to update.
56
+ # value: The value to set for the attribute.
57
+ #
58
+ # Example:
59
+ #
60
+ # <tt>person.write_attribute(:title, "Mr.")</tt>
61
+ #
62
+ # This will also cause the observing +Document+ to notify it's parent if
63
+ # there is any.
64
+ def write_attribute(name, value)
65
+ run_callbacks(:before_update)
66
+ @attributes[name] = fields[name].set(value)
67
+ run_callbacks(:after_update)
68
+ notify
69
+ end
70
+
71
+ # Writes the supplied attributes +Hash+ to the +Document+. This will only
72
+ # overwrite existing attributes if they are present in the new +Hash+, all
73
+ # others will be preserved.
74
+ #
75
+ # Options:
76
+ #
77
+ # attrs: The +Hash+ of new attributes to set on the +Document+
78
+ #
79
+ # Example:
80
+ #
81
+ # <tt>person.write_attributes(:title => "Mr.")</tt>
82
+ #
83
+ # This will also cause the observing +Document+ to notify it's parent if
84
+ # there is any.
85
+ def write_attributes(attrs)
86
+ process(attrs)
87
+ notify
88
+ end
89
+ end
90
+
91
+ module ClassMethods
92
+ # Defines attribute setters for the associations specified by the names.
93
+ # This will work for a has one or has many association.
94
+ #
95
+ # Example:
96
+ #
97
+ # class Person < Mongoid::Document
98
+ # has_one :name
99
+ # has_many :addresses
100
+ #
101
+ # accepts_nested_attributes_for :name, :addresses
102
+ # end
103
+ def accepts_nested_attributes_for(*args)
104
+ args.flatten.each do |name|
105
+ define_method("#{name}_attributes=") do |attrs|
106
+ association = send(name)
107
+ update(association, true)
108
+ association.nested_build(attrs)
109
+ end
110
+ end
11
111
  end
12
112
  end
13
113
  end
@@ -5,7 +5,6 @@ require "mongoid/commands/delete_all"
5
5
  require "mongoid/commands/destroy"
6
6
  require "mongoid/commands/destroy_all"
7
7
  require "mongoid/commands/save"
8
- require "mongoid/commands/quick_save"
9
8
  require "mongoid/commands/validate"
10
9
 
11
10
  module Mongoid #:nodoc:
@@ -56,19 +55,12 @@ module Mongoid #:nodoc:
56
55
  # returns false then a +ValidationError+ will be raised.
57
56
  def save!
58
57
  if new_record?
59
- return Create.execute(self) || (raise ValidationsError.new(self.errors.full_messages))
58
+ return Create.execute(self) || (raise Errors::Validations.new(self.errors))
60
59
  else
61
- return Save.execute(self) || (raise ValidationsError.new(self.errors.full_messages))
60
+ return Save.execute(self) || (raise Errors::Validations.new(self.errors))
62
61
  end
63
62
  end
64
63
 
65
- # Performs a save with no validations and no callbacks. This is used in
66
- # relational association saves, and is not recommended for use by
67
- # anything else.
68
- def quick_save
69
- QuickSave.execute(self)
70
- end
71
-
72
64
  # Update the attributes of the +Document+. Will call save after the
73
65
  # attributes have been updated.
74
66
  def update_attributes(attrs = {})
@@ -97,7 +89,7 @@ module Mongoid #:nodoc:
97
89
  # validation.
98
90
  def create!(attributes = {})
99
91
  document = Create.execute(new(attributes))
100
- raise ValidationsError.new(self.errors.full_messages) unless document.errors.empty?
92
+ raise Errors::Validations.new(self.errors) unless document.errors.empty?
101
93
  return document
102
94
  end
103
95
 
@@ -14,12 +14,8 @@ module Mongoid #:nodoc:
14
14
  return false unless Validate.execute(doc)
15
15
  doc.run_callbacks :before_save
16
16
  parent = doc.parent
17
- if parent
18
- Save.execute(parent)
19
- else
20
- collection = doc.collection
21
- collection ? collection.save(doc.attributes) : raise(MissingParentError.new(doc))
22
- end
17
+ doc.new_record = false
18
+ parent ? Save.execute(parent) : doc.collection.save(doc.attributes)
23
19
  doc.run_callbacks :after_save
24
20
  return true
25
21
  end
@@ -21,7 +21,7 @@ module Mongoid #:nodoc:
21
21
  #
22
22
  # Returns: <tt>Mongo::Collection</tt>
23
23
  def collection
24
- return nil if embedded?
24
+ raise Errors::InvalidCollection.new(self) if embedded?
25
25
  @collection_name ||= self.to_s.demodulize.tableize
26
26
  @collection ||= Mongoid.database.collection(@collection_name)
27
27
  end
@@ -195,6 +195,11 @@ module Mongoid #:nodoc:
195
195
  @new_record == true
196
196
  end
197
197
 
198
+ # Sets the new_record boolean - used after document is saved.
199
+ def new_record=(saved)
200
+ @new_record = saved
201
+ end
202
+
198
203
  # Set the changed state of the +Document+ then notify observers that it has changed.
199
204
  #
200
205
  # Example:
@@ -223,34 +228,6 @@ module Mongoid #:nodoc:
223
228
  add_observer(object)
224
229
  end
225
230
 
226
- # Read a value from the +Document+ attributes. If the value does not exist
227
- # it will return nil.
228
- #
229
- # Options:
230
- #
231
- # name: The name of the attribute to get.
232
- #
233
- # Example:
234
- #
235
- # <tt>person.read_attribute(:title)</tt>
236
- def read_attribute(name)
237
- fields[name].get(@attributes[name])
238
- end
239
-
240
- # Remove a value from the +Document+ attributes. If the value does not exist
241
- # it will fail gracefully.
242
- #
243
- # Options:
244
- #
245
- # name: The name of the attribute to remove.
246
- #
247
- # Example:
248
- #
249
- # <tt>person.remove_attribute(:title)</tt>
250
- def remove_attribute(name)
251
- @attributes.delete(name)
252
- end
253
-
254
231
  # Reloads the +Document+ attributes from the database.
255
232
  def reload
256
233
  @attributes = collection.find_one(:_id => id).with_indifferent_access
@@ -290,47 +267,6 @@ module Mongoid #:nodoc:
290
267
  notify
291
268
  end
292
269
 
293
- # Write a single attribute to the +Document+ attribute +Hash+. This will
294
- # also fire the before and after update callbacks, and perform any
295
- # necessary typecasting.
296
- #
297
- # Options:
298
- #
299
- # name: The name of the attribute to update.
300
- # value: The value to set for the attribute.
301
- #
302
- # Example:
303
- #
304
- # <tt>person.write_attribute(:title, "Mr.")</tt>
305
- #
306
- # This will also cause the observing +Document+ to notify it's parent if
307
- # there is any.
308
- def write_attribute(name, value)
309
- run_callbacks(:before_update)
310
- @attributes[name] = fields[name].set(value)
311
- run_callbacks(:after_update)
312
- notify
313
- end
314
-
315
- # Writes the supplied attributes +Hash+ to the +Document+. This will only
316
- # overwrite existing attributes if they are present in the new +Hash+, all
317
- # others will be preserved.
318
- #
319
- # Options:
320
- #
321
- # attrs: The +Hash+ of new attributes to set on the +Document+
322
- #
323
- # Example:
324
- #
325
- # <tt>person.write_attributes(:title => "Mr.")</tt>
326
- #
327
- # This will also cause the observing +Document+ to notify it's parent if
328
- # there is any.
329
- def write_attributes(attrs)
330
- process(attrs)
331
- notify
332
- end
333
-
334
270
  protected
335
271
  def generate_key
336
272
  if primary_key
@@ -0,0 +1,34 @@
1
+ module Mongoid #:nodoc
2
+ module Errors #:nodoc
3
+
4
+ # Raised when invalid options are passed into a constructor.
5
+ class InvalidOptions < RuntimeError; end
6
+
7
+ # Raised when the database connection has not been set up.
8
+ class InvalidDatabase < RuntimeError; end
9
+
10
+ # Raised when a persisence method ending in ! fails validation.
11
+ class Validations < RuntimeError
12
+ def initialize(errors)
13
+ @errors = errors
14
+ end
15
+ def message
16
+ "Validation failed: #{@errors.full_messages}"
17
+ end
18
+ end
19
+
20
+ # This error is raised when trying to access a Mongo::Collection from an
21
+ # embedded document.
22
+ class InvalidCollection < RuntimeError
23
+ def initialize(klass)
24
+ @klass = klass
25
+ end
26
+ def message
27
+ "Access to the collection for #{@klass.name} is not allowed " +
28
+ "since it is an embedded document, please access a collection from " +
29
+ "the root document"
30
+ end
31
+ end
32
+
33
+ end
34
+ end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mongoid}
8
- s.version = "0.9.8"
8
+ s.version = "0.9.9"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Durran Jordan"]
12
- s.date = %q{2009-12-12}
12
+ s.date = %q{2009-12-13}
13
13
  s.email = %q{durran@gmail.com}
14
14
  s.extra_rdoc_files = [
15
15
  "README.textile"
@@ -24,11 +24,12 @@ Gem::Specification.new do |s|
24
24
  "lib/mongoid.rb",
25
25
  "lib/mongoid/associations.rb",
26
26
  "lib/mongoid/associations/belongs_to.rb",
27
+ "lib/mongoid/associations/belongs_to_related.rb",
27
28
  "lib/mongoid/associations/has_many.rb",
29
+ "lib/mongoid/associations/has_many_related.rb",
28
30
  "lib/mongoid/associations/has_one.rb",
31
+ "lib/mongoid/associations/has_one_related.rb",
29
32
  "lib/mongoid/associations/options.rb",
30
- "lib/mongoid/associations/relates_to_many.rb",
31
- "lib/mongoid/associations/relates_to_one.rb",
32
33
  "lib/mongoid/attributes.rb",
33
34
  "lib/mongoid/commands.rb",
34
35
  "lib/mongoid/commands/create.rb",
@@ -36,12 +37,12 @@ Gem::Specification.new do |s|
36
37
  "lib/mongoid/commands/delete_all.rb",
37
38
  "lib/mongoid/commands/destroy.rb",
38
39
  "lib/mongoid/commands/destroy_all.rb",
39
- "lib/mongoid/commands/quick_save.rb",
40
40
  "lib/mongoid/commands/save.rb",
41
41
  "lib/mongoid/commands/validate.rb",
42
42
  "lib/mongoid/criteria.rb",
43
43
  "lib/mongoid/document.rb",
44
44
  "lib/mongoid/dynamic_finder.rb",
45
+ "lib/mongoid/errors.rb",
45
46
  "lib/mongoid/extensions.rb",
46
47
  "lib/mongoid/extensions/array/accessors.rb",
47
48
  "lib/mongoid/extensions/array/assimilation.rb",
@@ -71,12 +72,13 @@ Gem::Specification.new do |s|
71
72
  "spec/integration/mongoid/document_spec.rb",
72
73
  "spec/spec.opts",
73
74
  "spec/spec_helper.rb",
75
+ "spec/unit/mongoid/associations/belongs_to_related_spec.rb",
74
76
  "spec/unit/mongoid/associations/belongs_to_spec.rb",
77
+ "spec/unit/mongoid/associations/has_many_related_spec.rb",
75
78
  "spec/unit/mongoid/associations/has_many_spec.rb",
79
+ "spec/unit/mongoid/associations/has_one_related_spec.rb",
76
80
  "spec/unit/mongoid/associations/has_one_spec.rb",
77
81
  "spec/unit/mongoid/associations/options_spec.rb",
78
- "spec/unit/mongoid/associations/relates_to_many_spec.rb",
79
- "spec/unit/mongoid/associations/relates_to_one_spec.rb",
80
82
  "spec/unit/mongoid/associations_spec.rb",
81
83
  "spec/unit/mongoid/attributes_spec.rb",
82
84
  "spec/unit/mongoid/commands/create_spec.rb",
@@ -84,13 +86,13 @@ Gem::Specification.new do |s|
84
86
  "spec/unit/mongoid/commands/delete_spec.rb",
85
87
  "spec/unit/mongoid/commands/destroy_all_spec.rb",
86
88
  "spec/unit/mongoid/commands/destroy_spec.rb",
87
- "spec/unit/mongoid/commands/quick_save_spec.rb",
88
89
  "spec/unit/mongoid/commands/save_spec.rb",
89
90
  "spec/unit/mongoid/commands/validate_spec.rb",
90
91
  "spec/unit/mongoid/commands_spec.rb",
91
92
  "spec/unit/mongoid/criteria_spec.rb",
92
93
  "spec/unit/mongoid/document_spec.rb",
93
94
  "spec/unit/mongoid/dynamic_finder_spec.rb",
95
+ "spec/unit/mongoid/errors_spec.rb",
94
96
  "spec/unit/mongoid/extensions/array/accessors_spec.rb",
95
97
  "spec/unit/mongoid/extensions/array/assimilation_spec.rb",
96
98
  "spec/unit/mongoid/extensions/array/conversions_spec.rb",
@@ -123,12 +125,13 @@ Gem::Specification.new do |s|
123
125
  "spec/integration/mongoid/associations_spec.rb",
124
126
  "spec/integration/mongoid/document_spec.rb",
125
127
  "spec/spec_helper.rb",
128
+ "spec/unit/mongoid/associations/belongs_to_related_spec.rb",
126
129
  "spec/unit/mongoid/associations/belongs_to_spec.rb",
130
+ "spec/unit/mongoid/associations/has_many_related_spec.rb",
127
131
  "spec/unit/mongoid/associations/has_many_spec.rb",
132
+ "spec/unit/mongoid/associations/has_one_related_spec.rb",
128
133
  "spec/unit/mongoid/associations/has_one_spec.rb",
129
134
  "spec/unit/mongoid/associations/options_spec.rb",
130
- "spec/unit/mongoid/associations/relates_to_many_spec.rb",
131
- "spec/unit/mongoid/associations/relates_to_one_spec.rb",
132
135
  "spec/unit/mongoid/associations_spec.rb",
133
136
  "spec/unit/mongoid/attributes_spec.rb",
134
137
  "spec/unit/mongoid/commands/create_spec.rb",
@@ -136,13 +139,13 @@ Gem::Specification.new do |s|
136
139
  "spec/unit/mongoid/commands/delete_spec.rb",
137
140
  "spec/unit/mongoid/commands/destroy_all_spec.rb",
138
141
  "spec/unit/mongoid/commands/destroy_spec.rb",
139
- "spec/unit/mongoid/commands/quick_save_spec.rb",
140
142
  "spec/unit/mongoid/commands/save_spec.rb",
141
143
  "spec/unit/mongoid/commands/validate_spec.rb",
142
144
  "spec/unit/mongoid/commands_spec.rb",
143
145
  "spec/unit/mongoid/criteria_spec.rb",
144
146
  "spec/unit/mongoid/document_spec.rb",
145
147
  "spec/unit/mongoid/dynamic_finder_spec.rb",
148
+ "spec/unit/mongoid/errors_spec.rb",
146
149
  "spec/unit/mongoid/extensions/array/accessors_spec.rb",
147
150
  "spec/unit/mongoid/extensions/array/assimilation_spec.rb",
148
151
  "spec/unit/mongoid/extensions/array/conversions_spec.rb",