couch_potato-rails2 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +5 -0
  3. data/CHANGES.md +148 -0
  4. data/CREDITS +6 -0
  5. data/Gemfile +4 -0
  6. data/MIT-LICENSE.txt +19 -0
  7. data/README.md +450 -0
  8. data/Rakefile +82 -0
  9. data/couch_potato.gemspec +27 -0
  10. data/init.rb +3 -0
  11. data/lib/core_ext/date.rb +14 -0
  12. data/lib/core_ext/object.rb +5 -0
  13. data/lib/core_ext/string.rb +12 -0
  14. data/lib/core_ext/symbol.rb +15 -0
  15. data/lib/core_ext/time.rb +23 -0
  16. data/lib/couch_potato.rb +48 -0
  17. data/lib/couch_potato/database.rb +179 -0
  18. data/lib/couch_potato/persistence.rb +124 -0
  19. data/lib/couch_potato/persistence/active_model_compliance.rb +44 -0
  20. data/lib/couch_potato/persistence/attachments.rb +31 -0
  21. data/lib/couch_potato/persistence/callbacks.rb +29 -0
  22. data/lib/couch_potato/persistence/dirty_attributes.rb +56 -0
  23. data/lib/couch_potato/persistence/ghost_attributes.rb +12 -0
  24. data/lib/couch_potato/persistence/json.rb +47 -0
  25. data/lib/couch_potato/persistence/magic_timestamps.rb +23 -0
  26. data/lib/couch_potato/persistence/properties.rb +79 -0
  27. data/lib/couch_potato/persistence/simple_property.rb +82 -0
  28. data/lib/couch_potato/persistence/type_caster.rb +40 -0
  29. data/lib/couch_potato/railtie.rb +25 -0
  30. data/lib/couch_potato/rspec.rb +2 -0
  31. data/lib/couch_potato/rspec/matchers.rb +39 -0
  32. data/lib/couch_potato/rspec/matchers/json2.js +482 -0
  33. data/lib/couch_potato/rspec/matchers/list_as_matcher.rb +54 -0
  34. data/lib/couch_potato/rspec/matchers/map_to_matcher.rb +49 -0
  35. data/lib/couch_potato/rspec/matchers/print_r.js +60 -0
  36. data/lib/couch_potato/rspec/matchers/reduce_to_matcher.rb +50 -0
  37. data/lib/couch_potato/rspec/stub_db.rb +46 -0
  38. data/lib/couch_potato/validation.rb +16 -0
  39. data/lib/couch_potato/validation/with_active_model.rb +27 -0
  40. data/lib/couch_potato/validation/with_validatable.rb +41 -0
  41. data/lib/couch_potato/version.rb +3 -0
  42. data/lib/couch_potato/view/base_view_spec.rb +84 -0
  43. data/lib/couch_potato/view/custom_view_spec.rb +42 -0
  44. data/lib/couch_potato/view/custom_views.rb +52 -0
  45. data/lib/couch_potato/view/lists.rb +23 -0
  46. data/lib/couch_potato/view/model_view_spec.rb +75 -0
  47. data/lib/couch_potato/view/properties_view_spec.rb +47 -0
  48. data/lib/couch_potato/view/raw_view_spec.rb +25 -0
  49. data/lib/couch_potato/view/view_query.rb +82 -0
  50. data/rails/init.rb +4 -0
  51. data/rails/reload_classes.rb +47 -0
  52. data/spec/attachments_spec.rb +23 -0
  53. data/spec/callbacks_spec.rb +297 -0
  54. data/spec/create_spec.rb +35 -0
  55. data/spec/custom_view_spec.rb +239 -0
  56. data/spec/default_property_spec.rb +38 -0
  57. data/spec/destroy_spec.rb +29 -0
  58. data/spec/fixtures/address.rb +10 -0
  59. data/spec/fixtures/person.rb +6 -0
  60. data/spec/property_spec.rb +323 -0
  61. data/spec/rails_spec.rb +50 -0
  62. data/spec/railtie_spec.rb +65 -0
  63. data/spec/spec.opts +2 -0
  64. data/spec/spec_helper.rb +44 -0
  65. data/spec/unit/active_model_compliance_spec.rb +98 -0
  66. data/spec/unit/attributes_spec.rb +135 -0
  67. data/spec/unit/base_view_spec_spec.rb +106 -0
  68. data/spec/unit/callbacks_spec.rb +46 -0
  69. data/spec/unit/couch_potato_spec.rb +39 -0
  70. data/spec/unit/create_spec.rb +69 -0
  71. data/spec/unit/custom_views_spec.rb +15 -0
  72. data/spec/unit/database_spec.rb +317 -0
  73. data/spec/unit/date_spec.rb +22 -0
  74. data/spec/unit/dirty_attributes_spec.rb +136 -0
  75. data/spec/unit/initialize_spec.rb +38 -0
  76. data/spec/unit/json_spec.rb +30 -0
  77. data/spec/unit/lists_spec.rb +20 -0
  78. data/spec/unit/model_view_spec_spec.rb +13 -0
  79. data/spec/unit/properties_view_spec_spec.rb +31 -0
  80. data/spec/unit/rspec_matchers_spec.rb +124 -0
  81. data/spec/unit/rspec_stub_db_spec.rb +35 -0
  82. data/spec/unit/string_spec.rb +7 -0
  83. data/spec/unit/time_spec.rb +15 -0
  84. data/spec/unit/validation_spec.rb +67 -0
  85. data/spec/unit/view_query_spec.rb +86 -0
  86. data/spec/update_spec.rb +40 -0
  87. data/spec/view_updates_spec.rb +28 -0
  88. metadata +243 -0
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'callbacks' do
4
+ class Tree
5
+ include CouchPotato::Persistence
6
+
7
+ before_validation :grow_leaf
8
+
9
+ property :leaf_count
10
+ property :watered
11
+
12
+ def grow_leaf
13
+ self.leaf_count ||= 0
14
+ self.leaf_count += 1
15
+ end
16
+ end
17
+
18
+ class AppleTree < Tree
19
+ attr_accessor :watered
20
+
21
+ before_validation :water
22
+
23
+ def water
24
+ self.watered = true
25
+ end
26
+
27
+ def watered?
28
+ watered
29
+ end
30
+ end
31
+
32
+ context 'inheritance' do
33
+ it "should call the callbacks of the super class" do
34
+ tree = AppleTree.new :leaf_count => 1
35
+ tree.valid?
36
+ tree.leaf_count.should == 2
37
+ end
38
+
39
+ it "should call the callbacks of the child class" do
40
+ tree = AppleTree.new :leaf_count => 1
41
+ tree.valid?
42
+ tree.should be_watered
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe CouchPotato, 'full_url_to_database' do
4
+ before(:each) do
5
+ @original_database_name = CouchPotato::Config.database_name
6
+ end
7
+ after(:each) do
8
+ CouchPotato::Config.database_name = @original_database_name
9
+ end
10
+
11
+ it "should add the default localhost and port if only a name is set" do
12
+ CouchPotato::Config.database_name = 'test'
13
+ CouchPotato.full_url_to_database.should == 'http://127.0.0.1:5984/test'
14
+ end
15
+
16
+ it "should return the set url" do
17
+ CouchPotato::Config.database_name = 'http://db.local/test'
18
+ CouchPotato.full_url_to_database.should == 'http://db.local/test'
19
+ end
20
+ end
21
+
22
+ describe CouchPotato, 'validation_framework' do
23
+ before(:each) do
24
+ @original_validation_framework = CouchPotato::Config.validation_framework
25
+ end
26
+ after(:each) do
27
+ CouchPotato::Config.validation_framework = @original_validation_framework
28
+ end
29
+
30
+ it "should allow setting the validation_framework to :active_model" do
31
+ CouchPotato::Config.validation_framework = :active_model
32
+ CouchPotato::Config.validation_framework.should == :active_model
33
+ end
34
+
35
+ it "should allow setting the validation_framework to :validatable" do
36
+ CouchPotato::Config.validation_framework = :validatable
37
+ CouchPotato::Config.validation_framework.should == :validatable
38
+ end
39
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ describe "create" do
4
+
5
+ describe "succeeds" do
6
+ before(:each) do
7
+ Time.zone = nil
8
+ end
9
+
10
+ def create_comment
11
+ comment = Comment.new :title => 'my_title'
12
+ CouchPotato::Database.new(stub('database', :save_doc => {'rev' => '123', 'id' => '456'}, :info => nil)).save_document!(comment)
13
+ comment
14
+ end
15
+
16
+ it "should assign the id" do
17
+ create_comment._id.should == '456'
18
+ end
19
+
20
+ it "should assign the revision" do
21
+ create_comment._rev.should == '123'
22
+ end
23
+
24
+ it "should set created at in the current time zone" do
25
+ Time.zone = 'Europe/Berlin'
26
+ Timecop.travel 2010, 1, 1, 12 do
27
+ create_comment.created_at.to_s.should == '2010-01-01 12:00:00 +0100'
28
+ end
29
+ end
30
+
31
+ it "should set updated at in the current time zone" do
32
+ Time.zone = 'Europe/Berlin'
33
+ Timecop.travel 2010, 1, 1, 12 do
34
+ create_comment.updated_at.to_s.should == '2010-01-01 12:00:00 +0100'
35
+ end
36
+ end
37
+ end
38
+
39
+ describe "fails" do
40
+ before(:each) do
41
+ @comment = Comment.new
42
+ CouchPotato::Database.new(stub('database', :info => nil)).save_document(@comment)
43
+ end
44
+
45
+ it "should not assign an id" do
46
+ @comment._id.should be_nil
47
+ end
48
+
49
+ it "should not assign a revision" do
50
+ @comment._rev.should be_nil
51
+ end
52
+
53
+ it "should not set created at" do
54
+ @comment.created_at.should be_nil
55
+ end
56
+
57
+ it "should set updated at" do
58
+ @comment.updated_at.should be_nil
59
+ end
60
+
61
+ describe "with bank" do
62
+ it "should raise an exception" do
63
+ lambda {
64
+ @comment.save!
65
+ }.should raise_error
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe CouchPotato::View::CustomViews do
4
+
5
+ class MyViewSpec; end
6
+ class ModelWithView
7
+ include CouchPotato::Persistence
8
+ view :all, :type => MyViewSpec
9
+ end
10
+
11
+ it "should use a custom viewspec class" do
12
+ MyViewSpec.should_receive(:new)
13
+ ModelWithView.all
14
+ end
15
+ end
@@ -0,0 +1,317 @@
1
+ require 'spec_helper'
2
+
3
+ class DbTestUser
4
+ include CouchPotato::Persistence
5
+ end
6
+
7
+ # namespaced model
8
+ module Parent
9
+ class Child
10
+ include CouchPotato::Persistence
11
+ end
12
+ end
13
+
14
+ describe CouchPotato::Database, 'new' do
15
+ it "should raise an exception if the database doesn't exist" do
16
+ lambda {
17
+ CouchPotato::Database.new CouchRest.database('couch_potato_invalid')
18
+ }.should raise_error('Database \'couch_potato_invalid\' does not exist.')
19
+ end
20
+ end
21
+
22
+ describe CouchPotato::Database, 'full_url_to_database' do
23
+ before(:all) do
24
+ @database_url = CouchPotato::Config.database_name
25
+ end
26
+
27
+ after(:all) do
28
+ CouchPotato::Config.database_name = @database_url
29
+ end
30
+
31
+ it "should return the full URL when it starts with https" do
32
+ CouchPotato::Config.database_name = "https://example.com/database"
33
+ CouchPotato.full_url_to_database.should == 'https://example.com/database'
34
+ end
35
+
36
+ it "should return the full URL when it starts with http" do
37
+ CouchPotato::Config.database_name = "http://example.com/database"
38
+ CouchPotato.full_url_to_database.should == 'http://example.com/database'
39
+ end
40
+
41
+ it "should use localhost when no protocol was specified" do
42
+ CouchPotato::Config.database_name = "database"
43
+ CouchPotato.full_url_to_database.should == 'http://127.0.0.1:5984/database'
44
+ end
45
+ end
46
+
47
+ describe CouchPotato::Database, 'load' do
48
+ it "should raise an exception if nil given" do
49
+ db = CouchPotato::Database.new(stub('couchrest db', :info => nil))
50
+ lambda {
51
+ db.load nil
52
+ }.should raise_error("Can't load a document without an id (got nil)")
53
+ end
54
+
55
+ it "should set itself on the model" do
56
+ user = mock('user').as_null_object
57
+ DbTestUser.stub!(:new).and_return(user)
58
+ db = CouchPotato::Database.new(stub('couchrest db', :info => nil, :get => DbTestUser.json_create({JSON.create_id => 'DbTestUser'})))
59
+ user.should_receive(:database=).with(db)
60
+ db.load '1'
61
+ end
62
+
63
+ it "should load namespaced models" do
64
+ db = CouchPotato::Database.new(stub('couchrest db', :info => nil, :get => Parent::Child.json_create({JSON.create_id => 'Parent::Child'})))
65
+ db.load('1').class.should == Parent::Child
66
+ end
67
+ end
68
+
69
+ describe CouchPotato::Database, 'load!' do
70
+ it "should raise an error if no document found" do
71
+ couchrest_db = stub('couchrest db', :info => nil)
72
+ couchrest_db.stub(:get).and_raise(RestClient::ResourceNotFound)
73
+ db = CouchPotato::Database.new(couchrest_db)
74
+ lambda {
75
+ db.load! '1'
76
+ }.should raise_error(CouchPotato::NotFound)
77
+ end
78
+ end
79
+
80
+ describe CouchPotato::Database, 'save_document' do
81
+ before(:each) do
82
+ @db = CouchPotato::Database.new(stub('couchrest db').as_null_object)
83
+ end
84
+
85
+ it "should set itself on the model for a new object before doing anything else" do
86
+ @db.stub(:valid_document?).and_return false
87
+ user = stub('user', :new? => true).as_null_object
88
+ user.should_receive(:database=).with(@db)
89
+ @db.save_document user
90
+ end
91
+
92
+ class Category
93
+ include CouchPotato::Persistence
94
+ property :name
95
+ validates_presence_of :name
96
+ end
97
+
98
+ it "should return false when creating a new document and the validations failed" do
99
+ CouchPotato.database.save_document(Category.new).should == false
100
+ end
101
+
102
+ it "should return false when saving an existing document and the validations failed" do
103
+ category = Category.new(:name => "pizza")
104
+ CouchPotato.database.save_document(category).should == true
105
+ category.name = nil
106
+ CouchPotato.database.save_document(category).should == false
107
+ end
108
+
109
+ describe "when creating with validate options" do
110
+ it "should not run the validations when saved with false" do
111
+ category = Category.new
112
+ @db.save_document(category, false)
113
+ category.new?.should == false
114
+ end
115
+
116
+ it "should run the validations when saved with true" do
117
+ category = Category.new
118
+ @db.save_document(category, true)
119
+ category.new?.should == true
120
+ end
121
+
122
+ it "should run the validations when saved with default" do
123
+ category = Category.new
124
+ @db.save_document(category)
125
+ category.new?.should == true
126
+ end
127
+ end
128
+
129
+ describe "when updating with validate options" do
130
+ it "should not run the validations when saved with false" do
131
+ category = Category.new(:name => 'food')
132
+ @db.save_document(category)
133
+ category.new?.should be_false
134
+ category.name = nil
135
+ @db.save_document(category, false)
136
+ category.dirty?.should be_false
137
+ end
138
+
139
+ it "should run the validations when saved with true" do
140
+ category = Category.new(:name => "food")
141
+ @db.save_document(category)
142
+ category.new?.should == false
143
+ category.name = nil
144
+ @db.save_document(category, true)
145
+ category.dirty?.should == true
146
+ category.valid?.should == false
147
+ end
148
+
149
+ it "should run the validations when saved with default" do
150
+ category = Category.new(:name => "food")
151
+ @db.save_document(category)
152
+ category.new?.should == false
153
+ category.name = nil
154
+ @db.save_document(category)
155
+ category.dirty?.should == true
156
+ end
157
+ end
158
+
159
+ describe "when saving documents with errors set in callbacks" do
160
+ class Vulcan
161
+ include CouchPotato::Persistence
162
+ before_validation_on_create :set_errors
163
+ before_validation_on_update :set_errors
164
+
165
+ property :name
166
+ validates_presence_of :name
167
+
168
+ def set_errors
169
+ errors.add(:validation, "failed")
170
+ end
171
+ end
172
+
173
+ it "should keep errors added in before_validation_on_* callbacks when creating a new object" do
174
+ spock = Vulcan.new(:name => 'spock')
175
+ @db.save_document(spock)
176
+ spock.errors.on(:validation).should == 'failed'
177
+ end
178
+
179
+ it "should keep errors added in before_validation_on_* callbacks when creating a new object" do
180
+ spock = Vulcan.new(:name => 'spock')
181
+ @db.save_document(spock, false)
182
+ spock.new?.should == false
183
+ spock.name = "spock's father"
184
+ @db.save_document(spock)
185
+ spock.errors.on(:validation).should == 'failed'
186
+ end
187
+
188
+ it "should keep errors generated from normal validations together with errors set in normal validations" do
189
+ spock = Vulcan.new
190
+ @db.save_document(spock)
191
+ spock.errors.on(:validation).should == 'failed'
192
+ spock.errors.on(:name).should =~ /can't be (empty|blank)/
193
+ end
194
+
195
+ it "should clear errors on subsequent, valid saves when creating" do
196
+ spock = Vulcan.new
197
+ @db.save_document(spock)
198
+
199
+ spock.name = 'Spock'
200
+ @db.save_document(spock)
201
+ spock.errors.on(:name).should == nil
202
+ end
203
+
204
+ it "should clear errors on subsequent, valid saves when updating" do
205
+ spock = Vulcan.new(:name => 'spock')
206
+ @db.save_document(spock, false)
207
+
208
+ spock.name = nil
209
+ @db.save_document(spock)
210
+ spock.errors.on(:name).should =~ /can't be (empty|blank)/
211
+
212
+ spock.name = 'Spock'
213
+ @db.save_document(spock)
214
+ spock.errors.on(:name).should == nil
215
+ end
216
+
217
+ end
218
+ end
219
+
220
+ describe CouchPotato::Database, 'first' do
221
+ before(:each) do
222
+ @couchrest_db = stub('couchrest db').as_null_object
223
+ @db = CouchPotato::Database.new(@couchrest_db)
224
+ @result = stub('result')
225
+ @spec = stub('view spec', :process_results => [@result]).as_null_object
226
+ CouchPotato::View::ViewQuery.stub(:new => stub('view query', :query_view! => {'rows' => [@result]}))
227
+ end
228
+
229
+ it "should return the first result from a view query" do
230
+ @db.first(@spec).should == @result
231
+ end
232
+
233
+ it "should return nil if there are no results" do
234
+ @spec.stub(:process_results => [])
235
+ @db.first(@spec).should be_nil
236
+ end
237
+ end
238
+
239
+ describe CouchPotato::Database, 'first!' do
240
+ before(:each) do
241
+ @couchrest_db = stub('couchrest db').as_null_object
242
+ @db = CouchPotato::Database.new(@couchrest_db)
243
+ @result = stub('result')
244
+ @spec = stub('view spec', :process_results => [@result]).as_null_object
245
+ CouchPotato::View::ViewQuery.stub(:new => stub('view query', :query_view! => {'rows' => [@result]}))
246
+ end
247
+
248
+ it "should return the first result from a view query" do
249
+ @db.first!(@spec).should == @result
250
+ end
251
+
252
+ it "should raise an error if there are no results" do
253
+ @spec.stub(:process_results => [])
254
+ lambda {
255
+ @db.first!(@spec)
256
+ }.should raise_error(CouchPotato::NotFound)
257
+ end
258
+ end
259
+
260
+ describe CouchPotato::Database, 'view' do
261
+ before(:each) do
262
+ @couchrest_db = stub('couchrest db').as_null_object
263
+ @db = CouchPotato::Database.new(@couchrest_db)
264
+ @result = stub('result')
265
+ @spec = stub('view spec', :process_results => [@result]).as_null_object
266
+ CouchPotato::View::ViewQuery.stub(:new => stub('view query', :query_view! => {'rows' => [@result]}))
267
+ end
268
+
269
+ it "should initialze a view query with map/reduce/list funtions" do
270
+ @spec.stub(:design_document => 'design_doc', :view_name => 'my_view',
271
+ :map_function => '<map_code>', :reduce_function => '<reduce_code>',
272
+ :list_name => 'my_list', :list_function => '<list_code>')
273
+ CouchPotato::View::ViewQuery.should_receive(:new).with(
274
+ @couchrest_db,
275
+ 'design_doc',
276
+ {'my_view' => {
277
+ :map => '<map_code>',
278
+ :reduce => '<reduce_code>'
279
+ }},
280
+ {'my_list' => '<list_code>'})
281
+ @db.view(@spec)
282
+ end
283
+
284
+ it "should initialze a view query with only map/reduce functions" do
285
+ @spec.stub(:design_document => 'design_doc', :view_name => 'my_view',
286
+ :map_function => '<map_code>', :reduce_function => '<reduce_code>',
287
+ :list_name => nil, :list_function => nil)
288
+ CouchPotato::View::ViewQuery.should_receive(:new).with(
289
+ @couchrest_db,
290
+ 'design_doc',
291
+ {'my_view' => {
292
+ :map => '<map_code>',
293
+ :reduce => '<reduce_code>'
294
+ }}, nil)
295
+ @db.view(@spec)
296
+ end
297
+
298
+ it "should set itself on returned results that have an accessor" do
299
+ @result.stub(:respond_to?).with(:database=).and_return(true)
300
+ @result.should_receive(:database=).with(@db)
301
+ @db.view(@spec)
302
+ end
303
+
304
+ it "should not set itself on returned results that don't have an accessor" do
305
+ @result.stub(:respond_to?).with(:database=).and_return(false)
306
+ @result.should_not_receive(:database=).with(@db)
307
+ @db.view(@spec)
308
+ end
309
+
310
+ it "should not try to set itself on result sets that are not collections" do
311
+ lambda {
312
+ @spec.stub(:process_results => 1)
313
+ }.should_not raise_error
314
+
315
+ @db.view(@spec)
316
+ end
317
+ end