brianmario-couchrest 0.23

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 (92) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +95 -0
  3. data/Rakefile +75 -0
  4. data/THANKS.md +18 -0
  5. data/examples/model/example.rb +144 -0
  6. data/examples/word_count/markov +38 -0
  7. data/examples/word_count/views/books/chunked-map.js +3 -0
  8. data/examples/word_count/views/books/united-map.js +1 -0
  9. data/examples/word_count/views/markov/chain-map.js +6 -0
  10. data/examples/word_count/views/markov/chain-reduce.js +7 -0
  11. data/examples/word_count/views/word_count/count-map.js +6 -0
  12. data/examples/word_count/views/word_count/count-reduce.js +3 -0
  13. data/examples/word_count/word_count.rb +46 -0
  14. data/examples/word_count/word_count_query.rb +40 -0
  15. data/examples/word_count/word_count_views.rb +26 -0
  16. data/lib/couchrest.rb +198 -0
  17. data/lib/couchrest/commands/generate.rb +71 -0
  18. data/lib/couchrest/commands/push.rb +103 -0
  19. data/lib/couchrest/core/database.rb +303 -0
  20. data/lib/couchrest/core/design.rb +79 -0
  21. data/lib/couchrest/core/document.rb +87 -0
  22. data/lib/couchrest/core/response.rb +16 -0
  23. data/lib/couchrest/core/server.rb +88 -0
  24. data/lib/couchrest/core/view.rb +4 -0
  25. data/lib/couchrest/helper/pager.rb +103 -0
  26. data/lib/couchrest/helper/streamer.rb +44 -0
  27. data/lib/couchrest/helper/upgrade.rb +51 -0
  28. data/lib/couchrest/mixins.rb +4 -0
  29. data/lib/couchrest/mixins/attachments.rb +31 -0
  30. data/lib/couchrest/mixins/callbacks.rb +483 -0
  31. data/lib/couchrest/mixins/class_proxy.rb +108 -0
  32. data/lib/couchrest/mixins/design_doc.rb +90 -0
  33. data/lib/couchrest/mixins/document_queries.rb +44 -0
  34. data/lib/couchrest/mixins/extended_attachments.rb +68 -0
  35. data/lib/couchrest/mixins/extended_document_mixins.rb +7 -0
  36. data/lib/couchrest/mixins/properties.rb +129 -0
  37. data/lib/couchrest/mixins/validation.rb +242 -0
  38. data/lib/couchrest/mixins/views.rb +169 -0
  39. data/lib/couchrest/monkeypatches.rb +113 -0
  40. data/lib/couchrest/more/casted_model.rb +28 -0
  41. data/lib/couchrest/more/extended_document.rb +215 -0
  42. data/lib/couchrest/more/property.rb +40 -0
  43. data/lib/couchrest/support/blank.rb +42 -0
  44. data/lib/couchrest/support/class.rb +176 -0
  45. data/lib/couchrest/validation/auto_validate.rb +163 -0
  46. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  47. data/lib/couchrest/validation/validation_errors.rb +118 -0
  48. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  49. data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
  50. data/lib/couchrest/validation/validators/format_validator.rb +117 -0
  51. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  52. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  53. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  54. data/lib/couchrest/validation/validators/length_validator.rb +134 -0
  55. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  56. data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
  57. data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
  58. data/spec/couchrest/core/couchrest_spec.rb +201 -0
  59. data/spec/couchrest/core/database_spec.rb +699 -0
  60. data/spec/couchrest/core/design_spec.rb +138 -0
  61. data/spec/couchrest/core/document_spec.rb +267 -0
  62. data/spec/couchrest/core/server_spec.rb +35 -0
  63. data/spec/couchrest/helpers/pager_spec.rb +122 -0
  64. data/spec/couchrest/helpers/streamer_spec.rb +23 -0
  65. data/spec/couchrest/more/casted_extended_doc_spec.rb +40 -0
  66. data/spec/couchrest/more/casted_model_spec.rb +98 -0
  67. data/spec/couchrest/more/extended_doc_attachment_spec.rb +130 -0
  68. data/spec/couchrest/more/extended_doc_spec.rb +509 -0
  69. data/spec/couchrest/more/extended_doc_subclass_spec.rb +98 -0
  70. data/spec/couchrest/more/extended_doc_view_spec.rb +355 -0
  71. data/spec/couchrest/more/property_spec.rb +136 -0
  72. data/spec/fixtures/attachments/README +3 -0
  73. data/spec/fixtures/attachments/couchdb.png +0 -0
  74. data/spec/fixtures/attachments/test.html +11 -0
  75. data/spec/fixtures/more/article.rb +34 -0
  76. data/spec/fixtures/more/card.rb +20 -0
  77. data/spec/fixtures/more/course.rb +14 -0
  78. data/spec/fixtures/more/event.rb +6 -0
  79. data/spec/fixtures/more/invoice.rb +17 -0
  80. data/spec/fixtures/more/person.rb +8 -0
  81. data/spec/fixtures/more/question.rb +6 -0
  82. data/spec/fixtures/more/service.rb +12 -0
  83. data/spec/fixtures/views/lib.js +3 -0
  84. data/spec/fixtures/views/test_view/lib.js +3 -0
  85. data/spec/fixtures/views/test_view/only-map.js +4 -0
  86. data/spec/fixtures/views/test_view/test-map.js +3 -0
  87. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  88. data/spec/spec.opts +6 -0
  89. data/spec/spec_helper.rb +26 -0
  90. data/utils/remap.rb +27 -0
  91. data/utils/subset.rb +30 -0
  92. metadata +200 -0
@@ -0,0 +1,98 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require File.join(FIXTURE_PATH, 'more', 'card')
3
+ require File.join(FIXTURE_PATH, 'more', 'course')
4
+
5
+ # add a default value
6
+ Card.property :bg_color, :default => '#ccc'
7
+
8
+ class BusinessCard < Card
9
+ property :extension_code
10
+ property :job_title
11
+ end
12
+
13
+ class DesignBusinessCard < BusinessCard
14
+ property :bg_color, :default => '#eee'
15
+ end
16
+
17
+ class OnlineCourse < Course
18
+ property :url
19
+ view_by :url
20
+ end
21
+
22
+ class Animal < CouchRest::ExtendedDocument
23
+ use_database TEST_SERVER.default_database
24
+ property :name
25
+ view_by :name
26
+ end
27
+
28
+ class Dog < Animal; end
29
+
30
+ describe "Subclassing an ExtendedDocument" do
31
+
32
+ before(:each) do
33
+ @card = BusinessCard.new
34
+ end
35
+
36
+ it "shouldn't messup the parent's properties" do
37
+ Card.properties.should_not == BusinessCard.properties
38
+ end
39
+
40
+ it "should share the same db default" do
41
+ @card.database.uri.should == Card.database.uri
42
+ end
43
+
44
+ it "should share the same autovalidation details" do
45
+ @card.auto_validation.should be_true
46
+ end
47
+
48
+ it "should have kept the validation details" do
49
+ @card.should_not be_valid
50
+ end
51
+
52
+ it "should have added the new validation details" do
53
+ validated_fields = @card.class.validators.contexts[:default].map{|v| v.field_name}
54
+ validated_fields.should include(:extension_code)
55
+ validated_fields.should include(:job_title)
56
+ end
57
+
58
+ it "should not add to the parent's validations" do
59
+ validated_fields = Card.validators.contexts[:default].map{|v| v.field_name}
60
+ validated_fields.should_not include(:extension_code)
61
+ validated_fields.should_not include(:job_title)
62
+ end
63
+
64
+ it "should inherit default property values" do
65
+ @card.bg_color.should == '#ccc'
66
+ end
67
+
68
+ it "should be able to overwrite a default property" do
69
+ DesignBusinessCard.new.bg_color.should == '#eee'
70
+ end
71
+
72
+ it "should have a design doc slug based on the subclass name" do
73
+ Course.refresh_design_doc
74
+ OnlineCourse.design_doc_slug.should =~ /^OnlineCourse/
75
+ end
76
+
77
+ it "should have its own design_doc_fresh" do
78
+ Animal.refresh_design_doc
79
+ Dog.design_doc_fresh.should_not == true
80
+ Dog.refresh_design_doc
81
+ Dog.design_doc_fresh.should == true
82
+ end
83
+
84
+ it "should not add views to the parent's design_doc" do
85
+ Course.design_doc['views'].keys.should_not include('by_url')
86
+ end
87
+
88
+ it "should not add the parent's views to its design doc" do
89
+ Course.refresh_design_doc
90
+ OnlineCourse.refresh_design_doc
91
+ OnlineCourse.design_doc['views'].keys.should_not include('by_title')
92
+ end
93
+
94
+ it "should have an all view with a guard clause for couchrest-type == subclass name in the map function" do
95
+ OnlineCourse.design_doc['views']['all']['map'].should =~ /if \(doc\['couchrest-type'\] == 'OnlineCourse'\)/
96
+ end
97
+ end
98
+
@@ -0,0 +1,355 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
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
+
29
+ it "should have a design doc" do
30
+ Article.design_doc["views"]["by_date"].should_not be_nil
31
+ end
32
+
33
+ it "should save the design doc" do
34
+ Article.by_date #rescue nil
35
+ doc = Article.database.get Article.design_doc.id
36
+ doc['views']['by_date'].should_not be_nil
37
+ end
38
+
39
+ it "should return the matching raw view result" do
40
+ view = Article.by_date :raw => true
41
+ view['rows'].length.should == 4
42
+ end
43
+
44
+ it "should not include non-Articles" do
45
+ Article.database.save_doc({"date" => 1})
46
+ view = Article.by_date :raw => true
47
+ view['rows'].length.should == 4
48
+ end
49
+
50
+ it "should return the matching objects (with default argument :descending => true)" do
51
+ articles = Article.by_date
52
+ articles.collect{|a|a.title}.should == @titles.reverse
53
+ end
54
+
55
+ it "should allow you to override default args" do
56
+ articles = Article.by_date :descending => false
57
+ articles.collect{|a|a.title}.should == @titles
58
+ end
59
+ end
60
+
61
+ describe "another model with a simple view" do
62
+ before(:all) do
63
+ reset_test_db!
64
+ %w{aaa bbb ddd eee}.each do |title|
65
+ Course.new(:title => title).save
66
+ end
67
+ end
68
+ it "should make the design doc upon first query" do
69
+ Course.by_title
70
+ doc = Course.design_doc
71
+ doc['views']['all']['map'].should include('Course')
72
+ end
73
+ it "should can query via view" do
74
+ # register methods with method-missing, for local dispatch. method
75
+ # missing lookup table, no heuristics.
76
+ view = Course.view :by_title
77
+ designed = Course.by_title
78
+ view.should == designed
79
+ end
80
+ it "should get them" do
81
+ rs = Course.by_title
82
+ rs.length.should == 4
83
+ end
84
+ it "should yield" do
85
+ courses = []
86
+ Course.view(:by_title) do |course|
87
+ courses << course
88
+ end
89
+ courses[0]["doc"]["title"].should =='aaa'
90
+ end
91
+ it "should yield with by_key method" do
92
+ courses = []
93
+ Course.by_title do |course|
94
+ courses << course
95
+ end
96
+ courses[0]["doc"]["title"].should =='aaa'
97
+ end
98
+ end
99
+
100
+
101
+ describe "a ducktype view" do
102
+ before(:all) do
103
+ @id = TEST_SERVER.default_database.save_doc({:dept => true})['id']
104
+ end
105
+ it "should setup" do
106
+ duck = Course.get(@id) # from a different db
107
+ duck["dept"].should == true
108
+ end
109
+ it "should make the design doc" do
110
+ @as = Course.by_dept
111
+ @doc = Course.design_doc
112
+ @doc["views"]["by_dept"]["map"].should_not include("couchrest")
113
+ end
114
+ it "should not look for class" do |variable|
115
+ @as = Course.by_dept
116
+ @as[0]['_id'].should == @id
117
+ end
118
+ end
119
+
120
+ describe "a model class not tied to a database" do
121
+ before(:all) do
122
+ reset_test_db!
123
+ @db = TEST_SERVER.default_database
124
+ %w{aaa bbb ddd eee}.each do |title|
125
+ u = Unattached.new(:title => title)
126
+ u.database = @db
127
+ u.save
128
+ @first_id ||= u.id
129
+ end
130
+ end
131
+ it "should barf on all if no database given" do
132
+ lambda{Unattached.all}.should raise_error
133
+ end
134
+ it "should query all" do
135
+ rs = Unattached.all :database=>@db
136
+ rs.length.should == 4
137
+ end
138
+ it "should barf on query if no database given" do
139
+ lambda{Unattached.view :by_title}.should raise_error
140
+ end
141
+ it "should make the design doc upon first query" do
142
+ Unattached.by_title :database=>@db
143
+ doc = Unattached.design_doc
144
+ doc['views']['all']['map'].should include('Unattached')
145
+ end
146
+ it "should merge query params" do
147
+ rs = Unattached.by_title :database=>@db, :startkey=>"bbb", :endkey=>"eee"
148
+ rs.length.should == 3
149
+ end
150
+ it "should query via view" do
151
+ view = Unattached.view :by_title, :database=>@db
152
+ designed = Unattached.by_title :database=>@db
153
+ view.should == designed
154
+ end
155
+ it "should yield" do
156
+ things = []
157
+ Unattached.view(:by_title, :database=>@db) do |thing|
158
+ things << thing
159
+ end
160
+ things[0]["doc"]["title"].should =='aaa'
161
+ end
162
+ it "should yield with by_key method" do
163
+ things = []
164
+ Unattached.by_title(:database=>@db) do |thing|
165
+ things << thing
166
+ end
167
+ things[0]["doc"]["title"].should =='aaa'
168
+ end
169
+ it "should barf on get if no database given" do
170
+ lambda{Unattached.get("aaa")}.should raise_error
171
+ end
172
+ it "should get from specific database" do
173
+ u = Unattached.get(@first_id, @db)
174
+ u.title.should == "aaa"
175
+ end
176
+ it "should barf on first if no database given" do
177
+ lambda{Unattached.first}.should raise_error
178
+ end
179
+ it "should get first" do
180
+ u = Unattached.first :database=>@db
181
+ u.title.should =~ /\A...\z/
182
+ end
183
+ it "should barf on all_design_doc_versions if no database given" do
184
+ lambda{Unattached.all_design_doc_versions}.should raise_error
185
+ end
186
+ it "should clean up design docs left around on specific database" do
187
+ Unattached.by_title :database=>@db
188
+ Unattached.all_design_doc_versions(@db)["rows"].length.should == 1
189
+ Unattached.view_by :questions
190
+ Unattached.by_questions :database=>@db
191
+ Unattached.all_design_doc_versions(@db)["rows"].length.should == 2
192
+ Unattached.cleanup_design_docs!(@db)
193
+ Unattached.all_design_doc_versions(@db)["rows"].length.should == 1
194
+ end
195
+ end
196
+
197
+ describe "class proxy" do
198
+ before(:all) do
199
+ reset_test_db!
200
+ @us = Unattached.on(TEST_SERVER.default_database)
201
+ %w{aaa bbb ddd eee}.each do |title|
202
+ u = @us.new(:title => title)
203
+ u.save
204
+ @first_id ||= u.id
205
+ end
206
+ end
207
+ it "should query all" do
208
+ rs = @us.all
209
+ rs.length.should == 4
210
+ end
211
+ it "should make the design doc upon first query" do
212
+ @us.by_title
213
+ doc = @us.design_doc
214
+ doc['views']['all']['map'].should include('Unattached')
215
+ end
216
+ it "should merge query params" do
217
+ rs = @us.by_title :startkey=>"bbb", :endkey=>"eee"
218
+ rs.length.should == 3
219
+ end
220
+ it "should query via view" do
221
+ view = @us.view :by_title
222
+ designed = @us.by_title
223
+ view.should == designed
224
+ end
225
+ it "should yield" do
226
+ things = []
227
+ @us.view(:by_title) do |thing|
228
+ things << thing
229
+ end
230
+ things[0]["doc"]["title"].should =='aaa'
231
+ end
232
+ it "should yield with by_key method" do
233
+ things = []
234
+ @us.by_title do |thing|
235
+ things << thing
236
+ end
237
+ things[0]["doc"]["title"].should =='aaa'
238
+ end
239
+ it "should get from specific database" do
240
+ u = @us.get(@first_id)
241
+ u.title.should == "aaa"
242
+ end
243
+ it "should get first" do
244
+ u = @us.first
245
+ u.title.should =~ /\A...\z/
246
+ end
247
+ it "should clean up design docs left around on specific database" do
248
+ @us.by_title
249
+ @us.all_design_doc_versions["rows"].length.should == 1
250
+ Unattached.view_by :professor
251
+ @us.by_professor
252
+ @us.all_design_doc_versions["rows"].length.should == 2
253
+ @us.cleanup_design_docs!
254
+ @us.all_design_doc_versions["rows"].length.should == 1
255
+ end
256
+ end
257
+
258
+ describe "a model with a compound key view" do
259
+ before(:all) do
260
+ Article.design_doc_fresh = false
261
+ Article.by_user_id_and_date.each{|a| a.destroy(true)}
262
+ Article.database.bulk_delete
263
+ written_at = Time.now - 24 * 3600 * 7
264
+ @titles = ["uniq one", "even more interesting", "less fun", "not junk"]
265
+ @user_ids = ["quentin", "aaron"]
266
+ @titles.each_with_index do |title,i|
267
+ u = i % 2
268
+ a = Article.new(:title => title, :user_id => @user_ids[u])
269
+ a.date = written_at
270
+ a.save
271
+ written_at += 24 * 3600
272
+ end
273
+ end
274
+ it "should create the design doc" do
275
+ Article.by_user_id_and_date rescue nil
276
+ doc = Article.design_doc
277
+ doc['views']['by_date'].should_not be_nil
278
+ end
279
+ it "should sort correctly" do
280
+ articles = Article.by_user_id_and_date
281
+ articles.collect{|a|a['user_id']}.should == ['aaron', 'aaron', 'quentin',
282
+ 'quentin']
283
+ articles[1].title.should == 'not junk'
284
+ end
285
+ it "should be queryable with couchrest options" do
286
+ articles = Article.by_user_id_and_date :limit => 1, :startkey => 'quentin'
287
+ articles.length.should == 1
288
+ articles[0].title.should == "even more interesting"
289
+ end
290
+ end
291
+
292
+ describe "with a custom view" do
293
+ before(:all) do
294
+ @titles = ["very uniq one", "even less interesting", "some fun",
295
+ "really junk", "crazy bob"]
296
+ @tags = ["cool", "lame"]
297
+ @titles.each_with_index do |title,i|
298
+ u = i % 2
299
+ a = Article.new(:title => title, :tags => [@tags[u]])
300
+ a.save
301
+ end
302
+ end
303
+ it "should be available raw" do
304
+ view = Article.by_tags :raw => true
305
+ view['rows'].length.should == 5
306
+ end
307
+
308
+ it "should be default to :reduce => false" do
309
+ ars = Article.by_tags
310
+ ars.first.tags.first.should == 'cool'
311
+ end
312
+
313
+ it "should be raw when reduce is true" do
314
+ view = Article.by_tags :reduce => true, :group => true
315
+ view['rows'].find{|r|r['key'] == 'cool'}['value'].should == 3
316
+ end
317
+ end
318
+
319
+ # TODO: moved to Design, delete
320
+ describe "adding a view" do
321
+ before(:each) do
322
+ reset_test_db!
323
+ Article.by_date
324
+ @design_docs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
325
+ end
326
+ it "should not create a design doc on view definition" do
327
+ Article.view_by :created_at
328
+ newdocs = Article.database.documents :startkey => "_design/", :endkey => "_design/\u9999"
329
+ newdocs["rows"].length.should == @design_docs["rows"].length
330
+ end
331
+ it "should create a new version of the design document on view access" do
332
+ ddocs = Article.all_design_doc_versions["rows"].length
333
+ Article.view_by :updated_at
334
+ Article.by_updated_at
335
+ Article.all_design_doc_versions["rows"].length.should == ddocs + 1
336
+ Article.design_doc["views"].keys.should include("by_updated_at")
337
+ end
338
+ end
339
+
340
+ describe "with a lot of designs left around" do
341
+ before(:each) do
342
+ reset_test_db!
343
+ Article.by_date
344
+ Article.view_by :field
345
+ Article.by_field
346
+ end
347
+ it "should clean them up" do
348
+ Article.view_by :stream
349
+ Article.by_stream
350
+ Article.all_design_doc_versions["rows"].length.should > 1
351
+ Article.cleanup_design_docs!
352
+ Article.all_design_doc_versions["rows"].length.should == 1
353
+ end
354
+ end
355
+ end