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.
- data/LICENSE +176 -0
- data/README.md +68 -0
- data/Rakefile +68 -0
- data/THANKS.md +19 -0
- data/examples/model/example.rb +144 -0
- data/history.txt +159 -0
- data/lib/couchrest/casted_array.rb +25 -0
- data/lib/couchrest/casted_model.rb +55 -0
- data/lib/couchrest/extended_document.rb +323 -0
- data/lib/couchrest/mixins/attribute_protection.rb +74 -0
- data/lib/couchrest/mixins/callbacks.rb +532 -0
- data/lib/couchrest/mixins/class_proxy.rb +120 -0
- data/lib/couchrest/mixins/collection.rb +260 -0
- data/lib/couchrest/mixins/design_doc.rb +127 -0
- data/lib/couchrest/mixins/document_queries.rb +82 -0
- data/lib/couchrest/mixins/extended_attachments.rb +73 -0
- data/lib/couchrest/mixins/properties.rb +162 -0
- data/lib/couchrest/mixins/validation.rb +245 -0
- data/lib/couchrest/mixins/views.rb +148 -0
- data/lib/couchrest/mixins.rb +11 -0
- data/lib/couchrest/property.rb +50 -0
- data/lib/couchrest/support/couchrest.rb +19 -0
- data/lib/couchrest/support/rails.rb +42 -0
- data/lib/couchrest/typecast.rb +175 -0
- data/lib/couchrest/validation/auto_validate.rb +156 -0
- data/lib/couchrest/validation/contextual_validators.rb +78 -0
- data/lib/couchrest/validation/validation_errors.rb +125 -0
- data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
- data/lib/couchrest/validation/validators/confirmation_validator.rb +107 -0
- data/lib/couchrest/validation/validators/format_validator.rb +122 -0
- data/lib/couchrest/validation/validators/formats/email.rb +66 -0
- data/lib/couchrest/validation/validators/formats/url.rb +43 -0
- data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
- data/lib/couchrest/validation/validators/length_validator.rb +139 -0
- data/lib/couchrest/validation/validators/method_validator.rb +89 -0
- data/lib/couchrest/validation/validators/numeric_validator.rb +109 -0
- data/lib/couchrest/validation/validators/required_field_validator.rb +114 -0
- data/lib/couchrest/validation.rb +245 -0
- data/lib/couchrest_extended_document.rb +21 -0
- data/spec/couchrest/attribute_protection_spec.rb +150 -0
- data/spec/couchrest/casted_extended_doc_spec.rb +79 -0
- data/spec/couchrest/casted_model_spec.rb +406 -0
- data/spec/couchrest/extended_doc_attachment_spec.rb +148 -0
- data/spec/couchrest/extended_doc_inherited_spec.rb +40 -0
- data/spec/couchrest/extended_doc_spec.rb +868 -0
- data/spec/couchrest/extended_doc_subclass_spec.rb +99 -0
- data/spec/couchrest/extended_doc_view_spec.rb +529 -0
- data/spec/couchrest/property_spec.rb +648 -0
- data/spec/fixtures/attachments/README +3 -0
- data/spec/fixtures/attachments/couchdb.png +0 -0
- data/spec/fixtures/attachments/test.html +11 -0
- data/spec/fixtures/more/article.rb +35 -0
- data/spec/fixtures/more/card.rb +22 -0
- data/spec/fixtures/more/cat.rb +22 -0
- data/spec/fixtures/more/course.rb +25 -0
- data/spec/fixtures/more/event.rb +8 -0
- data/spec/fixtures/more/invoice.rb +17 -0
- data/spec/fixtures/more/person.rb +9 -0
- data/spec/fixtures/more/question.rb +6 -0
- data/spec/fixtures/more/service.rb +12 -0
- data/spec/fixtures/more/user.rb +22 -0
- data/spec/fixtures/views/lib.js +3 -0
- data/spec/fixtures/views/test_view/lib.js +3 -0
- data/spec/fixtures/views/test_view/only-map.js +4 -0
- data/spec/fixtures/views/test_view/test-map.js +3 -0
- data/spec/fixtures/views/test_view/test-reduce.js +3 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +49 -0
- data/utils/remap.rb +27 -0
- data/utils/subset.rb +30 -0
- 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
|