davber_couch_potato 0.3.0

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 (80) hide show
  1. data/CHANGES.md +106 -0
  2. data/MIT-LICENSE.txt +19 -0
  3. data/README.md +409 -0
  4. data/VERSION.yml +5 -0
  5. data/init.rb +3 -0
  6. data/lib/core_ext/date.rb +21 -0
  7. data/lib/core_ext/object.rb +5 -0
  8. data/lib/core_ext/string.rb +8 -0
  9. data/lib/core_ext/symbol.rb +15 -0
  10. data/lib/core_ext/time.rb +21 -0
  11. data/lib/couch_potato/database.rb +161 -0
  12. data/lib/couch_potato/persistence/active_model_compliance.rb +44 -0
  13. data/lib/couch_potato/persistence/attachments.rb +31 -0
  14. data/lib/couch_potato/persistence/callbacks.rb +62 -0
  15. data/lib/couch_potato/persistence/dirty_attributes.rb +56 -0
  16. data/lib/couch_potato/persistence/ghost_attributes.rb +22 -0
  17. data/lib/couch_potato/persistence/json.rb +46 -0
  18. data/lib/couch_potato/persistence/magic_timestamps.rb +20 -0
  19. data/lib/couch_potato/persistence/properties.rb +86 -0
  20. data/lib/couch_potato/persistence/simple_property.rb +72 -0
  21. data/lib/couch_potato/persistence/type_caster.rb +40 -0
  22. data/lib/couch_potato/persistence.rb +105 -0
  23. data/lib/couch_potato/railtie.rb +18 -0
  24. data/lib/couch_potato/rspec/matchers/json2.js +482 -0
  25. data/lib/couch_potato/rspec/matchers/list_as_matcher.rb +54 -0
  26. data/lib/couch_potato/rspec/matchers/map_to_matcher.rb +49 -0
  27. data/lib/couch_potato/rspec/matchers/print_r.js +60 -0
  28. data/lib/couch_potato/rspec/matchers/reduce_to_matcher.rb +50 -0
  29. data/lib/couch_potato/rspec/matchers.rb +39 -0
  30. data/lib/couch_potato/rspec/stub_db.rb +46 -0
  31. data/lib/couch_potato/rspec.rb +2 -0
  32. data/lib/couch_potato/validation/with_active_model.rb +27 -0
  33. data/lib/couch_potato/validation/with_validatable.rb +37 -0
  34. data/lib/couch_potato/validation.rb +16 -0
  35. data/lib/couch_potato/view/base_view_spec.rb +67 -0
  36. data/lib/couch_potato/view/custom_view_spec.rb +42 -0
  37. data/lib/couch_potato/view/custom_views.rb +52 -0
  38. data/lib/couch_potato/view/lists.rb +23 -0
  39. data/lib/couch_potato/view/model_view_spec.rb +75 -0
  40. data/lib/couch_potato/view/properties_view_spec.rb +47 -0
  41. data/lib/couch_potato/view/raw_view_spec.rb +25 -0
  42. data/lib/couch_potato/view/view_query.rb +78 -0
  43. data/lib/couch_potato.rb +79 -0
  44. data/rails/init.rb +4 -0
  45. data/rails/reload_classes.rb +47 -0
  46. data/spec/attachments_spec.rb +23 -0
  47. data/spec/callbacks_spec.rb +308 -0
  48. data/spec/create_spec.rb +34 -0
  49. data/spec/custom_view_spec.rb +239 -0
  50. data/spec/default_property_spec.rb +38 -0
  51. data/spec/destroy_spec.rb +29 -0
  52. data/spec/fixtures/address.rb +10 -0
  53. data/spec/fixtures/person.rb +6 -0
  54. data/spec/property_spec.rb +315 -0
  55. data/spec/rails_spec.rb +51 -0
  56. data/spec/spec.opts +4 -0
  57. data/spec/spec_helper.rb +47 -0
  58. data/spec/unit/active_model_compliance_spec.rb +98 -0
  59. data/spec/unit/attributes_spec.rb +125 -0
  60. data/spec/unit/base_view_spec_spec.rb +73 -0
  61. data/spec/unit/callbacks_spec.rb +72 -0
  62. data/spec/unit/couch_potato_spec.rb +39 -0
  63. data/spec/unit/create_spec.rb +58 -0
  64. data/spec/unit/custom_views_spec.rb +15 -0
  65. data/spec/unit/database_spec.rb +266 -0
  66. data/spec/unit/date_spec.rb +22 -0
  67. data/spec/unit/dirty_attributes_spec.rb +166 -0
  68. data/spec/unit/json_create_id_spec.rb +14 -0
  69. data/spec/unit/lists_spec.rb +20 -0
  70. data/spec/unit/model_view_spec_spec.rb +13 -0
  71. data/spec/unit/properties_view_spec_spec.rb +31 -0
  72. data/spec/unit/rspec_matchers_spec.rb +124 -0
  73. data/spec/unit/rspec_stub_db_spec.rb +35 -0
  74. data/spec/unit/string_spec.rb +7 -0
  75. data/spec/unit/time_spec.rb +22 -0
  76. data/spec/unit/validation_spec.rb +67 -0
  77. data/spec/unit/view_query_spec.rb +78 -0
  78. data/spec/update_spec.rb +40 -0
  79. data/spec/view_updates_spec.rb +28 -0
  80. metadata +205 -0
@@ -0,0 +1,266 @@
1
+ require 'spec_helper'
2
+
3
+ class DbTestUser
4
+ end
5
+
6
+ # namespaced model
7
+ module Parent
8
+ class Child
9
+ include CouchPotato::Persistence
10
+ end
11
+ end
12
+
13
+ describe CouchPotato::Database, 'new' do
14
+ it "should raise an exception if the database doesn't exist" do
15
+ lambda {
16
+ CouchPotato::Database.new CouchRest.database('couch_potato_invalid')
17
+ }.should raise_error('Database \'couch_potato_invalid\' does not exist.')
18
+ end
19
+ end
20
+
21
+ describe CouchPotato::Database, 'full_url_to_database' do
22
+ before(:all) do
23
+ @database_url = CouchPotato::Config.database_name
24
+ end
25
+
26
+ after(:all) do
27
+ CouchPotato::Config.database_name = @database_url
28
+ end
29
+
30
+ it "should return the full URL when it starts with https" do
31
+ CouchPotato::Config.database_name = "https://example.com/database"
32
+ CouchPotato.full_url_to_database.should == 'https://example.com/database'
33
+ end
34
+
35
+ it "should return the full URL when it starts with http" do
36
+ CouchPotato::Config.database_name = "http://example.com/database"
37
+ CouchPotato.full_url_to_database.should == 'http://example.com/database'
38
+ end
39
+
40
+ it "should use localhost when no protocol was specified" do
41
+ CouchPotato::Config.database_name = "database"
42
+ CouchPotato.full_url_to_database.should == 'http://127.0.0.1:5984/database'
43
+ end
44
+ end
45
+
46
+ describe CouchPotato::Database, 'load' do
47
+ it "should raise an exception if nil given" do
48
+ db = CouchPotato::Database.new(stub('couchrest db', :info => nil))
49
+ lambda {
50
+ db.load nil
51
+ }.should raise_error("Can't load a document without an id (got nil)")
52
+ end
53
+
54
+ it "should set itself on the model" do
55
+ user = mock 'user'
56
+ DbTestUser.stub!(:new).and_return(user)
57
+ db = CouchPotato::Database.new(stub('couchrest db', :info => nil, :get => DbTestUser.json_create({JSON.create_id => 'DbTestUser'})))
58
+ user.should_receive(:database=).with(db)
59
+ db.load '1'
60
+ end
61
+
62
+ it "should load namespaced models" do
63
+ db = CouchPotato::Database.new(stub('couchrest db', :info => nil, :get => Parent::Child.json_create({JSON.create_id => 'Parent::Child'})))
64
+ db.load('1').class.should == Parent::Child
65
+ end
66
+
67
+ end
68
+
69
+ describe CouchPotato::Database, 'save_document' do
70
+ before(:each) do
71
+ @db = CouchPotato::Database.new(stub('couchrest db').as_null_object)
72
+ end
73
+
74
+ it "should set itself on the model for a new object before doing anything else" do
75
+ @db.stub(:valid_document?).and_return false
76
+ user = stub('user', :new? => true).as_null_object
77
+ user.should_receive(:database=).with(@db)
78
+ @db.save_document user
79
+ end
80
+
81
+ class Category
82
+ include CouchPotato::Persistence
83
+ property :name
84
+ validates_presence_of :name
85
+ end
86
+
87
+ it "should return false when creating a new document and the validations failed" do
88
+ CouchPotato.database.save_document(Category.new).should == false
89
+ end
90
+
91
+ it "should return false when saving an existing document and the validations failed" do
92
+ category = Category.new(:name => "pizza")
93
+ CouchPotato.database.save_document(category).should == true
94
+ category.name = nil
95
+ CouchPotato.database.save_document(category).should == false
96
+ end
97
+
98
+ describe "when creating with validate options" do
99
+ it "should not run the validations when saved with false" do
100
+ category = Category.new
101
+ @db.save_document(category, false)
102
+ category.new?.should == false
103
+ end
104
+
105
+ it "should run the validations when saved with true" do
106
+ category = Category.new
107
+ @db.save_document(category, true)
108
+ category.new?.should == true
109
+ end
110
+
111
+ it "should run the validations when saved with default" do
112
+ category = Category.new
113
+ @db.save_document(category)
114
+ category.new?.should == true
115
+ end
116
+ end
117
+
118
+ describe "when updating with validate options" do
119
+ it "should not run the validations when saved with false" do
120
+ category = Category.new(:name => 'food')
121
+ @db.save_document(category)
122
+ category.new?.should == false
123
+ category.name = nil
124
+ @db.save_document(category, false)
125
+ category.dirty?.should == false
126
+ end
127
+
128
+ it "should run the validations when saved with true" do
129
+ category = Category.new(:name => "food")
130
+ @db.save_document(category)
131
+ category.new?.should == false
132
+ category.name = nil
133
+ @db.save_document(category, true)
134
+ category.dirty?.should == true
135
+ category.valid?.should == false
136
+ end
137
+
138
+ it "should run the validations when saved with default" do
139
+ category = Category.new(:name => "food")
140
+ @db.save_document(category)
141
+ category.new?.should == false
142
+ category.name = nil
143
+ @db.save_document(category)
144
+ category.dirty?.should == true
145
+ end
146
+ end
147
+
148
+ describe "when saving documents with errors set in callbacks" do
149
+ class Vulcan
150
+ include CouchPotato::Persistence
151
+ before_validation_on_create :set_errors
152
+ before_validation_on_update :set_errors
153
+
154
+ property :name
155
+ validates_presence_of :name
156
+
157
+ def set_errors
158
+ errors.add(:validation, "failed")
159
+ end
160
+ end
161
+
162
+ it "should keep errors added in before_validation_on_* callbacks when creating a new object" do
163
+ spock = Vulcan.new(:name => 'spock')
164
+ @db.save_document(spock)
165
+ spock.errors.on(:validation).should == 'failed'
166
+ end
167
+
168
+ it "should keep errors added in before_validation_on_* callbacks when creating a new object" do
169
+ spock = Vulcan.new(:name => 'spock')
170
+ @db.save_document(spock, false)
171
+ spock.new?.should == false
172
+ spock.name = "spock's father"
173
+ @db.save_document(spock)
174
+ spock.errors.on(:validation).should == 'failed'
175
+ end
176
+
177
+ it "should keep errors generated from normal validations together with errors set in normal validations" do
178
+ spock = Vulcan.new
179
+ @db.save_document(spock)
180
+ spock.errors.on(:validation).should == 'failed'
181
+ spock.errors.on(:name).should =~ /can't be (empty|blank)/
182
+ end
183
+
184
+ it "should clear errors on subsequent, valid saves when creating" do
185
+ spock = Vulcan.new
186
+ @db.save_document(spock)
187
+
188
+ spock.name = 'Spock'
189
+ @db.save_document(spock)
190
+ spock.errors.on(:name).should == nil
191
+ end
192
+
193
+ it "should clear errors on subsequent, valid saves when updating" do
194
+ spock = Vulcan.new(:name => 'spock')
195
+ @db.save_document(spock, false)
196
+
197
+ spock.name = nil
198
+ @db.save_document(spock)
199
+ spock.errors.on(:name).should =~ /can't be (empty|blank)/
200
+
201
+ spock.name = 'Spock'
202
+ @db.save_document(spock)
203
+ spock.errors.on(:name).should == nil
204
+ end
205
+
206
+ end
207
+ end
208
+
209
+ describe CouchPotato::Database, 'view' do
210
+ before(:each) do
211
+ @couchrest_db = stub('couchrest db').as_null_object
212
+ @db = CouchPotato::Database.new(@couchrest_db)
213
+ @result = stub('result')
214
+ @spec = stub('view spec', :process_results => [@result]).as_null_object
215
+ CouchPotato::View::ViewQuery.stub(:new => stub('view query', :query_view! => {'rows' => [@result]}))
216
+ end
217
+
218
+ it "should initialze a view query with map/reduce/list funtions" do
219
+ @spec.stub(:design_document => 'design_doc', :view_name => 'my_view',
220
+ :map_function => '<map_code>', :reduce_function => '<reduce_code>',
221
+ :list_name => 'my_list', :list_function => '<list_code>')
222
+ CouchPotato::View::ViewQuery.should_receive(:new).with(
223
+ @couchrest_db,
224
+ 'design_doc',
225
+ {'my_view' => {
226
+ :map => '<map_code>',
227
+ :reduce => '<reduce_code>'
228
+ }},
229
+ {'my_list' => '<list_code>'})
230
+ @db.view(@spec)
231
+ end
232
+
233
+ it "should initialze a view query with only map/reduce functions" do
234
+ @spec.stub(:design_document => 'design_doc', :view_name => 'my_view',
235
+ :map_function => '<map_code>', :reduce_function => '<reduce_code>',
236
+ :list_name => nil, :list_function => nil)
237
+ CouchPotato::View::ViewQuery.should_receive(:new).with(
238
+ @couchrest_db,
239
+ 'design_doc',
240
+ {'my_view' => {
241
+ :map => '<map_code>',
242
+ :reduce => '<reduce_code>'
243
+ }}, nil)
244
+ @db.view(@spec)
245
+ end
246
+
247
+ it "should set itself on returned results that have an accessor" do
248
+ @result.stub(:respond_to?).with(:database=).and_return(true)
249
+ @result.should_receive(:database=).with(@db)
250
+ @db.view(@spec)
251
+ end
252
+
253
+ it "should not set itself on returned results that don't have an accessor" do
254
+ @result.stub(:respond_to?).with(:database=).and_return(false)
255
+ @result.should_not_receive(:database=).with(@db)
256
+ @db.view(@spec)
257
+ end
258
+
259
+ it "should not try to set itself on result sets that are not collections" do
260
+ lambda {
261
+ @spec.stub(:process_results => 1)
262
+ }.should_not raise_error
263
+
264
+ @db.view(@spec)
265
+ end
266
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Date, 'to_json' do
4
+ it "should format the date in a way that i can use it for sorting in couchdb" do
5
+ date = Date.parse('2009-01-01')
6
+ date.to_json.should == "\"2009/01/01\""
7
+ end
8
+ end
9
+
10
+ describe Date, 'to_s(:json)' do
11
+ it "should format it in the same way as to_json does so i can use this to do queries over date attributes" do
12
+ date = Date.parse('2009-01-01')
13
+ date.to_s(:json).should == "2009/01/01"
14
+ end
15
+ end
16
+
17
+ describe Date, 'to_s' do
18
+ it "should leave the original to_s untouched" do
19
+ date = Date.parse('2009-01-01')
20
+ date.to_s.should == "2009-01-01"
21
+ end
22
+ end
@@ -0,0 +1,166 @@
1
+ require 'spec_helper'
2
+
3
+ class Plate
4
+ include CouchPotato::Persistence
5
+
6
+ property :food
7
+ end
8
+
9
+ describe 'dirty attribute tracking' do
10
+ before(:each) do
11
+ @couchrest_db = stub('database', :save_doc => {'id' => '1', 'rev' => '2'}, :info => nil)
12
+ @db = CouchPotato::Database.new(@couchrest_db)
13
+ end
14
+
15
+ describe "save" do
16
+ it "should not save when nothing dirty" do
17
+ plate = Plate.new :food => 'sushi'
18
+ @db.save_document!(plate)
19
+ @couchrest_db.should_not_receive(:save_doc)
20
+ @db.save_document(plate)
21
+ end
22
+
23
+ it "should return true when not dirty" do
24
+ plate = Plate.new :food => 'sushi'
25
+ @db.save_document!(plate)
26
+ @db.save_document(plate).should be_true
27
+ end
28
+
29
+ it "should save when there are dirty attributes" do
30
+ plate = Plate.new :food => 'sushi'
31
+ @db.save_document!(plate)
32
+ plate.food = 'burger'
33
+ @couchrest_db.should_receive(:save_doc)
34
+ @db.save_document(plate)
35
+ end
36
+
37
+ it "should correctly track dirty hashes (deep clone)" do
38
+ plate = Plate.new :food => {:veggies => ['carrots', 'peas']}
39
+ @db.save_document(plate)
40
+ plate.food[:veggies] << 'beans'
41
+ @couchrest_db.should_receive(:save_doc)
42
+ @db.save_document(plate)
43
+ end
44
+
45
+ it "should correctly track dirty hashes (deep clone) after a save" do
46
+ plate = Plate.new :food => {:veggies => ['carrots', 'peas']}
47
+ @db.save_document(plate)
48
+ plate.food[:veggies] << 'beans'
49
+ @db.save_document(plate)
50
+ plate.food[:veggies] << 'cauliflower'
51
+ @couchrest_db.should_receive(:save_doc)
52
+ @db.save_document(plate)
53
+ end
54
+ end
55
+
56
+ describe "newly created object" do
57
+
58
+ before(:each) do
59
+ @plate = Plate.new :food => 'sushi'
60
+ end
61
+
62
+ describe "access old values" do
63
+ it "should return the old value" do
64
+ @plate.food = 'burger'
65
+ @plate.food_was.should == 'sushi'
66
+ end
67
+
68
+ describe "with type BigDecimal" do
69
+ before(:each) do
70
+ class ::Plate
71
+ property :price
72
+ end
73
+ end
74
+ it "should not dup BigDecimal" do
75
+
76
+ lambda {
77
+ Plate.new :price => BigDecimal.new("5.23")
78
+ }.should_not raise_error(TypeError)
79
+ end
80
+
81
+ it "should return the old value" do
82
+ plate = Plate.new :price => BigDecimal.new("5.23")
83
+ plate.price = BigDecimal.new("2.23")
84
+ plate.price_was.should == 5.23
85
+ end
86
+
87
+ end
88
+ end
89
+
90
+ describe "check for dirty" do
91
+ it "should return true if attribute changed" do
92
+ @plate.food = 'burger'
93
+ @plate.should be_food_changed
94
+ end
95
+
96
+ it "should return false if attribute not changed" do
97
+ @plate.should_not be_food_changed
98
+ end
99
+
100
+ it "should return false if attribute forced not changed" do
101
+ @plate.food = 'burger'
102
+ @plate.food_not_changed
103
+ @plate.should_not be_food_changed
104
+ end
105
+
106
+ it "should return true if forced dirty" do
107
+ @plate.is_dirty
108
+ @plate.should be_dirty
109
+ end
110
+ end
111
+ end
112
+
113
+ describe "object loaded from database" do
114
+ before(:each) do
115
+ couchrest_db = stub('database', :get => Plate.json_create({'_id' => '1', '_rev' => '2', 'food' => 'sushi', JSON.create_id => 'Plate'}), :info => nil)
116
+ @plate = CouchPotato::Database.new(couchrest_db).load_document '1'
117
+ end
118
+
119
+ describe "access old values" do
120
+ it "should return the old value" do
121
+ @plate.food = 'burger'
122
+ @plate.food_was.should == 'sushi'
123
+ end
124
+ end
125
+
126
+ describe "check for dirty" do
127
+ it "should return true if attribute changed" do
128
+ @plate.food = 'burger'
129
+ @plate.should be_food_changed
130
+ end
131
+
132
+ it "should return true if array attribute changed" do
133
+ couchrest_db = stub('database', :get => Plate.json_create({'_id' => '1', '_rev' => '2', 'food' => ['sushi'], JSON.create_id => 'Plate'}), :info => nil)
134
+ plate = CouchPotato::Database.new(couchrest_db).load_document '1'
135
+ plate.food << 'burger'
136
+ plate.should be_food_changed
137
+ end
138
+
139
+ it "should return false if attribute not changed" do
140
+ @plate.should_not be_food_changed
141
+ end
142
+ end
143
+ end
144
+
145
+
146
+ describe "after save" do
147
+ it "should reset all attributes to not dirty" do
148
+ couchrest_db = stub('database', :get => Plate.json_create({'_id' => '1', '_rev' => '2', 'food' => 'sushi', JSON.create_id => 'Plate'}), :info => nil, :save_doc => {})
149
+ db = CouchPotato::Database.new(couchrest_db)
150
+ @plate = db.load_document '1'
151
+ @plate.food = 'burger'
152
+ db.save! @plate
153
+ @plate.should_not be_food_changed
154
+ end
155
+
156
+ it "should reset a forced dirty state" do
157
+ couchrest_db = stub('database', :get => Plate.json_create({'_id' => '1', '_rev' => '2', 'food' => 'sushi', JSON.create_id => 'Plate'}), :info => nil, :save_doc => {'rev' => '3'})
158
+ db = CouchPotato::Database.new(couchrest_db)
159
+ @plate = db.load_document '1'
160
+ @plate.is_dirty
161
+ db.save! @plate
162
+ @plate.should_not be_dirty
163
+ end
164
+ end
165
+
166
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ class Drink
4
+ include CouchPotato::Persistence
5
+
6
+ property :alcohol
7
+ end
8
+
9
+ describe "json module" do
10
+ it "should inject JSON.create_id into hash representation of a persistence object" do
11
+ sake = Drink.new(:alcohol => "18%")
12
+ sake.to_hash[JSON.create_id].should eql("Drink")
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe CouchPotato::View::Lists, '.list' do
4
+ it "should make the list function available via .lists" do
5
+ clazz = Class.new
6
+ clazz.send :include, CouchPotato::View::Lists
7
+ clazz.list 'my_list', '<list_code>'
8
+
9
+ clazz.lists('my_list').should == '<list_code>'
10
+ end
11
+
12
+ it "should make the list available to subclasses" do
13
+ clazz = Class.new
14
+ clazz.send :include, CouchPotato::View::Lists
15
+ clazz.list 'my_list', '<list_code>'
16
+ sub_clazz = Class.new clazz
17
+
18
+ sub_clazz.lists('my_list').should == '<list_code>'
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe CouchPotato::View::ModelViewSpec, 'map_function' do
4
+ it "should include conditions" do
5
+ spec = CouchPotato::View::ModelViewSpec.new Object, 'all', {:conditions => 'doc.closed = true'}, {}
6
+ spec.map_function.should include("if(doc.#{CouchPotato.type_field} && doc.#{CouchPotato.type_field} == 'Object' && (doc.closed = true))")
7
+ end
8
+
9
+ it "should not include conditions when they are nil" do
10
+ spec = CouchPotato::View::ModelViewSpec.new Object, 'all', {}, {}
11
+ spec.map_function.should include("if(doc.#{CouchPotato.type_field} && doc.#{CouchPotato.type_field} == 'Object')")
12
+ end
13
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+ require 'couch_potato/rspec'
3
+
4
+ class Contract
5
+ include CouchPotato::Persistence
6
+
7
+ property :date
8
+ property :terms
9
+
10
+ view :by_date, :type => :properties, :key => :_id, :properties => [:date]
11
+ end
12
+
13
+ describe CouchPotato::View::PropertiesViewSpec do
14
+ it "should map the given properties" do
15
+ Contract.by_date.should map(
16
+ Contract.new(:date => '2010-01-01', :_id => '1')
17
+ ).to(['1', {"date" => "2010-01-01"}])
18
+ end
19
+
20
+ it "should reduce to the number of documents" do
21
+ Contract.by_date.should reduce(
22
+ ['1', {"date" => "2010-01-01"}], ['2', {"date" => "2010-01-02"}]
23
+ ).to(2)
24
+ end
25
+
26
+ it "should rereduce the number of documents" do
27
+ Contract.by_date.should rereduce(
28
+ nil, [12, 13]
29
+ ).to(25)
30
+ end
31
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+ require 'couch_potato/rspec'
3
+
4
+ describe CouchPotato::RSpec::MapToMatcher do
5
+
6
+ describe "basic map function" do
7
+ before(:each) do
8
+ @view_spec = stub(:map_function => "function(doc) {emit(doc.name, doc.tags.length);}")
9
+ end
10
+
11
+ it "should pass if the given function emits the expected javascript" do
12
+ @view_spec.should map({:name => 'horst', :tags => ['person', 'male']}).to(['horst', 2])
13
+ end
14
+
15
+ it "should not pass if the given function emits different javascript" do
16
+ @view_spec.should_not map({:name => 'horst', :tags => ['person', 'male']}).to(['horst', 3])
17
+ end
18
+ end
19
+
20
+ describe "functions emitting multiple times" do
21
+ before(:each) do
22
+ @view_spec = stub(:map_function => "function(doc) {emit(doc.name, doc.tags.length); emit(doc.tags[0], doc.tags[1])};")
23
+ end
24
+
25
+ it "should pass if the given function emits the expected javascript" do
26
+ @view_spec.should map({:name => 'horst', :tags => ['person', 'male']}).to(['horst', 2], ['person', 'male'])
27
+ end
28
+
29
+ it "should return false if the given function emits different javascript" do
30
+ @view_spec.should_not map({:name => 'horst', :tags => ['person', 'male']}).to(['horst', 2], ['male', 'person'])
31
+ end
32
+ end
33
+
34
+ describe "failing specs" do
35
+ before(:each) do
36
+ @view_spec = stub(:map_function => "function(doc) {emit(doc.name, null)}")
37
+ end
38
+
39
+ it "should have a nice error message for failing should" do
40
+ lambda {
41
+ @view_spec.should map({:name => 'bill'}).to(['linus', nil])
42
+ }.should raise_error('Expected to map to [["linus", nil]] but got [["bill", nil]].')
43
+ end
44
+
45
+ it "should have a nice error message for failing should not" do
46
+ lambda {
47
+ @view_spec.should_not map({:name => 'bill'}).to(['bill', nil])
48
+ }.should raise_error('Expected not to map to [["bill", nil]] but did.')
49
+ end
50
+ end
51
+ end
52
+
53
+ describe CouchPotato::RSpec::ReduceToMatcher do
54
+ before(:each) do
55
+ @view_spec = stub(:reduce_function => "function(docs, keys, rereduce) {
56
+ if(rereduce) {
57
+ return(sum(keys) * 2);
58
+ } else {
59
+ return(sum(keys));
60
+ };
61
+ }")
62
+ end
63
+
64
+ it "should pass if the given function return the expected javascript" do
65
+ @view_spec.should reduce([], [1, 2, 3]).to(6)
66
+ end
67
+
68
+ it "should not pass if the given function returns different javascript" do
69
+ @view_spec.should_not reduce([], [1, 2, 3]).to(7)
70
+ end
71
+
72
+ describe "rereduce" do
73
+ it "should pass if the given function return the expected javascript" do
74
+ @view_spec.should rereduce([], [1, 2, 3]).to(12)
75
+ end
76
+
77
+ it "should not pass if the given function returns different javascript" do
78
+ @view_spec.should_not rereduce([], [1, 2, 3]).to(13)
79
+ end
80
+ end
81
+
82
+ describe 'failing specs' do
83
+
84
+ it "should have a nice error message for failing should" do
85
+ lambda {
86
+ @view_spec.should reduce([], [1, 2, 3]).to(7)
87
+ }.should raise_error('Expected to reduce to 7 but got 6.')
88
+ end
89
+
90
+ it "should have a nice error message for failing should not" do
91
+ lambda {
92
+ @view_spec.should_not reduce([], [1, 2, 3]).to(6)
93
+ }.should raise_error('Expected not to reduce to 6 but did.')
94
+ end
95
+ end
96
+ end
97
+
98
+ describe CouchPotato::RSpec::ListAsMatcher do
99
+ before(:each) do
100
+ @view_spec = stub(:list_function => "function() {var row = getRow(); send(JSON.stringify([{text: row.text + ' world'}]));}")
101
+ end
102
+
103
+ it "should pass if the function return the expected json" do
104
+ @view_spec.should list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello world'}])
105
+ end
106
+
107
+ it "should not pass if the function does not return the expected json" do
108
+ @view_spec.should_not list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello there'}])
109
+ end
110
+
111
+ describe "failing specs" do
112
+ it "should have a nice error message for failing should" do
113
+ lambda {
114
+ @view_spec.should list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello there'}])
115
+ }.should raise_error('Expected to list as [{"text"=>"hello there"}] but got [{"text"=>"hello world"}].')
116
+ end
117
+
118
+ it "should have a nice error message for failing should not" do
119
+ lambda {
120
+ @view_spec.should_not list({'rows' => [{:text => 'hello'}]}).as([{'text' => 'hello world'}])
121
+ }.should raise_error('Expected to not list as [{"text"=>"hello world"}] but did.')
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'couch_potato/rspec'
3
+
4
+ class WithStubbedView
5
+ include CouchPotato::Persistence
6
+
7
+ view :stubbed_view, :key => :x
8
+ end
9
+
10
+ describe "stubbing the db" do
11
+ it "should replace CouchPotato.database with a stub" do
12
+ stub_db
13
+ CouchPotato.database.should be_a(Spec::Mocks::Mock)
14
+ end
15
+
16
+ it "should return the stub" do
17
+ db = stub_db
18
+ CouchPotato.database.should == db
19
+ end
20
+ end
21
+
22
+ describe "stubbing a view" do
23
+ before(:each) do
24
+ @db = stub_db
25
+ @db.stub_view(WithStubbedView, :stubbed_view).with('123').and_return([:result])
26
+ end
27
+
28
+ it "should stub the view to return a stub" do
29
+ WithStubbedView.stubbed_view('123').should be_a(Spec::Mocks::Mock)
30
+ end
31
+
32
+ it "should stub the database to return fake results when called with the stub" do
33
+ @db.view(WithStubbedView.stubbed_view('123')).should == [:result]
34
+ end
35
+ end