lookout-mongo_mapper 0.11.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +33 -0
  3. data/UPGRADES +26 -0
  4. data/bin/mmconsole +59 -0
  5. data/examples/attr_accessible.rb +22 -0
  6. data/examples/attr_protected.rb +22 -0
  7. data/examples/cache_key.rb +24 -0
  8. data/examples/custom_types.rb +24 -0
  9. data/examples/identity_map.rb +33 -0
  10. data/examples/identity_map/automatic.rb +2 -0
  11. data/examples/keys.rb +40 -0
  12. data/examples/modifiers/set.rb +25 -0
  13. data/examples/plugins.rb +38 -0
  14. data/examples/querying.rb +35 -0
  15. data/examples/safe.rb +43 -0
  16. data/examples/scopes.rb +52 -0
  17. data/examples/validating/embedded_docs.rb +29 -0
  18. data/lib/mongo_mapper.rb +94 -0
  19. data/lib/mongo_mapper/connection.rb +96 -0
  20. data/lib/mongo_mapper/document.rb +42 -0
  21. data/lib/mongo_mapper/embedded_document.rb +32 -0
  22. data/lib/mongo_mapper/exceptions.rb +30 -0
  23. data/lib/mongo_mapper/extensions/array.rb +19 -0
  24. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  25. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  26. data/lib/mongo_mapper/extensions/date.rb +25 -0
  27. data/lib/mongo_mapper/extensions/float.rb +14 -0
  28. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  29. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  30. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  31. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  32. data/lib/mongo_mapper/extensions/object.rb +26 -0
  33. data/lib/mongo_mapper/extensions/object_id.rb +32 -0
  34. data/lib/mongo_mapper/extensions/set.rb +20 -0
  35. data/lib/mongo_mapper/extensions/string.rb +18 -0
  36. data/lib/mongo_mapper/extensions/time.rb +28 -0
  37. data/lib/mongo_mapper/locale/en.yml +5 -0
  38. data/lib/mongo_mapper/middleware/identity_map.rb +16 -0
  39. data/lib/mongo_mapper/plugins.rb +22 -0
  40. data/lib/mongo_mapper/plugins/accessible.rb +52 -0
  41. data/lib/mongo_mapper/plugins/active_model.rb +18 -0
  42. data/lib/mongo_mapper/plugins/associations.rb +90 -0
  43. data/lib/mongo_mapper/plugins/associations/base.rb +92 -0
  44. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +54 -0
  45. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +34 -0
  46. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
  47. data/lib/mongo_mapper/plugins/associations/collection.rb +27 -0
  48. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +44 -0
  49. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +133 -0
  50. data/lib/mongo_mapper/plugins/associations/many_association.rb +63 -0
  51. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  52. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +118 -0
  53. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
  54. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
  55. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
  56. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +22 -0
  57. data/lib/mongo_mapper/plugins/associations/one_association.rb +48 -0
  58. data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
  59. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
  60. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +95 -0
  61. data/lib/mongo_mapper/plugins/associations/proxy.rb +134 -0
  62. data/lib/mongo_mapper/plugins/associations/single_association.rb +46 -0
  63. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  64. data/lib/mongo_mapper/plugins/callbacks.rb +29 -0
  65. data/lib/mongo_mapper/plugins/clone.rb +22 -0
  66. data/lib/mongo_mapper/plugins/dirty.rb +60 -0
  67. data/lib/mongo_mapper/plugins/document.rb +41 -0
  68. data/lib/mongo_mapper/plugins/dynamic_querying.rb +45 -0
  69. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
  70. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +56 -0
  71. data/lib/mongo_mapper/plugins/embedded_document.rb +53 -0
  72. data/lib/mongo_mapper/plugins/equality.rb +23 -0
  73. data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
  74. data/lib/mongo_mapper/plugins/indexes.rb +13 -0
  75. data/lib/mongo_mapper/plugins/inspect.rb +16 -0
  76. data/lib/mongo_mapper/plugins/keys.rb +313 -0
  77. data/lib/mongo_mapper/plugins/keys/key.rb +61 -0
  78. data/lib/mongo_mapper/plugins/logger.rb +18 -0
  79. data/lib/mongo_mapper/plugins/modifiers.rb +134 -0
  80. data/lib/mongo_mapper/plugins/pagination.rb +16 -0
  81. data/lib/mongo_mapper/plugins/persistence.rb +69 -0
  82. data/lib/mongo_mapper/plugins/protected.rb +45 -0
  83. data/lib/mongo_mapper/plugins/querying.rb +165 -0
  84. data/lib/mongo_mapper/plugins/querying/decorator.rb +36 -0
  85. data/lib/mongo_mapper/plugins/rails.rb +58 -0
  86. data/lib/mongo_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
  87. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  88. data/lib/mongo_mapper/plugins/sci.rb +36 -0
  89. data/lib/mongo_mapper/plugins/scopes.rb +27 -0
  90. data/lib/mongo_mapper/plugins/serialization.rb +109 -0
  91. data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
  92. data/lib/mongo_mapper/plugins/touch.rb +18 -0
  93. data/lib/mongo_mapper/plugins/userstamps.rb +18 -0
  94. data/lib/mongo_mapper/plugins/validations.rb +86 -0
  95. data/lib/mongo_mapper/railtie.rb +48 -0
  96. data/lib/mongo_mapper/railtie/database.rake +65 -0
  97. data/lib/mongo_mapper/translation.rb +10 -0
  98. data/lib/mongo_mapper/version.rb +4 -0
  99. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +24 -0
  100. data/lib/rails/generators/mongo_mapper/config/templates/mongo.yml +18 -0
  101. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +23 -0
  102. data/lib/rails/generators/mongo_mapper/model/templates/model.rb +13 -0
  103. data/test/_NOTE_ON_TESTING +1 -0
  104. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +64 -0
  105. data/test/functional/associations/test_belongs_to_proxy.rb +238 -0
  106. data/test/functional/associations/test_in_array_proxy.rb +349 -0
  107. data/test/functional/associations/test_many_documents_as_proxy.rb +231 -0
  108. data/test/functional/associations/test_many_documents_proxy.rb +866 -0
  109. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +239 -0
  110. data/test/functional/associations/test_many_embedded_proxy.rb +289 -0
  111. data/test/functional/associations/test_many_polymorphic_proxy.rb +303 -0
  112. data/test/functional/associations/test_one_as_proxy.rb +491 -0
  113. data/test/functional/associations/test_one_embedded_polymorphic_proxy.rb +208 -0
  114. data/test/functional/associations/test_one_embedded_proxy.rb +100 -0
  115. data/test/functional/associations/test_one_proxy.rb +383 -0
  116. data/test/functional/test_accessible.rb +198 -0
  117. data/test/functional/test_associations.rb +46 -0
  118. data/test/functional/test_binary.rb +27 -0
  119. data/test/functional/test_caching.rb +77 -0
  120. data/test/functional/test_callbacks.rb +232 -0
  121. data/test/functional/test_dirty.rb +301 -0
  122. data/test/functional/test_document.rb +282 -0
  123. data/test/functional/test_dynamic_querying.rb +75 -0
  124. data/test/functional/test_embedded_document.rb +288 -0
  125. data/test/functional/test_equality.rb +20 -0
  126. data/test/functional/test_identity_map.rb +513 -0
  127. data/test/functional/test_indexes.rb +50 -0
  128. data/test/functional/test_logger.rb +20 -0
  129. data/test/functional/test_modifiers.rb +537 -0
  130. data/test/functional/test_pagination.rb +91 -0
  131. data/test/functional/test_protected.rb +201 -0
  132. data/test/functional/test_querying.rb +935 -0
  133. data/test/functional/test_safe.rb +76 -0
  134. data/test/functional/test_sci.rb +240 -0
  135. data/test/functional/test_scopes.rb +171 -0
  136. data/test/functional/test_timestamps.rb +62 -0
  137. data/test/functional/test_touch.rb +125 -0
  138. data/test/functional/test_userstamps.rb +44 -0
  139. data/test/functional/test_validations.rb +414 -0
  140. data/test/models.rb +261 -0
  141. data/test/support/railtie.rb +4 -0
  142. data/test/support/railtie/autoloaded.rb +2 -0
  143. data/test/support/railtie/not_autoloaded.rb +3 -0
  144. data/test/support/railtie/parent.rb +3 -0
  145. data/test/test_active_model_lint.rb +18 -0
  146. data/test/test_helper.rb +93 -0
  147. data/test/unit/associations/test_base.rb +146 -0
  148. data/test/unit/associations/test_belongs_to_association.rb +29 -0
  149. data/test/unit/associations/test_many_association.rb +63 -0
  150. data/test/unit/associations/test_one_association.rb +47 -0
  151. data/test/unit/associations/test_proxy.rb +100 -0
  152. data/test/unit/serializers/test_json_serializer.rb +216 -0
  153. data/test/unit/serializers/test_xml_serializer.rb +196 -0
  154. data/test/unit/test_clone.rb +69 -0
  155. data/test/unit/test_document.rb +249 -0
  156. data/test/unit/test_dynamic_finder.rb +125 -0
  157. data/test/unit/test_embedded_document.rb +682 -0
  158. data/test/unit/test_equality.rb +38 -0
  159. data/test/unit/test_exceptions.rb +12 -0
  160. data/test/unit/test_extensions.rb +380 -0
  161. data/test/unit/test_identity_map_middleware.rb +34 -0
  162. data/test/unit/test_inspect.rb +47 -0
  163. data/test/unit/test_key.rb +205 -0
  164. data/test/unit/test_keys.rb +65 -0
  165. data/test/unit/test_mongo_mapper.rb +143 -0
  166. data/test/unit/test_pagination.rb +11 -0
  167. data/test/unit/test_plugins.rb +89 -0
  168. data/test/unit/test_rails.rb +183 -0
  169. data/test/unit/test_rails_compatibility.rb +38 -0
  170. data/test/unit/test_rails_reflect_on_association.rb +118 -0
  171. data/test/unit/test_railtie.rb +66 -0
  172. data/test/unit/test_serialization.rb +166 -0
  173. data/test/unit/test_time_zones.rb +44 -0
  174. data/test/unit/test_translation.rb +27 -0
  175. data/test/unit/test_validations.rb +562 -0
  176. metadata +285 -0
@@ -0,0 +1,91 @@
1
+ require 'test_helper'
2
+
3
+ class PaginationTest < Test::Unit::TestCase
4
+ context "Paginating" do
5
+ setup do
6
+ @document = Doc do
7
+ key :first_name, String
8
+ key :last_name, String
9
+ key :age, Integer
10
+
11
+ def self.per_page; 1 end
12
+ end
13
+
14
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
15
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
16
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
17
+ end
18
+
19
+ should "return the total pages" do
20
+ result = @document.paginate(:per_page => 2, :page => 1)
21
+ result.total_pages.should == 2
22
+ end
23
+
24
+ should "return the total pages when defaulting to the document class per_page" do
25
+ result = @document.paginate(:page => 1)
26
+ result.total_pages.should == 3
27
+ end
28
+
29
+ should "return the total of records" do
30
+ result = @document.paginate(:per_page => 2, :page => 1)
31
+ result.total_entries.should == 3
32
+ end
33
+
34
+ should "return the items" do
35
+ result = @document.paginate(:per_page => 2, :page => 1, :order => 'first_name')
36
+ result.size.should == 2
37
+ result.should == [@doc1, @doc3]
38
+ end
39
+
40
+ should "accept conditions" do
41
+ result = @document.paginate({
42
+ :last_name => 'Nunemaker',
43
+ :order => "age DESC",
44
+ :per_page => 2,
45
+ :page => 1,
46
+ })
47
+ result.should == [@doc1, @doc3]
48
+ result.first.age.should == 27
49
+
50
+ result = @document.paginate({
51
+ :conditions => {:last_name => 'Nunemaker'},
52
+ :order => "age DESC",
53
+ :per_page => 2,
54
+ :page => 1} )
55
+ result.should == [@doc1, @doc3]
56
+ result.first.age.should == 27
57
+ end
58
+
59
+ should "withstand rigor" do
60
+ result = @document.paginate({
61
+ :per_page => 1,
62
+ :page => 1,
63
+ :order => 'age desc',
64
+ :last_name => 'Nunemaker'
65
+ })
66
+ result.should == [@doc1]
67
+ result.total_entries.should == 2
68
+ result.total_pages.should == 2
69
+
70
+ result = @document.paginate({
71
+ :per_page => 1,
72
+ :page => 2,
73
+ :order => 'age desc',
74
+ :last_name => 'Nunemaker'
75
+ })
76
+ result.should == [@doc3]
77
+ result.total_entries.should == 2
78
+ result.total_pages.should == 2
79
+
80
+ result = @document.paginate({
81
+ :per_page => 2,
82
+ :page => 1,
83
+ :order => 'age desc',
84
+ :last_name => 'Nunemaker'
85
+ })
86
+ result.should == [@doc1, @doc3]
87
+ result.total_entries.should == 2
88
+ result.total_pages.should == 1
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,201 @@
1
+ require 'test_helper'
2
+
3
+ class ProtectedTest < Test::Unit::TestCase
4
+ context 'A document with protected attributes' do
5
+ setup do
6
+ @doc_class = Doc do
7
+ key :name, String
8
+ key :admin, Boolean, :default => false
9
+
10
+ attr_protected :admin
11
+ end
12
+
13
+ @doc = @doc_class.create(:name => 'Steve Sloan')
14
+ end
15
+
16
+ should "have protected attributes class method" do
17
+ @doc_class.protected_attributes.should == [:admin].to_set
18
+ end
19
+
20
+ should "default protected attributes to nil" do
21
+ Doc().protected_attributes.should be_nil
22
+ end
23
+
24
+ should "have protected attributes instance method" do
25
+ @doc.protected_attributes.should equal(@doc_class.protected_attributes)
26
+ end
27
+
28
+ should "raise error if there are accessible attributes" do
29
+ doc = Doc('Post')
30
+ doc.attr_accessible :name
31
+ lambda { doc.attr_protected :admin }.
32
+ should raise_error(/Declare either attr_protected or attr_accessible for Post/)
33
+ end
34
+
35
+ should "know if using protected attributes" do
36
+ @doc_class.protected_attributes?.should be(true)
37
+ Doc().protected_attributes?.should be(false)
38
+ end
39
+
40
+ should "work with :protected shortcut when defining key" do
41
+ Doc() do
42
+ key :user_id, ObjectId, :protected => true
43
+ end.protected_attributes.should == [:user_id].to_set
44
+ end
45
+
46
+ should "assign protected attribute through accessor" do
47
+ @doc.admin = true
48
+ @doc.admin.should be_true
49
+ end
50
+
51
+ should "ignore protected attribute on #initialize" do
52
+ doc = @doc_class.new(:name => 'John', :admin => true)
53
+ doc.admin.should be_false
54
+ doc.name.should == 'John'
55
+ end
56
+
57
+ should "not ignore protected attributes on #initialize from the database" do
58
+ doc = @doc_class.new(:name => 'John')
59
+ doc.admin = true
60
+ doc.save!
61
+
62
+ doc = @doc_class.first(:name => 'John')
63
+ doc.admin.should be_true
64
+ doc.name.should == 'John'
65
+ end
66
+
67
+ should "not ignore protected attributes on #reload" do
68
+ doc = @doc_class.new(:name => 'John')
69
+ doc.admin = true
70
+ doc.save!
71
+
72
+ doc.reload
73
+ doc.admin.should be_true
74
+ doc.name.should == 'John'
75
+ end
76
+
77
+ should "ignore protected attribute on #update_attribute" do
78
+ @doc.update_attribute('admin', true)
79
+ @doc.admin.should be_true
80
+ end
81
+
82
+ should "ignore protected attribute on #update_attributes" do
83
+ @doc.update_attributes(:name => 'Ren Hoek', :admin => true)
84
+ @doc.name.should == 'Ren Hoek'
85
+ @doc.admin.should be_false
86
+ end
87
+
88
+ should "ignore protected attribute on #update_attributes!" do
89
+ @doc.update_attributes!(:name => 'Stimpson J. Cat', :admin => true)
90
+ @doc.name.should == 'Stimpson J. Cat'
91
+ @doc.admin.should be_false
92
+ end
93
+
94
+ should "ignore protecteds attribute on #attributes=" do
95
+ @doc.attributes = {:name => 'Stimpson J. Cat', :admin => true}
96
+ @doc.name.should == 'Stimpson J. Cat'
97
+ @doc.admin.should be_false
98
+ end
99
+
100
+ should "be indifferent to whether the protected keys are strings or symbols" do
101
+ @doc.update_attributes!("name" => 'Stimpson J. Cat', "admin" => true)
102
+ @doc.name.should == 'Stimpson J. Cat'
103
+ @doc.admin.should be_false
104
+ end
105
+
106
+ should "accept nil as constructor's argument without raising exception" do
107
+ lambda { @doc_class.new(nil) }.should_not raise_error
108
+ end
109
+ end
110
+
111
+ context "Single collection inherited protected attributes" do
112
+ setup do
113
+ class ::GrandParent
114
+ include MongoMapper::Document
115
+
116
+ key :site_id, ObjectId
117
+ attr_protected :site_id
118
+ end
119
+ GrandParent.collection.remove
120
+
121
+ class ::Child < ::GrandParent
122
+ key :position, Integer
123
+
124
+ attr_protected :position
125
+ end
126
+
127
+ class ::GrandChild < ::Child; end
128
+
129
+ class ::OtherChild < ::GrandParent
130
+ key :blog_id, ObjectId
131
+
132
+ attr_protected :blog_id
133
+ end
134
+ end
135
+
136
+ teardown do
137
+ Object.send :remove_const, 'GrandParent' if defined?(::GrandParent)
138
+ Object.send :remove_const, 'Child' if defined?(::Child)
139
+ Object.send :remove_const, 'GrandChild' if defined?(::GrandChild)
140
+ Object.send :remove_const, 'OtherChild' if defined?(::OtherChild)
141
+ end
142
+
143
+ should "share keys down the inheritance trail" do
144
+ GrandParent.protected_attributes.should == [:site_id].to_set
145
+ Child.protected_attributes.should == [:site_id, :position].to_set
146
+ GrandChild.protected_attributes.should == [:site_id, :position].to_set
147
+ OtherChild.protected_attributes.should == [:site_id, :blog_id].to_set
148
+ end
149
+ end
150
+
151
+ context 'An embedded document with protected attributes' do
152
+ setup do
153
+ @doc_class = Doc('Project')
154
+ @edoc_class = EDoc('Person') do
155
+ key :name, String
156
+ key :admin, Boolean, :default => false
157
+
158
+ attr_protected :admin
159
+ end
160
+ @doc_class.many :people, :class => @edoc_class
161
+
162
+ @doc = @doc_class.create(:title => 'MongoMapper')
163
+ @edoc = @edoc_class.new(:name => 'Steve Sloan')
164
+ @doc.people << @edoc
165
+ end
166
+
167
+ should "have protected attributes class method" do
168
+ @edoc_class.protected_attributes.should == [:admin].to_set
169
+ end
170
+
171
+ should "default protected attributes to nil" do
172
+ EDoc().protected_attributes.should be_nil
173
+ end
174
+
175
+ should "have protected attributes instance method" do
176
+ @edoc.protected_attributes.should equal(@edoc_class.protected_attributes)
177
+ end
178
+
179
+ should "assign protected attribute through accessor" do
180
+ @edoc.admin = true
181
+ @edoc.admin.should be_true
182
+ end
183
+
184
+ should "not ignore protected attribute on #update_attribute" do
185
+ @edoc.update_attribute('admin', true)
186
+ @edoc.admin.should be_true
187
+ end
188
+
189
+ should "ignore protected attribute on #update_attributes" do
190
+ @edoc.update_attributes(:name => 'Ren Hoek', :admin => true)
191
+ @edoc.name.should == 'Ren Hoek'
192
+ @edoc.admin.should be_false
193
+ end
194
+
195
+ should "ignore protected attribute on #update_attributes!" do
196
+ @edoc.update_attributes!(:name => 'Stimpson J. Cat', :admin => true)
197
+ @edoc.name.should == 'Stimpson J. Cat'
198
+ @edoc.admin.should be_false
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,935 @@
1
+ require 'test_helper'
2
+
3
+ class QueryingTesting < Test::Unit::TestCase
4
+ def setup
5
+ @document = Doc do
6
+ key :first_name, String
7
+ key :last_name, String
8
+ key :age, Integer
9
+ key :date, Date
10
+ end
11
+ end
12
+
13
+ context ".query" do
14
+ setup do
15
+ @query = @document.query
16
+ end
17
+
18
+ should "set model to self" do
19
+ @query.model.should == @document
20
+ end
21
+
22
+ should "always return new instance" do
23
+ @document.query.should_not equal(@query)
24
+ end
25
+
26
+ should "apply options" do
27
+ @document.query(:foo => 'bar')[:foo].should == 'bar'
28
+ end
29
+ end
30
+
31
+ context ".criteria_hash" do
32
+ setup do
33
+ @hash = @document.criteria_hash
34
+ end
35
+
36
+ should "set object id keys on hash" do
37
+ @hash.object_ids.should == [:_id]
38
+ end
39
+
40
+ should "always return new instance" do
41
+ @document.criteria_hash.should_not equal(@hash)
42
+ end
43
+
44
+ should "apply provided criteria" do
45
+ @document.criteria_hash(:foo => 'bar')[:foo].should == 'bar'
46
+ end
47
+ end
48
+
49
+ context ".create (single document)" do
50
+ setup do
51
+ @doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
52
+ end
53
+
54
+ should "create a document in correct collection" do
55
+ @document.count.should == 1
56
+ end
57
+
58
+ should "automatically set id" do
59
+ @doc.id.should be_instance_of(BSON::ObjectId)
60
+ @doc._id.should be_instance_of(BSON::ObjectId)
61
+ end
62
+
63
+ should "no longer be new?" do
64
+ @doc.new?.should be_false
65
+ end
66
+
67
+ should "return instance of document" do
68
+ @doc.should be_instance_of(@document)
69
+ @doc.first_name.should == 'John'
70
+ @doc.last_name.should == 'Nunemaker'
71
+ @doc.age.should == 27
72
+ end
73
+
74
+ should "not fail if no attributes provided" do
75
+ document = Doc()
76
+ lambda { document.create }.should change { document.count }.by(1)
77
+ end
78
+ end
79
+
80
+ context ".create (multiple documents)" do
81
+ setup do
82
+ @docs = @document.create([
83
+ {:first_name => 'John', :last_name => 'Nunemaker', :age => '27'},
84
+ {:first_name => 'Steve', :last_name => 'Smith', :age => '28'},
85
+ ])
86
+ end
87
+
88
+ should "create multiple documents" do
89
+ @document.count.should == 2
90
+ end
91
+
92
+ should "return an array of doc instances" do
93
+ @docs.map do |doc|
94
+ doc.should be_instance_of(@document)
95
+ end
96
+ end
97
+ end
98
+
99
+ context ".update (single document)" do
100
+ setup do
101
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
102
+ @doc = @document.update(doc._id, {:age => 40})
103
+ end
104
+
105
+ should "update attributes provided" do
106
+ @doc.age.should == 40
107
+ end
108
+
109
+ should "not update existing attributes that were not set to update" do
110
+ @doc.first_name.should == 'John'
111
+ @doc.last_name.should == 'Nunemaker'
112
+ end
113
+
114
+ should "not create new document" do
115
+ @document.count.should == 1
116
+ end
117
+
118
+ should "raise error if not provided id" do
119
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
120
+ lambda { @document.update }.should raise_error(ArgumentError)
121
+ end
122
+
123
+ should "raise error if not provided attributes" do
124
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
125
+ lambda { @document.update(doc._id) }.should raise_error(ArgumentError)
126
+ lambda { @document.update(doc._id, [1]) }.should raise_error(ArgumentError)
127
+ end
128
+ end
129
+
130
+ context ".update (multiple documents)" do
131
+ setup do
132
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
133
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
134
+
135
+ @docs = @document.update({
136
+ @doc1._id => {:age => 30},
137
+ @doc2._id => {:age => 30},
138
+ })
139
+ end
140
+
141
+ should "not create any new documents" do
142
+ @document.count.should == 2
143
+ end
144
+
145
+ should "should return an array of doc instances" do
146
+ @docs.map do |doc|
147
+ doc.should be_instance_of(@document)
148
+ end
149
+ end
150
+
151
+ should "update the documents" do
152
+ @document.find(@doc1._id).age.should == 30
153
+ @document.find(@doc2._id).age.should == 30
154
+ end
155
+
156
+ should "raise error if not a hash" do
157
+ lambda { @document.update([1, 2]) }.should raise_error(ArgumentError)
158
+ end
159
+ end
160
+
161
+ context ".find" do
162
+ setup do
163
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
164
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
165
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
166
+ end
167
+
168
+ should "return nil if nothing provided for find" do
169
+ @document.find.should be_nil
170
+ end
171
+
172
+ should "raise document not found if nothing provided for find!" do
173
+ assert_raises(MongoMapper::DocumentNotFound) do
174
+ @document.find!
175
+ end
176
+ end
177
+
178
+ context "(with a single id)" do
179
+ should "work" do
180
+ @document.find(@doc1._id).should == @doc1
181
+ end
182
+
183
+ should "return nil if document not found with find" do
184
+ @document.find(123).should be_nil
185
+ end
186
+
187
+ should "raise error if document not found with find!" do
188
+ assert_raises(MongoMapper::DocumentNotFound) { @document.find!(123) }
189
+ end
190
+ end
191
+
192
+ context "(with multiple id's)" do
193
+ should "work as arguments" do
194
+ @document.find(@doc1._id, @doc2._id).should == [@doc1, @doc2]
195
+ end
196
+
197
+ should "work as arguments with string ids" do
198
+ @document.find(@doc1._id.to_s, @doc2._id.to_s).should == [@doc1, @doc2]
199
+ end
200
+
201
+ should "work as array" do
202
+ @document.find([@doc1._id, @doc2._id]).should == [@doc1, @doc2]
203
+ end
204
+
205
+ should "work as array with string ids" do
206
+ @document.find([@doc1._id.to_s, @doc2._id.to_s]).should == [@doc1, @doc2]
207
+ end
208
+
209
+ should "compact not found when using find" do
210
+ @document.find(@doc1._id, BSON::ObjectId.new.to_s).should == [@doc1]
211
+ end
212
+
213
+ should "raise error if not all found when using find!" do
214
+ assert_raises(MongoMapper::DocumentNotFound) do
215
+ @document.find!(@doc1._id, BSON::ObjectId.new.to_s)
216
+ end
217
+ end
218
+
219
+ should "return array if array with one element" do
220
+ @document.find([@doc1._id]).should == [@doc1]
221
+ end
222
+ end
223
+
224
+ should "be able to find using condition auto-detection" do
225
+ @document.first(:first_name => 'John').should == @doc1
226
+ @document.all(:last_name => 'Nunemaker', :order => 'age desc').should == [@doc1, @doc3]
227
+ end
228
+
229
+ context "#all" do
230
+ should "find all documents with options" do
231
+ @document.all(:order => 'first_name').should == [@doc1, @doc3, @doc2]
232
+ @document.all(:last_name => 'Nunemaker', :order => 'age desc').should == [@doc1, @doc3]
233
+ end
234
+ end
235
+
236
+ context "#first" do
237
+ should "find first document with options" do
238
+ @document.first(:order => 'first_name').should == @doc1
239
+ @document.first(:age => 28).should == @doc2
240
+ end
241
+ end
242
+
243
+ context "#last" do
244
+ should "find last document with options" do
245
+ @document.last(:order => 'age').should == @doc2
246
+ @document.last(:order => 'age', :age => 28).should == @doc2
247
+ end
248
+ end
249
+
250
+ context "#find_each" do
251
+ should "yield all documents found based on options" do
252
+ yield_documents = []
253
+ @document.find_each(:order => "first_name") {|doc| yield_documents << doc }
254
+ yield_documents.should == [@doc1, @doc3, @doc2]
255
+
256
+ yield_documents = []
257
+ @document.find_each(:last_name => 'Nunemaker', :order => 'age desc') {|doc| yield_documents << doc }
258
+ yield_documents.should == [@doc1, @doc3]
259
+ end
260
+ end
261
+ end # finding documents
262
+
263
+ context ".find_by_id" do
264
+ setup do
265
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
266
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
267
+ end
268
+
269
+ should "be able to find by id" do
270
+ @document.find_by_id(@doc1._id).should == @doc1
271
+ @document.find_by_id(@doc2._id).should == @doc2
272
+ end
273
+
274
+ should "return nil if document not found" do
275
+ @document.find_by_id(1234).should be_nil
276
+ end
277
+ end
278
+
279
+ context ".first_or_create" do
280
+ should "find if exists" do
281
+ created = @document.create(:first_name => 'John', :last_name => 'Nunemaker')
282
+ lambda {
283
+ found = @document.first_or_create(:first_name => 'John', :last_name => 'Nunemaker')
284
+ found.should == created
285
+ }.should_not change { @document.count }
286
+ end
287
+
288
+ should "create if not found" do
289
+ lambda {
290
+ created = @document.first_or_create(:first_name => 'John', :last_name => 'Nunemaker')
291
+ created.first_name.should == 'John'
292
+ created.last_name.should == 'Nunemaker'
293
+ }.should change { @document.count }.by(1)
294
+ end
295
+
296
+ should "disregard non-keys when creating, but use them in the query" do
297
+ assert_nothing_raised do
298
+ @document.create(:first_name => 'John', :age => 9)
299
+ lambda {
300
+ @document.first_or_create(:first_name => 'John', :age.gt => 10).first_name.should == 'John'
301
+ }.should change { @document.count }.by(1)
302
+ end
303
+ end
304
+ end
305
+
306
+ context ".first_or_new" do
307
+ should "find if exists" do
308
+ created = @document.create(:first_name => 'John', :last_name => 'Nunemaker')
309
+ lambda {
310
+ found = @document.first_or_new(:first_name => 'John', :last_name => 'Nunemaker')
311
+ found.should == created
312
+ }.should_not change { @document.count }
313
+ end
314
+
315
+ should "initialize if not found" do
316
+ lambda {
317
+ created = @document.first_or_new(:first_name => 'John', :last_name => 'Nunemaker')
318
+ created.first_name.should == 'John'
319
+ created.last_name.should == 'Nunemaker'
320
+ created.should be_new
321
+ }.should_not change { @document.count }
322
+ end
323
+
324
+ should "disregard non-keys when initializing, but use them in the query" do
325
+ assert_nothing_raised do
326
+ @document.create(:first_name => 'John', :age => 9)
327
+ @document.first_or_new(:first_name => 'John', :age.gt => 10).first_name.should == 'John'
328
+ end
329
+ end
330
+ end
331
+
332
+ context ".delete (single document)" do
333
+ setup do
334
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
335
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
336
+ @document.delete(@doc1._id)
337
+ end
338
+
339
+ should "remove document from collection" do
340
+ @document.count.should == 1
341
+ end
342
+
343
+ should "not remove other documents" do
344
+ @document.find(@doc2._id).should_not be(nil)
345
+ end
346
+ end
347
+
348
+ context ".delete (multiple documents)" do
349
+ should "work with multiple arguments" do
350
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
351
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
352
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
353
+ @document.delete(@doc1._id, @doc2._id)
354
+
355
+ @document.count.should == 1
356
+ end
357
+
358
+ should "work with array as argument" do
359
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
360
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
361
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
362
+ @document.delete([@doc1._id, @doc2._id])
363
+
364
+ @document.count.should == 1
365
+ end
366
+ end
367
+
368
+ context ".delete_all" do
369
+ setup do
370
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
371
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
372
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
373
+ end
374
+
375
+ should "remove all documents when given no conditions" do
376
+ @document.delete_all
377
+ @document.count.should == 0
378
+ end
379
+
380
+ should "only remove matching documents when given conditions" do
381
+ @document.delete_all({:first_name => 'John'})
382
+ @document.count.should == 2
383
+ end
384
+
385
+ should "convert the conditions to mongo criteria" do
386
+ @document.delete_all(:age => [26, 27])
387
+ @document.count.should == 1
388
+ end
389
+ end
390
+
391
+ context ".destroy (single document)" do
392
+ setup do
393
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
394
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
395
+ @document.destroy(@doc1._id)
396
+ end
397
+
398
+ should "remove document from collection" do
399
+ @document.count.should == 1
400
+ end
401
+
402
+ should "not remove other documents" do
403
+ @document.find(@doc2._id).should_not be(nil)
404
+ end
405
+ end
406
+
407
+ context ".destroy (multiple documents)" do
408
+ should "work with multiple arguments" do
409
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
410
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
411
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
412
+ @document.destroy(@doc1._id, @doc2._id)
413
+
414
+ @document.count.should == 1
415
+ end
416
+
417
+ should "work with array as argument" do
418
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
419
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
420
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
421
+ @document.destroy([@doc1._id, @doc2._id])
422
+
423
+ @document.count.should == 1
424
+ end
425
+ end
426
+
427
+ context ".destroy_all" do
428
+ setup do
429
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
430
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
431
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
432
+ end
433
+
434
+ should "remove all documents when given no conditions" do
435
+ @document.destroy_all
436
+ @document.count.should == 0
437
+ end
438
+
439
+ should "only remove matching documents when given conditions" do
440
+ @document.destroy_all(:first_name => 'John')
441
+ @document.count.should == 2
442
+ @document.destroy_all(:age => 26)
443
+ @document.count.should == 1
444
+ end
445
+
446
+ should "convert the conditions to mongo criteria" do
447
+ @document.destroy_all(:age => [26, 27])
448
+ @document.count.should == 1
449
+ end
450
+ end
451
+
452
+ context ".count" do
453
+ setup do
454
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
455
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
456
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
457
+ end
458
+
459
+ should "count all with no arguments" do
460
+ @document.count.should == 3
461
+ end
462
+
463
+ should "return 0 if there are no documents in the collection" do
464
+ @document.delete_all
465
+ @document.count.should == 0
466
+ end
467
+
468
+ should "return 0 if the collection does not exist" do
469
+ klass = Doc do
470
+ set_collection_name 'foobarbazwickdoesnotexist'
471
+ end
472
+
473
+ klass.count.should == 0
474
+ end
475
+
476
+ should "return count for matching documents if conditions provided" do
477
+ @document.count(:age => 27).should == 1
478
+ end
479
+
480
+ should "convert the conditions to mongo criteria" do
481
+ @document.count(:age => [26, 27]).should == 2
482
+ end
483
+ end
484
+
485
+ context ".size" do
486
+ should "return 0 if no documents" do
487
+ @document.count.should == 0
488
+ end
489
+
490
+ should "return the number of documents" do
491
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
492
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
493
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
494
+ @document.count.should == 3
495
+ end
496
+ end
497
+
498
+ context ".empty?" do
499
+ should "be true if no documents" do
500
+ @document.empty?.should be_true
501
+ end
502
+
503
+ should "be false if documents present" do
504
+ @doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
505
+ @document.empty?.should be_false
506
+ end
507
+ end
508
+
509
+ context ".exists?" do
510
+ setup do
511
+ @doc = @document.create(:first_name => "James", :age => 27)
512
+ end
513
+
514
+ should "be true when at least one document exists" do
515
+ @document.exists?.should == true
516
+ end
517
+
518
+ should "be false when no documents exist" do
519
+ @doc.destroy
520
+ @document.exists?.should == false
521
+ end
522
+
523
+ should "be true when at least one document exists that matches the conditions" do
524
+ @document.exists?(:first_name => "James").should == true
525
+ end
526
+
527
+ should "be false when no documents exist with the provided conditions" do
528
+ @document.exists?(:first_name => "Jean").should == false
529
+ end
530
+ end
531
+
532
+ context "to_a" do
533
+ should "return an array" do
534
+ @document.to_a.class.should == Array
535
+ end
536
+
537
+ should "return everything" do
538
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
539
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
540
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
541
+ @document.to_a.size.should == 3
542
+ end
543
+ end
544
+
545
+ context ".where" do
546
+ setup do
547
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
548
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
549
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
550
+ @query = @document.where(:last_name => 'Nunemaker')
551
+ end
552
+
553
+ should "fetch documents when kicker called" do
554
+ docs = @query.all
555
+ docs.should include(@doc1)
556
+ docs.should include(@doc3)
557
+ docs.should_not include(@doc2)
558
+ end
559
+
560
+ should "be chainable" do
561
+ @query.sort(:age).first.should == @doc3
562
+ end
563
+ end
564
+
565
+ context ".fields" do
566
+ setup do
567
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
568
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
569
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
570
+ @query = @document.fields(:age)
571
+ end
572
+
573
+ should "fetch documents when kicker called" do
574
+ docs = @query.all
575
+ docs.should include(@doc1)
576
+ docs.should include(@doc3)
577
+ docs.should include(@doc2)
578
+ docs.each do |doc|
579
+ doc.age.should_not be_nil
580
+ doc.first_name.should be_nil # key was not loaded
581
+ doc.last_name.should be_nil # key was not loaded
582
+ end
583
+ end
584
+
585
+ should "be chainable" do
586
+ @query.sort(:age).all.map(&:age).should == [26, 27, 28]
587
+ end
588
+ end
589
+
590
+ context ".limit" do
591
+ setup do
592
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
593
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
594
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
595
+ @query = @document.limit(2)
596
+ end
597
+
598
+ should "fetch documents when kicker called" do
599
+ docs = @query.all
600
+ docs.size.should == 2
601
+ end
602
+
603
+ should "be chainable" do
604
+ result = [26, 27]
605
+ @query.sort(:age).all.map(&:age).should == result
606
+ @query.count.should > result.size
607
+ end
608
+ end
609
+
610
+ context ".skip" do
611
+ setup do
612
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
613
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
614
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
615
+ @query = @document.skip(1)
616
+ end
617
+
618
+ should "fetch documents when kicker called" do
619
+ docs = @query.all
620
+ docs.size.should == 2 # skipping 1 out of 3
621
+ end
622
+
623
+ should "be chainable" do
624
+ result = [27, 28]
625
+ @query.sort(:age).all.map(&:age).should == result
626
+ @query.count.should > result.size
627
+ end
628
+ end
629
+
630
+ context ".sort" do
631
+ setup do
632
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
633
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
634
+ @doc3 = @document.create(:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26')
635
+ @query = @document.sort(:age)
636
+ end
637
+
638
+ should "fetch documents when kicker called" do
639
+ @query.all.should == [@doc3, @doc1, @doc2]
640
+ end
641
+
642
+ should "be chainable" do
643
+ result = [28]
644
+ @query.skip(2).all.map(&:age).should == result
645
+ @query.count.should > result.size
646
+ end
647
+ end
648
+
649
+ context "#update_attributes (new document)" do
650
+ setup do
651
+ @doc = @document.new(:first_name => 'John', :age => '27')
652
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
653
+ end
654
+
655
+ should "insert document into the collection" do
656
+ @document.count.should == 1
657
+ end
658
+
659
+ should "assign an id for the document" do
660
+ @doc.id.should be_instance_of(BSON::ObjectId)
661
+ end
662
+
663
+ should "save attributes" do
664
+ @doc.first_name.should == 'Johnny'
665
+ @doc.age.should == 30
666
+ end
667
+
668
+ should "update attributes in the database" do
669
+ doc = @doc.reload
670
+ doc.should == @doc
671
+ doc.first_name.should == 'Johnny'
672
+ doc.age.should == 30
673
+ end
674
+
675
+ should "allow updating custom attributes" do
676
+ @doc.update_attributes(:gender => 'mALe')
677
+ @doc.reload.gender.should == 'mALe'
678
+ end
679
+ end
680
+
681
+ context "#update_attributes (existing document)" do
682
+ setup do
683
+ @doc = @document.create(:first_name => 'John', :age => '27')
684
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
685
+ end
686
+
687
+ should "not insert document into collection" do
688
+ @document.count.should == 1
689
+ end
690
+
691
+ should "update attributes" do
692
+ @doc.first_name.should == 'Johnny'
693
+ @doc.age.should == 30
694
+ end
695
+
696
+ should "update attributes in the database" do
697
+ doc = @doc.reload
698
+ doc.first_name.should == 'Johnny'
699
+ doc.age.should == 30
700
+ end
701
+ end
702
+
703
+ context "#update_attributes (return value)" do
704
+ setup do
705
+ @document.key :foo, String, :required => true
706
+ end
707
+
708
+ should "be true if document valid" do
709
+ @document.new.update_attributes(:foo => 'bar').should be_true
710
+ end
711
+
712
+ should "be false if document not valid" do
713
+ @document.new.update_attributes({}).should be_false
714
+ end
715
+ end
716
+
717
+ context "#update_attribute" do
718
+ setup do
719
+ @doc = @document.create(:first_name => 'John', :age => '27')
720
+ end
721
+
722
+ should "accept symbols as keys" do
723
+ @doc.update_attribute(:first_name, 'Chris').should be_true
724
+ @doc.reload.first_name.should == 'Chris'
725
+ end
726
+
727
+ should "update the attribute" do
728
+ @doc.update_attribute('first_name', 'Chris').should be_true
729
+ @doc.reload.first_name.should == 'Chris'
730
+ end
731
+
732
+ should "update the attribute without invoking validations" do
733
+ @document.key :name, String, :required => true
734
+
735
+ @doc.expects(:valid?).never
736
+ @doc.update_attribute('name', '').should be_true
737
+ @doc.reload.name.should == ''
738
+ @document.count.should == 1
739
+ end
740
+ end
741
+
742
+ context "#save (new document)" do
743
+ setup do
744
+ @doc = @document.new(:first_name => 'John', :age => '27')
745
+ @doc.save
746
+ end
747
+
748
+ should "insert document into the collection" do
749
+ @document.count.should == 1
750
+ end
751
+
752
+ should "assign an id for the document" do
753
+ @doc.id.should be_instance_of(BSON::ObjectId)
754
+ end
755
+
756
+ should "save attributes" do
757
+ @doc.first_name.should == 'John'
758
+ @doc.age.should == 27
759
+ end
760
+
761
+ should "update attributes in the database" do
762
+ doc = @doc.reload
763
+ doc.should == @doc
764
+ doc.first_name.should == 'John'
765
+ doc.age.should == 27
766
+ end
767
+
768
+ should "allow to add custom attributes to the document" do
769
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male', :tags => [1, "2"])
770
+ @doc.save
771
+ doc = @doc.reload
772
+ doc.gender.should == 'male'
773
+ doc.tags.should == [1, "2"]
774
+ end
775
+
776
+ should "allow to use custom methods to assign properties" do
777
+ klass = Doc do
778
+ key :name, String
779
+
780
+ def realname=(value)
781
+ self.name = value
782
+ end
783
+ end
784
+
785
+ person = klass.new(:realname => 'David')
786
+ person.save
787
+ person.reload.name.should == 'David'
788
+ end
789
+
790
+ context "with key of type date" do
791
+ should "save the date value as a Time object" do
792
+ doc = @document.new(:first_name => 'John', :age => '27', :date => "2009-12-01")
793
+ doc.save
794
+ doc.date.should == Date.new(2009, 12, 1)
795
+ end
796
+ end
797
+ end
798
+
799
+ context "#save (existing document)" do
800
+ setup do
801
+ @doc = @document.create(:first_name => 'John', :age => '27')
802
+ @doc.first_name = 'Johnny'
803
+ @doc.age = 30
804
+ @doc.save
805
+ end
806
+
807
+ should "not insert document into collection" do
808
+ @document.count.should == 1
809
+ end
810
+
811
+ should "update attributes" do
812
+ @doc.first_name.should == 'Johnny'
813
+ @doc.age.should == 30
814
+ end
815
+
816
+ should "update attributes in the database" do
817
+ doc = @doc.reload
818
+ doc.first_name.should == 'Johnny'
819
+ doc.age.should == 30
820
+ end
821
+
822
+ should "allow updating custom attributes" do
823
+ @doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male')
824
+ @doc.gender = 'Male'
825
+ @doc.save
826
+ @doc.reload.gender.should == 'Male'
827
+ end
828
+ end
829
+
830
+ context "#save (with validations off)" do
831
+ setup do
832
+ @document = Doc do
833
+ key :name, String, :required => true
834
+ end
835
+ end
836
+
837
+ should "insert invalid document" do
838
+ doc = @document.new
839
+ doc.expects(:valid?).never
840
+ doc.save(:validate => false)
841
+ @document.count.should == 1
842
+ end
843
+ end
844
+
845
+ context "#save (with options)" do
846
+ setup do
847
+ @document = Doc do
848
+ key :name, String
849
+ end
850
+ @document.ensure_index :name, :unique => true
851
+ end
852
+ teardown { drop_indexes(@document) }
853
+
854
+ should "allow passing safe" do
855
+ @document.create(:name => 'John')
856
+ assert_raises(Mongo::OperationFailure) do
857
+ @document.new(:name => 'John').save(:safe => true)
858
+ end
859
+ end
860
+
861
+ should "raise argument error if options has unsupported key" do
862
+ assert_raises(ArgumentError) do
863
+ @document.new.save(:foo => true)
864
+ end
865
+ end
866
+ end
867
+
868
+ context "#save! (with options)" do
869
+ setup do
870
+ @document = Doc { key :name, String }
871
+ @document.ensure_index :name, :unique => true
872
+ end
873
+ teardown { drop_indexes(@document) }
874
+
875
+ should "allow passing safe" do
876
+ @document.create(:name => 'John')
877
+ assert_raises(Mongo::OperationFailure) do
878
+ @document.new(:name => 'John').save!(:safe => true)
879
+ end
880
+ end
881
+
882
+ should "raise argument error if options has unsupported key" do
883
+ assert_raises(ArgumentError) do
884
+ @document.new.save!(:foo => true)
885
+ end
886
+ end
887
+
888
+ should "raise argument error if using validate as that would be pointless with save!" do
889
+ assert_raises(ArgumentError) do
890
+ @document.new.save!(:validate => false)
891
+ end
892
+ end
893
+ end
894
+
895
+ context "#destroy" do
896
+ setup do
897
+ @doc = @document.create(:first_name => 'John', :age => '27')
898
+ @doc.destroy
899
+ end
900
+
901
+ should "remove the document from the collection" do
902
+ @document.count.should == 0
903
+ end
904
+ end
905
+
906
+ context "#delete" do
907
+ setup do
908
+ @doc1 = @document.create(:first_name => 'John', :last_name => 'Nunemaker', :age => '27')
909
+ @doc2 = @document.create(:first_name => 'Steve', :last_name => 'Smith', :age => '28')
910
+
911
+ @document.class_eval do
912
+ before_destroy :before_destroy_callback
913
+ after_destroy :after_destroy_callback
914
+
915
+ def history; @history ||= [] end
916
+ def before_destroy_callback; history << :after_destroy end
917
+ def after_destroy_callback; history << :after_destroy end
918
+ end
919
+
920
+ @doc1.delete
921
+ end
922
+
923
+ should "remove document from collection" do
924
+ @document.count.should == 1
925
+ end
926
+
927
+ should "not remove other documents" do
928
+ @document.find(@doc2.id).should_not be(nil)
929
+ end
930
+
931
+ should "not call before/after destroy callbacks" do
932
+ @doc1.history.should == []
933
+ end
934
+ end
935
+ end