couchrest_extended_document 1.0.0.beta5

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 (71) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +68 -0
  3. data/Rakefile +68 -0
  4. data/THANKS.md +19 -0
  5. data/examples/model/example.rb +144 -0
  6. data/history.txt +159 -0
  7. data/lib/couchrest/casted_array.rb +25 -0
  8. data/lib/couchrest/casted_model.rb +55 -0
  9. data/lib/couchrest/extended_document.rb +323 -0
  10. data/lib/couchrest/mixins/attribute_protection.rb +74 -0
  11. data/lib/couchrest/mixins/callbacks.rb +532 -0
  12. data/lib/couchrest/mixins/class_proxy.rb +120 -0
  13. data/lib/couchrest/mixins/collection.rb +260 -0
  14. data/lib/couchrest/mixins/design_doc.rb +127 -0
  15. data/lib/couchrest/mixins/document_queries.rb +82 -0
  16. data/lib/couchrest/mixins/extended_attachments.rb +73 -0
  17. data/lib/couchrest/mixins/properties.rb +162 -0
  18. data/lib/couchrest/mixins/validation.rb +245 -0
  19. data/lib/couchrest/mixins/views.rb +148 -0
  20. data/lib/couchrest/mixins.rb +11 -0
  21. data/lib/couchrest/property.rb +50 -0
  22. data/lib/couchrest/support/couchrest.rb +19 -0
  23. data/lib/couchrest/support/rails.rb +42 -0
  24. data/lib/couchrest/typecast.rb +175 -0
  25. data/lib/couchrest/validation/auto_validate.rb +156 -0
  26. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  27. data/lib/couchrest/validation/validation_errors.rb +125 -0
  28. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  29. data/lib/couchrest/validation/validators/confirmation_validator.rb +107 -0
  30. data/lib/couchrest/validation/validators/format_validator.rb +122 -0
  31. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  32. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  33. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  34. data/lib/couchrest/validation/validators/length_validator.rb +139 -0
  35. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  36. data/lib/couchrest/validation/validators/numeric_validator.rb +109 -0
  37. data/lib/couchrest/validation/validators/required_field_validator.rb +114 -0
  38. data/lib/couchrest/validation.rb +245 -0
  39. data/lib/couchrest_extended_document.rb +21 -0
  40. data/spec/couchrest/attribute_protection_spec.rb +150 -0
  41. data/spec/couchrest/casted_extended_doc_spec.rb +79 -0
  42. data/spec/couchrest/casted_model_spec.rb +406 -0
  43. data/spec/couchrest/extended_doc_attachment_spec.rb +148 -0
  44. data/spec/couchrest/extended_doc_inherited_spec.rb +40 -0
  45. data/spec/couchrest/extended_doc_spec.rb +868 -0
  46. data/spec/couchrest/extended_doc_subclass_spec.rb +99 -0
  47. data/spec/couchrest/extended_doc_view_spec.rb +529 -0
  48. data/spec/couchrest/property_spec.rb +648 -0
  49. data/spec/fixtures/attachments/README +3 -0
  50. data/spec/fixtures/attachments/couchdb.png +0 -0
  51. data/spec/fixtures/attachments/test.html +11 -0
  52. data/spec/fixtures/more/article.rb +35 -0
  53. data/spec/fixtures/more/card.rb +22 -0
  54. data/spec/fixtures/more/cat.rb +22 -0
  55. data/spec/fixtures/more/course.rb +25 -0
  56. data/spec/fixtures/more/event.rb +8 -0
  57. data/spec/fixtures/more/invoice.rb +17 -0
  58. data/spec/fixtures/more/person.rb +9 -0
  59. data/spec/fixtures/more/question.rb +6 -0
  60. data/spec/fixtures/more/service.rb +12 -0
  61. data/spec/fixtures/more/user.rb +22 -0
  62. data/spec/fixtures/views/lib.js +3 -0
  63. data/spec/fixtures/views/test_view/lib.js +3 -0
  64. data/spec/fixtures/views/test_view/only-map.js +4 -0
  65. data/spec/fixtures/views/test_view/test-map.js +3 -0
  66. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  67. data/spec/spec.opts +5 -0
  68. data/spec/spec_helper.rb +49 -0
  69. data/utils/remap.rb +27 -0
  70. data/utils/subset.rb +30 -0
  71. metadata +200 -0
@@ -0,0 +1,99 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+ require File.join(FIXTURE_PATH, 'more', 'person')
3
+ require File.join(FIXTURE_PATH, 'more', 'card')
4
+ require File.join(FIXTURE_PATH, 'more', 'course')
5
+
6
+ # add a default value
7
+ Card.property :bg_color, :default => '#ccc'
8
+
9
+ class BusinessCard < Card
10
+ property :extension_code
11
+ property :job_title
12
+ end
13
+
14
+ class DesignBusinessCard < BusinessCard
15
+ property :bg_color, :default => '#eee'
16
+ end
17
+
18
+ class OnlineCourse < Course
19
+ property :url
20
+ view_by :url
21
+ end
22
+
23
+ class Animal < CouchRest::ExtendedDocument
24
+ use_database TEST_SERVER.default_database
25
+ property :name
26
+ view_by :name
27
+ end
28
+
29
+ class Dog < Animal; end
30
+
31
+ describe "Subclassing an ExtendedDocument" do
32
+
33
+ before(:each) do
34
+ @card = BusinessCard.new
35
+ end
36
+
37
+ it "shouldn't messup the parent's properties" do
38
+ Card.properties.should_not == BusinessCard.properties
39
+ end
40
+
41
+ it "should share the same db default" do
42
+ @card.database.uri.should == Card.database.uri
43
+ end
44
+
45
+ it "should share the same autovalidation details" do
46
+ @card.auto_validation.should be_true
47
+ end
48
+
49
+ it "should have kept the validation details" do
50
+ @card.should_not be_valid
51
+ end
52
+
53
+ it "should have added the new validation details" do
54
+ validated_fields = @card.class.validators.contexts[:default].map{|v| v.field_name}
55
+ validated_fields.should include(:extension_code)
56
+ validated_fields.should include(:job_title)
57
+ end
58
+
59
+ it "should not add to the parent's validations" do
60
+ validated_fields = Card.validators.contexts[:default].map{|v| v.field_name}
61
+ validated_fields.should_not include(:extension_code)
62
+ validated_fields.should_not include(:job_title)
63
+ end
64
+
65
+ it "should inherit default property values" do
66
+ @card.bg_color.should == '#ccc'
67
+ end
68
+
69
+ it "should be able to overwrite a default property" do
70
+ DesignBusinessCard.new.bg_color.should == '#eee'
71
+ end
72
+
73
+ it "should have a design doc slug based on the subclass name" do
74
+ Course.refresh_design_doc
75
+ OnlineCourse.design_doc_slug.should =~ /^OnlineCourse/
76
+ end
77
+
78
+ it "should have its own design_doc_fresh" do
79
+ Animal.refresh_design_doc
80
+ Dog.send(:design_doc_fresh, Dog.database).should_not == true
81
+ Dog.refresh_design_doc
82
+ Dog.send(:design_doc_fresh, Dog.database).should == true
83
+ end
84
+
85
+ it "should not add views to the parent's design_doc" do
86
+ Course.design_doc['views'].keys.should_not include('by_url')
87
+ end
88
+
89
+ it "should not add the parent's views to its design doc" do
90
+ Course.refresh_design_doc
91
+ OnlineCourse.refresh_design_doc
92
+ OnlineCourse.design_doc['views'].keys.should_not include('by_title')
93
+ end
94
+
95
+ it "should have an all view with a guard clause for couchrest-type == subclass name in the map function" do
96
+ OnlineCourse.design_doc['views']['all']['map'].should =~ /if \(doc\['couchrest-type'\] == 'OnlineCourse'\)/
97
+ end
98
+ end
99
+
@@ -0,0 +1,529 @@
1
+ require File.expand_path("../../spec_helper", __FILE__)
2
+ require File.join(FIXTURE_PATH, 'more', 'article')
3
+ require File.join(FIXTURE_PATH, 'more', 'course')
4
+
5
+ describe "ExtendedDocument views" do
6
+
7
+ class Unattached < CouchRest::ExtendedDocument
8
+ # Note: no use_database here
9
+ property :title
10
+ property :questions
11
+ property :professor
12
+ view_by :title
13
+ end
14
+
15
+ describe "a model with simple views and a default param" do
16
+ before(:all) do
17
+ Article.all.map{|a| a.destroy(true)}
18
+ Article.database.bulk_delete
19
+ written_at = Time.now - 24 * 3600 * 7
20
+ @titles = ["this and that", "also interesting", "more fun", "some junk"]
21
+ @titles.each do |title|
22
+ a = Article.new(:title => title)
23
+ a.date = written_at
24
+ a.save
25
+ written_at += 24 * 3600
26
+ end
27
+ end
28
+ it "should have a design doc" do
29
+ Article.design_doc["views"]["by_date"].should_not be_nil
30
+ end
31
+ it "should save the design doc" do
32
+ Article.by_date #rescue nil
33
+ doc = Article.database.get Article.design_doc.id
34
+ doc['views']['by_date'].should_not be_nil
35
+ end
36
+ it "should save design doc if a view changed" do
37
+ Article.by_date
38
+ orig = Article.stored_design_doc
39
+ orig['views']['by_date']['map'] = "function() { }"
40
+ Article.database.save_doc(orig)
41
+ rev = Article.stored_design_doc['_rev']
42
+ Article.req_design_doc_refresh # prepare for re-load
43
+ Article.by_date
44
+ orig = Article.stored_design_doc
45
+ orig['views']['by_date']['map'].should eql(Article.design_doc['views']['by_date']['map'])
46
+ orig['_rev'].should_not eql(rev)
47
+ end
48
+ it "should not save design doc if not changed" do
49
+ Article.by_date
50
+ orig = Article.stored_design_doc['_rev']
51
+ Article.req_design_doc_refresh
52
+ Article.by_date
53
+ Article.stored_design_doc['_rev'].should eql(orig)
54
+ end
55
+
56
+
57
+ it "should return the matching raw view result" do
58
+ view = Article.by_date :raw => true
59
+ view['rows'].length.should == 4
60
+ end
61
+ it "should not include non-Articles" do
62
+ Article.database.save_doc({"date" => 1})
63
+ view = Article.by_date :raw => true
64
+ view['rows'].length.should == 4
65
+ end
66
+ it "should return the matching objects (with default argument :descending => true)" do
67
+ articles = Article.by_date
68
+ articles.collect{|a|a.title}.should == @titles.reverse
69
+ end
70
+ it "should allow you to override default args" do
71
+ articles = Article.by_date :descending => false
72
+ articles.collect{|a|a.title}.should == @titles
73
+ end
74
+ it "should allow you to create a new view on the fly" do
75
+ lambda{Article.by_title}.should raise_error
76
+ Article.view_by :title
77
+ lambda{Article.by_title}.should_not raise_error
78
+ end
79
+
80
+ end
81
+
82
+ describe "another model with a simple view" do
83
+ before(:all) do
84
+ reset_test_db!
85
+ %w{aaa bbb ddd eee}.each do |title|
86
+ Course.new(:title => title).save
87
+ end
88
+ end
89
+ it "should make the design doc upon first query" do
90
+ Course.by_title
91
+ doc = Course.design_doc
92
+ doc['views']['all']['map'].should include('Course')
93
+ end
94
+ it "should can query via view" do
95
+ # register methods with method-missing, for local dispatch. method
96
+ # missing lookup table, no heuristics.
97
+ view = Course.view :by_title
98
+ designed = Course.by_title
99
+ view.should == designed
100
+ end
101
+ it "should get them" do
102
+ rs = Course.by_title
103
+ rs.length.should == 4
104
+ end
105
+ it "should yield" do
106
+ courses = []
107
+ Course.view(:by_title) do |course|
108
+ courses << course
109
+ end
110
+ courses[0]["doc"]["title"].should =='aaa'
111
+ end
112
+ it "should yield with by_key method" do
113
+ courses = []
114
+ Course.by_title do |course|
115
+ courses << course
116
+ end
117
+ courses[0]["doc"]["title"].should =='aaa'
118
+ end
119
+ end
120
+
121
+ describe "find a single item using a view" do
122
+ before(:all) do
123
+ reset_test_db!
124
+ %w{aaa bbb ddd eee}.each do |title|
125
+ Course.new(:title => title, :active => (title == 'bbb')).save
126
+ end
127
+ end
128
+
129
+ it "should return single matched record" do
130
+ course = Course.find_by_title('bbb')
131
+ course.should_not be_nil
132
+ course.title.should eql('bbb') # Ensure really is a Course!
133
+ end
134
+
135
+ it "should return nil if not found" do
136
+ course = Course.find_by_title('fff')
137
+ course.should be_nil
138
+ end
139
+
140
+ it "should peform search on view with two properties" do
141
+ course = Course.find_by_title_and_active(['bbb', true])
142
+ course.should_not be_nil
143
+ course.title.should eql('bbb') # Ensure really is a Course!
144
+ end
145
+
146
+ it "should return nil if not found" do
147
+ course = Course.find_by_title_and_active(['bbb', false])
148
+ course.should be_nil
149
+ end
150
+
151
+ it "should raise exception if view not present" do
152
+ lambda { Course.find_by_foobar('123') }.should raise_error(NoMethodError)
153
+ end
154
+ end
155
+
156
+ describe "a ducktype view" do
157
+ before(:all) do
158
+ reset_test_db!
159
+ @id = DB.save_doc({:dept => true})['id']
160
+ end
161
+ it "should setup" do
162
+ duck = Course.get(@id) # from a different db
163
+ duck["dept"].should == true
164
+ end
165
+ it "should make the design doc" do
166
+ @as = Course.by_dept
167
+ @doc = Course.design_doc
168
+ @doc["views"]["by_dept"]["map"].should_not include("couchrest")
169
+ end
170
+ it "should not look for class" do
171
+ @as = Course.by_dept
172
+ @as[0]['_id'].should == @id
173
+ end
174
+ end
175
+
176
+ describe "a model class not tied to a database" do
177
+ before(:all) do
178
+ reset_test_db!
179
+ @db = DB
180
+ %w{aaa bbb ddd eee}.each do |title|
181
+ u = Unattached.new(:title => title)
182
+ u.database = @db
183
+ u.save
184
+ @first_id ||= u.id
185
+ end
186
+ end
187
+ it "should barf on all if no database given" do
188
+ lambda{Unattached.all}.should raise_error
189
+ end
190
+ it "should query all" do
191
+ # Unattached.cleanup_design_docs!(@db)
192
+ rs = Unattached.all :database => @db
193
+ rs.length.should == 4
194
+ end
195
+ it "should barf on query if no database given" do
196
+ lambda{Unattached.view :by_title}.should raise_error
197
+ end
198
+ it "should make the design doc upon first query" do
199
+ Unattached.by_title :database => @db
200
+ doc = Unattached.design_doc
201
+ doc['views']['all']['map'].should include('Unattached')
202
+ end
203
+ it "should merge query params" do
204
+ rs = Unattached.by_title :database=>@db, :startkey=>"bbb", :endkey=>"eee"
205
+ rs.length.should == 3
206
+ end
207
+ it "should query via view" do
208
+ view = Unattached.view :by_title, :database=>@db
209
+ designed = Unattached.by_title :database=>@db
210
+ view.should == designed
211
+ end
212
+ it "should yield" do
213
+ things = []
214
+ Unattached.view(:by_title, :database=>@db) do |thing|
215
+ things << thing
216
+ end
217
+ things[0]["doc"]["title"].should =='aaa'
218
+ end
219
+ it "should yield with by_key method" do
220
+ things = []
221
+ Unattached.by_title(:database=>@db) do |thing|
222
+ things << thing
223
+ end
224
+ things[0]["doc"]["title"].should =='aaa'
225
+ end
226
+ it "should return nil on get if no database given" do
227
+ Unattached.get("aaa").should be_nil
228
+ end
229
+ it "should barf on get! if no database given" do
230
+ lambda{Unattached.get!("aaa")}.should raise_error
231
+ end
232
+ it "should get from specific database" do
233
+ u = Unattached.get(@first_id, @db)
234
+ u.title.should == "aaa"
235
+ end
236
+ it "should barf on first if no database given" do
237
+ lambda{Unattached.first}.should raise_error
238
+ end
239
+ it "should get first" do
240
+ u = Unattached.first :database=>@db
241
+ u.title.should =~ /\A...\z/
242
+ end
243
+ it "should barf on all_design_doc_versions if no database given" do
244
+ lambda{Unattached.all_design_doc_versions}.should raise_error
245
+ end
246
+ it "should be able to cleanup the db/bump the revision number" do
247
+ # if the previous specs were not run, the model_design_doc will be blank
248
+ Unattached.use_database DB
249
+ Unattached.view_by :questions
250
+ Unattached.by_questions(:database => @db)
251
+ original_revision = Unattached.model_design_doc(@db)['_rev']
252
+ Unattached.save_design_doc!(@db)
253
+ Unattached.model_design_doc(@db)['_rev'].should_not == original_revision
254
+ end
255
+ end
256
+
257
+ describe "class proxy" do
258
+ before(:all) do
259
+ reset_test_db!
260
+ # setup the class default doc to save the design doc
261
+ Unattached.use_database nil # just to be sure it is really unattached
262
+ @us = Unattached.on(DB)
263
+ %w{aaa bbb ddd eee}.each do |title|
264
+ u = @us.new(:title => title)
265
+ u.save
266
+ @first_id ||= u.id
267
+ end
268
+ end
269
+ it "should query all" do
270
+ rs = @us.all
271
+ rs.length.should == 4
272
+ end
273
+ it "should count" do
274
+ @us.count.should == 4
275
+ end
276
+ it "should make the design doc upon first query" do
277
+ @us.by_title
278
+ doc = @us.design_doc
279
+ doc['views']['all']['map'].should include('Unattached')
280
+ end
281
+ it "should merge query params" do
282
+ rs = @us.by_title :startkey=>"bbb", :endkey=>"eee"
283
+ rs.length.should == 3
284
+ end
285
+ it "should query via view" do
286
+ view = @us.view :by_title
287
+ designed = @us.by_title
288
+ view.should == designed
289
+ end
290
+ it "should yield" do
291
+ things = []
292
+ @us.view(:by_title) do |thing|
293
+ things << thing
294
+ end
295
+ things[0]["doc"]["title"].should =='aaa'
296
+ end
297
+ it "should yield with by_key method" do
298
+ things = []
299
+ @us.by_title do |thing|
300
+ things << thing
301
+ end
302
+ things[0]["doc"]["title"].should =='aaa'
303
+ end
304
+ it "should get from specific database" do
305
+ u = @us.get(@first_id)
306
+ u.title.should == "aaa"
307
+ end
308
+ it "should get first" do
309
+ u = @us.first
310
+ u.title.should =~ /\A...\z/
311
+ end
312
+ it "should set database on first retreived document" do
313
+ u = @us.first
314
+ u.database.should === DB
315
+ end
316
+ it "should set database on all retreived documents" do
317
+ @us.all.each do |u|
318
+ u.database.should === DB
319
+ end
320
+ end
321
+ it "should set database on each retreived document" do
322
+ rs = @us.by_title :startkey=>"bbb", :endkey=>"eee"
323
+ rs.length.should == 3
324
+ rs.each do |u|
325
+ u.database.should === DB
326
+ end
327
+ end
328
+ it "should set database on document retreived by id" do
329
+ u = @us.get(@first_id)
330
+ u.database.should === DB
331
+ end
332
+ it "should not attempt to set database on raw results using :all" do
333
+ @us.all(:raw => true).each do |u|
334
+ u.respond_to?(:database).should be_false
335
+ end
336
+ end
337
+ it "should not attempt to set database on raw results using view" do
338
+ @us.by_title(:raw => true).each do |u|
339
+ u.respond_to?(:database).should be_false
340
+ end
341
+ end
342
+ # Sam Lown 2010-04-07
343
+ # Removed as unclear why this should happen as before my changes
344
+ # this happend by accident, not explicitly.
345
+ # If requested, this feature should be added as a specific method.
346
+ #
347
+ #it "should clean up design docs left around on specific database" do
348
+ # @us.by_title
349
+ # original_id = @us.model_design_doc['_rev']
350
+ # Unattached.view_by :professor
351
+ # @us.by_professor
352
+ # @us.model_design_doc['_rev'].should_not == original_id
353
+ #end
354
+ end
355
+
356
+ describe "a model with a compound key view" do
357
+ before(:all) do
358
+ Article.by_user_id_and_date.each{|a| a.destroy(true)}
359
+ Article.database.bulk_delete
360
+ written_at = Time.now - 24 * 3600 * 7
361
+ @titles = ["uniq one", "even more interesting", "less fun", "not junk"]
362
+ @user_ids = ["quentin", "aaron"]
363
+ @titles.each_with_index do |title,i|
364
+ u = i % 2
365
+ a = Article.new(:title => title, :user_id => @user_ids[u])
366
+ a.date = written_at
367
+ a.save
368
+ written_at += 24 * 3600
369
+ end
370
+ end
371
+ it "should create the design doc" do
372
+ Article.by_user_id_and_date rescue nil
373
+ doc = Article.design_doc
374
+ doc['views']['by_date'].should_not be_nil
375
+ end
376
+ it "should sort correctly" do
377
+ articles = Article.by_user_id_and_date
378
+ articles.collect{|a|a['user_id']}.should == ['aaron', 'aaron', 'quentin',
379
+ 'quentin']
380
+ articles[1].title.should == 'not junk'
381
+ end
382
+ it "should be queryable with couchrest options" do
383
+ articles = Article.by_user_id_and_date :limit => 1, :startkey => 'quentin'
384
+ articles.length.should == 1
385
+ articles[0].title.should == "even more interesting"
386
+ end
387
+ end
388
+
389
+ describe "with a custom view" do
390
+ before(:all) do
391
+ @titles = ["very uniq one", "even less interesting", "some fun",
392
+ "really junk", "crazy bob"]
393
+ @tags = ["cool", "lame"]
394
+ @titles.each_with_index do |title,i|
395
+ u = i % 2
396
+ a = Article.new(:title => title, :tags => [@tags[u]])
397
+ a.save
398
+ end
399
+ end
400
+ it "should be available raw" do
401
+ view = Article.by_tags :raw => true
402
+ view['rows'].length.should == 5
403
+ end
404
+
405
+ it "should be default to :reduce => false" do
406
+ ars = Article.by_tags
407
+ ars.first.tags.first.should == 'cool'
408
+ end
409
+
410
+ it "should be raw when reduce is true" do
411
+ view = Article.by_tags :reduce => true, :group => true
412
+ view['rows'].find{|r|r['key'] == 'cool'}['value'].should == 3
413
+ end
414
+ end
415
+
416
+ # TODO: moved to Design, delete
417
+ describe "adding a view" do
418
+ before(:each) do
419
+ reset_test_db!
420
+ Article.by_date
421
+ @original_doc_rev = Article.model_design_doc['_rev']
422
+ @design_docs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
423
+ end
424
+ it "should not create a design doc on view definition" do
425
+ Article.view_by :created_at
426
+ newdocs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
427
+ newdocs["rows"].length.should == @design_docs["rows"].length
428
+ end
429
+ it "should create a new version of the design document on view access" do
430
+ ddocs = Article.all_design_doc_versions["rows"].length
431
+ Article.view_by :updated_at
432
+ Article.by_updated_at
433
+ @original_doc_rev.should_not == Article.model_design_doc['_rev']
434
+ Article.design_doc["views"].keys.should include("by_updated_at")
435
+ end
436
+ end
437
+
438
+ describe "with a collection" do
439
+ before(:all) do
440
+ reset_test_db!
441
+ titles = ["very uniq one", "really interesting", "some fun",
442
+ "really awesome", "crazy bob", "this rocks", "super rad"]
443
+ titles.each_with_index do |title,i|
444
+ a = Article.new(:title => title, :date => Date.today)
445
+ a.save
446
+ end
447
+
448
+ titles = ["yesterday very uniq one", "yesterday really interesting", "yesterday some fun",
449
+ "yesterday really awesome", "yesterday crazy bob", "yesterday this rocks"]
450
+ titles.each_with_index do |title,i|
451
+ a = Article.new(:title => title, :date => Date.today - 1)
452
+ a.save
453
+ end
454
+ end
455
+ require 'date'
456
+ it "should return a proxy that looks like an array of 7 Article objects" do
457
+ articles = Article.by_date :key => Date.today
458
+ articles.class.should == Array
459
+ articles.size.should == 7
460
+ end
461
+ it "should get a subset of articles using paginate" do
462
+ articles = Article.by_date :key => Date.today
463
+ articles.paginate(:page => 1, :per_page => 3).size.should == 3
464
+ articles.paginate(:page => 2, :per_page => 3).size.should == 3
465
+ articles.paginate(:page => 3, :per_page => 3).size.should == 1
466
+ end
467
+ it "should get all articles, a few at a time, using paginated each" do
468
+ articles = Article.by_date :key => Date.today
469
+ articles.paginated_each(:per_page => 3) do |a|
470
+ a.should_not be_nil
471
+ end
472
+ end
473
+ it "should provide a class method to access the collection directly" do
474
+ articles = Article.collection_proxy_for('Article', 'by_date', :descending => true,
475
+ :key => Date.today, :include_docs => true)
476
+ articles.class.should == Array
477
+ articles.size.should == 7
478
+ end
479
+ it "should provide a class method for paginate" do
480
+ articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
481
+ :per_page => 3, :descending => true, :key => Date.today, :include_docs => true)
482
+ articles.size.should == 3
483
+
484
+ articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
485
+ :per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true)
486
+ articles.size.should == 3
487
+
488
+ articles = Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
489
+ :per_page => 3, :page => 3, :descending => true, :key => Date.today, :include_docs => true)
490
+ articles.size.should == 1
491
+ end
492
+ it "should provide a class method for paginated_each" do
493
+ options = { :design_doc => 'Article', :view_name => 'by_date',
494
+ :per_page => 3, :page => 1, :descending => true, :key => Date.today,
495
+ :include_docs => true }
496
+ Article.paginated_each(options) do |a|
497
+ a.should_not be_nil
498
+ end
499
+ end
500
+ it "should provide a class method to get a collection for a view" do
501
+ articles = Article.find_all_article_details(:key => Date.today)
502
+ articles.class.should == Array
503
+ articles.size.should == 7
504
+ end
505
+ it "should raise an exception if design_doc is not provided" do
506
+ lambda{Article.collection_proxy_for(nil, 'by_date')}.should raise_error
507
+ lambda{Article.paginate(:view_name => 'by_date')}.should raise_error
508
+ end
509
+ it "should raise an exception if view_name is not provided" do
510
+ lambda{Article.collection_proxy_for('Article', nil)}.should raise_error
511
+ lambda{Article.paginate(:design_doc => 'Article')}.should raise_error
512
+ end
513
+ it "should be able to span multiple keys" do
514
+ articles = Article.by_date :startkey => Date.today, :endkey => Date.today - 1
515
+ articles.paginate(:page => 1, :per_page => 3).size.should == 3
516
+ articles.paginate(:page => 2, :per_page => 3).size.should == 3
517
+ articles.paginate(:page => 3, :per_page => 3).size.should == 3
518
+ articles.paginate(:page => 4, :per_page => 3).size.should == 3
519
+ articles.paginate(:page => 5, :per_page => 3).size.should == 1
520
+ end
521
+ it "should pass database parameter to pager" do
522
+ proxy = mock(:proxy)
523
+ proxy.stub!(:paginate)
524
+ ::CouchRest::Mixins::Collection::CollectionProxy.should_receive(:new).with('database', anything(), anything(), anything(), anything()).and_return(proxy)
525
+ Article.paginate(:design_doc => 'Article', :view_name => 'by_date', :database => 'database')
526
+ end
527
+ end
528
+
529
+ end