simply_stored 0.1.4

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 (121) hide show
  1. data/lib/simply_stored/class_methods_base.rb +31 -0
  2. data/lib/simply_stored/couch/belongs_to.rb +117 -0
  3. data/lib/simply_stored/couch/ext/couch_potato.rb +16 -0
  4. data/lib/simply_stored/couch/has_many.rb +148 -0
  5. data/lib/simply_stored/couch/has_one.rb +93 -0
  6. data/lib/simply_stored/couch/validations.rb +74 -0
  7. data/lib/simply_stored/couch/views/array_property_view_spec.rb +22 -0
  8. data/lib/simply_stored/couch/views.rb +1 -0
  9. data/lib/simply_stored/couch.rb +278 -0
  10. data/lib/simply_stored/instance_methods.rb +143 -0
  11. data/lib/simply_stored/simpledb/associations.rb +196 -0
  12. data/lib/simply_stored/simpledb/attributes.rb +173 -0
  13. data/lib/simply_stored/simpledb/storag.rb +85 -0
  14. data/lib/simply_stored/simpledb/validations.rb +88 -0
  15. data/lib/simply_stored/simpledb.rb +212 -0
  16. data/lib/simply_stored/storage.rb +93 -0
  17. data/lib/simply_stored.rb +9 -0
  18. data/test/custom_views_test.rb +33 -0
  19. data/test/fixtures/couch.rb +182 -0
  20. data/test/fixtures/simpledb/item.rb +11 -0
  21. data/test/fixtures/simpledb/item_daddy.rb +8 -0
  22. data/test/fixtures/simpledb/log_item.rb +3 -0
  23. data/test/fixtures/simpledb/namespace_bar.rb +5 -0
  24. data/test/fixtures/simpledb/namespace_foo.rb +7 -0
  25. data/test/fixtures/simpledb/protected_item.rb +3 -0
  26. data/test/simply_stored_couch_test.rb +1684 -0
  27. data/test/simply_stored_simpledb_test.rb +1341 -0
  28. data/test/test_helper.rb +22 -0
  29. data/test/vendor/dhaka-2.2.1/lib/dhaka/dot/dot.rb +29 -0
  30. data/test/vendor/dhaka-2.2.1/lib/dhaka/evaluator/evaluator.rb +133 -0
  31. data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/closure_hash.rb +15 -0
  32. data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/grammar.rb +240 -0
  33. data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/grammar_symbol.rb +27 -0
  34. data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/precedence.rb +19 -0
  35. data/test/vendor/dhaka-2.2.1/lib/dhaka/grammar/production.rb +36 -0
  36. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/accept_actions.rb +36 -0
  37. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/alphabet.rb +21 -0
  38. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/compiled_lexer.rb +46 -0
  39. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/dfa.rb +121 -0
  40. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexeme.rb +32 -0
  41. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexer.rb +70 -0
  42. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/lexer_run.rb +78 -0
  43. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_grammar.rb +392 -0
  44. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_parser.rb +2010 -0
  45. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/regex_tokenizer.rb +14 -0
  46. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/specification.rb +96 -0
  47. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/state.rb +68 -0
  48. data/test/vendor/dhaka-2.2.1/lib/dhaka/lexer/state_machine.rb +37 -0
  49. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/action.rb +55 -0
  50. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/channel.rb +58 -0
  51. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/compiled_parser.rb +51 -0
  52. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/conflict.rb +54 -0
  53. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/item.rb +42 -0
  54. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parse_result.rb +50 -0
  55. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parse_tree.rb +66 -0
  56. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser.rb +165 -0
  57. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_methods.rb +11 -0
  58. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_run.rb +39 -0
  59. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/parser_state.rb +74 -0
  60. data/test/vendor/dhaka-2.2.1/lib/dhaka/parser/token.rb +22 -0
  61. data/test/vendor/dhaka-2.2.1/lib/dhaka/runtime.rb +51 -0
  62. data/test/vendor/dhaka-2.2.1/lib/dhaka/tokenizer/tokenizer.rb +190 -0
  63. data/test/vendor/dhaka-2.2.1/lib/dhaka.rb +62 -0
  64. data/test/vendor/dhaka-2.2.1/test/all_tests.rb +5 -0
  65. data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_evaluator.rb +64 -0
  66. data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_evaluator_test.rb +43 -0
  67. data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_grammar.rb +41 -0
  68. data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_grammar_test.rb +9 -0
  69. data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_test_methods.rb +9 -0
  70. data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_tokenizer.rb +39 -0
  71. data/test/vendor/dhaka-2.2.1/test/arithmetic/arithmetic_tokenizer_test.rb +38 -0
  72. data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_evaluator.rb +43 -0
  73. data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_grammar.rb +24 -0
  74. data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_grammar_test.rb +30 -0
  75. data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_lexer_specification.rb +23 -0
  76. data/test/vendor/dhaka-2.2.1/test/arithmetic_precedence/arithmetic_precedence_parser_test.rb +33 -0
  77. data/test/vendor/dhaka-2.2.1/test/brackets/bracket_grammar.rb +23 -0
  78. data/test/vendor/dhaka-2.2.1/test/brackets/bracket_tokenizer.rb +22 -0
  79. data/test/vendor/dhaka-2.2.1/test/brackets/brackets_test.rb +28 -0
  80. data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_driver.rb +46 -0
  81. data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_driver_test.rb +276 -0
  82. data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_evaluator.rb +284 -0
  83. data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_evaluator_test.rb +38 -0
  84. data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_grammar.rb +104 -0
  85. data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer.rb +109 -0
  86. data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer_specification.rb +37 -0
  87. data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_lexer_test.rb +58 -0
  88. data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_parser.rb +879 -0
  89. data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_parser_test.rb +55 -0
  90. data/test/vendor/dhaka-2.2.1/test/chittagong/chittagong_test.rb +170 -0
  91. data/test/vendor/dhaka-2.2.1/test/core/another_lalr_but_not_slr_grammar.rb +20 -0
  92. data/test/vendor/dhaka-2.2.1/test/core/compiled_parser_test.rb +44 -0
  93. data/test/vendor/dhaka-2.2.1/test/core/dfa_test.rb +170 -0
  94. data/test/vendor/dhaka-2.2.1/test/core/evaluator_test.rb +22 -0
  95. data/test/vendor/dhaka-2.2.1/test/core/grammar_test.rb +83 -0
  96. data/test/vendor/dhaka-2.2.1/test/core/lalr_but_not_slr_grammar.rb +19 -0
  97. data/test/vendor/dhaka-2.2.1/test/core/lexer_test.rb +139 -0
  98. data/test/vendor/dhaka-2.2.1/test/core/malformed_grammar.rb +7 -0
  99. data/test/vendor/dhaka-2.2.1/test/core/malformed_grammar_test.rb +8 -0
  100. data/test/vendor/dhaka-2.2.1/test/core/nullable_grammar.rb +21 -0
  101. data/test/vendor/dhaka-2.2.1/test/core/parse_result_test.rb +44 -0
  102. data/test/vendor/dhaka-2.2.1/test/core/parser_state_test.rb +24 -0
  103. data/test/vendor/dhaka-2.2.1/test/core/parser_test.rb +131 -0
  104. data/test/vendor/dhaka-2.2.1/test/core/precedence_grammar.rb +17 -0
  105. data/test/vendor/dhaka-2.2.1/test/core/precedence_grammar_test.rb +9 -0
  106. data/test/vendor/dhaka-2.2.1/test/core/rr_conflict_grammar.rb +21 -0
  107. data/test/vendor/dhaka-2.2.1/test/core/simple_grammar.rb +22 -0
  108. data/test/vendor/dhaka-2.2.1/test/core/sr_conflict_grammar.rb +16 -0
  109. data/test/vendor/dhaka-2.2.1/test/dhaka_test_helper.rb +17 -0
  110. data/test/vendor/dhaka-2.2.1/test/fake_logger.rb +17 -0
  111. data/test/vendor/simplerdb-0.2/lib/simplerdb/client_exception.rb +10 -0
  112. data/test/vendor/simplerdb-0.2/lib/simplerdb/db.rb +146 -0
  113. data/test/vendor/simplerdb-0.2/lib/simplerdb/query_language.rb +266 -0
  114. data/test/vendor/simplerdb-0.2/lib/simplerdb/server.rb +33 -0
  115. data/test/vendor/simplerdb-0.2/lib/simplerdb/servlet.rb +191 -0
  116. data/test/vendor/simplerdb-0.2/lib/simplerdb.rb +3 -0
  117. data/test/vendor/simplerdb-0.2/test/functional_test.rb +81 -0
  118. data/test/vendor/simplerdb-0.2/test/query_evaluator_test.rb +73 -0
  119. data/test/vendor/simplerdb-0.2/test/query_parser_test.rb +64 -0
  120. data/test/vendor/simplerdb-0.2/test/simplerdb_test.rb +80 -0
  121. metadata +182 -0
@@ -0,0 +1,1684 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+ require File.expand_path(File.dirname(__FILE__) + '/fixtures/couch')
3
+
4
+ class CouchTest < Test::Unit::TestCase
5
+ context "A simply stored couch instance" do
6
+ setup do
7
+ CouchPotato::Config.database_name = 'simply_stored_test'
8
+ recreate_db
9
+ end
10
+
11
+ context "when creating instances" do
12
+ should "populate the attributes" do
13
+ user = User.create(:title => "Mr.", :name => "Host Master")
14
+ assert_equal "Mr.", user.title
15
+ assert_equal "Host Master", user.name
16
+ end
17
+
18
+ should "save the instance" do
19
+ user = User.create(:title => "Mr.")
20
+ assert !user.new_record?
21
+ end
22
+
23
+ context "with a bang" do
24
+ should 'not raise an exception when saving succeeded' do
25
+ assert_nothing_raised do
26
+ User.create!(:title => "Mr.")
27
+ end
28
+ end
29
+
30
+ should 'save the user' do
31
+ user = User.create!(:title => "Mr.")
32
+ assert !user.new_record?
33
+ end
34
+
35
+ should 'raise an error when the validations failed' do
36
+ assert_raises(CouchPotato::Database::ValidationsFailedError) do
37
+ User.create!(:title => nil)
38
+ end
39
+ end
40
+ end
41
+
42
+ context "with a block" do
43
+ should 'call the block with the record' do
44
+ user = User.create do |u|
45
+ u.title = "Mr."
46
+ end
47
+
48
+ assert_equal "Mr.", user.title
49
+ end
50
+
51
+ should 'save the record' do
52
+ user = User.create do |u|
53
+ u.title = "Mr."
54
+ end
55
+ assert !user.new_record?
56
+ end
57
+
58
+ should 'assign attributes via the hash' do
59
+ user = User.create(:title => "Mr.") do |u|
60
+ u.name = "Host Master"
61
+ end
62
+
63
+ assert_equal "Mr.", user.title
64
+ assert_equal "Host Master", user.name
65
+ end
66
+ end
67
+ end
68
+
69
+ context "when saving an instance" do
70
+ should "um, save the instance" do
71
+ user = User.new(:title => "Mr.")
72
+ assert user.new_record?
73
+ user.save
74
+ assert !user.new_record?
75
+ end
76
+
77
+ context "when using save!" do
78
+ should 'raise an exception when a validation isnt fulfilled' do
79
+ user = User.new
80
+ assert_raises(CouchPotato::Database::ValidationsFailedError) do
81
+ user.save!
82
+ end
83
+ end
84
+ end
85
+
86
+ context "when using save(false)" do
87
+ should "not run the validations" do
88
+ user = User.new
89
+ user.save(false)
90
+ assert !user.new?
91
+ assert !user.dirty?
92
+ end
93
+ end
94
+ end
95
+
96
+ context "when destroying an instance" do
97
+ should "remove the instance" do
98
+ user = User.create(:title => "Mr")
99
+ assert_difference 'User.find(:all).size', -1 do
100
+ user.destroy
101
+ end
102
+ end
103
+
104
+ should 'return the frozen instance, brrrr' do
105
+ user = User.create(:title => "Mr")
106
+ assert_equal user, user.destroy
107
+ end
108
+ end
109
+
110
+ context "when updating attributes" do
111
+ should "merge in the updated attributes" do
112
+ user = User.create(:title => "Mr.")
113
+ user.update_attributes(:title => "Mrs.")
114
+ assert_equal "Mrs.", user.title
115
+ end
116
+
117
+ should "save the instance" do
118
+ user = User.create(:title => "Mr.")
119
+ user.update_attributes(:title => "Mrs.")
120
+ assert !user.dirty?
121
+ end
122
+ end
123
+
124
+ context "when finding instances" do
125
+ context "with find(:all)" do
126
+ should "return all instances" do
127
+ User.create(:title => "Mr.")
128
+ User.create(:title => "Mrs.")
129
+ assert_equal 2, User.find(:all).size
130
+ end
131
+ end
132
+
133
+ context "to find all instances" do
134
+ should 'generate a default find_all view' do
135
+ assert User.respond_to?(:all_documents)
136
+ end
137
+
138
+ should 'return all the users when calling all' do
139
+ User.create(:title => "Mr.")
140
+ User.create(:title => "Mrs.")
141
+ assert_equal 2, User.all.size
142
+ end
143
+ end
144
+
145
+ context "to find one instance" do
146
+ should 'return one user when calling first' do
147
+ user = User.create(:title => "Mr.")
148
+ assert_equal user, User.first
149
+ end
150
+
151
+ should 'return nil when no user found' do
152
+ assert_nil User.first
153
+ end
154
+ end
155
+
156
+ context "when finding with just an identifier" do
157
+ should "find just one instance" do
158
+ user = User.create(:title => "Mr.")
159
+ assert User.find(user.id).kind_of?(User)
160
+ end
161
+
162
+ should 'raise an error when no record was found' do
163
+ assert_raises(SimplyStored::RecordNotFound) do
164
+ User.find('abc')
165
+ end
166
+ end
167
+
168
+ should 'raise an error when nil was specified' do
169
+ assert_raises(SimplyStored::Error) do
170
+ User.find(nil)
171
+ end
172
+ end
173
+
174
+ should 'raise an error when the record was not of the expected type' do
175
+ post = Post.create
176
+ assert_raises(SimplyStored::RecordNotFound) do
177
+ User.find(post.id)
178
+ end
179
+ end
180
+ end
181
+
182
+ context "with a find_by prefix" do
183
+ setup do
184
+ recreate_db
185
+ end
186
+
187
+ should "create a view for the called finder" do
188
+ User.find_by_name("joe")
189
+ assert User.respond_to?(:by_name)
190
+ end
191
+
192
+ should 'not create the view when it already exists' do
193
+ User.expects(:view).never
194
+ User.find_by_name_and_created_at("joe", 'foo')
195
+ end
196
+
197
+ should "create a method to prevent future loops through method_missing" do
198
+ assert !User.respond_to?(:find_by_title)
199
+ User.find_by_title("Mr.")
200
+ assert User.respond_to?(:find_by_title)
201
+ end
202
+
203
+ should "call the generated view and return the result" do
204
+ user = User.create(:homepage => "http://www.peritor.com", :title => "Mr.")
205
+ assert_equal user, User.find_by_homepage("http://www.peritor.com")
206
+ end
207
+
208
+ should 'find only one instance when using find_by' do
209
+ User.create(:title => "Mr.")
210
+ assert User.find_by_title("Mr.").is_a?(User)
211
+ end
212
+
213
+ should "raise an error if the parameters don't match" do
214
+ assert_raise(ArgumentError) do
215
+ User.find_by_title()
216
+ end
217
+
218
+ assert_raise(ArgumentError) do
219
+ User.find_by_title(1,2,3,4,5)
220
+ end
221
+ end
222
+ end
223
+
224
+ context "with a find_all_by prefix" do
225
+ should "create a view for the called finder" do
226
+ User.find_all_by_name("joe")
227
+ assert User.respond_to?(:by_name)
228
+ end
229
+
230
+ should 'not create the view when it already exists' do
231
+ User.expects(:view).never
232
+ User.find_all_by_name_and_created_at("joe", "foo")
233
+ end
234
+
235
+ should "create a method to prevent future loops through method_missing" do
236
+ assert !User.respond_to?(:find_all_by_foo_attribute)
237
+ User.find_all_by_foo_attribute("Mr.")
238
+ assert User.respond_to?(:find_all_by_foo_attribute)
239
+ end
240
+
241
+ should "call the generated view and return the result" do
242
+ user = User.create(:homepage => "http://www.peritor.com", :title => "Mr.")
243
+ assert_equal [user], User.find_all_by_homepage("http://www.peritor.com")
244
+ end
245
+
246
+ should "return an emtpy array if none found" do
247
+ recreate_db
248
+ assert_equal [], User.find_all_by_title('Mr. Magoooo')
249
+ end
250
+
251
+ should 'find all instances when using find_all_by' do
252
+ User.create(:title => "Mr.")
253
+ User.create(:title => "Mr.")
254
+ assert_equal 2, User.find_all_by_title("Mr.").size
255
+ end
256
+
257
+ should "raise an error if the parameters don't match" do
258
+ assert_raise(ArgumentError) do
259
+ User.find_all_by_title()
260
+ end
261
+
262
+ assert_raise(ArgumentError) do
263
+ User.find_all_by_title(1,2,3,4,5)
264
+ end
265
+ end
266
+ end
267
+ end
268
+
269
+ context "when counting" do
270
+ setup do
271
+ recreate_db
272
+ end
273
+
274
+ context "when counting all" do
275
+ should "return the number of objects in the database" do
276
+ CountMe.create(:title => "Mr.")
277
+ CountMe.create(:title => "Mrs.")
278
+ assert_equal 2, CountMe.find(:all).size
279
+ assert_equal 2, CountMe.count
280
+ end
281
+
282
+ should "only count the correct class" do
283
+ CountMe.create(:title => "Mr.")
284
+ DontCountMe.create(:title => 'Foo')
285
+ assert_equal 1, CountMe.find(:all).size
286
+ assert_equal 1, CountMe.count
287
+ end
288
+ end
289
+
290
+ context "when counting by prefix" do
291
+ should "return the number of matching objects" do
292
+ CountMe.create(:title => "Mr.")
293
+ CountMe.create(:title => "Mrs.")
294
+ assert_equal 1, CountMe.find_all_by_title('Mr.').size
295
+ assert_equal 1, CountMe.count_by_title('Mr.')
296
+ end
297
+
298
+ should "only count the correct class" do
299
+ CountMe.create(:title => "Mr.")
300
+ DontCountMe.create(:title => 'Mr.')
301
+ assert_equal 1, CountMe.find_all_by_title('Mr.').size
302
+ assert_equal 1, CountMe.count_by_title('Mr.')
303
+ end
304
+ end
305
+
306
+ end
307
+
308
+ context "with associations" do
309
+ context "with belongs_to" do
310
+ should "generate a view for the association" do
311
+ assert Post.respond_to?(:association_post_belongs_to_user)
312
+ end
313
+
314
+ should "add the foreign key id to the referencing object" do
315
+ user = User.create(:title => "Mr.")
316
+ post = Post.create(:user => user)
317
+
318
+ post = Post.find(post.id)
319
+ assert_equal user.id, post.user_id
320
+ end
321
+
322
+ should "fetch the object from the database when requested through the getter" do
323
+ user = User.create(:title => "Mr.")
324
+ post = Post.create(:user => user)
325
+
326
+ post = Post.find(post.id)
327
+ assert_equal user, post.user
328
+ end
329
+
330
+ should "mark the referencing object as dirty" do
331
+ user = User.create(:title => "Mr.")
332
+ post = Post.create
333
+ post.user = user
334
+ assert post.dirty?
335
+ end
336
+
337
+ should "allow assigning a different object and store the id accordingly" do
338
+ user = User.create(:title => "Mr.")
339
+ user2 = User.create(:title => "Mrs.")
340
+ post = Post.create(:user => user)
341
+ post.user = user2
342
+ post.save
343
+
344
+ post = Post.find(post.id)
345
+ assert_equal user2, post.user
346
+ end
347
+
348
+ should "check the class and raise an error if not matching in belongs_to setter" do
349
+ post = Post.create
350
+ assert_raise(ArgumentError, 'expected Post got String') do
351
+ post.user = 'foo'
352
+ end
353
+ end
354
+
355
+ should 'not query for the object twice in getter' do
356
+ user = User.create(:title => "Mr.")
357
+ post = Post.create(:user => user)
358
+ post = Post.find(post.id)
359
+ User.expects(:find).returns "user"
360
+ post.user
361
+ User.expects(:find).never
362
+ post.user
363
+ end
364
+
365
+ should 'use cache in getter' do
366
+ post = Post.create
367
+ post.instance_variable_set("@user", 'foo')
368
+ assert_equal 'foo', post.user
369
+ end
370
+
371
+ should "ignore the cache if force_reload is given as an option" do
372
+ user = User.create(:name => 'Dude', :title => 'Mr.')
373
+ post = Post.create(:user => user)
374
+ post.reload
375
+ post.instance_variable_set("@user", 'foo')
376
+ assert_not_equal 'foo', post.user(:force_reload => true)
377
+ end
378
+
379
+ should 'set cache in setter' do
380
+ post = Post.create
381
+ user = User.create
382
+ assert_nil post.instance_variable_get("@user")
383
+ post.user = user
384
+ assert_equal user, post.instance_variable_get("@user")
385
+ end
386
+
387
+ should "not hit the database when the id column is empty" do
388
+ User.expects(:find).never
389
+ post = Post.create
390
+ post.user
391
+ end
392
+
393
+ should "know when the associated object changed" do
394
+ post = Post.create(:user => User.create(:title => "Mr."))
395
+ user2 = User.create(:title => "Mr.")
396
+ post.user = user2
397
+ assert post.user_changed?
398
+ end
399
+
400
+ should "not be changed when an association has not changed" do
401
+ post = Post.create(:user => User.create(:title => "Mr."))
402
+ assert !post.user_changed?
403
+ end
404
+
405
+ should "not be changed when assigned the same object" do
406
+ user = User.create(:title => "Mr.")
407
+ post = Post.create(:user => user)
408
+ post.user = user
409
+ assert !post.user_changed?
410
+ end
411
+
412
+ should "not be changed after saving" do
413
+ user = User.create(:title => "Mr.")
414
+ post = Post.new
415
+ post.user = user
416
+ assert post.user_changed?
417
+ post.save!
418
+ assert !post.user_changed?
419
+ end
420
+ end
421
+
422
+ context "with has_many" do
423
+ should "create a fetch method for the associated objects" do
424
+ user = User.new
425
+ assert user.respond_to?(:posts)
426
+ end
427
+
428
+ should "fetch the associated objects" do
429
+ user = User.create(:title => "Mr.")
430
+ 3.times {
431
+ post = Post.new
432
+ post.user = user
433
+ post.save!
434
+ }
435
+ assert_equal 3, user.posts.size
436
+ user.posts
437
+ end
438
+
439
+ should "verify the given options for the accessor method" do
440
+ user = User.create(:title => "Mr.")
441
+ assert_raise(ArgumentError) do
442
+ user.posts(:foo => false)
443
+ end
444
+ end
445
+
446
+ should "verify the given options for the association defintion" do
447
+ assert_raise(ArgumentError) do
448
+ User.instance_eval do
449
+ has_many :foo, :bar => :do
450
+ end
451
+ end
452
+ end
453
+
454
+ should "only fetch objects of the correct type" do
455
+ user = User.create(:title => "Mr.")
456
+ post = Post.new
457
+ post.user = user
458
+ post.save!
459
+
460
+ comment = Comment.new
461
+ comment.user = user
462
+ comment.save!
463
+
464
+ assert_equal 1, user.posts.size
465
+ end
466
+
467
+ should "getter should user cache" do
468
+ user = User.create(:title => "Mr.")
469
+ post = Post.new
470
+ post.user = user
471
+ post.save!
472
+ user.posts
473
+ assert_equal [post], user.instance_variable_get("@posts")
474
+ end
475
+
476
+ should "add methods to handle associated objects" do
477
+ user = User.new(:title => "Mr.")
478
+ assert user.respond_to?(:add_post)
479
+ assert user.respond_to?(:remove_post)
480
+ assert user.respond_to?(:remove_all_posts)
481
+ end
482
+
483
+ should 'ignore the cache when requesting explicit reload' do
484
+ user = User.create(:title => "Mr.")
485
+ assert_equal [], user.posts
486
+ post = Post.new
487
+ post.user = user
488
+ post.save!
489
+ assert_equal [post], user.posts(:force_reload => true)
490
+ end
491
+
492
+ context "when adding items" do
493
+ should "add the item to the internal cache" do
494
+ daddy = User.new(:title => "Mr.")
495
+ item = Post.new
496
+ assert_equal [], daddy.posts
497
+ daddy.add_post(item)
498
+ assert_equal [item], daddy.posts
499
+ assert_equal [item], daddy.instance_variable_get("@posts")
500
+ end
501
+
502
+ should "raise an error when the added item is not an object of the expected class" do
503
+ user = User.new
504
+ assert_raise(ArgumentError, 'excepted Post got String') do
505
+ user.add_post('foo')
506
+ end
507
+ end
508
+
509
+ should "save the added item" do
510
+ post = Post.new
511
+ user = User.create(:title => "Mr.")
512
+ user.add_post(post)
513
+ assert !post.new_record?
514
+ end
515
+
516
+ should 'set the forein key on the added object' do
517
+ post = Post.new
518
+ user = User.create(:title => "Mr.")
519
+ user.add_post(post)
520
+ assert_equal user.id, post.user_id
521
+ end
522
+ end
523
+
524
+ context "when removing items" do
525
+ should "should unset the foreign key" do
526
+ user = User.create(:title => "Mr.")
527
+ post = Post.create(:user => user)
528
+
529
+ user.remove_post(post)
530
+ assert_nil post.user_id
531
+ end
532
+
533
+ should "remove the item from the cache" do
534
+ user = User.create(:title => "Mr.")
535
+ post = Post.create(:user => user)
536
+ assert user.posts.include?(post)
537
+ user.remove_post(post)
538
+ assert !user.posts.any?{|p| post.id == p.id}
539
+ assert_equal [], user.instance_variable_get("@posts")
540
+ end
541
+
542
+ should "save the removed item with the nullified foreign key" do
543
+ user = User.create(:title => "Mr.")
544
+ post = Post.create(:user => user)
545
+
546
+ user.remove_post(post)
547
+ post = Post.find(post.id)
548
+ assert_nil post.user_id
549
+ end
550
+
551
+ should 'raise an error when another object is the owner of the object to be removed' do
552
+ user = User.create(:title => "Mr.")
553
+ mrs = User.create(:title => "Mrs.")
554
+ post = Post.create(:user => user)
555
+ assert_raise(ArgumentError) do
556
+ mrs.remove_post(post)
557
+ end
558
+ end
559
+
560
+ should 'raise an error when the object is the wrong type' do
561
+ user = User.new
562
+ assert_raise(ArgumentError, 'excepted Post got String') do
563
+ user.remove_post('foo')
564
+ end
565
+ end
566
+
567
+ should "delete the object when dependent:destroy" do
568
+ Category.instance_eval do
569
+ has_many :tags, :dependent => :destroy
570
+ end
571
+
572
+ category = Category.create(:name => "food")
573
+ tag = Tag.create(:name => "food", :category => category)
574
+ assert !tag.new?
575
+ category.remove_tag(tag)
576
+
577
+ assert_equal [], Tag.find(:all)
578
+ end
579
+
580
+ end
581
+
582
+ context "when removing all items" do
583
+ should 'nullify the foreign keys on all referenced items' do
584
+ user = User.create(:title => "Mr.")
585
+ post = Post.create(:user => user)
586
+ post2 = Post.create(:user => user)
587
+ user.remove_all_posts
588
+ post = Post.find(post.id)
589
+ post2 = Post.find(post2.id)
590
+ assert_nil post.user_id
591
+ assert_nil post2.user_id
592
+ end
593
+
594
+ should 'empty the cache' do
595
+ user = User.create(:title => "Mr.")
596
+ post = Post.create(:user => user)
597
+ post2 = Post.create(:user => user)
598
+ user.remove_all_posts
599
+ assert_equal [], user.posts
600
+ assert_equal [], user.instance_variable_get("@posts")
601
+ end
602
+ end
603
+
604
+ context 'when destroying the parent objects' do
605
+ should "delete relations when dependent is destroy" do
606
+ Category.instance_eval do
607
+ has_many :tags, :dependent => :destroy
608
+ end
609
+
610
+ category = Category.create(:name => "food")
611
+ tag = Tag.create(:name => "food", :category => category)
612
+
613
+ assert_equal [tag], Tag.find(:all)
614
+ category.destroy
615
+ assert_equal [], Tag.find(:all)
616
+ end
617
+
618
+ should "nullify relations when dependent is nullify" do
619
+
620
+ user = User.create(:title => "Mr.")
621
+ post = Post.create(:user => user)
622
+
623
+ user.destroy
624
+ post = Post.find(post.id)
625
+ assert_nil post.user_id
626
+ end
627
+
628
+ should "nullify the foreign key even if validation forbids" do
629
+ user = User.create(:title => "Mr.")
630
+ post = StrictPost.create(:user => user)
631
+
632
+ user.destroy
633
+ post = StrictPost.find(post.id)
634
+ assert_nil post.user_id
635
+ end
636
+ end
637
+ end
638
+
639
+ context "with has_many :trough" do
640
+ setup do
641
+ @journal_1 = Journal.create
642
+ @journal_2 = Journal.create
643
+ @reader_1 = Reader.create
644
+ @reader_2 = Reader.create
645
+ end
646
+
647
+ should "raise an exception if there is no :through relation" do
648
+
649
+ assert_raise(ArgumentError) do
650
+ class FooHasManyThroughBar
651
+ include SimplyStored::Couch
652
+ has_many :foos, :through => :bars
653
+ end
654
+ end
655
+ end
656
+
657
+ should "define a getter" do
658
+ assert @journal_1.respond_to?(:readers)
659
+ assert @reader_1.respond_to?(:journals)
660
+ end
661
+
662
+ should "load the objects through" do
663
+ membership = Membership.new
664
+ membership.journal = @journal_1
665
+ membership.reader = @reader_1
666
+ assert membership.save
667
+
668
+ assert_equal @journal_1, membership.journal
669
+ assert_equal @reader_1, membership.reader
670
+ assert_equal [membership], @journal_1.reload.memberships
671
+ assert_equal [membership], @journal_1.reload.memberships
672
+
673
+ assert_equal [@reader_1], @journal_1.readers
674
+ assert_equal [@journal_1], @reader_1.journals
675
+
676
+ membership_2 = Membership.new
677
+ membership_2.journal = @journal_1
678
+ membership_2.reader = @reader_2
679
+ assert membership_2.save
680
+
681
+ assert_equal [@reader_1.id, @reader_2.id].sort, @journal_1.reload.readers.map(&:id).sort
682
+ assert_equal [@journal_1.id], @reader_1.reload.journals.map(&:id).sort
683
+ assert_equal [@journal_1.id], @reader_2.reload.journals.map(&:id).sort
684
+
685
+ membership_3 = Membership.new
686
+ membership_3.journal = @journal_2
687
+ membership_3.reader = @reader_2
688
+ assert membership_3.save
689
+
690
+ assert_equal [@reader_1.id, @reader_2.id].sort, @journal_1.reload.readers.map(&:id).sort
691
+ assert_equal [@reader_2.id].sort, @journal_2.reload.readers.map(&:id).sort
692
+ assert_equal [@journal_1.id], @reader_1.reload.journals.map(&:id).sort
693
+ assert_equal [@journal_1.id, @journal_2.id].sort, @reader_2.reload.journals.map(&:id).sort
694
+
695
+ membership_3.destroy
696
+
697
+ assert_equal [@reader_1.id, @reader_2.id].sort, @journal_1.reload.readers.map(&:id).sort
698
+ assert_equal [], @journal_2.reload.readers
699
+ assert_equal [@journal_1.id], @reader_1.reload.journals.map(&:id).sort
700
+ assert_equal [@journal_1.id], @reader_2.reload.journals.map(&:id).sort
701
+ end
702
+
703
+ should "verify the given options" do
704
+ assert_raise(ArgumentError) do
705
+ @journal_1.readers(:foo => true)
706
+ end
707
+ end
708
+
709
+ should "not try to destroy/nullify through-objects on parent object delete" do
710
+ membership = Membership.new
711
+ membership.journal = @journal_1
712
+ membership.reader = @reader_1
713
+ assert membership.save
714
+
715
+ @reader_1.reload
716
+ @journal_1.reload
717
+
718
+ Reader.any_instance.expects("journal=").never
719
+ Journal.any_instance.expects(:readers).never
720
+
721
+ @journal_1.delete
722
+ end
723
+
724
+ end
725
+
726
+ context "with has_one" do
727
+
728
+ should "add a getter method" do
729
+ assert Instance.new.respond_to?(:identity)
730
+ end
731
+
732
+ should "fetch the object when invoking the getter" do
733
+ instance = Instance.create
734
+ identity = Identity.create(:instance => instance)
735
+ assert_equal identity, instance.identity
736
+ end
737
+
738
+ should "verify the given options for the accessor method" do
739
+ instance = Instance.create
740
+ assert_raise(ArgumentError) do
741
+ instance.identity(:foo => :var)
742
+ end
743
+ end
744
+
745
+ should "verify the given options for the association defintion" do
746
+ assert_raise(ArgumentError) do
747
+ User.instance_eval do
748
+ has_one :foo, :bar => :do
749
+ end
750
+ end
751
+ end
752
+
753
+ should "store the fetched object into the cache" do
754
+ instance = Instance.create
755
+ identity = Identity.create(:instance => instance)
756
+ instance.identity
757
+ assert_equal identity, instance.instance_variable_get("@identity")
758
+ end
759
+
760
+ should "not fetch from the database when object is in cache" do
761
+ instance = Instance.create
762
+ identity = Identity.create(:instance => instance)
763
+ instance.identity
764
+ CouchPotato.database.expects(:view).never
765
+ instance.identity
766
+ end
767
+
768
+ should "update the foreign object to have the owner's id in the forein key" do
769
+ instance = Instance.create
770
+ identity = Identity.create
771
+ instance.identity = identity
772
+ identity.reload
773
+ assert_equal instance.id, identity.instance_id
774
+ end
775
+
776
+ should "update the cache when setting" do
777
+ instance = Instance.create
778
+ identity = Identity.create
779
+ instance.identity = identity
780
+ CouchPotato.expects(:database).never
781
+ assert_equal identity, instance.identity
782
+ end
783
+
784
+ should "set the foreign key value to nil when assigning nil" do
785
+ instance = Instance.create
786
+ identity = Identity.create(:instance => instance)
787
+ instance.identity = nil
788
+ identity = Identity.find(identity.id)
789
+ assert_nil identity.instance_id
790
+ end
791
+
792
+ should 'check the class' do
793
+ instance = Instance.create
794
+ assert_raise(ArgumentError, 'expected Item got String') do
795
+ instance.identity = 'foo'
796
+ end
797
+ end
798
+
799
+ should 'delete the dependent objects when dependent is set to destroy' do
800
+ identity = Identity.create
801
+ mag = Magazine.create
802
+ mag.identity = identity
803
+ mag.identity = nil
804
+ assert_nil Identity.find_by_id(identity.id)
805
+ end
806
+
807
+ should 'unset the id on the foreign object when a new object is set' do
808
+ instance = Instance.create
809
+ identity = Identity.create(:instance => instance)
810
+ identity2 = Identity.create
811
+
812
+ instance.identity = identity2
813
+ identity = Identity.find(identity.id)
814
+ assert_nil identity.instance_id
815
+ end
816
+
817
+ should 'delete the foreign object when a new object is set and dependent is set to destroy' do
818
+ identity = Identity.create
819
+ identity2 = Identity.create
820
+ mag = Magazine.create
821
+ mag.identity = identity
822
+ mag.identity = identity2
823
+ assert_nil Identity.find_by_id(identity.id)
824
+ end
825
+
826
+ should 'delete the foreign object when parent is destroyed and dependent is set to destroy' do
827
+ identity = Identity.create
828
+ mag = Magazine.create
829
+ mag.identity = identity
830
+
831
+ mag.destroy
832
+ assert_nil Identity.find_by_id(identity.id)
833
+ end
834
+
835
+ should 'nullify the foreign objects foreign key when parent is destroyed' do
836
+ identity = Identity.create
837
+ instance = Instance.create
838
+ instance.identity = identity
839
+ instance.destroy
840
+ identity = Identity.find(identity.id)
841
+ assert_nil identity.instance_id
842
+ end
843
+ end
844
+ end
845
+
846
+ context "attribute proctection against mass assignment" do
847
+
848
+ context "when using attr_protected" do
849
+ setup do
850
+ Category.instance_eval do
851
+ @_accessible_attributes = []
852
+ attr_protected :parent, :alias
853
+ end
854
+ end
855
+
856
+ should "not allow to set with mass assignment using attributes=" do
857
+ item = Category.new
858
+ item.attributes = {:parent => 'a', :name => 'c'}
859
+ assert_equal 'c', item.name
860
+ assert_nil item.parent
861
+ end
862
+
863
+ should "not allow to set with mass assignment using attributes= - ignore string vs. symbol" do
864
+ item = Category.new
865
+ item.attributes = {'parent' => 'a', 'name' => 'c'}
866
+ assert_equal 'c', item.name
867
+ assert_nil item.parent
868
+ end
869
+
870
+ should "not allow to set with mass assignment using the constructor" do
871
+ item = Category.new(:parent => 'a', :name => 'c')
872
+ assert_equal 'c', item.name
873
+ assert_nil item.parent
874
+ end
875
+
876
+ should "not allow to set with mass assignment using update_attributes" do
877
+ item = Category.new
878
+ item.update_attributes(:parent => 'a', :name => 'c')
879
+ assert_equal 'c', item.name
880
+ assert_nil item.parent
881
+ end
882
+ end
883
+
884
+ context "attr_accessible" do
885
+ setup do
886
+ Category.instance_eval do
887
+ @_protected_attributes = []
888
+ attr_accessible :name
889
+ end
890
+ end
891
+
892
+ should "not allow to set with mass assignment using attributes=" do
893
+ item = Category.new
894
+ item.attributes = {:parent => 'a', :name => 'c'}
895
+ assert_equal 'c', item.name
896
+ assert_nil item.parent
897
+ end
898
+
899
+ should "not allow to set with mass assignment using the constructor" do
900
+ item = Category.new(:parent => 'a', :name => 'c')
901
+ assert_equal 'c', item.name
902
+ assert_nil item.parent
903
+ end
904
+
905
+ should "not allow to set with mass assignment using update_attributes" do
906
+ item = Category.new
907
+ item.update_attributes(:parent => 'a', :name => 'c')
908
+ # item.reload
909
+ assert_equal 'c', item.name
910
+ assert_nil item.parent
911
+ end
912
+ end
913
+ end
914
+
915
+ context "with additional validations" do
916
+ context "with validates_inclusion_of" do
917
+ should "validate inclusion of an attribute in an array" do
918
+ category = Category.new(:name => "other")
919
+ assert !category.save
920
+ end
921
+
922
+ should "validate when the attribute is an array" do
923
+ category = Category.new(:name => ['drinks', 'food'])
924
+ assert_nothing_raised do
925
+ category.save!
926
+ end
927
+ end
928
+
929
+ should "add an error message" do
930
+ category = Category.new(:name => "other")
931
+ category.valid?
932
+ assert_match(/must be one or more of food, drinks, party/, category.errors.full_messages.first)
933
+ end
934
+
935
+ should "allow blank" do
936
+ category = Category.new(:name => nil)
937
+ assert category.valid?
938
+ end
939
+ end
940
+
941
+ context "with validates_format_of" do
942
+ class ValidatedUser
943
+ include SimplyStored::Couch
944
+ property :name
945
+ validates_format_of :name, :with => /Paul/
946
+ end
947
+
948
+ should 'validate the format and fail when not matched' do
949
+ user = ValidatedUser.new(:name => "John")
950
+ assert !user.valid?
951
+ end
952
+
953
+ should 'succeed when matched' do
954
+ user = ValidatedUser.new(:name => "Paul")
955
+ assert user.valid?
956
+ end
957
+
958
+ should 'fail when empty' do
959
+ user = ValidatedUser.new(:name => nil)
960
+ assert !user.valid?
961
+ end
962
+
963
+ context "with allow_blank" do
964
+ class ValidatedBlankUser
965
+ include SimplyStored::Couch
966
+ property :name
967
+ validates_format_of :name, :with => /Paul/, :allow_blank => true
968
+ end
969
+
970
+ should 'not fail when nil' do
971
+ user = ValidatedBlankUser.new(:name => nil)
972
+ assert user.valid?
973
+ end
974
+
975
+ should 'not fail when empty string' do
976
+ user = ValidatedBlankUser.new(:name => '')
977
+ assert user.valid?
978
+ end
979
+
980
+ should 'fail when not matching' do
981
+ user = ValidatedBlankUser.new(:name => 'John')
982
+ assert !user.valid?
983
+ end
984
+
985
+ should 'not fail when matching' do
986
+ user = ValidatedBlankUser.new(:name => 'Paul')
987
+ assert user.valid?
988
+ end
989
+
990
+ end
991
+ end
992
+
993
+ context "with validates_uniqueness_of" do
994
+ should "add a view on the unique attribute" do
995
+ assert UniqueUser.by_name
996
+ end
997
+
998
+ should "set an error when a different with the same instance exists" do
999
+ assert UniqueUser.create(:name => "Host Master")
1000
+ user = UniqueUser.create(:name => "Host Master")
1001
+ assert !user.valid?
1002
+ end
1003
+
1004
+ should "not have an error when we're the only one around" do
1005
+ user = UniqueUser.create(:name => "Host Master")
1006
+ assert !user.new_record?
1007
+ end
1008
+
1009
+ should "not have an error when it's the same instance" do
1010
+ user = UniqueUser.create(:name => "Host Master")
1011
+ user = UniqueUser.find(user.id)
1012
+ assert user.valid?
1013
+ end
1014
+
1015
+ should 'have a nice error message' do
1016
+ assert UniqueUser.create(:name => "Host Master")
1017
+ user = UniqueUser.create(:name => "Host Master")
1018
+ assert_equal "Name is already taken", user.errors.on(:name)
1019
+ end
1020
+ end
1021
+ end
1022
+
1023
+ context "when reloading an instance" do
1024
+ should "reload new attributes from the database" do
1025
+ user = User.create(:title => "Mr.", :name => "Host Master")
1026
+ user2 = User.find(user.id)
1027
+ user2.update_attributes(:title => "Mrs.", :name => "Hostess Masteress")
1028
+ user.reload
1029
+ assert_equal "Mrs.", user.title
1030
+ assert_equal "Hostess Masteress", user.name
1031
+ end
1032
+
1033
+ should "not be dirty after reloading" do
1034
+ user = User.create(:title => "Mr.", :name => "Host Master")
1035
+ user2 = User.find(user.id)
1036
+ user2.update_attributes(:title => "Mrs.", :name => "Hostess Masteress")
1037
+ user.reload
1038
+ assert !user.dirty?
1039
+ end
1040
+
1041
+ should "ensure that association caches for has_many are cleared" do
1042
+ user = User.create(:title => "Mr.", :name => "Host Master")
1043
+ post = Post.create(:user => user)
1044
+ assert_equal 1, user.posts.size
1045
+ assert_not_nil user.instance_variable_get("@posts")
1046
+ user.reload
1047
+ assert_nil user.instance_variable_get("@posts")
1048
+ assert_not_nil user.posts.first
1049
+ end
1050
+
1051
+ should "ensure that association caches for belongs_to are cleared" do
1052
+ user = User.create(:title => "Mr.", :name => "Host Master")
1053
+ post = Post.create(:user => user)
1054
+ post.user
1055
+ assert_not_nil post.instance_variable_get("@user")
1056
+ post.reload
1057
+ assert_nil post.instance_variable_get("@user")
1058
+ assert_not_nil post.user
1059
+ end
1060
+
1061
+ should "update the revision" do
1062
+ user = User.create(:title => "Mr.", :name => "Host Master")
1063
+ user2 = User.find(user.id)
1064
+ user2.update_attributes(:title => "Mrs.", :name => "Hostess Masteress")
1065
+ user.reload
1066
+ assert_equal user._rev, user2._rev
1067
+ end
1068
+ end
1069
+
1070
+ context "with s3 interaction" do
1071
+ setup do
1072
+ CouchLogItem.instance_variable_set(:@_s3_connection, nil)
1073
+
1074
+ bucket = stub(:bckt) do
1075
+ stubs(:put).returns(true)
1076
+ stubs(:get).returns(true)
1077
+ end
1078
+
1079
+ @bucket = bucket
1080
+
1081
+ @s3 = stub(:s3) do
1082
+ stubs(:bucket).returns(bucket)
1083
+ end
1084
+
1085
+ RightAws::S3.stubs(:new).returns @s3
1086
+ @log_item = CouchLogItem.new
1087
+ end
1088
+
1089
+ context "when saving the attachment" do
1090
+ should "fetch the collection" do
1091
+ @log_item.log_data = "Yay! It logged!"
1092
+ RightAws::S3.expects(:new).with('abcdef', 'secret!', :multi_thread => true).returns(@s3)
1093
+ @log_item.save
1094
+ end
1095
+
1096
+ should "upload the file" do
1097
+ @log_item.log_data = "Yay! It logged!"
1098
+ @bucket.expects(:put).with(anything, "Yay! It logged!", {}, anything)
1099
+ @log_item.save
1100
+ end
1101
+
1102
+ should "use the specified bucket" do
1103
+ @log_item.log_data = "Yay! It logged!"
1104
+ CouchLogItem._s3_options[:log_data][:bucket] = 'mybucket'
1105
+ @s3.expects(:bucket).with('mybucket').returns(@bucket)
1106
+ @log_item.save
1107
+ end
1108
+
1109
+ should "create the bucket if it doesn't exist" do
1110
+ @log_item.log_data = "Yay! log me"
1111
+ CouchLogItem._s3_options[:log_data][:bucket] = 'mybucket'
1112
+
1113
+ @s3.expects(:bucket).with('mybucket').returns(nil)
1114
+ @s3.expects(:bucket).with('mybucket', true, 'private').returns(@bucket)
1115
+ @log_item.save
1116
+ end
1117
+
1118
+ should "raise an error if the bucket is not ours" do
1119
+ @log_item.log_data = "Yay! log me too"
1120
+ CouchLogItem._s3_options[:log_data][:bucket] = 'mybucket'
1121
+
1122
+ @s3.expects(:bucket).with('mybucket').returns(nil)
1123
+ @s3.expects(:bucket).with('mybucket', true, 'private').raises(RightAws::AwsError, 'BucketAlreadyExists: The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again')
1124
+
1125
+ assert_raise(ArgumentError) do
1126
+ @log_item.save
1127
+ end
1128
+ end
1129
+
1130
+ should "not upload the attachment when it hasn't been changed" do
1131
+ @bucket.expects(:put).never
1132
+ @log_item.save
1133
+ end
1134
+
1135
+ should "set the permissions to private by default" do
1136
+ class Item
1137
+ include SimplyStored::Couch
1138
+ has_s3_attachment :log_data, :bucket => 'mybucket'
1139
+ end
1140
+ @bucket.expects(:put).with(anything, anything, {}, 'private')
1141
+ @log_item = Item.new
1142
+ @log_item.log_data = 'Yay!'
1143
+ @log_item.save
1144
+ end
1145
+
1146
+ should "set the permissions to whatever's specified in the options for the attachment" do
1147
+ @log_item.save
1148
+ old_perms = CouchLogItem._s3_options[:log_data][:permissions]
1149
+ CouchLogItem._s3_options[:log_data][:permissions] = 'public-read'
1150
+ @bucket.expects(:put).with(anything, anything, {}, 'public-read')
1151
+ @log_item.log_data = 'Yay!'
1152
+ @log_item.save
1153
+ CouchLogItem._s3_options[:log_data][:permissions] = old_perms
1154
+ end
1155
+
1156
+ should "use the full class name and the id as key" do
1157
+ @log_item.save
1158
+ @bucket.expects(:put).with("couch_log_items/log_data/#{@log_item.id}", 'Yay!', {}, anything)
1159
+ @log_item.log_data = 'Yay!'
1160
+ @log_item.save
1161
+ end
1162
+
1163
+ should "mark the attachment as not dirty after uploading" do
1164
+ @log_item.log_data = 'Yay!'
1165
+ @log_item.save
1166
+ assert !@log_item.instance_variable_get(:@_s3_attachments)[:log_data][:dirty]
1167
+ end
1168
+
1169
+ should 'store the attachment when the validations succeeded' do
1170
+ @log_item.log_data = 'Yay!'
1171
+ @log_item.stubs(:valid?).returns(true)
1172
+ @bucket.expects(:put)
1173
+ @log_item.save
1174
+ end
1175
+
1176
+ should "not store the attachment when the validations failed" do
1177
+ @log_item.log_data = 'Yay!'
1178
+ @log_item.stubs(:valid?).returns(false)
1179
+ @bucket.expects(:put).never
1180
+ @log_item.save
1181
+ end
1182
+
1183
+ should "save the attachment status" do
1184
+ @log_item.save
1185
+ @log_item.attributes["log_data_attachments"]
1186
+ end
1187
+
1188
+ should "save generate the url for the attachment" do
1189
+ @log_item._s3_options[:log_data][:bucket] = 'bucket-for-monsieur'
1190
+ @log_item._s3_options[:log_data][:permissions] = 'public-read'
1191
+ @log_item.save
1192
+ assert_equal "http://bucket-for-monsieur.s3.amazonaws.com/#{@log_item.s3_attachment_key(:log_data)}", @log_item.log_data_url
1193
+ end
1194
+
1195
+ should "add a short-lived access key for private attachments" do
1196
+ @log_item._s3_options[:log_data][:permissions] = 'private'
1197
+ @log_item.save
1198
+ assert @log_item.log_data_url.include?("https://bucket-for-monsieur.s3.amazonaws.com:443/#{@log_item.s3_attachment_key(:log_data)}")
1199
+ assert @log_item.log_data_url.include?("Signature=")
1200
+ assert @log_item.log_data_url.include?("Expires=")
1201
+ end
1202
+
1203
+ should "serialize data other than strings to json" do
1204
+ @log_item.log_data = ['one log entry', 'and another one']
1205
+ @bucket.expects(:put).with(anything, '["one log entry","and another one"]', {}, anything)
1206
+ @log_item.save
1207
+ end
1208
+ end
1209
+
1210
+ context "when fetching the data" do
1211
+ should "fetch the data from s3 and set the attachment attribute" do
1212
+ @log_item.instance_variable_set(:@_s3_attachments, {})
1213
+ @bucket.expects(:get).with("couch_log_items/log_data/#{@log_item.id}").returns("Yay!")
1214
+ assert_equal "Yay!", @log_item.log_data
1215
+ end
1216
+
1217
+ should "not mark the the attachment as dirty" do
1218
+ @log_item.instance_variable_set(:@_s3_attachments, {})
1219
+ @bucket.expects(:get).with("couch_log_items/log_data/#{@log_item.id}").returns("Yay!")
1220
+ @log_item.log_data
1221
+ assert !@log_item._s3_attachments[:log_data][:dirty]
1222
+ end
1223
+
1224
+ should "not try to fetch the attachment if the value is already set" do
1225
+ @log_item.log_data = "Yay!"
1226
+ @bucket.expects(:get).never
1227
+ assert_equal "Yay!", @log_item.log_data
1228
+ end
1229
+ end
1230
+ end
1231
+
1232
+ context "when using soft deletable" do
1233
+ should "know when it is enabled" do
1234
+ assert Hemorrhoid.soft_deleting_enabled?
1235
+ assert !User.soft_deleting_enabled?
1236
+ end
1237
+
1238
+ should "define a :deleted_at attribute" do
1239
+ h = Hemorrhoid.new
1240
+ assert h.respond_to?(:deleted_at)
1241
+ assert h.respond_to?(:deleted_at=)
1242
+ assert_equal :deleted_at, Hemorrhoid.soft_delete_attribute
1243
+ end
1244
+
1245
+ should "define a hard delete methods" do
1246
+ h = Hemorrhoid.new
1247
+ assert h.respond_to?(:destroy!)
1248
+ assert h.respond_to?(:delete!)
1249
+ end
1250
+
1251
+ context "when deleting" do
1252
+ setup do
1253
+ @user = User.new(:name => 'BigT', :title => 'Dr.')
1254
+ @user.save!
1255
+ @hemorrhoid = Hemorrhoid.new
1256
+ @hemorrhoid.user = @user
1257
+ @hemorrhoid.save!
1258
+ end
1259
+
1260
+ should "not delete the object but populate the soft_delete_attribute" do
1261
+ now = Time.now
1262
+ Time.stubs(:now).returns(now)
1263
+ assert_nil @hemorrhoid.deleted_at
1264
+ assert @hemorrhoid.delete
1265
+ assert_equal now, @hemorrhoid.deleted_at
1266
+ end
1267
+
1268
+ should "survive reloads with the new attribute" do
1269
+ assert_nil @hemorrhoid.deleted_at
1270
+ assert @hemorrhoid.delete
1271
+ @hemorrhoid.reload
1272
+ assert_not_nil @hemorrhoid.deleted_at
1273
+ end
1274
+
1275
+ should "know when it is deleted" do
1276
+ assert !@hemorrhoid.deleted?
1277
+ @hemorrhoid.delete
1278
+ assert @hemorrhoid.deleted?
1279
+ end
1280
+
1281
+ should "not consider objects without soft-deleted as deleted" do
1282
+ assert !@user.deleted?
1283
+ @user.delete
1284
+ assert !@user.deleted?
1285
+ end
1286
+
1287
+ should "not delete in DB" do
1288
+ CouchPotato.database.expects(:destroy_document).never
1289
+ @hemorrhoid.destroy
1290
+ end
1291
+
1292
+ should "really delete if asked to" do
1293
+ CouchPotato.database.expects(:destroy_document).with(@hemorrhoid)
1294
+ @hemorrhoid.destroy!
1295
+ end
1296
+
1297
+ context "callbacks" do
1298
+
1299
+ should "still fire the callbacks" do
1300
+ @hemorrhoid = Hemorrhoid.create
1301
+ $before = nil
1302
+ $after = nil
1303
+ def @hemorrhoid.before_destroy_callback
1304
+ $before = "now"
1305
+ end
1306
+
1307
+ def @hemorrhoid.after_destroy_callback
1308
+ $after = "now"
1309
+ end
1310
+
1311
+ @hemorrhoid.destroy
1312
+
1313
+ assert_not_nil $before
1314
+ assert_not_nil $after
1315
+ end
1316
+
1317
+ should "not fire the callbacks on the real destroy if the object is already deleted" do
1318
+ @hemorrhoid = Hemorrhoid.create
1319
+ def @hemorrhoid.before_destroy_callback
1320
+ raise "Callback called even though #{skip_callbacks.inspect}"
1321
+ end
1322
+
1323
+ def @hemorrhoid.after_destroy_callback
1324
+ raise "Callback called even though #{skip_callbacks.inspect}"
1325
+ end
1326
+
1327
+ def @hemorrhoid.deleted?
1328
+ true
1329
+ end
1330
+
1331
+ assert_nothing_raised do
1332
+ @hemorrhoid.destroy!
1333
+ end
1334
+ end
1335
+
1336
+ should "not fire the callbacks on the real destroy if the object is not deleted" do
1337
+ @hemorrhoid = Hemorrhoid.create
1338
+ $before = nil
1339
+ $after = nil
1340
+ def @hemorrhoid.before_destroy_callback
1341
+ $before = "now"
1342
+ end
1343
+
1344
+ def @hemorrhoid.after_destroy_callback
1345
+ $after = "now"
1346
+ end
1347
+
1348
+ @hemorrhoid.destroy!
1349
+
1350
+ assert_not_nil $before
1351
+ assert_not_nil $after
1352
+ end
1353
+ end
1354
+
1355
+ context "when handling the dependent objects" do
1356
+ setup do
1357
+ @sub = SubHemorrhoid.new
1358
+ @sub.hemorrhoid = @hemorrhoid
1359
+ @sub.save!
1360
+
1361
+ @easy_sub = EasySubHemorrhoid.new
1362
+ @easy_sub.hemorrhoid = @hemorrhoid
1363
+ @easy_sub.save!
1364
+
1365
+ @rash = Rash.new
1366
+ @rash.hemorrhoid = @hemorrhoid
1367
+ @rash.save!
1368
+
1369
+ @hemorrhoid.reload
1370
+ end
1371
+
1372
+ should "delete them" do
1373
+ @hemorrhoid.delete
1374
+ @sub.reload
1375
+ assert @sub.deleted?
1376
+ assert_raise(SimplyStored::RecordNotFound) do
1377
+ EasySubHemorrhoid.find(@easy_sub.id, :with_deleted => true)
1378
+ end
1379
+ @rash = Rash.find(@rash.id)
1380
+ assert_nil @rash.hemorrhoid_id
1381
+ end
1382
+
1383
+ should "really delete them if the parent is really deleted" do
1384
+ @hemorrhoid.delete!
1385
+ assert_raise(SimplyStored::RecordNotFound) do
1386
+ EasySubHemorrhoid.find(@sub.id, :with_deleted => true)
1387
+ end
1388
+
1389
+ assert_raise(SimplyStored::RecordNotFound) do
1390
+ EasySubHemorrhoid.find(@easy_sub.id, :with_deleted => true)
1391
+ end
1392
+
1393
+ @rash = Rash.find(@rash.id)
1394
+ assert_nil @rash.hemorrhoid_id
1395
+ end
1396
+
1397
+ should "not nullify dependents if they are soft-deletable" do
1398
+ small_rash = SmallRash.create(:hemorrhoid => @hemorrhoid)
1399
+ @hemorrhoid.reload
1400
+ @hemorrhoid.destroy
1401
+ small_rash = SmallRash.find(small_rash.id)
1402
+ assert_not_nil small_rash.hemorrhoid_id
1403
+ assert_equal @hemorrhoid.id, small_rash.hemorrhoid_id
1404
+ end
1405
+ end
1406
+
1407
+ end
1408
+
1409
+ context "when loading" do
1410
+ setup do
1411
+ @user = User.new(:name => 'BigT', :title => 'Dr.')
1412
+ @user.save!
1413
+ @hemorrhoid = Hemorrhoid.new
1414
+ @hemorrhoid.user = @user
1415
+ @hemorrhoid.save!
1416
+ end
1417
+
1418
+ context "by id" do
1419
+ should "not be found by default" do
1420
+ @hemorrhoid.destroy
1421
+ assert_raise(SimplyStored::RecordNotFound) do
1422
+ Hemorrhoid.find(@hemorrhoid.id)
1423
+ end
1424
+ end
1425
+
1426
+ should "be found if supplied with :with_deleted" do
1427
+ @hemorrhoid.destroy
1428
+
1429
+ assert_not_nil Hemorrhoid.find(@hemorrhoid.id, :with_deleted => true)
1430
+ end
1431
+
1432
+ should "not be found if it is really gone" do
1433
+ old_id = @hemorrhoid.id
1434
+ @hemorrhoid.destroy!
1435
+
1436
+ assert_raise(SimplyStored::RecordNotFound) do
1437
+ Hemorrhoid.find(old_id)
1438
+ end
1439
+ end
1440
+
1441
+ should "always reload" do
1442
+ @hemorrhoid.destroy
1443
+ assert_nothing_raised do
1444
+ @hemorrhoid.reload
1445
+ end
1446
+ assert_not_nil @hemorrhoid.deleted_at
1447
+ end
1448
+ end
1449
+
1450
+ context "all" do
1451
+ setup do
1452
+ recreate_db
1453
+ @hemorrhoid = Hemorrhoid.create
1454
+ assert @hemorrhoid.destroy
1455
+ assert @hemorrhoid.reload.deleted?
1456
+ end
1457
+
1458
+ should "not load deleted" do
1459
+ assert_equal [], Hemorrhoid.find(:all)
1460
+ assert_equal [], Hemorrhoid.find(:all, :with_deleted => false)
1461
+ end
1462
+
1463
+ should "load non-deleted" do
1464
+ hemorrhoid = Hemorrhoid.create
1465
+ assert_not_equal [], Hemorrhoid.find(:all)
1466
+ assert_not_equal [], Hemorrhoid.find(:all, :with_deleted => false)
1467
+ end
1468
+
1469
+ should "load deleted if asked to" do
1470
+ assert_equal [@hemorrhoid.id], Hemorrhoid.find(:all, :with_deleted => true).map(&:id)
1471
+ end
1472
+ end
1473
+
1474
+ context "first" do
1475
+ setup do
1476
+ recreate_db
1477
+ @hemorrhoid = Hemorrhoid.create
1478
+ assert @hemorrhoid.destroy
1479
+ assert @hemorrhoid.reload.deleted?
1480
+ end
1481
+
1482
+ should "not load deleted" do
1483
+ assert_nil Hemorrhoid.find(:first)
1484
+ assert_nil Hemorrhoid.find(:first, :with_deleted => false)
1485
+ end
1486
+
1487
+ should "load non-deleted" do
1488
+ hemorrhoid = Hemorrhoid.create
1489
+ assert_not_nil Hemorrhoid.find(:first)
1490
+ assert_not_nil Hemorrhoid.find(:first, :with_deleted => false)
1491
+ end
1492
+
1493
+ should "load deleted if asked to" do
1494
+ assert_equal @hemorrhoid, Hemorrhoid.find(:first, :with_deleted => true)
1495
+ end
1496
+ end
1497
+
1498
+ context "find_by and find_all_by" do
1499
+ setup do
1500
+ recreate_db
1501
+ @hemorrhoid = Hemorrhoid.create(:nickname => 'Claas', :size => 3)
1502
+ @hemorrhoid.destroy
1503
+ end
1504
+
1505
+ context "find_by" do
1506
+ should "not load deleted" do
1507
+ assert_nil Hemorrhoid.find_by_nickname('Claas')
1508
+ assert_nil Hemorrhoid.find_by_nickname('Claas', :with_deleted => false)
1509
+
1510
+ assert_nil Hemorrhoid.find_by_nickname_and_size('Claas', 3)
1511
+ assert_nil Hemorrhoid.find_by_nickname_and_size('Claas', 3, :with_deleted => false)
1512
+ end
1513
+
1514
+ should "load non-deleted" do
1515
+ hemorrhoid = Hemorrhoid.create(:nickname => 'OtherNick', :size => 3)
1516
+ assert_equal hemorrhoid.id, Hemorrhoid.find_by_nickname('OtherNick', :with_deleted => true).id
1517
+ assert_equal hemorrhoid.id, Hemorrhoid.find_by_nickname('OtherNick').id
1518
+ end
1519
+
1520
+ should "load deleted if asked to" do
1521
+ assert_not_nil Hemorrhoid.find_by_nickname('Claas', :with_deleted => true)
1522
+ assert_equal @hemorrhoid.id, Hemorrhoid.find_by_nickname('Claas', :with_deleted => true).id
1523
+
1524
+ assert_not_nil Hemorrhoid.find_by_nickname_and_size('Claas', 3, :with_deleted => true)
1525
+ assert_equal @hemorrhoid.id, Hemorrhoid.find_by_nickname_and_size('Claas', 3, :with_deleted => true).id
1526
+ end
1527
+ end
1528
+
1529
+ context "find_all_by" do
1530
+ should "not load deleted" do
1531
+ assert_equal [], Hemorrhoid.find_all_by_nickname('Claas')
1532
+ assert_equal [], Hemorrhoid.find_all_by_nickname('Claas', :with_deleted => false)
1533
+
1534
+ assert_equal [], Hemorrhoid.find_all_by_nickname_and_size('Claas', 3)
1535
+ assert_equal [], Hemorrhoid.find_all_by_nickname_and_size('Claas', 3, :with_deleted => false)
1536
+ end
1537
+
1538
+ should "load non-deleted" do
1539
+ hemorrhoid = Hemorrhoid.create(:nickname => 'Lampe', :size => 4)
1540
+ assert_equal [hemorrhoid.id], Hemorrhoid.find_all_by_nickname('Lampe').map(&:id)
1541
+ end
1542
+
1543
+ should "load deleted if asked to" do
1544
+ assert_equal [@hemorrhoid.id], Hemorrhoid.find_all_by_nickname('Claas', :with_deleted => true).map(&:id)
1545
+ assert_equal [@hemorrhoid.id], Hemorrhoid.find_all_by_nickname_and_size('Claas', 3, :with_deleted => true).map(&:id)
1546
+ end
1547
+ end
1548
+
1549
+ should "reuse the same view - when find_all_by is called first" do
1550
+ assert_equal [], Hemorrhoid.find_all_by_nickname('Claas')
1551
+ assert_nil Hemorrhoid.find_by_nickname('Claas')
1552
+ end
1553
+
1554
+ should "reuse the same view - when find_by is called first" do
1555
+ assert_nil Hemorrhoid.find_by_nickname('Claas')
1556
+ assert_equal [], Hemorrhoid.find_all_by_nickname('Claas')
1557
+ end
1558
+ end
1559
+
1560
+ context "by relation" do
1561
+ setup do
1562
+ @hemorrhoid.destroy
1563
+ end
1564
+
1565
+ context "has_many" do
1566
+ should "not load deleted by default" do
1567
+ assert_equal [], @user.hemorrhoids
1568
+ end
1569
+
1570
+ should "load deleted if asked to" do
1571
+ assert_equal [@hemorrhoid.id], @user.hemorrhoids(:with_deleted => true).map(&:id)
1572
+ end
1573
+ end
1574
+
1575
+ context "has_many :through" do
1576
+ setup do
1577
+ @user = User.create(:name => 'BigT', :title => 'Dr.')
1578
+ @pain = Pain.create
1579
+
1580
+ @hemorrhoid = Hemorrhoid.new
1581
+ @hemorrhoid.user = @user
1582
+ @hemorrhoid.pain = @pain
1583
+ @hemorrhoid.save!
1584
+
1585
+ @hemorrhoid.destroy
1586
+ end
1587
+
1588
+ should "not load deleted by default" do
1589
+ assert_equal [], @user.pains
1590
+ end
1591
+
1592
+ should "load deleted if asked to" do
1593
+ assert_equal [@pain.id], @user.pains(:with_deleted => true).map(&:id)
1594
+ end
1595
+ end
1596
+
1597
+ context "has_one" do
1598
+ setup do
1599
+ @spot = Spot.create
1600
+
1601
+ @hemorrhoid = Hemorrhoid.new
1602
+ @hemorrhoid.spot = @spot
1603
+ @hemorrhoid.save!
1604
+
1605
+ @hemorrhoid.destroy
1606
+ end
1607
+
1608
+ should "not load deleted by default" do
1609
+ assert_nil @spot.hemorrhoid
1610
+ end
1611
+
1612
+ should "load deleted if asked to" do
1613
+ assert_equal @hemorrhoid.id, @spot.hemorrhoid(:with_deleted => true).id
1614
+ end
1615
+ end
1616
+
1617
+ context "belongs_to" do
1618
+ setup do
1619
+ @hemorrhoid = Hemorrhoid.new
1620
+ @hemorrhoid.save!
1621
+
1622
+ @sub = SubHemorrhoid.new
1623
+ @sub.hemorrhoid = @hemorrhoid
1624
+ @sub.save!
1625
+
1626
+ @hemorrhoid.destroy
1627
+ end
1628
+
1629
+ should "not load deleted by default" do
1630
+ @sub.reload
1631
+ assert_raise(SimplyStored::RecordNotFound) do
1632
+ assert_nil @sub.hemorrhoid
1633
+ end
1634
+ end
1635
+
1636
+ should "load deleted if asked to" do
1637
+ @sub.reload
1638
+ assert_equal @hemorrhoid.id, @sub.hemorrhoid(:with_deleted => true).id
1639
+ end
1640
+ end
1641
+
1642
+ end
1643
+
1644
+ end
1645
+
1646
+ context "when counting" do
1647
+ setup do
1648
+ recreate_db
1649
+ @hemorrhoid = Hemorrhoid.create(:nickname => 'Claas')
1650
+ assert @hemorrhoid.destroy
1651
+ assert @hemorrhoid.reload.deleted?
1652
+ end
1653
+
1654
+ should "not count deleted" do
1655
+ assert_equal 0, Hemorrhoid.count
1656
+ assert_equal 0, Hemorrhoid.count(:with_deleted => false)
1657
+ end
1658
+
1659
+ should "count non-deleted" do
1660
+ hemorrhoid = Hemorrhoid.create(:nickname => 'Claas')
1661
+ assert_equal 1, Hemorrhoid.count
1662
+ assert_equal 1, Hemorrhoid.count(:with_deleted => false)
1663
+ end
1664
+
1665
+ should "count deleted if asked to" do
1666
+ assert_equal 1, Hemorrhoid.count(:with_deleted => true)
1667
+ end
1668
+
1669
+ context "count_by" do
1670
+ should "not count deleted" do
1671
+ assert_equal 0, Hemorrhoid.count_by_nickname('Claas')
1672
+ assert_equal 0, Hemorrhoid.count_by_nickname('Claas', :with_deleted => false)
1673
+ end
1674
+
1675
+ should "count deleted if asked to" do
1676
+ assert_equal 1, Hemorrhoid.count_by_nickname('Claas', :with_deleted => true)
1677
+ end
1678
+ end
1679
+ end
1680
+
1681
+ end
1682
+
1683
+ end
1684
+ end