couch_potato-rails2 0.5.6

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 (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,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe "create" do
4
+ before(:each) do
5
+ recreate_db
6
+ end
7
+ describe "succeeds" do
8
+ it "should store the class" do
9
+ @comment = Comment.new :title => 'my_title'
10
+ CouchPotato.database.save_document! @comment
11
+ CouchPotato.couchrest_database.get(@comment.id).send(JSON.create_id).should == 'Comment'
12
+ end
13
+
14
+ it "should persist a given created_at" do
15
+ @comment = Comment.new :created_at => Time.parse('2010-01-02 12:34:48 +0000'), :title => '-'
16
+ CouchPotato.database.save_document! @comment
17
+ CouchPotato.couchrest_database.get(@comment.id).created_at.should == Time.parse('2010-01-02 12:34:48 +0000')
18
+ end
19
+
20
+ it "should persist a given updated_at" do
21
+ @comment = Comment.new :updated_at => Time.parse('2010-01-02 12:34:48 +0000'), :title => '-'
22
+ CouchPotato.database.save_document! @comment
23
+ CouchPotato.couchrest_database.get(@comment.id).updated_at.should == Time.parse('2010-01-02 12:34:48 +0000')
24
+ end
25
+ end
26
+
27
+ describe "fails" do
28
+ it "should not store anything" do
29
+ @comment = Comment.new
30
+ CouchPotato.database.save_document @comment
31
+ CouchPotato.couchrest_database.documents['rows'].should be_empty
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,239 @@
1
+ require 'spec_helper'
2
+
3
+ class Build
4
+ include CouchPotato::Persistence
5
+
6
+ property :state
7
+ property :time
8
+
9
+ view :timeline, :key => :time
10
+ view :count, :key => :time, :reduce => true
11
+ view :minimal_timeline, :key => :time, :properties => [:state], :type => :properties
12
+ view :key_array_timeline, :key => [:time, :state]
13
+ view :custom_timeline, :map => "function(doc) { emit(doc._id, {state: 'custom_' + doc.state}); }", :type => :custom
14
+ view :custom_timeline_returns_docs, :map => "function(doc) { emit(doc._id, null); }", :include_docs => true, :type => :custom
15
+ view :custom_with_reduce, :map => "function(doc) {if(doc.foreign_key) {emit(doc.foreign_key, 1);} else {emit(doc._id, 1)}}", :reduce => "function(key, values) {return({\"count\": sum(values)});}", :group => true, :type => :custom
16
+ view :custom_count_with_reduce, :map => "function(doc) {if(doc.foreign_key) {emit(doc.foreign_key, 1);} else {emit(doc._id, 1)}}", :reduce => "function(key, values) {return(sum(values));}", :group => true, :type => :custom
17
+ view :raw, :type => :raw, :map => "function(doc) {emit(doc._id, doc.state)}"
18
+ view :filtered_raw, :type => :raw, :map => "function(doc) {emit(doc._id, doc.state)}", :results_filter => lambda{|res| res['rows'].map{|row| row['value']}}
19
+ view :with_view_options, :group => true, :key => :time
20
+ end
21
+
22
+ class CustomBuild < Build
23
+ end
24
+
25
+ describe 'view' do
26
+ before(:each) do
27
+ recreate_db
28
+ @db = CouchPotato.database
29
+ end
30
+
31
+ it "should return instances of the class" do
32
+ @db.save_document Build.new(:state => 'success', :time => '2008-01-01')
33
+ results = @db.view(Build.timeline)
34
+ results.map(&:class).should == [Build]
35
+ end
36
+
37
+ it "should return the ids if there document was not included" do
38
+ build = Build.new(:state => 'success', :time => '2008-01-01')
39
+ @db.save_document build
40
+ results = @db.view(Build.timeline(:include_docs => false))
41
+ results.should == [build.id]
42
+ end
43
+
44
+ it "should pass the view options to the view query" do
45
+ query = mock 'query'
46
+ CouchPotato::View::ViewQuery.stub!(:new).and_return(query)
47
+ query.should_receive(:query_view!).with(hash_including(:key => 1)).and_return('rows' => [])
48
+ @db.view Build.timeline(:key => 1)
49
+ end
50
+
51
+ it "should not return documents that don't have a matching JSON.create_id" do
52
+ CouchPotato.couchrest_database.save_doc({:time => 'x'})
53
+ @db.view(Build.timeline).should == []
54
+ end
55
+
56
+ it "should count documents" do
57
+ @db.save_document! Build.new(:state => 'success', :time => '2008-01-01')
58
+ @db.view(Build.count(:reduce => true)).should == 1
59
+ end
60
+
61
+ it "should count zero documents" do
62
+ @db.view(Build.count(:reduce => true)).should == 0
63
+ end
64
+
65
+ it "should return the total_rows" do
66
+ @db.save_document! Build.new(:state => 'success', :time => '2008-01-01')
67
+ @db.view(Build.count(:reduce => false)).total_rows.should == 1
68
+ end
69
+
70
+ describe "with multiple keys" do
71
+ it "should return the documents with matching keys" do
72
+ build = Build.new(:state => 'success', :time => '2008-01-01')
73
+ @db.save! build
74
+ @db.view(Build.timeline(:keys => ['2008-01-01'])).should == [build]
75
+ end
76
+
77
+ it "should not return documents with non-matching keys" do
78
+ build = Build.new(:state => 'success', :time => '2008-01-01')
79
+ @db.save! build
80
+ @db.view(Build.timeline(:keys => ['2008-01-02'])).should be_empty
81
+ end
82
+ end
83
+
84
+ describe "properties defined" do
85
+ it "should assign the configured properties" do
86
+ CouchPotato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => 'Build')
87
+ @db.view(Build.minimal_timeline).first.state.should == 'success'
88
+ end
89
+
90
+ it "should not assign the properties not configured" do
91
+ CouchPotato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => 'Build')
92
+ @db.view(Build.minimal_timeline).first.time.should be_nil
93
+ end
94
+
95
+ it "should assign the id even if it is not configured" do
96
+ id = CouchPotato.couchrest_database.save_doc(:state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => 'Build')['id']
97
+ @db.view(Build.minimal_timeline).first._id.should == id
98
+ end
99
+ end
100
+
101
+ describe "no properties defined" do
102
+ it "should assign all properties to the objects by default" do
103
+ id = CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => 'Build'})['id']
104
+ result = @db.view(Build.timeline).first
105
+ result.state.should == 'success'
106
+ result.time.should == '2008-01-01'
107
+ result._id.should == id
108
+ end
109
+ end
110
+
111
+ describe "map function given" do
112
+ it "should still return instances of the class" do
113
+ CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
114
+ @db.view(Build.custom_timeline).map(&:class).should == [Build]
115
+ end
116
+
117
+ it "should assign the properties from the value" do
118
+ CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
119
+ @db.view(Build.custom_timeline).map(&:state).should == ['custom_success']
120
+ end
121
+
122
+ it "should assign the id" do
123
+ doc = CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
124
+ @db.view(Build.custom_timeline).map(&:_id).should == [doc['id']]
125
+ end
126
+
127
+ it "should leave the other properties blank" do
128
+ CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
129
+ @db.view(Build.custom_timeline).map(&:time).should == [nil]
130
+ end
131
+
132
+ describe "that returns null documents" do
133
+ it "should return instances of the class" do
134
+ CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
135
+ @db.view(Build.custom_timeline_returns_docs).map(&:class).should == [Build]
136
+ end
137
+
138
+ it "should assign the properties from the value" do
139
+ CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01'})
140
+ @db.view(Build.custom_timeline_returns_docs).map(&:state).should == ['success']
141
+ end
142
+
143
+ it "should still return instance of class if document included JSON.create_id" do
144
+ CouchPotato.couchrest_database.save_doc({:state => 'success', :time => '2008-01-01', JSON.create_id.to_sym => "Build"})
145
+ view_data = @db.view(Build.custom_timeline_returns_docs)
146
+ view_data.map(&:class).should == [Build]
147
+ view_data.map(&:state).should == ['success']
148
+ end
149
+ end
150
+
151
+ describe "additional reduce function given" do
152
+ it "should still assign the id" do
153
+ doc = CouchPotato.couchrest_database.save_doc({})
154
+ CouchPotato.couchrest_database.save_doc({:foreign_key => doc['id']})
155
+ @db.view(Build.custom_with_reduce).map(&:_id).should == [doc['id']]
156
+ end
157
+
158
+ describe "when the additional reduce function is a typical count" do
159
+ it "should parse the reduce count" do
160
+ doc = CouchPotato.couchrest_database.save_doc({})
161
+ CouchPotato.couchrest_database.save_doc({:foreign_key => doc['id']})
162
+ @db.view(Build.custom_count_with_reduce(:reduce => true)).should == 2
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ describe "with array as key" do
169
+ it "should create a map function with the composite key" do
170
+ CouchPotato::View::ViewQuery.should_receive(:new) do |db, design_name, view, list|
171
+ view['key_array_timeline'][:map].should match(/emit\(\[doc\['time'\], doc\['state'\]\]/)
172
+
173
+ stub('view query', :query_view! => {'rows' => []})
174
+ end
175
+ @db.view Build.key_array_timeline
176
+ end
177
+ end
178
+
179
+ describe "raw view" do
180
+ it "should return the raw data" do
181
+ @db.save_document Build.new(:state => 'success', :time => '2008-01-01')
182
+ @db.view(Build.raw)['rows'][0]['value'].should == 'success'
183
+ end
184
+
185
+ it "should return filtred raw data" do
186
+ @db.save_document Build.new(:state => 'success', :time => '2008-01-01')
187
+ @db.view(Build.filtered_raw).should == ['success']
188
+ end
189
+
190
+ it "should pass view options declared in the view declaration to the query" do
191
+ view_query = mock 'view_query'
192
+ CouchPotato::View::ViewQuery.stub!(:new).and_return(view_query)
193
+ view_query.should_receive(:query_view!).with(hash_including(:group => true)).and_return({'rows' => []})
194
+ @db.view(Build.with_view_options)
195
+ end
196
+ end
197
+
198
+ describe "inherited views" do
199
+ it "should support parent views for objects of the subclass" do
200
+ @db.save_document CustomBuild.new(:state => 'success', :time => '2008-01-01')
201
+ @db.view(CustomBuild.timeline).size.should == 1
202
+ @db.view(CustomBuild.timeline).first.should be_kind_of(CustomBuild)
203
+ end
204
+ end
205
+
206
+ describe "list functions" do
207
+ class Coworker
208
+ include CouchPotato::Persistence
209
+
210
+ property :name
211
+
212
+ view :all_with_list, :key => :name, :list => :append_doe
213
+ view :all, :key => :name
214
+
215
+ list :append_doe, <<-JS
216
+ function(head, req) {
217
+ var row;
218
+ send('{"rows": [');
219
+ while(row = getRow()) {
220
+ row.doc.name = row.doc.name + ' doe';
221
+ send(JSON.stringify(row));
222
+ };
223
+ send(']}');
224
+ }
225
+ JS
226
+ end
227
+
228
+ it "should use the list function declared at class level" do
229
+ @db.save! Coworker.new(:name => 'joe')
230
+ @db.view(Coworker.all_with_list).first.name.should == 'joe doe'
231
+ end
232
+
233
+ it "should use the list function passed at runtime" do
234
+ @db.save! Coworker.new(:name => 'joe')
235
+ @db.view(Coworker.all(:list => :append_doe)).first.name.should == 'joe doe'
236
+ end
237
+
238
+ end
239
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ class Test
4
+ include CouchPotato::Persistence
5
+
6
+ property :test, :default => 'Test value'
7
+ property :complex, :default => [1, 2, 3]
8
+ end
9
+
10
+ describe 'default properties' do
11
+ before(:all) do
12
+ recreate_db
13
+ end
14
+
15
+ it "should use the default value if nothing is supplied" do
16
+ t = Test.new
17
+
18
+ t.test.should == 'Test value'
19
+ end
20
+
21
+ it "should persist the default value if nothing is supplied" do
22
+ t = Test.new
23
+ CouchPotato.database.save_document! t
24
+
25
+ t = CouchPotato.database.load_document t.id
26
+ t.test.should == 'Test value'
27
+ end
28
+
29
+ it "should not have the same default for two instances of the object" do
30
+ t = Test.new
31
+ t2 = Test.new
32
+ t.complex.object_id.should_not == t2.complex.object_id
33
+ end
34
+
35
+ it "should not return the default value when the actual value is empty" do
36
+ t = Test.new(:complex => []).complex.should == []
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'destroy' do
4
+ before(:all) do
5
+ recreate_db
6
+ end
7
+
8
+ before(:each) do
9
+ @comment = Comment.new :title => 'title'
10
+ CouchPotato.database.save_document! @comment
11
+ @comment_id = @comment.id
12
+ CouchPotato.database.destroy_document @comment
13
+ end
14
+
15
+ it "should unset the id" do
16
+ @comment._id.should be_nil
17
+ end
18
+
19
+ it "should unset the revision" do
20
+ @comment._rev.should be_nil
21
+ end
22
+
23
+ it "should remove the document from the database" do
24
+ lambda {
25
+ CouchPotato.couchrest_database.get(@comment_id).should
26
+ }.should raise_error(RestClient::ResourceNotFound)
27
+ end
28
+
29
+ end
@@ -0,0 +1,10 @@
1
+ class Address
2
+ include CouchPotato::Persistence
3
+
4
+ property :street
5
+ property :city
6
+ property :state
7
+ property :zip
8
+ property :country
9
+ property :verified, :type => :boolean
10
+ end
@@ -0,0 +1,6 @@
1
+ class Person
2
+ include CouchPotato::Persistence
3
+
4
+ property :name, :type => Address
5
+ property :ship_address
6
+ end
@@ -0,0 +1,323 @@
1
+ require 'spec_helper'
2
+ require 'fixtures/address'
3
+ require 'fixtures/person'
4
+
5
+ class Watch
6
+ include CouchPotato::Persistence
7
+
8
+ property :time, :type => Time
9
+ property :date, :type => Date
10
+ property :overwritten_read
11
+ property :overwritten_write
12
+
13
+ def overwritten_read
14
+ super.to_s
15
+ end
16
+
17
+ def overwritten_write=(value)
18
+ super value.to_s
19
+ end
20
+ end
21
+
22
+ class CuckooClock < Watch
23
+ property :cuckoo
24
+ end
25
+
26
+ describe 'properties' do
27
+ before(:all) do
28
+ recreate_db
29
+ end
30
+
31
+ it "should allow me to overwrite read accessor and call super" do
32
+ Watch.new(:overwritten_read => 1).overwritten_read.should == '1'
33
+ end
34
+
35
+ it "should allow me to overwrite write accessor and call super" do
36
+ Watch.new(:overwritten_write => 1).overwritten_write.should == '1'
37
+ end
38
+
39
+ it "should return the property names" do
40
+ Comment.property_names.should == [:created_at, :updated_at, :title]
41
+ end
42
+
43
+ it "should persist a string" do
44
+ c = Comment.new :title => 'my title'
45
+ CouchPotato.database.save_document! c
46
+ c = CouchPotato.database.load_document c.id
47
+ c.title.should == 'my title'
48
+ end
49
+
50
+ it "should persist a number" do
51
+ c = Comment.new :title => 3
52
+ CouchPotato.database.save_document! c
53
+ c = CouchPotato.database.load_document c.id
54
+ c.title.should == 3
55
+ end
56
+
57
+ it "should persist a hash" do
58
+ c = Comment.new :title => {'key' => 'value'}
59
+ CouchPotato.database.save_document! c
60
+ c = CouchPotato.database.load_document c.id
61
+ c.title.should == {'key' => 'value'}
62
+ end
63
+
64
+ def it_should_persist value
65
+ c = Comment.new :title => value
66
+ CouchPotato.database.save_document! c
67
+ c = CouchPotato.database.load_document c.id
68
+ c.title.should == value
69
+ end
70
+
71
+ it "should persist a child class" do
72
+ it_should_persist Child.new('text' => 'some text')
73
+ end
74
+
75
+ it "should persist a hash with a child class" do
76
+ it_should_persist 'child' => Child.new('text' => 'some text')
77
+ end
78
+
79
+ it "should persist an array with a child class" do
80
+ it_should_persist [Child.new('text' => 'some text')]
81
+ end
82
+
83
+ it "should persist something very complex" do
84
+ something_very_complex = [
85
+ [
86
+ [
87
+ {
88
+ 'what' => [
89
+ {
90
+ 'ever' => Child.new('text' => 'some text')
91
+ }
92
+ ],
93
+ 'number' => 3
94
+ },
95
+ "string"
96
+ ],
97
+ Child.new('text' => 'nothing')
98
+ ]
99
+ ]
100
+ it_should_persist something_very_complex
101
+ end
102
+
103
+ it "should persist an object" do
104
+ p = Person.new
105
+ a = Address.new :city => 'Denver'
106
+ p.ship_address = a
107
+ CouchPotato.database.save_document! p
108
+ p = CouchPotato.database.load_document p.id
109
+ p.ship_address.should === a
110
+ end
111
+
112
+ it "should persist null for a null " do
113
+ p = Person.new
114
+ p.ship_address = nil
115
+ CouchPotato.database.save_document! p
116
+ p = CouchPotato.database.load_document p.id
117
+ p.ship_address.should be_nil
118
+ end
119
+
120
+ it "should actually pass the null value down in the JSON document " do
121
+ p = Person.new
122
+ p.ship_address = nil
123
+ db = mock(:database)
124
+ db.should_receive(:save_doc).with do |attributes|
125
+ attributes.has_key?(:ship_address).should == true
126
+ end.and_return({})
127
+ CouchPotato.database.stub(:couchrest_database).and_return(db)
128
+ CouchPotato.database.save_document! p
129
+ end
130
+
131
+ it "should persist false for a false" do
132
+ p = Person.new
133
+ p.ship_address = false
134
+ CouchPotato.database.save_document! p
135
+ p = CouchPotato.database.load_document p.id
136
+ p.ship_address.should be_false
137
+ end
138
+
139
+ describe "time properties" do
140
+ it "should persist a Time as utc" do
141
+ time = Time.now
142
+ w = Watch.new :time => time
143
+ CouchPotato.database.save_document! w
144
+ w = CouchPotato.database.load_document w.id
145
+ w.time.to_s.should == time.utc.to_s
146
+ end
147
+
148
+ it "should parse a string and persist it as utc time" do
149
+ w = Watch.new :time => '2009-01-01 13:25 +0100'
150
+ CouchPotato.database.save_document! w
151
+ w = CouchPotato.database.load_document w.id
152
+ w.time.should be_a(Time)
153
+ w.time.should == Time.parse('2009-01-01 12:25 +0000')
154
+ end
155
+
156
+ it "should store nil" do
157
+ w = Watch.new :time => nil
158
+ CouchPotato.database.save_document! w
159
+ w = CouchPotato.database.load_document w.id
160
+ w.time.should be_nil
161
+ end
162
+
163
+ it "should store an empty string as nil" do
164
+ w = Watch.new :time => ''
165
+ CouchPotato.database.save_document! w
166
+ w = CouchPotato.database.load_document w.id
167
+ w.time.should be_nil
168
+ end
169
+ end
170
+
171
+ describe "date properties" do
172
+ it "should persist a date" do
173
+ date = Date.today
174
+ w = Watch.new :date => date
175
+ CouchPotato.database.save_document! w
176
+ w = CouchPotato.database.load_document w.id
177
+ w.date.should == date
178
+ end
179
+
180
+ it "should parse a string and persist it as a date" do
181
+ w = Watch.new :date => '2009-01-10'
182
+ CouchPotato.database.save_document! w
183
+ w = CouchPotato.database.load_document w.id
184
+ w.date.should == Date.parse('2009-01-10')
185
+ end
186
+
187
+ it "should store nil" do
188
+ w = Watch.new :date => nil
189
+ CouchPotato.database.save_document! w
190
+ w = CouchPotato.database.load_document w.id
191
+ w.date.should be_nil
192
+ end
193
+
194
+ it "should store an empty string as nil" do
195
+ w = Watch.new :date => ''
196
+ CouchPotato.database.save_document! w
197
+ w = CouchPotato.database.load_document w.id
198
+ w.date.should be_nil
199
+ end
200
+ end
201
+
202
+ describe "boolean properties" do
203
+ it "should persist '0' as false" do
204
+ a = Address.new
205
+ a.verified = '0'
206
+ CouchPotato.database.save_document! a
207
+ a = CouchPotato.database.load_document a.id
208
+ a.verified.should be_false
209
+ end
210
+
211
+ it "should persist 0 as false" do
212
+ a = Address.new
213
+ a.verified = 0
214
+ CouchPotato.database.save_document! a
215
+ a = CouchPotato.database.load_document a.id
216
+ a.verified.should be_false
217
+ end
218
+
219
+ it "should persist 'false' as false" do
220
+ a = Address.new
221
+ a.verified = 'false'
222
+ CouchPotato.database.save_document! a
223
+ a = CouchPotato.database.load_document a.id
224
+ a.verified.should be_false
225
+ end
226
+
227
+ it "should persist '1' as true" do
228
+ a = Address.new
229
+ a.verified = '1'
230
+ CouchPotato.database.save_document! a
231
+ a = CouchPotato.database.load_document a.id
232
+ a.verified.should be_true
233
+ end
234
+
235
+ it "should persist 1 as true" do
236
+ a = Address.new
237
+ a.verified = 1
238
+ CouchPotato.database.save_document! a
239
+ a = CouchPotato.database.load_document a.id
240
+ a.verified.should be_true
241
+ end
242
+
243
+ it "should leave nil as nil" do
244
+ a = Address.new
245
+ a.verified = nil
246
+ CouchPotato.database.save_document! a
247
+ a = CouchPotato.database.load_document a.id
248
+ a.verified.should be_nil
249
+ end
250
+ end
251
+
252
+ describe "predicate" do
253
+ it "should return true if property set" do
254
+ Comment.new(:title => 'title').title?.should be_true
255
+ end
256
+
257
+ it "should return false if property nil" do
258
+ Comment.new.title?.should be_false
259
+ end
260
+
261
+ it "should return false if property false" do
262
+ Comment.new(:title => false).title?.should be_false
263
+ end
264
+
265
+ it "should return false if property blank" do
266
+ Comment.new(:title => '').title?.should be_false
267
+ end
268
+ end
269
+
270
+ describe "with subclasses" do
271
+ it "should include properties of superclasses" do
272
+ CuckooClock.properties.map(&:name).should include(:time)
273
+ CuckooClock.properties.map(&:name).should include(:cuckoo)
274
+ end
275
+
276
+ it "should return attributes of superclasses" do
277
+ clock = CuckooClock.new(:time => Time.now, :cuckoo => 'bavarian')
278
+ clock.attributes[:time].should_not == nil
279
+ clock.attributes[:cuckoo].should == 'bavarian'
280
+ end
281
+ end
282
+
283
+ describe "inspecting an object" do
284
+ let(:comment) do
285
+ comment = Comment.new(:title => 'title')
286
+ comment.instance_eval do
287
+ @_id = "123456abcdef"
288
+ @_rev = "1-654321fedcba"
289
+ end
290
+ comment
291
+ end
292
+
293
+ it "should not include change-tracking variables" do
294
+ comment.inspect.should_not include('title_was')
295
+ end
296
+
297
+ it "should include the normal persistent variables" do
298
+ comment.inspect.should include('title: "title"')
299
+ end
300
+
301
+ it "should include the id" do
302
+ comment.inspect.should include(%Q{_id: "123456abcdef",})
303
+ end
304
+
305
+ it "should include the revision" do
306
+ comment.inspect.should include(%Q{_rev: "1-654321fedcba",})
307
+ end
308
+
309
+ it "should return a complete string" do
310
+ # stub to work around (un)sorted hash on different rubies
311
+ comment.stub!(:attributes).and_return([['created_at', ''], ['updated_at', ''], ['title', 'title']])
312
+ comment.inspect.should == %Q{#<Comment _id: "123456abcdef", _rev: "1-654321fedcba", created_at: "", updated_at: "", title: "title">}
313
+ end
314
+
315
+ it "should include complex datatypes fully inspected" do
316
+ comment.title = {'en' => 'Blog post'}
317
+ comment.inspect.should include('title: {"en"=>"Blog post"}')
318
+
319
+ comment.title = nil
320
+ comment.inspect.should include('title: nil')
321
+ end
322
+ end
323
+ end