couchrest_model 1.1.2 → 1.2.0.beta

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 (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