couchrest_model 1.1.2 → 1.2.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/README.md +8 -2
  2. data/VERSION +1 -1
  3. data/couchrest_model.gemspec +2 -1
  4. data/history.md +8 -0
  5. data/lib/couchrest/model/base.rb +0 -20
  6. data/lib/couchrest/model/configuration.rb +2 -0
  7. data/lib/couchrest/model/core_extensions/time_parsing.rb +35 -9
  8. data/lib/couchrest/model/designs/design.rb +182 -0
  9. data/lib/couchrest/model/designs/view.rb +91 -48
  10. data/lib/couchrest/model/designs.rb +72 -19
  11. data/lib/couchrest/model/document_queries.rb +15 -45
  12. data/lib/couchrest/model/properties.rb +43 -2
  13. data/lib/couchrest/model/proxyable.rb +20 -54
  14. data/lib/couchrest/model/typecast.rb +1 -1
  15. data/lib/couchrest/model/validations/uniqueness.rb +7 -6
  16. data/lib/couchrest_model.rb +1 -5
  17. data/spec/fixtures/models/article.rb +22 -20
  18. data/spec/fixtures/models/base.rb +15 -7
  19. data/spec/fixtures/models/course.rb +7 -4
  20. data/spec/fixtures/models/project.rb +4 -1
  21. data/spec/fixtures/models/sale_entry.rb +5 -3
  22. data/spec/unit/base_spec.rb +51 -5
  23. data/spec/unit/core_extensions/time_parsing.rb +41 -0
  24. data/spec/unit/designs/design_spec.rb +291 -0
  25. data/spec/unit/designs/view_spec.rb +135 -40
  26. data/spec/unit/designs_spec.rb +341 -30
  27. data/spec/unit/dirty_spec.rb +67 -0
  28. data/spec/unit/inherited_spec.rb +2 -2
  29. data/spec/unit/property_protection_spec.rb +3 -1
  30. data/spec/unit/property_spec.rb +43 -3
  31. data/spec/unit/proxyable_spec.rb +57 -98
  32. data/spec/unit/subclass_spec.rb +14 -5
  33. data/spec/unit/validations_spec.rb +14 -12
  34. metadata +172 -129
  35. data/lib/couchrest/model/class_proxy.rb +0 -135
  36. data/lib/couchrest/model/collection.rb +0 -273
  37. data/lib/couchrest/model/design_doc.rb +0 -115
  38. data/lib/couchrest/model/support/couchrest_design.rb +0 -33
  39. data/lib/couchrest/model/views.rb +0 -148
  40. data/spec/unit/class_proxy_spec.rb +0 -167
  41. data/spec/unit/collection_spec.rb +0 -86
  42. data/spec/unit/design_doc_spec.rb +0 -212
  43. data/spec/unit/view_spec.rb +0 -352
@@ -81,6 +81,53 @@ describe "Model Base" do
81
81
  @doc.name.should eql("foobar")
82
82
  end
83
83
  end
84
+ describe "multipart attributes" do
85
+ context "with valid params" do
86
+ it "should parse a legal date" do
87
+ valid_date_params = { "exec_date(1i)"=>"2011",
88
+ "exec_date(2i)"=>"10",
89
+ "exec_date(3i)"=>"18"}
90
+ @obj = WithDateAndTime.new valid_date_params
91
+ @obj.exec_date.should_not be_nil
92
+ @obj.exec_date.should be_kind_of(Date)
93
+ @obj.exec_date.should == Date.new(2011, 10 ,18)
94
+ end
95
+
96
+ it "should parse a legal time" do
97
+ valid_time_params = { "exec_time(1i)"=>"2011",
98
+ "exec_time(2i)"=>"10",
99
+ "exec_time(3i)"=>"18",
100
+ "exec_time(4i)"=>"15",
101
+ "exec_time(5i)"=>"15",
102
+ "exec_time(6i)"=>"15",}
103
+ @obj = WithDateAndTime.new valid_time_params
104
+ @obj.exec_time.should_not be_nil
105
+ @obj.exec_time.should be_kind_of(Time)
106
+ @obj.exec_time.should == Time.utc(2011, 10 ,18, 15, 15, 15)
107
+ end
108
+ end
109
+
110
+ context "with invalid params" do
111
+ before(:each) do
112
+ @invalid_date_params = { "exec_date(1i)"=>"2011",
113
+ "exec_date(2i)"=>"foo",
114
+ "exec_date(3i)"=>"18"}
115
+ end
116
+ it "should still create a model if there are invalid attributes" do
117
+ @obj = WithDateAndTime.new @invalid_date_params
118
+ @obj.should_not be_nil
119
+ @obj.should be_kind_of(WithDateAndTime)
120
+ end
121
+ it "should not crash because of an empty value" do
122
+ @invalid_date_params["exec_date(2i)"] = ""
123
+ @obj = WithDateAndTime.new @invalid_date_params
124
+ @obj.should_not be_nil
125
+ @obj.exec_date.should_not be_kind_of(Date)
126
+ @obj.should be_kind_of(WithDateAndTime)
127
+ end
128
+ end
129
+ end
130
+
84
131
 
85
132
  describe "ActiveModel compatability Basic" do
86
133
 
@@ -355,25 +402,24 @@ describe "Model Base" do
355
402
 
356
403
  describe "counting all instances of a model" do
357
404
  before(:each) do
358
- @db = reset_test_db!
405
+ reset_test_db!
359
406
  end
360
-
407
+
361
408
  it ".count should return 0 if there are no docuemtns" do
362
409
  WithTemplateAndUniqueID.count.should == 0
363
410
  end
364
-
411
+
365
412
  it ".count should return the number of documents" do
366
413
  WithTemplateAndUniqueID.new('slug' => '1').save
367
414
  WithTemplateAndUniqueID.new('slug' => '2').save
368
415
  WithTemplateAndUniqueID.new('slug' => '3').save
369
-
370
416
  WithTemplateAndUniqueID.count.should == 3
371
417
  end
372
418
  end
373
419
 
374
420
  describe "finding the first instance of a model" do
375
421
  before(:each) do
376
- @db = reset_test_db!
422
+ reset_test_db!
377
423
  WithTemplateAndUniqueID.new('slug' => '1').save
378
424
  WithTemplateAndUniqueID.new('slug' => '2').save
379
425
  WithTemplateAndUniqueID.new('slug' => '3').save
@@ -9,6 +9,47 @@ describe "Time Parsing core extension" do
9
9
  Time.respond_to?("parse_iso8601").should be_true
10
10
  end
11
11
 
12
+ describe "#as_json" do
13
+
14
+ it "should convert local time to JSON string" do
15
+ time = Time.new(2011, 04, 01, 19, 05, 30, "+02:00")
16
+ time.as_json.should eql("2011-04-01T19:05:30.000+02:00")
17
+ end
18
+
19
+ it "should convert utc time to JSON string" do
20
+ time = Time.utc(2011, 04, 01, 19, 05, 30)
21
+ time.as_json.should eql("2011-04-01T19:05:30.000Z")
22
+ end
23
+
24
+ it "should convert local time with fraction to JSON" do
25
+ time = Time.new(2011, 04, 01, 19, 05, 30.123, "+02:00")
26
+ time.as_json.should eql("2011-04-01T19:05:30.123+02:00")
27
+ end
28
+
29
+ it "should convert utc time with fraction to JSON" do
30
+ time = Time.utc(2011, 04, 01, 19, 05, 30.123)
31
+ time.as_json.should eql("2011-04-01T19:05:30.123Z")
32
+ end
33
+
34
+ it "should allow fraction digits" do
35
+ time = Time.utc(2011, 04, 01, 19, 05, 30.123456)
36
+ time.as_json(:fraction_digits => 6).should eql("2011-04-01T19:05:30.123456Z")
37
+ end
38
+
39
+ it "should use CouchRest::Model::Base.time_fraction_digits config option" do
40
+ CouchRest::Model::Base.time_fraction_digits = 6
41
+ time = Time.utc(2011, 04, 01, 19, 05, 30.123456)
42
+ time.as_json.should eql("2011-04-01T19:05:30.123456Z")
43
+ CouchRest::Model::Base.time_fraction_digits = 3 # Back to normal
44
+ end
45
+
46
+ it "should cope with a nil options parameter" do
47
+ time = Time.utc(2011, 04, 01, 19, 05, 30.123456)
48
+ lambda { time.as_json(nil) }.should_not raise_error
49
+ end
50
+
51
+ end
52
+
12
53
  describe ".parse_iso8601" do
13
54
 
14
55
  describe "parsing" do
@@ -0,0 +1,291 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe CouchRest::Model::Designs::Design do
6
+
7
+ before :all do
8
+ reset_test_db!
9
+ end
10
+
11
+ class DesignSampleModel < CouchRest::Model::Base
12
+ use_database DB
13
+ property :name
14
+ property :surname
15
+ design do
16
+ view :by_name
17
+ end
18
+ design :stats do
19
+ view :by_surname
20
+ end
21
+ end
22
+
23
+ describe "class methods" do
24
+
25
+ before :all do
26
+ @klass = CouchRest::Model::Designs::Design
27
+ end
28
+
29
+ describe ".method_name" do
30
+ it "should return standard method name" do
31
+ @klass.method_name.should eql('design_doc')
32
+ end
33
+
34
+ it "should add prefix to standard method name" do
35
+ @klass.method_name('stats').should eql('stats_design_doc')
36
+ end
37
+ end
38
+
39
+ end
40
+
41
+ describe "base methods" do
42
+
43
+ before :each do
44
+ @model = mock("ModelExample")
45
+ @model.stub(:to_s).and_return("ModelExample")
46
+ @obj = CouchRest::Model::Designs::Design.new(@model)
47
+ end
48
+
49
+
50
+ describe "initialisation without prefix" do
51
+ it "should associate model and set method name" do
52
+ @obj.model.should eql(@model)
53
+ @obj.method_name.should eql("design_doc")
54
+ end
55
+
56
+ it "should generate correct id" do
57
+ @obj['_id'].should eql("_design/ModelExample")
58
+ end
59
+
60
+ it "should apply defaults" do
61
+ @obj['language'].should eql('javascript')
62
+ end
63
+ end
64
+
65
+ describe "initialisation with prefix" do
66
+
67
+ it "should associate model and set method name" do
68
+ @obj = CouchRest::Model::Designs::Design.new(@model, 'stats')
69
+ @obj.model.should eql(@model)
70
+ @obj.method_name.should eql("stats_design_doc")
71
+ end
72
+
73
+ it "should generate correct id with prefix" do
74
+ @obj = CouchRest::Model::Designs::Design.new(@model, 'stats')
75
+ @obj['_id'].should eql("_design/ModelExample_stats")
76
+ end
77
+
78
+ end
79
+
80
+
81
+
82
+ describe "#sync and #sync!" do
83
+
84
+ it "should skip if auto update disabled" do
85
+ @obj.auto_update = false
86
+ @obj.should_not_receive(:sync!)
87
+ @obj.sync
88
+ end
89
+
90
+ describe "with real model" do
91
+
92
+ before :all do
93
+ reset_test_db!
94
+ @mod = DesignSampleModel
95
+ @doc = @mod.design_doc
96
+ @db = @mod.database
97
+ end
98
+
99
+ it "should not have been saved up until sync called" do
100
+ lambda { @mod.database.get(@doc['_id']) }.should raise_error(RestClient::ResourceNotFound)
101
+ end
102
+
103
+
104
+ it "should save a design that is in cache and has changed" do
105
+ @doc.sync # put in cache
106
+ @doc['views']['all']['map'] += '// comment'
107
+ # This would fail if changes were not detected!
108
+ @doc.sync
109
+ doc = @db.get(@doc['_id'])
110
+ doc['views']['all']['map'].should eql(@doc['views']['all']['map'])
111
+ end
112
+
113
+ it "should not save a design that is not in cache and has not changed" do
114
+ @doc.sync # put doc in cache
115
+ @doc.send(:set_cache_checksum, @doc.database, nil)
116
+
117
+ @db.should_not_receive(:save_doc)
118
+ @doc.should_receive(:set_cache_checksum)
119
+ @doc.sync
120
+ end
121
+
122
+ it "should not reload a design that is in cache and has not changed" do
123
+ @doc.sync
124
+ @doc.should_not_receive(:load_from_database)
125
+ @doc.sync
126
+ end
127
+
128
+ it "should be re-created if database destroyed" do
129
+ @doc.sync # saved
130
+ reset_test_db!
131
+ @db.should_receive(:save_doc).with(@doc)
132
+ @doc.sync
133
+ end
134
+
135
+ it "should not update the local design definition" do
136
+ @doc.sync!
137
+ doc = @db.get(@doc['_id'])
138
+ doc['views']['test'] = {'map' => "function(d) { if (d) { emit(d._id, null); } }"}
139
+ @db.save_doc(doc)
140
+ @doc.send(:set_cache_checksum, @doc.database, nil)
141
+ @doc.sync
142
+ @doc['views'].should_not have_key('test')
143
+ @doc['_rev'].should be_nil
144
+ end
145
+
146
+ it "should save a non existant design" do
147
+ begin
148
+ doc = @db.get(@doc['_id'])
149
+ rescue
150
+ doc = nil
151
+ end
152
+ @db.delete_doc(doc) if doc
153
+ @doc.sync!
154
+ doc = @db.get(@doc['_id'])
155
+ doc.should_not be_nil
156
+ doc['views']['all'].should eql(@doc['views']['all'])
157
+ end
158
+
159
+ end
160
+ end
161
+
162
+
163
+ describe "checksum" do
164
+
165
+ before :all do
166
+ @mod = DesignSampleModel
167
+ @doc = @mod.design_doc
168
+ end
169
+
170
+ it "should return fresh checksum when not calculated earlier" do
171
+ @doc.checksum.should_not be_blank
172
+ end
173
+
174
+ it "should provide same checksum without refresh on re-request" do
175
+ chk = @doc.checksum
176
+ @doc.should_not_receive(:chaecksum!)
177
+ @doc.checksum.should eql(chk)
178
+ end
179
+
180
+ it "should provide new checksum if the design has changed" do
181
+ chk = @doc.checksum
182
+ @doc['views']['all']['map'] += '// comment'
183
+ @doc.checksum.should_not eql(chk)
184
+ end
185
+
186
+ end
187
+
188
+ describe "database" do
189
+ it "should provide model's database" do
190
+ @mod = DesignSampleModel
191
+ @doc = @mod.design_doc
192
+ @mod.should_receive(:database)
193
+ @doc.database
194
+ end
195
+ end
196
+
197
+
198
+ describe "#uri" do
199
+ it "should provide complete url" do
200
+ @doc = DesignSampleModel.design_doc
201
+ @doc.uri.should eql("#{DesignSampleModel.database.root}/_design/DesignSampleModel")
202
+ end
203
+ end
204
+
205
+ describe "#view" do
206
+ it "should instantiate a new view and pass options" do
207
+ CouchRest::Model::Designs::View.should_receive(:new).with(@obj, @model, {}, 'by_test')
208
+ @obj.view('by_test', {})
209
+ end
210
+ end
211
+
212
+ describe "#view_names" do
213
+ it "should provide a list of all the views available" do
214
+ @doc = DesignSampleModel.design_doc
215
+ @doc.view_names.should eql(['by_name', 'all'])
216
+ end
217
+ end
218
+
219
+ describe "#has_view?" do
220
+ before :each do
221
+ @doc = DesignSampleModel.design_doc
222
+ end
223
+
224
+ it "should tell us if a view exists" do
225
+ @doc.has_view?('by_name').should be_true
226
+ end
227
+
228
+ it "should tell us if a view exists as symbol" do
229
+ @doc.has_view?(:by_name).should be_true
230
+ end
231
+
232
+ it "should tell us if a view does not exist" do
233
+ @doc.has_view?(:by_foobar).should be_false
234
+ end
235
+ end
236
+
237
+ describe "#create_view" do
238
+ before :each do
239
+ @doc = DesignSampleModel.design_doc
240
+ @doc['views'] = @doc['views'].clone
241
+ end
242
+
243
+ it "should forward view creation to View model" do
244
+ CouchRest::Model::Designs::View.should_receive(:define_and_create).with(@doc, 'by_other_name', {})
245
+ @doc.create_view('by_other_name')
246
+ end
247
+
248
+ it "should forward view creation to View model with opts" do
249
+ CouchRest::Model::Designs::View.should_receive(:define_and_create).with(@doc, 'by_other_name', {:by => 'name'})
250
+ @doc.create_view('by_other_name', :by => 'name')
251
+ end
252
+ end
253
+
254
+
255
+ describe "#create_filter" do
256
+ before :each do
257
+ @doc = DesignSampleModel.design_doc
258
+ end
259
+
260
+ it "should add simple filter" do
261
+ @doc.create_filter('test', 'foobar')
262
+ @doc['filters']['test'].should eql('foobar')
263
+ @doc['filters'] = nil # cleanup
264
+ end
265
+ end
266
+
267
+ end
268
+
269
+
270
+ describe "Checksum calculations" do
271
+
272
+ it "should calculate a consistent checksum for model" do
273
+ #WithTemplateAndUniqueID.design_doc.checksum.should eql('caa2b4c27abb82b4e37421de76d96ffc')
274
+ WithTemplateAndUniqueID.design_doc.checksum.should eql('f0973aaa72e4db0efeb2a281ea297cec')
275
+ end
276
+
277
+ it "should calculate checksum for complex model" do
278
+ #Article.design_doc.checksum.should eql('70dff8caea143bf40fad09adf0701104')
279
+ Article.design_doc.checksum.should eql('7ef39bffdf5837e8b078411ac417d860')
280
+ end
281
+
282
+ it "should cache the generated checksum value" do
283
+ Article.design_doc.checksum
284
+ Article.design_doc['couchrest-hash'].should_not be_blank
285
+ Article.first
286
+ end
287
+
288
+ end
289
+
290
+
291
+ end
@@ -16,6 +16,7 @@ describe "Design View" do
16
16
  describe "(unit tests)" do
17
17
 
18
18
  before :each do
19
+ @mod = DesignViewModel
19
20
  @klass = CouchRest::Model::Designs::View
20
21
  end
21
22
 
@@ -30,14 +31,15 @@ describe "Design View" do
30
31
  describe "with CouchRest Model" do
31
32
 
32
33
  it "should setup attributes" do
33
- @obj = @klass.new(DesignViewModel, {}, 'test_view')
34
- @obj.model.should eql(DesignViewModel)
34
+ @obj = @klass.new(@mod.design_doc, @mod, {}, 'test_view')
35
+ @obj.design_doc.should eql(@mod.design_doc)
36
+ @obj.model.should eql(@mod)
35
37
  @obj.name.should eql('test_view')
36
38
  @obj.query.should be_empty
37
39
  end
38
40
 
39
41
  it "should complain if there is no name" do
40
- lambda { @klass.new(DesignViewModel, {}, nil) }.should raise_error
42
+ lambda { @klass.new(@mod.design_doc, @mod, {}, nil) }.should raise_error(/Name must be provided/)
41
43
  end
42
44
 
43
45
  end
@@ -45,56 +47,142 @@ describe "Design View" do
45
47
  describe "with previous view instance" do
46
48
 
47
49
  before :each do
48
- first = @klass.new(DesignViewModel, {}, 'test_view')
49
- @obj = @klass.new(first, {:foo => :bar})
50
+ first = @klass.new(@mod.design_doc, @mod, {}, 'test_view')
51
+ @obj = @klass.new(@mod.design_doc, first, {:foo => :bar})
50
52
  end
51
53
 
52
54
  it "should copy attributes" do
53
- @obj.model.should eql(DesignViewModel)
55
+ @obj.model.should eql(@mod)
54
56
  @obj.name.should eql('test_view')
55
57
  @obj.query.should eql({:foo => :bar})
56
58
  end
57
59
 
58
60
  end
59
61
 
60
- end
62
+ describe "with proxy in query for first initialization" do
63
+ it "should set model to proxy object and remove from query" do
64
+ proxy = mock("Proxy")
65
+ @obj = @klass.new(@mod.design_doc, @mod, {:proxy => proxy}, 'test_view')
66
+ @obj.model.should eql(proxy)
67
+ end
68
+ end
69
+
70
+ describe "with proxy in query for chained instance" do
71
+ it "should set the model to proxy object instead of parents model" do
72
+ proxy = mock("Proxy")
73
+ @obj = @klass.new(@mod.design_doc, @mod, {}, 'test_view')
74
+ @obj.model.should eql(@mod)
75
+ @obj = @obj.proxy(proxy)
76
+ @obj.model.should eql(proxy)
77
+ end
78
+ end
61
79
 
62
- describe ".create" do
80
+ end
63
81
 
82
+ describe ".define_and_create" do
64
83
  before :each do
65
- @design_doc = {}
66
- DesignViewModel.stub!(:design_doc).and_return(@design_doc)
84
+ @design_doc = { }
67
85
  end
68
86
 
69
- it "should add a basic view" do
70
- @klass.create(DesignViewModel, 'test_view', :map => 'foo')
71
- @design_doc['views']['test_view'].should_not be_nil
87
+ it "should call define and create_model_methods method" do
88
+ @klass.should_receive(:define).with(@design_doc, 'test', {}).and_return(nil)
89
+ @klass.should_receive(:create_model_methods).with(@design_doc, 'test', {}).and_return(nil)
90
+ @klass.define_and_create(@design_doc, 'test')
72
91
  end
73
92
 
74
- it "should auto generate mapping from name" do
75
- lambda { @klass.create(DesignViewModel, 'by_title') }.should_not raise_error
76
- str = @design_doc['views']['by_title']['map']
77
- str.should include("((doc['#{DesignViewModel.model_type_key}'] == 'DesignViewModel') && (doc['title'] != null))")
78
- str.should include("emit(doc['title'], 1);")
79
- str = @design_doc['views']['by_title']['reduce']
80
- str.should include("return sum(values);")
93
+ it "should call define and create_model_methods method with opts" do
94
+ @klass.should_receive(:define).with(@design_doc, 'test', {:foo => :bar}).and_return(nil)
95
+ @klass.should_receive(:create_model_methods).with(@design_doc, 'test', {:foo => :bar}).and_return(nil)
96
+ @klass.define_and_create(@design_doc, 'test', {:foo => :bar})
81
97
  end
82
98
 
83
- it "should auto generate mapping from name with and" do
84
- @klass.create(DesignViewModel, 'by_title_and_name')
85
- str = @design_doc['views']['by_title_and_name']['map']
86
- str.should include("(doc['title'] != null) && (doc['name'] != null)")
87
- str.should include("emit([doc['title'], doc['name']], 1);")
88
- str = @design_doc['views']['by_title_and_name']['reduce']
89
- str.should include("return sum(values);")
99
+ end
100
+
101
+ describe ".define" do
102
+
103
+ describe "with no auto update" do
104
+ before :each do
105
+ @design_doc = { }
106
+ @design_doc.stub!(:model).and_return(DesignViewModel)
107
+ @design_doc.stub!(:auto_update).and_return(false)
108
+ end
109
+
110
+ it "should set map view to true" do
111
+ @klass.define(@design_doc, 'test_view')
112
+ @design_doc['views']['test_view']['map'].should eql(true)
113
+ @design_doc['views']['test_view']['reduce'].should be_false
114
+ end
115
+
116
+ it "should set reduce to true if set" do
117
+ @klass.define(@design_doc, 'test_view', :reduce => true)
118
+ @design_doc['views']['test_view']['map'].should eql(true)
119
+ @design_doc['views']['test_view']['reduce'].should eql(true)
120
+ end
90
121
  end
91
122
 
123
+ describe "with auto update" do
124
+
125
+ before :each do
126
+ @design_doc = { }
127
+ @design_doc.stub!(:model).and_return(DesignViewModel)
128
+ @design_doc.stub!(:auto_update).and_return(true)
129
+ end
130
+
131
+ it "should add a basic view" do
132
+ @klass.define(@design_doc, 'test_view', :map => 'foo')
133
+ @design_doc['views']['test_view'].should_not be_nil
134
+ end
135
+
136
+ it "should auto generate mapping from name" do
137
+ lambda { @klass.define(@design_doc, 'by_title') }.should_not raise_error
138
+ str = @design_doc['views']['by_title']['map']
139
+ str.should include("((doc['#{DesignViewModel.model_type_key}'] == 'DesignViewModel') && (doc['title'] != null))")
140
+ str.should include("emit(doc['title'], 1);")
141
+ str = @design_doc['views']['by_title']['reduce']
142
+ str.should include("return sum(values);")
143
+ end
144
+
145
+ it "should auto generate mapping from name with and" do
146
+ @klass.define(@design_doc, 'by_title_and_name')
147
+ str = @design_doc['views']['by_title_and_name']['map']
148
+ str.should include("(doc['title'] != null) && (doc['name'] != null)")
149
+ str.should include("emit([doc['title'], doc['name']], 1);")
150
+ str = @design_doc['views']['by_title_and_name']['reduce']
151
+ str.should include("return sum(values);")
152
+ end
153
+ end
154
+
155
+ describe ".create_model_methods" do
156
+ before :each do
157
+ @model = DesignViewModel
158
+ @design_doc = { }
159
+ @design_doc.stub!(:model).and_return(@model)
160
+ @design_doc.stub!(:method_name).and_return("design_doc")
161
+ @model.stub!('design_doc').and_return(@design_doc)
162
+ end
163
+ it "should create standard view method" do
164
+ @klass.create_model_methods(@design_doc, 'by_name')
165
+ @model.should respond_to('by_name')
166
+ @design_doc.should_receive('view').with('by_name', {})
167
+ @model.by_name
168
+ end
169
+ it "should create find_ view method" do
170
+ @klass.create_model_methods(@design_doc, 'by_name')
171
+ @model.should respond_to('find_by_name')
172
+ view = mock("View")
173
+ view.should_receive('key').with('fred').and_return(view)
174
+ view.should_receive('first').and_return(nil)
175
+ @design_doc.should_receive('view').and_return(view)
176
+ @model.find_by_name('fred')
177
+ end
178
+ end
92
179
  end
93
180
 
181
+
94
182
  describe "instance methods" do
95
183
 
96
184
  before :each do
97
- @obj = @klass.new(DesignViewModel, {}, 'test_view')
185
+ @obj = @klass.new(@mod.design_doc, @mod, {}, 'test_view')
98
186
  end
99
187
 
100
188
  describe "#rows" do
@@ -468,6 +556,20 @@ describe "Design View" do
468
556
  end
469
557
  end
470
558
 
559
+ describe "#stale" do
560
+ it "should update query with ok" do
561
+ @obj.should_receive(:update_query).with(:stale => 'ok')
562
+ @obj.stale('ok')
563
+ end
564
+ it "should update query with update_after" do
565
+ @obj.should_receive(:update_query).with(:stale => 'update_after')
566
+ @obj.stale('update_after')
567
+ end
568
+ it "should fail if anything else is provided" do
569
+ lambda { @obj.stale('yes') }.should raise_error(/can only be set with/)
570
+ end
571
+ end
572
+
471
573
  describe "#include_docs" do
472
574
  it "should call include_docs! on new view" do
473
575
  @obj.should_receive(:update_query).and_return(@obj)
@@ -532,13 +634,6 @@ describe "Design View" do
532
634
  end
533
635
  end
534
636
 
535
- describe "#design_doc" do
536
- it "should call design_doc on model" do
537
- @obj.model.should_receive(:design_doc)
538
- @obj.send(:design_doc)
539
- end
540
- end
541
-
542
637
  describe "#can_reduce?" do
543
638
  it "should check and prove true" do
544
639
  @obj.should_receive(:name).and_return('test_view')
@@ -557,8 +652,8 @@ describe "Design View" do
557
652
  # disable real execution!
558
653
  @design_doc = mock("DesignDoc")
559
654
  @design_doc.stub!(:view_on)
560
- @obj.model.stub!(:save_design_doc)
561
- @obj.model.stub!(:design_doc).and_return(@design_doc)
655
+ @design_doc.stub!(:sync)
656
+ @obj.stub!(:design_doc).and_return(@design_doc)
562
657
  end
563
658
 
564
659
  it "should return previous result if set" do
@@ -582,7 +677,7 @@ describe "Design View" do
582
677
 
583
678
  it "should call to save the design document" do
584
679
  @obj.should_receive(:can_reduce?).and_return(false)
585
- @obj.model.should_receive(:save_design_doc).with(DB)
680
+ @design_doc.should_receive(:sync).with(DB)
586
681
  @obj.send(:execute)
587
682
  end
588
683
 
@@ -595,9 +690,9 @@ describe "Design View" do
595
690
 
596
691
  it "should remove nil values from query" do
597
692
  @obj.should_receive(:can_reduce?).and_return(true)
598
- @obj.stub!(:use_database).and_return('database')
693
+ @obj.stub!(:use_database).and_return(@mod.database)
599
694
  @obj.query = {:reduce => true, :limit => nil, :skip => nil}
600
- @design_doc.should_receive(:view_on).with('database', 'test_view', {:reduce => true})
695
+ @design_doc.should_receive(:view_on).with(@mod.database, 'test_view', {:reduce => true})
601
696
  @obj.send(:execute)
602
697
  end
603
698