mongo_doc_rails2 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. data/.document +5 -0
  2. data/.gitignore +8 -0
  3. data/HISTORY.md +11 -0
  4. data/LICENSE +20 -0
  5. data/README.textile +185 -0
  6. data/Rakefile +188 -0
  7. data/TODO +40 -0
  8. data/VERSION +1 -0
  9. data/data/.gitignore +2 -0
  10. data/examples/simple_document.rb +46 -0
  11. data/examples/simple_object.rb +34 -0
  12. data/features/collections.feature +9 -0
  13. data/features/embed_hash.feature +16 -0
  14. data/features/finders.feature +76 -0
  15. data/features/indexes.feature +28 -0
  16. data/features/mongodb.yml +7 -0
  17. data/features/mongodoc_base.feature +128 -0
  18. data/features/new_record.feature +36 -0
  19. data/features/partial_updates.feature +95 -0
  20. data/features/removing_documents.feature +68 -0
  21. data/features/saving_an_object.feature +15 -0
  22. data/features/scopes.feature +66 -0
  23. data/features/step_definitions/collection_steps.rb +17 -0
  24. data/features/step_definitions/document_steps.rb +149 -0
  25. data/features/step_definitions/documents.rb +40 -0
  26. data/features/step_definitions/embed_hash_steps.rb +6 -0
  27. data/features/step_definitions/finder_steps.rb +15 -0
  28. data/features/step_definitions/index_steps.rb +10 -0
  29. data/features/step_definitions/json_steps.rb +9 -0
  30. data/features/step_definitions/object_steps.rb +50 -0
  31. data/features/step_definitions/objects.rb +24 -0
  32. data/features/step_definitions/partial_update_steps.rb +31 -0
  33. data/features/step_definitions/query_steps.rb +66 -0
  34. data/features/step_definitions/removing_documents_steps.rb +14 -0
  35. data/features/step_definitions/scope_steps.rb +18 -0
  36. data/features/step_definitions/string_casting_steps.rb +29 -0
  37. data/features/step_definitions/util_steps.rb +7 -0
  38. data/features/string_casting.feature +10 -0
  39. data/features/support/support.rb +10 -0
  40. data/features/using_criteria.feature +142 -0
  41. data/lib/mongo_doc.rb +12 -0
  42. data/lib/mongo_doc/associations.rb +109 -0
  43. data/lib/mongo_doc/associations/collection_proxy.rb +121 -0
  44. data/lib/mongo_doc/associations/document_proxy.rb +65 -0
  45. data/lib/mongo_doc/associations/hash_proxy.rb +102 -0
  46. data/lib/mongo_doc/associations/proxy_base.rb +48 -0
  47. data/lib/mongo_doc/attributes.rb +84 -0
  48. data/lib/mongo_doc/bson.rb +31 -0
  49. data/lib/mongo_doc/collection.rb +82 -0
  50. data/lib/mongo_doc/connection.rb +88 -0
  51. data/lib/mongo_doc/contexts.rb +31 -0
  52. data/lib/mongo_doc/contexts/ids.rb +41 -0
  53. data/lib/mongo_doc/contexts/mongo.rb +272 -0
  54. data/lib/mongo_doc/criteria.rb +70 -0
  55. data/lib/mongo_doc/cursor.rb +32 -0
  56. data/lib/mongo_doc/document.rb +205 -0
  57. data/lib/mongo_doc/ext.rb +16 -0
  58. data/lib/mongo_doc/ext/array.rb +5 -0
  59. data/lib/mongo_doc/ext/binary.rb +7 -0
  60. data/lib/mongo_doc/ext/boolean_class.rb +17 -0
  61. data/lib/mongo_doc/ext/date.rb +19 -0
  62. data/lib/mongo_doc/ext/date_time.rb +17 -0
  63. data/lib/mongo_doc/ext/dbref.rb +7 -0
  64. data/lib/mongo_doc/ext/hash.rb +7 -0
  65. data/lib/mongo_doc/ext/min_max_keys.rb +13 -0
  66. data/lib/mongo_doc/ext/nil_class.rb +5 -0
  67. data/lib/mongo_doc/ext/numeric.rb +17 -0
  68. data/lib/mongo_doc/ext/object.rb +19 -0
  69. data/lib/mongo_doc/ext/object_id.rb +7 -0
  70. data/lib/mongo_doc/ext/regexp.rb +5 -0
  71. data/lib/mongo_doc/ext/string.rb +5 -0
  72. data/lib/mongo_doc/ext/symbol.rb +5 -0
  73. data/lib/mongo_doc/ext/time.rb +9 -0
  74. data/lib/mongo_doc/finders.rb +38 -0
  75. data/lib/mongo_doc/index.rb +46 -0
  76. data/lib/mongo_doc/matchers.rb +35 -0
  77. data/lib/mongo_doc/root.rb +26 -0
  78. data/lib/mongo_doc/scope.rb +64 -0
  79. data/lib/mongo_doc/validations.rb +12 -0
  80. data/lib/mongo_doc/validations/macros.rb +11 -0
  81. data/lib/mongo_doc/validations/validates_embedded.rb +13 -0
  82. data/lib/mongoid/contexts/enumerable.rb +151 -0
  83. data/lib/mongoid/contexts/paging.rb +42 -0
  84. data/lib/mongoid/criteria.rb +239 -0
  85. data/lib/mongoid/criterion/complex.rb +21 -0
  86. data/lib/mongoid/criterion/exclusion.rb +65 -0
  87. data/lib/mongoid/criterion/inclusion.rb +93 -0
  88. data/lib/mongoid/criterion/optional.rb +136 -0
  89. data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
  90. data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
  91. data/lib/mongoid/matchers/all.rb +11 -0
  92. data/lib/mongoid/matchers/default.rb +26 -0
  93. data/lib/mongoid/matchers/exists.rb +13 -0
  94. data/lib/mongoid/matchers/gt.rb +11 -0
  95. data/lib/mongoid/matchers/gte.rb +11 -0
  96. data/lib/mongoid/matchers/in.rb +11 -0
  97. data/lib/mongoid/matchers/lt.rb +11 -0
  98. data/lib/mongoid/matchers/lte.rb +11 -0
  99. data/lib/mongoid/matchers/ne.rb +11 -0
  100. data/lib/mongoid/matchers/nin.rb +11 -0
  101. data/lib/mongoid/matchers/size.rb +11 -0
  102. data/mongo_doc_rails2.gemspec +237 -0
  103. data/mongod.example.yml +2 -0
  104. data/mongodb.example.yml +14 -0
  105. data/perf/mongo_doc_object.rb +83 -0
  106. data/perf/mongo_document.rb +84 -0
  107. data/perf/ruby_driver.rb +49 -0
  108. data/script/console +8 -0
  109. data/spec/array_including_argument_matcher.rb +62 -0
  110. data/spec/associations/collection_proxy_spec.rb +233 -0
  111. data/spec/associations/document_proxy_spec.rb +45 -0
  112. data/spec/associations/hash_proxy_spec.rb +181 -0
  113. data/spec/associations/proxy_base_spec.rb +92 -0
  114. data/spec/associations_spec.rb +218 -0
  115. data/spec/attributes_accessor_spec.rb +33 -0
  116. data/spec/attributes_spec.rb +145 -0
  117. data/spec/bson_matchers.rb +54 -0
  118. data/spec/bson_spec.rb +196 -0
  119. data/spec/collection_spec.rb +169 -0
  120. data/spec/connection_spec.rb +147 -0
  121. data/spec/contexts/ids_spec.rb +49 -0
  122. data/spec/contexts/mongo_spec.rb +235 -0
  123. data/spec/contexts_spec.rb +56 -0
  124. data/spec/criteria_spec.rb +69 -0
  125. data/spec/cursor_spec.rb +91 -0
  126. data/spec/document_ext.rb +9 -0
  127. data/spec/document_spec.rb +553 -0
  128. data/spec/embedded_save_spec.rb +73 -0
  129. data/spec/ext_spec.rb +89 -0
  130. data/spec/finders_spec.rb +61 -0
  131. data/spec/hash_matchers.rb +27 -0
  132. data/spec/index_spec.rb +79 -0
  133. data/spec/matchers_spec.rb +342 -0
  134. data/spec/mongodb.yml +6 -0
  135. data/spec/mongodb_pairs.yml +8 -0
  136. data/spec/new_record_spec.rb +128 -0
  137. data/spec/root_spec.rb +41 -0
  138. data/spec/scope_spec.rb +79 -0
  139. data/spec/spec.opts +2 -0
  140. data/spec/spec_helper.rb +14 -0
  141. data/spec/validations_spec.rb +30 -0
  142. metadata +346 -0
@@ -0,0 +1,2 @@
1
+ mongod: /usr/local/bin/mongod
2
+ dbpath: /data/db
@@ -0,0 +1,14 @@
1
+ development:
2
+ name: development
3
+ host: localhost
4
+ port: 27017
5
+ options:
6
+ auto_reconnect: true
7
+
8
+ test:
9
+ name: test
10
+ host: localhost
11
+ port: 27017
12
+ options:
13
+ auto_reconnect: true
14
+
@@ -0,0 +1,83 @@
1
+ require "rubygems"
2
+ require "ruby-prof"
3
+ require "mongo_doc"
4
+
5
+ class Person
6
+ attr_accessor :address, :birth_date, :name, :phones
7
+ end
8
+
9
+ class Name
10
+ attr_accessor :family, :given, :middle
11
+ end
12
+
13
+ class Address
14
+ attr_accessor :city, :post_code, :state, :street, :type
15
+ end
16
+
17
+ class Phone
18
+ attr_accessor :country_code, :number, :type
19
+ end
20
+
21
+ class MongoDocObject
22
+ attr_accessor :collection
23
+ attr_accessor :objects
24
+ attr_accessor :ids
25
+
26
+ def initialize
27
+ MongoDoc::Connection.name = 'mongo_doc_object_test'
28
+ self.collection = MongoDoc::Collection.new 'people'
29
+ collection.drop
30
+ end
31
+
32
+ def generate(count)
33
+ self.objects = []
34
+ self.ids = []
35
+ count.times do |i|
36
+ person = Person.new
37
+ person.birth_date = Date.new(1970, 1, 1)
38
+ person.phones = []
39
+
40
+ name = Name.new
41
+ name.given = "James #{i}"
42
+ name.family = "Kirk"
43
+ name.middle = "Tiberius"
44
+
45
+ address = Address.new
46
+ address.street = "1 Starfleet Command Way"
47
+ address.city = "San Francisco"
48
+ address.state = "CA"
49
+ address.post_code = "94133"
50
+ address.type = "Work"
51
+
52
+ phone = Phone.new
53
+ phone.country_code = 1
54
+ phone.number = "415-555-1212"
55
+ phone.type = "Mobile"
56
+
57
+ person.name = name
58
+ person.address = address
59
+ person.phones << phone
60
+
61
+ objects << person
62
+ end
63
+ end
64
+
65
+ def writes(count)
66
+ count.times do |i|
67
+ ids[i] = collection.save(objects[i])
68
+ end
69
+ end
70
+
71
+ def reads(count)
72
+ count.times do |i|
73
+ obj = collection.find_one(ids[i])
74
+ raise 'Not found' unless obj.name.given == objects[i].name.given
75
+ end
76
+ end
77
+
78
+ def query_all
79
+ collection.find.each do |obj|
80
+ given_name = obj.name.given
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,84 @@
1
+ require "benchmark"
2
+ require "ruby-prof"
3
+ require "mongo_doc"
4
+
5
+ class Person
6
+ include MongoDoc::Document
7
+
8
+ attr_accessor :birth_date, :type => Date
9
+
10
+ embed :name
11
+ embed :address
12
+ embed_many :phones
13
+ end
14
+
15
+ class Name
16
+ include MongoDoc::Document
17
+
18
+ attr_accessor :given
19
+ attr_accessor :family
20
+ attr_accessor :middle
21
+ end
22
+
23
+ class Address
24
+ include MongoDoc::Document
25
+
26
+ attr_accessor :street
27
+ attr_accessor :city
28
+ attr_accessor :state
29
+ attr_accessor :post_code
30
+ attr_accessor :type
31
+ end
32
+
33
+ class Phone
34
+ include MongoDoc::Document
35
+
36
+ attr_accessor :country_code
37
+ attr_accessor :number
38
+ attr_accessor :type
39
+ end
40
+
41
+ class MongoDocument
42
+ attr_accessor :collection
43
+ attr_accessor :documents
44
+
45
+ def initialize
46
+ MongoDoc::Connection.name = 'mongo_doc_object_test'
47
+ self.collection = MongoDoc::Collection.new 'people'
48
+ collection.drop
49
+ end
50
+
51
+ def generate(count)
52
+ self.documents = []
53
+ count.times do |i|
54
+ person = Person.new(:birth_date => Date.new(1970, 1, 1))
55
+ name = Name.new(:given => "James #{i}", :family => "Kirk", :middle => "Tiberius")
56
+ address = Address.new(:street => "1 Starfleet Command Way", :city => "San Francisco", :state => "CA", :post_code => "94133", :type => "Work")
57
+ phone = Phone.new(:country_code => 1, :number => "415-555-1212", :type => "Mobile")
58
+ person.name = name
59
+ person.address = address
60
+ person.phones << phone
61
+
62
+ documents << person
63
+ end
64
+ end
65
+
66
+ def writes(count)
67
+ count.times do |i|
68
+ documents[i].save
69
+ end
70
+ end
71
+
72
+ def reads(count)
73
+ count.times do |i|
74
+ doc = collection.find_one(documents[i]._id)
75
+ raise 'Not found' unless doc.name.given == documents[i].name.given
76
+ end
77
+ end
78
+
79
+ def query_all
80
+ collection.find.each do |doc|
81
+ given_name = doc.name.given
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,49 @@
1
+ require "rubygems"
2
+ require "ruby-prof"
3
+ require "mongo_doc"
4
+
5
+ class RubyDriver
6
+ attr_accessor :collection
7
+ attr_accessor :objects
8
+ attr_accessor :ids
9
+
10
+ def initialize
11
+ MongoDoc::Connection.name = 'ruby_driver_test'
12
+ self.collection = MongoDoc::Connection.database.collection 'people'
13
+ collection.drop
14
+ end
15
+
16
+ def generate(count)
17
+ self.objects = []
18
+ self.ids = []
19
+ count.times do |i|
20
+ person = {:birth_date => Date.new(1970, 1, 1).to_bson, :phones => []}
21
+ name = {:given => "James #{i}", :family => "Kirk", :middle => "Tiberius"}
22
+ address = {:street => "1 Starfleet Command Way", :city => "San Francisco", :state => "CA", :post_code => "94133", :type => "Work"}
23
+ phone = {:country_code => 1, :number => "415-555-1212", :type => "Mobile"}
24
+ person[:name] = name
25
+ person[:address] = address
26
+ person[:phones] << phone
27
+ objects << person
28
+ end
29
+ end
30
+
31
+ def writes(count)
32
+ count.times do |i|
33
+ ids[i] = collection.save(objects[i])
34
+ end
35
+ end
36
+
37
+ def reads(count)
38
+ count.times do |i|
39
+ obj = collection.find_one(ids[i])
40
+ raise 'Not found' unless obj and obj['name']['given'] == objects[i][:name][:given]
41
+ end
42
+ end
43
+
44
+ def query_all
45
+ collection.find().each do |obj|
46
+ given_name = obj['name']['given']
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+
5
+ require 'irb'
6
+ require 'mongo_doc'
7
+
8
+ IRB.start(__FILE__)
@@ -0,0 +1,62 @@
1
+ # From http://gist.github.com/62943
2
+ # Author http://github.com/trotter
3
+ module Spec
4
+ module Mocks
5
+ module ArgumentMatchers
6
+
7
+ class ArrayIncludingMatcher
8
+ # We'll allow an array of arguments to be passed in, so that you can do
9
+ # things like obj.should_receive(:blah).with(array_including('a', 'b'))
10
+ def initialize(*expected)
11
+ @expected = expected
12
+ end
13
+
14
+ # actual is the array (hopefully) passed to the method by the user.
15
+ # We'll check that it includes all the expected values, and return false
16
+ # if it doesn't or if we blow up because #include? is not defined.
17
+ def ==(actual)
18
+ @expected.each do |expected|
19
+ return false unless actual.include?(expected)
20
+ end
21
+ true
22
+ rescue NoMethodError => ex
23
+ return false
24
+ end
25
+
26
+ def description
27
+ "array_including(#{@expected.join(', ')})"
28
+ end
29
+ end
30
+
31
+ class ArrayNotIncludingMatcher
32
+ def initialize(*expected)
33
+ @expected = expected
34
+ end
35
+
36
+ def ==(actual)
37
+ @expected.each do |expected|
38
+ return false if actual.include?(expected)
39
+ end
40
+ true
41
+ rescue NoMethodError => ex
42
+ return false
43
+ end
44
+
45
+ def description
46
+ "array_not_including(#{@expected.join(', ')})"
47
+ end
48
+ end
49
+
50
+ # array_including is a helpful wrapper that allows us to actually type
51
+ # #with(array_including(...)) instead of ArrayIncludingMatcher.new(...)
52
+ def array_including(*args)
53
+ ArrayIncludingMatcher.new(*args)
54
+ end
55
+
56
+ def array_not_including(*args)
57
+ ArrayNotIncludingMatcher.new(*args)
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,233 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '/spec_helper'))
2
+
3
+ describe MongoDoc::Associations::CollectionProxy do
4
+ class CollectionProxyTest
5
+ include MongoDoc::Document
6
+
7
+ attr_accessor :name
8
+ end
9
+
10
+ let(:name) { 'embed_many_name' }
11
+ let(:root) { CollectionProxyTest.new }
12
+ let(:proxy) { MongoDoc::Associations::CollectionProxy.new(:assoc_name => name, :assoc_class => CollectionProxyTest, :root => root, :parent => root) }
13
+ let(:item) { CollectionProxyTest.new }
14
+
15
+ describe "#_modifier_path" do
16
+ it "cascades to child documents with our assoc name and $" do
17
+ document = stub
18
+ proxy.stub(:collection => [document])
19
+ document.should_receive(:_modifier_path=).with("new.path.#{name}.$")
20
+ MongoDoc::Associations::ProxyBase.stub(:is_document?).and_return(true)
21
+ proxy._modifier_path = 'new.path'
22
+ end
23
+ end
24
+
25
+ describe "#_selector_path=" do
26
+ it "cascades to child documents" do
27
+ document = stub
28
+ proxy.stub(:collection => [document])
29
+ document.should_receive("_selector_path=").with("new.path.#{name}")
30
+ MongoDoc::Associations::ProxyBase.stub(:is_document?).and_return(true)
31
+ proxy._selector_path = 'new.path'
32
+ end
33
+ end
34
+
35
+ context "#attach_document" do
36
+ it "sets the Document's modifier path to our modifier path" do
37
+ item.should_receive(:_modifier_path=).with('embed_many_name.$')
38
+ proxy.send(:attach_document, item)
39
+ end
40
+
41
+ it "sets the Document's selector path to our selector path" do
42
+ item.should_receive(:_selector_path=).with('embed_many_name')
43
+ proxy.send(:attach_document, item)
44
+ end
45
+ end
46
+
47
+ context "#<<" do
48
+ it "appends the item to the collection" do
49
+ (proxy << item).should include(item)
50
+ end
51
+
52
+ context "when the item is a Hash" do
53
+ let(:hash) {{:name => 'hash'}}
54
+
55
+ it "does not register a save observer" do
56
+ root.should_not_receive(:register_save_observer)
57
+ proxy << hash
58
+ end
59
+
60
+ it "does not set the root" do
61
+ hash.should_not_receive(:_root=)
62
+ proxy << hash
63
+ end
64
+
65
+ it "adds the hash to the collection" do
66
+ proxy << hash
67
+ proxy.should include(hash)
68
+ end
69
+ end
70
+
71
+ context "when the item is not a MongoDoc::Document" do
72
+ let(:other_item) {'not a doc'}
73
+
74
+ it "does not register a save observer" do
75
+ root.should_not_receive(:register_save_observer)
76
+ proxy << other_item
77
+ end
78
+
79
+ it "does not set the root" do
80
+ other_item.should_not_receive(:_root=)
81
+ proxy << other_item
82
+ end
83
+
84
+ it "adds the item to the collection" do
85
+ proxy << other_item
86
+ proxy.should include(other_item)
87
+ end
88
+ end
89
+
90
+ context "when the item is a MongoDoc::Document" do
91
+ it "registers a save observer" do
92
+ root.should_receive(:register_save_observer)
93
+ proxy << item
94
+ end
95
+
96
+ it "sets the root" do
97
+ proxy << item
98
+ item._root.should == root
99
+ end
100
+ end
101
+
102
+ context "when the item is an array" do
103
+ it "adds the array" do
104
+ array = ['something else']
105
+ proxy << array
106
+ proxy.should include(array)
107
+ end
108
+ end
109
+ end
110
+
111
+ context "#[]=" do
112
+ it "sets the item at the index" do
113
+ proxy[1] = item
114
+ proxy[1].should == item
115
+ end
116
+
117
+ context "when the item is not a MongoDoc::Document" do
118
+ let(:other_item) {'not a doc'}
119
+
120
+ it "does not register a save observer" do
121
+ root.should_not_receive(:register_save_observer)
122
+ proxy[1] = other_item
123
+ end
124
+
125
+ it "does not set the root" do
126
+ other_item.should_not_receive(:_root=)
127
+ proxy[1] = other_item
128
+ end
129
+
130
+ it "adds the item to the collection" do
131
+ proxy[1] = other_item
132
+ proxy.should include(other_item)
133
+ end
134
+ end
135
+
136
+ context "when the item is a MongoDoc::Document" do
137
+ it "registers a save observer" do
138
+ root.should_receive(:register_save_observer)
139
+ proxy[1] = item
140
+ end
141
+
142
+ it "sets the root" do
143
+ proxy[1] = item
144
+ item._root.should == root
145
+ end
146
+ end
147
+ end
148
+
149
+ context "#concat" do
150
+ it "appends the items from the array to self" do
151
+ proxy.concat([item])
152
+ proxy.should include(item)
153
+ end
154
+ end
155
+
156
+ context "#replace" do
157
+ it "clears the existing collection" do
158
+ proxy.should_receive(:clear)
159
+ proxy.replace([item])
160
+ end
161
+
162
+ it "concats the other onto self" do
163
+ other = [item]
164
+ proxy.should_receive(:concat).with(other)
165
+ proxy.replace(other)
166
+ end
167
+ end
168
+
169
+ context "#unshift" do
170
+ let(:proxy_with_item) { proxy << 'other' }
171
+
172
+ it "adds the item to the front of the collection" do
173
+ proxy_with_item.unshift(item)
174
+ proxy_with_item[0].should == item
175
+ end
176
+
177
+ context "when the item is not a MongoDoc::Document" do
178
+ let(:other_item) {'not a doc'}
179
+
180
+ it "does not register a save observer" do
181
+ root.should_not_receive(:register_save_observer)
182
+ proxy_with_item.unshift(other_item)
183
+ end
184
+
185
+ it "does not set the root" do
186
+ other_item.should_not_receive(:_root=)
187
+ proxy_with_item.unshift(other_item)
188
+ end
189
+
190
+ it "adds the item to the front of the collection" do
191
+ proxy_with_item.unshift(other_item)
192
+ proxy_with_item[0].should == other_item
193
+ end
194
+ end
195
+
196
+ context "when the item is a MongoDoc::Document" do
197
+ let(:new_item) { CollectionProxyTest.new }
198
+
199
+ it "registers a save observer" do
200
+ root.should_receive(:register_save_observer)
201
+ proxy_with_item.unshift(new_item)
202
+ end
203
+
204
+ it "sets the root" do
205
+ proxy_with_item.unshift(new_item)
206
+ new_item._root.should == root
207
+ end
208
+
209
+ it "adds the item to the front of the collection" do
210
+ proxy_with_item.unshift(new_item)
211
+ proxy_with_item[0].should == new_item
212
+ end
213
+ end
214
+ end
215
+
216
+ context "#build" do
217
+ let(:name) {'built'}
218
+
219
+ it "adds a built item to the collection" do
220
+ proxy.build({:name => name}).last.name.should == name
221
+ end
222
+
223
+ it "registers a save observer" do
224
+ root.should_receive(:register_save_observer)
225
+ proxy.build({:name => name})
226
+ end
227
+
228
+ it "sets the root" do
229
+ proxy.build({:name => name})
230
+ proxy.last._root.should == root
231
+ end
232
+ end
233
+ end