brianmario-couchrest 0.23

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 (92) hide show
  1. data/LICENSE +176 -0
  2. data/README.md +95 -0
  3. data/Rakefile +75 -0
  4. data/THANKS.md +18 -0
  5. data/examples/model/example.rb +144 -0
  6. data/examples/word_count/markov +38 -0
  7. data/examples/word_count/views/books/chunked-map.js +3 -0
  8. data/examples/word_count/views/books/united-map.js +1 -0
  9. data/examples/word_count/views/markov/chain-map.js +6 -0
  10. data/examples/word_count/views/markov/chain-reduce.js +7 -0
  11. data/examples/word_count/views/word_count/count-map.js +6 -0
  12. data/examples/word_count/views/word_count/count-reduce.js +3 -0
  13. data/examples/word_count/word_count.rb +46 -0
  14. data/examples/word_count/word_count_query.rb +40 -0
  15. data/examples/word_count/word_count_views.rb +26 -0
  16. data/lib/couchrest.rb +198 -0
  17. data/lib/couchrest/commands/generate.rb +71 -0
  18. data/lib/couchrest/commands/push.rb +103 -0
  19. data/lib/couchrest/core/database.rb +303 -0
  20. data/lib/couchrest/core/design.rb +79 -0
  21. data/lib/couchrest/core/document.rb +87 -0
  22. data/lib/couchrest/core/response.rb +16 -0
  23. data/lib/couchrest/core/server.rb +88 -0
  24. data/lib/couchrest/core/view.rb +4 -0
  25. data/lib/couchrest/helper/pager.rb +103 -0
  26. data/lib/couchrest/helper/streamer.rb +44 -0
  27. data/lib/couchrest/helper/upgrade.rb +51 -0
  28. data/lib/couchrest/mixins.rb +4 -0
  29. data/lib/couchrest/mixins/attachments.rb +31 -0
  30. data/lib/couchrest/mixins/callbacks.rb +483 -0
  31. data/lib/couchrest/mixins/class_proxy.rb +108 -0
  32. data/lib/couchrest/mixins/design_doc.rb +90 -0
  33. data/lib/couchrest/mixins/document_queries.rb +44 -0
  34. data/lib/couchrest/mixins/extended_attachments.rb +68 -0
  35. data/lib/couchrest/mixins/extended_document_mixins.rb +7 -0
  36. data/lib/couchrest/mixins/properties.rb +129 -0
  37. data/lib/couchrest/mixins/validation.rb +242 -0
  38. data/lib/couchrest/mixins/views.rb +169 -0
  39. data/lib/couchrest/monkeypatches.rb +113 -0
  40. data/lib/couchrest/more/casted_model.rb +28 -0
  41. data/lib/couchrest/more/extended_document.rb +215 -0
  42. data/lib/couchrest/more/property.rb +40 -0
  43. data/lib/couchrest/support/blank.rb +42 -0
  44. data/lib/couchrest/support/class.rb +176 -0
  45. data/lib/couchrest/validation/auto_validate.rb +163 -0
  46. data/lib/couchrest/validation/contextual_validators.rb +78 -0
  47. data/lib/couchrest/validation/validation_errors.rb +118 -0
  48. data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
  49. data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
  50. data/lib/couchrest/validation/validators/format_validator.rb +117 -0
  51. data/lib/couchrest/validation/validators/formats/email.rb +66 -0
  52. data/lib/couchrest/validation/validators/formats/url.rb +43 -0
  53. data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
  54. data/lib/couchrest/validation/validators/length_validator.rb +134 -0
  55. data/lib/couchrest/validation/validators/method_validator.rb +89 -0
  56. data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
  57. data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
  58. data/spec/couchrest/core/couchrest_spec.rb +201 -0
  59. data/spec/couchrest/core/database_spec.rb +699 -0
  60. data/spec/couchrest/core/design_spec.rb +138 -0
  61. data/spec/couchrest/core/document_spec.rb +267 -0
  62. data/spec/couchrest/core/server_spec.rb +35 -0
  63. data/spec/couchrest/helpers/pager_spec.rb +122 -0
  64. data/spec/couchrest/helpers/streamer_spec.rb +23 -0
  65. data/spec/couchrest/more/casted_extended_doc_spec.rb +40 -0
  66. data/spec/couchrest/more/casted_model_spec.rb +98 -0
  67. data/spec/couchrest/more/extended_doc_attachment_spec.rb +130 -0
  68. data/spec/couchrest/more/extended_doc_spec.rb +509 -0
  69. data/spec/couchrest/more/extended_doc_subclass_spec.rb +98 -0
  70. data/spec/couchrest/more/extended_doc_view_spec.rb +355 -0
  71. data/spec/couchrest/more/property_spec.rb +136 -0
  72. data/spec/fixtures/attachments/README +3 -0
  73. data/spec/fixtures/attachments/couchdb.png +0 -0
  74. data/spec/fixtures/attachments/test.html +11 -0
  75. data/spec/fixtures/more/article.rb +34 -0
  76. data/spec/fixtures/more/card.rb +20 -0
  77. data/spec/fixtures/more/course.rb +14 -0
  78. data/spec/fixtures/more/event.rb +6 -0
  79. data/spec/fixtures/more/invoice.rb +17 -0
  80. data/spec/fixtures/more/person.rb +8 -0
  81. data/spec/fixtures/more/question.rb +6 -0
  82. data/spec/fixtures/more/service.rb +12 -0
  83. data/spec/fixtures/views/lib.js +3 -0
  84. data/spec/fixtures/views/test_view/lib.js +3 -0
  85. data/spec/fixtures/views/test_view/only-map.js +4 -0
  86. data/spec/fixtures/views/test_view/test-map.js +3 -0
  87. data/spec/fixtures/views/test_view/test-reduce.js +3 -0
  88. data/spec/spec.opts +6 -0
  89. data/spec/spec_helper.rb +26 -0
  90. data/utils/remap.rb +27 -0
  91. data/utils/subset.rb +30 -0
  92. metadata +200 -0
@@ -0,0 +1,23 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe CouchRest::Streamer do
4
+ before(:all) do
5
+ @cr = CouchRest.new(COUCHHOST)
6
+ @db = @cr.database(TESTDB)
7
+ @db.delete! rescue nil
8
+ @db = @cr.create_db(TESTDB) rescue nil
9
+ @streamer = CouchRest::Streamer.new(@db)
10
+ @docs = (1..1000).collect{|i| {:integer => i, :string => i.to_s}}
11
+ @db.bulk_save(@docs)
12
+ end
13
+
14
+ it "should yield each row in a view" do
15
+ count = 0
16
+ sum = 0
17
+ @streamer.view("_all_docs") do |row|
18
+ count += 1
19
+ end
20
+ count.should == 1001
21
+ end
22
+
23
+ end
@@ -0,0 +1,40 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+ require File.join(FIXTURE_PATH, 'more', 'card')
3
+
4
+ class Car < CouchRest::ExtendedDocument
5
+ use_database TEST_SERVER.default_database
6
+
7
+ property :name
8
+ property :driver, :cast_as => 'Driver'
9
+ end
10
+
11
+ class Driver < CouchRest::ExtendedDocument
12
+ use_database TEST_SERVER.default_database
13
+ # You have to add a casted_by accessor if you want to reach a casted extended doc parent
14
+ attr_accessor :casted_by
15
+
16
+ property :name
17
+ end
18
+
19
+ describe "casting an extended document" do
20
+
21
+ before(:each) do
22
+ @car = Car.new(:name => 'Renault 306')
23
+ @driver = Driver.new(:name => 'Matt')
24
+ end
25
+
26
+ # it "should not create an empty casted object" do
27
+ # @car.driver.should be_nil
28
+ # end
29
+
30
+ it "should let you assign the casted attribute after instantializing an object" do
31
+ @car.driver = @driver
32
+ @car.driver.name.should == 'Matt'
33
+ end
34
+
35
+ it "should let the casted document who casted it" do
36
+ Car.new(:name => 'Renault 306', :driver => @driver)
37
+ @car.driver.casted_by.should == @car
38
+ end
39
+
40
+ end
@@ -0,0 +1,98 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'spec_helper')
2
+ require File.join(FIXTURE_PATH, 'more', 'card')
3
+
4
+ class WithCastedModelMixin < Hash
5
+ include CouchRest::CastedModel
6
+ property :name
7
+ end
8
+
9
+ class DummyModel < CouchRest::ExtendedDocument
10
+ use_database TEST_SERVER.default_database
11
+ raise "Default DB not set" if TEST_SERVER.default_database.nil?
12
+ property :casted_attribute, :cast_as => 'WithCastedModelMixin'
13
+ property :keywords, :cast_as => ["String"]
14
+ end
15
+
16
+ describe CouchRest::CastedModel do
17
+
18
+ describe "A non hash class including CastedModel" do
19
+ it "should fail raising and include error" do
20
+ lambda do
21
+ class NotAHashButWithCastedModelMixin
22
+ include CouchRest::CastedModel
23
+ property :name
24
+ end
25
+
26
+ end.should raise_error
27
+ end
28
+ end
29
+
30
+ describe "isolated" do
31
+ before(:each) do
32
+ @obj = WithCastedModelMixin.new
33
+ end
34
+ it "should automatically include the property mixin and define getters and setters" do
35
+ @obj.name = 'Matt'
36
+ @obj.name.should == 'Matt'
37
+ end
38
+ end
39
+
40
+ describe "casted as attribute" do
41
+ before(:each) do
42
+ @obj = DummyModel.new(:casted_attribute => {:name => 'whatever'})
43
+ @casted_obj = @obj.casted_attribute
44
+ end
45
+
46
+ it "should be available from its parent" do
47
+ @casted_obj.should be_an_instance_of(WithCastedModelMixin)
48
+ end
49
+
50
+ it "should have the getters defined" do
51
+ @casted_obj.name.should == 'whatever'
52
+ end
53
+
54
+ it "should know who casted it" do
55
+ @casted_obj.casted_by.should == @obj
56
+ end
57
+ end
58
+
59
+ describe "casted as an array of a different type" do
60
+ before(:each) do
61
+ @obj = DummyModel.new(:keywords => ['couch', 'sofa', 'relax', 'canapé'])
62
+ end
63
+
64
+ it "should cast the array propery" do
65
+ @obj.keywords.should be_an_instance_of(Array)
66
+ @obj.keywords.first.should == 'couch'
67
+ end
68
+
69
+ end
70
+
71
+ describe "saved document with casted models" do
72
+ before(:each) do
73
+ reset_test_db!
74
+ @obj = DummyModel.new(:casted_attribute => {:name => 'whatever'})
75
+ @obj.save.should be_true
76
+ @obj = DummyModel.get(@obj.id)
77
+ end
78
+
79
+ it "should be able to load with the casted models" do
80
+ casted_obj = @obj.casted_attribute
81
+ casted_obj.should_not be_nil
82
+ casted_obj.should be_an_instance_of(WithCastedModelMixin)
83
+ end
84
+
85
+ it "should have defined getters for the casted model" do
86
+ casted_obj = @obj.casted_attribute
87
+ casted_obj.name.should == "whatever"
88
+ end
89
+
90
+ it "should have defined setters for the casted model" do
91
+ casted_obj = @obj.casted_attribute
92
+ casted_obj.name = "test"
93
+ casted_obj.name.should == "test"
94
+ end
95
+
96
+ end
97
+
98
+ end
@@ -0,0 +1,130 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe "ExtendedDocument attachments" do
4
+
5
+ describe "#has_attachment?" do
6
+ before(:each) do
7
+ reset_test_db!
8
+ @obj = Basic.new
9
+ @obj.save.should == true
10
+ @file = File.open(FIXTURE_PATH + '/attachments/test.html')
11
+ @attachment_name = 'my_attachment'
12
+ @obj.create_attachment(:file => @file, :name => @attachment_name)
13
+ end
14
+
15
+ it 'should return false if there is no attachment' do
16
+ @obj.has_attachment?('bogus').should be_false
17
+ end
18
+
19
+ it 'should return true if there is an attachment' do
20
+ @obj.has_attachment?(@attachment_name).should be_true
21
+ end
22
+
23
+ it 'should return true if an object with an attachment is reloaded' do
24
+ @obj.save.should be_true
25
+ reloaded_obj = Basic.get(@obj.id)
26
+ reloaded_obj.has_attachment?(@attachment_name).should be_true
27
+ end
28
+
29
+ it 'should return false if an attachment has been removed' do
30
+ @obj.delete_attachment(@attachment_name)
31
+ @obj.has_attachment?(@attachment_name).should be_false
32
+ end
33
+ end
34
+
35
+ describe "creating an attachment" do
36
+ before(:each) do
37
+ @obj = Basic.new
38
+ @obj.save.should == true
39
+ @file_ext = File.open(FIXTURE_PATH + '/attachments/test.html')
40
+ @file_no_ext = File.open(FIXTURE_PATH + '/attachments/README')
41
+ @attachment_name = 'my_attachment'
42
+ @content_type = 'media/mp3'
43
+ end
44
+
45
+ it "should create an attachment from file with an extension" do
46
+ @obj.create_attachment(:file => @file_ext, :name => @attachment_name)
47
+ @obj.save.should == true
48
+ reloaded_obj = Basic.get(@obj.id)
49
+ reloaded_obj['_attachments'][@attachment_name].should_not be_nil
50
+ end
51
+
52
+ it "should create an attachment from file without an extension" do
53
+ @obj.create_attachment(:file => @file_no_ext, :name => @attachment_name)
54
+ @obj.save.should == true
55
+ reloaded_obj = Basic.get(@obj.id)
56
+ reloaded_obj['_attachments'][@attachment_name].should_not be_nil
57
+ end
58
+
59
+ it 'should raise ArgumentError if :file is missing' do
60
+ lambda{ @obj.create_attachment(:name => @attachment_name) }.should raise_error
61
+ end
62
+
63
+ it 'should raise ArgumentError if :name is missing' do
64
+ lambda{ @obj.create_attachment(:file => @file_ext) }.should raise_error
65
+ end
66
+
67
+ it 'should set the content-type if passed' do
68
+ @obj.create_attachment(:file => @file_ext, :name => @attachment_name, :content_type => @content_type)
69
+ @obj['_attachments'][@attachment_name]['content-type'].should == @content_type
70
+ end
71
+ end
72
+
73
+ describe 'reading, updating, and deleting an attachment' do
74
+ before(:each) do
75
+ @obj = Basic.new
76
+ @file = File.open(FIXTURE_PATH + '/attachments/test.html')
77
+ @attachment_name = 'my_attachment'
78
+ @obj.create_attachment(:file => @file, :name => @attachment_name)
79
+ @obj.save.should == true
80
+ @file.rewind
81
+ @content_type = 'media/mp3'
82
+ end
83
+
84
+ it 'should read an attachment that exists' do
85
+ @obj.read_attachment(@attachment_name).should == @file.read
86
+ end
87
+
88
+ it 'should update an attachment that exists' do
89
+ file = File.open(FIXTURE_PATH + '/attachments/README')
90
+ @file.should_not == file
91
+ @obj.update_attachment(:file => file, :name => @attachment_name)
92
+ @obj.save
93
+ reloaded_obj = Basic.get(@obj.id)
94
+ file.rewind
95
+ reloaded_obj.read_attachment(@attachment_name).should_not == @file.read
96
+ reloaded_obj.read_attachment(@attachment_name).should == file.read
97
+ end
98
+
99
+ it 'should se the content-type if passed' do
100
+ file = File.open(FIXTURE_PATH + '/attachments/README')
101
+ @file.should_not == file
102
+ @obj.update_attachment(:file => file, :name => @attachment_name, :content_type => @content_type)
103
+ @obj['_attachments'][@attachment_name]['content-type'].should == @content_type
104
+ end
105
+
106
+ it 'should delete an attachment that exists' do
107
+ @obj.delete_attachment(@attachment_name)
108
+ @obj.save
109
+ lambda{Basic.get(@obj.id).read_attachment(@attachment_name)}.should raise_error
110
+ end
111
+ end
112
+
113
+ describe "#attachment_url" do
114
+ before(:each) do
115
+ @obj = Basic.new
116
+ @file = File.open(FIXTURE_PATH + '/attachments/test.html')
117
+ @attachment_name = 'my_attachment'
118
+ @obj.create_attachment(:file => @file, :name => @attachment_name)
119
+ @obj.save.should == true
120
+ end
121
+
122
+ it 'should return nil if attachment does not exist' do
123
+ @obj.attachment_url('bogus').should be_nil
124
+ end
125
+
126
+ it 'should return the attachment URL as specified by CouchDB HttpDocumentApi' do
127
+ @obj.attachment_url(@attachment_name).should == "#{Basic.database}/#{@obj.id}/#{@attachment_name}"
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,509 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require File.join(FIXTURE_PATH, 'more', 'article')
3
+ require File.join(FIXTURE_PATH, 'more', 'course')
4
+
5
+
6
+ describe "ExtendedDocument" do
7
+
8
+ class WithDefaultValues < CouchRest::ExtendedDocument
9
+ use_database TEST_SERVER.default_database
10
+ property :preset, :default => {:right => 10, :top_align => false}
11
+ property :set_by_proc, :default => Proc.new{Time.now}, :cast_as => 'Time'
12
+ property :tags, :default => []
13
+ property :name
14
+ timestamps!
15
+ end
16
+
17
+ class WithCallBacks < CouchRest::ExtendedDocument
18
+ use_database TEST_SERVER.default_database
19
+ property :name
20
+ property :run_before_save
21
+ property :run_after_save
22
+ property :run_before_create
23
+ property :run_after_create
24
+ property :run_before_update
25
+ property :run_after_update
26
+
27
+ save_callback :before do |object|
28
+ object.run_before_save = true
29
+ end
30
+ save_callback :after do |object|
31
+ object.run_after_save = true
32
+ end
33
+ create_callback :before do |object|
34
+ object.run_before_create = true
35
+ end
36
+ create_callback :after do |object|
37
+ object.run_after_create = true
38
+ end
39
+ update_callback :before do |object|
40
+ object.run_before_update = true
41
+ end
42
+ update_callback :after do |object|
43
+ object.run_after_update = true
44
+ end
45
+ end
46
+
47
+ class WithTemplateAndUniqueID < CouchRest::ExtendedDocument
48
+ use_database TEST_SERVER.default_database
49
+ unique_id do |model|
50
+ model['important-field']
51
+ end
52
+ property :preset, :default => 'value'
53
+ property :has_no_default
54
+ end
55
+
56
+ before(:each) do
57
+ @obj = WithDefaultValues.new
58
+ end
59
+
60
+ describe "instance database connection" do
61
+ it "should use the default database" do
62
+ @obj.database.name.should == 'couchrest-test'
63
+ end
64
+
65
+ it "should override the default db" do
66
+ @obj.database = TEST_SERVER.database!('couchrest-extendedmodel-test')
67
+ @obj.database.name.should == 'couchrest-extendedmodel-test'
68
+ @obj.database.delete!
69
+ end
70
+ end
71
+
72
+ describe "a new model" do
73
+ it "should be a new_record" do
74
+ @obj = Basic.new
75
+ @obj.rev.should be_nil
76
+ @obj.should be_a_new_record
77
+ end
78
+ it "should be a new_document" do
79
+ @obj = Basic.new
80
+ @obj.rev.should be_nil
81
+ @obj.should be_a_new_document
82
+ end
83
+ end
84
+
85
+ describe "update attributes without saving" do
86
+ before(:each) do
87
+ a = Article.get "big-bad-danger" rescue nil
88
+ a.destroy if a
89
+ @art = Article.new(:title => "big bad danger")
90
+ @art.save
91
+ end
92
+ it "should work for attribute= methods" do
93
+ @art['title'].should == "big bad danger"
94
+ @art.update_attributes_without_saving('date' => Time.now, :title => "super danger")
95
+ @art['title'].should == "super danger"
96
+ end
97
+
98
+ it "should flip out if an attribute= method is missing" do
99
+ lambda {
100
+ @art.update_attributes_without_saving('slug' => "new-slug", :title => "super danger")
101
+ }.should raise_error
102
+ end
103
+
104
+ it "should not change other attributes if there is an error" do
105
+ lambda {
106
+ @art.update_attributes_without_saving('slug' => "new-slug", :title => "super danger")
107
+ }.should raise_error
108
+ @art['title'].should == "big bad danger"
109
+ end
110
+ end
111
+
112
+ describe "update attributes" do
113
+ before(:each) do
114
+ a = Article.get "big-bad-danger" rescue nil
115
+ a.destroy if a
116
+ @art = Article.new(:title => "big bad danger")
117
+ @art.save
118
+ end
119
+ it "should save" do
120
+ @art['title'].should == "big bad danger"
121
+ @art.update_attributes('date' => Time.now, :title => "super danger")
122
+ loaded = Article.get(@art.id)
123
+ loaded['title'].should == "super danger"
124
+ end
125
+ end
126
+
127
+ describe "with default" do
128
+ it "should have the default value set at initalization" do
129
+ @obj.preset.should == {:right => 10, :top_align => false}
130
+ end
131
+
132
+ it "should automatically call a proc default at initialization" do
133
+ @obj.set_by_proc.should be_an_instance_of(Time)
134
+ @obj.set_by_proc.should == @obj.set_by_proc
135
+ @obj.set_by_proc.should < Time.now
136
+ end
137
+
138
+ it "should let you overwrite the default values" do
139
+ obj = WithDefaultValues.new(:preset => 'test')
140
+ obj.preset = 'test'
141
+ end
142
+
143
+ it "should work with a default empty array" do
144
+ obj = WithDefaultValues.new(:tags => ['spec'])
145
+ obj.tags.should == ['spec']
146
+ end
147
+ end
148
+
149
+ describe "a doc with template values (CR::Model spec)" do
150
+ before(:all) do
151
+ WithTemplateAndUniqueID.all.map{|o| o.destroy(true)}
152
+ WithTemplateAndUniqueID.database.bulk_delete
153
+ @tmpl = WithTemplateAndUniqueID.new
154
+ @tmpl2 = WithTemplateAndUniqueID.new(:preset => 'not_value', 'important-field' => '1')
155
+ end
156
+ it "should have fields set when new" do
157
+ @tmpl.preset.should == 'value'
158
+ end
159
+ it "shouldn't override explicitly set values" do
160
+ @tmpl2.preset.should == 'not_value'
161
+ end
162
+ it "shouldn't override existing documents" do
163
+ @tmpl2.save
164
+ tmpl2_reloaded = WithTemplateAndUniqueID.get(@tmpl2.id)
165
+ @tmpl2.preset.should == 'not_value'
166
+ tmpl2_reloaded.preset.should == 'not_value'
167
+ end
168
+ end
169
+
170
+ describe "getting a model" do
171
+ before(:all) do
172
+ @art = Article.new(:title => 'All About Getting')
173
+ @art.save
174
+ end
175
+ it "should load and instantiate it" do
176
+ foundart = Article.get @art.id
177
+ foundart.title.should == "All About Getting"
178
+ end
179
+ end
180
+
181
+ describe "getting a model with a subobjects array" do
182
+ before(:all) do
183
+ course_doc = {
184
+ "title" => "Metaphysics 200",
185
+ "questions" => [
186
+ {
187
+ "q" => "Carve the ___ of reality at the ___.",
188
+ "a" => ["beast","joints"]
189
+ },{
190
+ "q" => "Who layed the smack down on Leibniz's Law?",
191
+ "a" => "Willard Van Orman Quine"
192
+ }
193
+ ]
194
+ }
195
+ r = Course.database.save_doc course_doc
196
+ @course = Course.get r['id']
197
+ end
198
+ it "should load the course" do
199
+ @course.title.should == "Metaphysics 200"
200
+ end
201
+ it "should instantiate them as such" do
202
+ @course["questions"][0].a[0].should == "beast"
203
+ end
204
+ end
205
+
206
+ describe "finding all instances of a model" do
207
+ before(:all) do
208
+ WithTemplateAndUniqueID.all.map{|o| o.destroy(true)}
209
+ WithTemplateAndUniqueID.database.bulk_delete
210
+ WithTemplateAndUniqueID.new('important-field' => '1').save
211
+ WithTemplateAndUniqueID.new('important-field' => '2').save
212
+ WithTemplateAndUniqueID.new('important-field' => '3').save
213
+ WithTemplateAndUniqueID.new('important-field' => '4').save
214
+ end
215
+ it "should make the design doc" do
216
+ WithTemplateAndUniqueID.all
217
+ d = WithTemplateAndUniqueID.design_doc
218
+ d['views']['all']['map'].should include('WithTemplateAndUniqueID')
219
+ end
220
+ it "should find all" do
221
+ rs = WithTemplateAndUniqueID.all
222
+ rs.length.should == 4
223
+ end
224
+ end
225
+
226
+ describe "finding the first instance of a model" do
227
+ before(:each) do
228
+ @db = reset_test_db!
229
+ WithTemplateAndUniqueID.new('important-field' => '1').save
230
+ WithTemplateAndUniqueID.new('important-field' => '2').save
231
+ WithTemplateAndUniqueID.new('important-field' => '3').save
232
+ WithTemplateAndUniqueID.new('important-field' => '4').save
233
+ end
234
+ it "should make the design doc" do
235
+ WithTemplateAndUniqueID.all
236
+ d = WithTemplateAndUniqueID.design_doc
237
+ d['views']['all']['map'].should include('WithTemplateAndUniqueID')
238
+ end
239
+ it "should find first" do
240
+ rs = WithTemplateAndUniqueID.first
241
+ rs['important-field'].should == "1"
242
+ end
243
+ it "should return nil if no instances are found" do
244
+ WithTemplateAndUniqueID.all.each {|obj| obj.destroy }
245
+ WithTemplateAndUniqueID.first.should be_nil
246
+ end
247
+ end
248
+
249
+ describe "getting a model with a subobject field" do
250
+ before(:all) do
251
+ course_doc = {
252
+ "title" => "Metaphysics 410",
253
+ "professor" => {
254
+ "name" => ["Mark", "Hinchliff"]
255
+ },
256
+ "final_test_at" => "2008/12/19 13:00:00 +0800"
257
+ }
258
+ r = Course.database.save_doc course_doc
259
+ @course = Course.get r['id']
260
+ end
261
+ it "should load the course" do
262
+ @course["professor"]["name"][1].should == "Hinchliff"
263
+ end
264
+ it "should instantiate the professor as a person" do
265
+ @course['professor'].last_name.should == "Hinchliff"
266
+ end
267
+ it "should instantiate the final_test_at as a Time" do
268
+ @course['final_test_at'].should == Time.parse("2008/12/19 13:00:00 +0800")
269
+ end
270
+ end
271
+
272
+ describe "timestamping" do
273
+ before(:each) do
274
+ oldart = Article.get "saving-this" rescue nil
275
+ oldart.destroy if oldart
276
+ @art = Article.new(:title => "Saving this")
277
+ @art.save
278
+ end
279
+
280
+ it "should define the updated_at and created_at getters and set the values" do
281
+ @obj.save
282
+ obj = WithDefaultValues.get(@obj.id)
283
+ obj.should be_an_instance_of(WithDefaultValues)
284
+ obj.created_at.should be_an_instance_of(Time)
285
+ obj.updated_at.should be_an_instance_of(Time)
286
+ obj.created_at.to_s.should == @obj.updated_at.to_s
287
+ end
288
+ it "should set the time on create" do
289
+ (Time.now - @art.created_at).should < 2
290
+ foundart = Article.get @art.id
291
+ foundart.created_at.should == foundart.updated_at
292
+ end
293
+ it "should set the time on update" do
294
+ @art.save
295
+ @art.created_at.should < @art.updated_at
296
+ end
297
+ end
298
+
299
+ describe "basic saving and retrieving" do
300
+ it "should work fine" do
301
+ @obj.name = "should be easily saved and retrieved"
302
+ @obj.save
303
+ saved_obj = WithDefaultValues.get(@obj.id)
304
+ saved_obj.should_not be_nil
305
+ end
306
+
307
+ it "should parse the Time attributes automatically" do
308
+ @obj.name = "should parse the Time attributes automatically"
309
+ @obj.set_by_proc.should be_an_instance_of(Time)
310
+ @obj.save
311
+ @obj.set_by_proc.should be_an_instance_of(Time)
312
+ saved_obj = WithDefaultValues.get(@obj.id)
313
+ saved_obj.set_by_proc.should be_an_instance_of(Time)
314
+ end
315
+ end
316
+
317
+ describe "saving a model" do
318
+ before(:all) do
319
+ @sobj = Basic.new
320
+ @sobj.save.should == true
321
+ end
322
+
323
+ it "should save the doc" do
324
+ doc = Basic.get(@sobj.id)
325
+ doc['_id'].should == @sobj.id
326
+ end
327
+
328
+ it "should be set for resaving" do
329
+ rev = @obj.rev
330
+ @sobj['another-key'] = "some value"
331
+ @sobj.save
332
+ @sobj.rev.should_not == rev
333
+ end
334
+
335
+ it "should set the id" do
336
+ @sobj.id.should be_an_instance_of(String)
337
+ end
338
+
339
+ it "should set the type" do
340
+ @sobj['couchrest-type'].should == 'Basic'
341
+ end
342
+ end
343
+
344
+ describe "saving a model with a unique_id configured" do
345
+ before(:each) do
346
+ @art = Article.new
347
+ @old = Article.database.get('this-is-the-title') rescue nil
348
+ Article.database.delete_doc(@old) if @old
349
+ end
350
+
351
+ it "should be a new document" do
352
+ @art.should be_a_new_document
353
+ @art.title.should be_nil
354
+ end
355
+
356
+ it "should require the title" do
357
+ lambda{@art.save}.should raise_error
358
+ @art.title = 'This is the title'
359
+ @art.save.should == true
360
+ end
361
+
362
+ it "should not change the slug on update" do
363
+ @art.title = 'This is the title'
364
+ @art.save.should == true
365
+ @art.title = 'new title'
366
+ @art.save.should == true
367
+ @art.slug.should == 'this-is-the-title'
368
+ end
369
+
370
+ it "should raise an error when the slug is taken" do
371
+ @art.title = 'This is the title'
372
+ @art.save.should == true
373
+ @art2 = Article.new(:title => 'This is the title!')
374
+ lambda{@art2.save}.should raise_error
375
+ end
376
+
377
+ it "should set the slug" do
378
+ @art.title = 'This is the title'
379
+ @art.save.should == true
380
+ @art.slug.should == 'this-is-the-title'
381
+ end
382
+
383
+ it "should set the id" do
384
+ @art.title = 'This is the title'
385
+ @art.save.should == true
386
+ @art.id.should == 'this-is-the-title'
387
+ end
388
+ end
389
+
390
+ describe "saving a model with a unique_id lambda" do
391
+ before(:each) do
392
+ @templated = WithTemplateAndUniqueID.new
393
+ @old = WithTemplateAndUniqueID.get('very-important') rescue nil
394
+ @old.destroy if @old
395
+ end
396
+
397
+ it "should require the field" do
398
+ lambda{@templated.save}.should raise_error
399
+ @templated['important-field'] = 'very-important'
400
+ @templated.save.should == true
401
+ end
402
+
403
+ it "should save with the id" do
404
+ @templated['important-field'] = 'very-important'
405
+ @templated.save.should == true
406
+ t = WithTemplateAndUniqueID.get('very-important')
407
+ t.should == @templated
408
+ end
409
+
410
+ it "should not change the id on update" do
411
+ @templated['important-field'] = 'very-important'
412
+ @templated.save.should == true
413
+ @templated['important-field'] = 'not-important'
414
+ @templated.save.should == true
415
+ t = WithTemplateAndUniqueID.get('very-important')
416
+ t.should == @templated
417
+ end
418
+
419
+ it "should raise an error when the id is taken" do
420
+ @templated['important-field'] = 'very-important'
421
+ @templated.save.should == true
422
+ lambda{WithTemplateAndUniqueID.new('important-field' => 'very-important').save}.should raise_error
423
+ end
424
+
425
+ it "should set the id" do
426
+ @templated['important-field'] = 'very-important'
427
+ @templated.save.should == true
428
+ @templated.id.should == 'very-important'
429
+ end
430
+ end
431
+
432
+ describe "destroying an instance" do
433
+ before(:each) do
434
+ @dobj = Basic.new
435
+ @dobj.save.should == true
436
+ end
437
+ it "should return true" do
438
+ result = @dobj.destroy
439
+ result.should == true
440
+ end
441
+ it "should be resavable" do
442
+ @dobj.destroy
443
+ @dobj.rev.should be_nil
444
+ @dobj.id.should be_nil
445
+ @dobj.save.should == true
446
+ end
447
+ it "should make it go away" do
448
+ @dobj.destroy
449
+ lambda{Basic.get(@dobj.id)}.should raise_error
450
+ end
451
+ end
452
+
453
+
454
+ describe "callbacks" do
455
+
456
+ before(:each) do
457
+ @doc = WithCallBacks.new
458
+ end
459
+
460
+ describe "save" do
461
+ it "should run the after filter after saving" do
462
+ @doc.run_after_save.should be_nil
463
+ @doc.save.should be_true
464
+ @doc.run_after_save.should be_true
465
+ end
466
+ end
467
+ describe "create" do
468
+ it "should run the before save filter when creating" do
469
+ @doc.run_before_save.should be_nil
470
+ @doc.create.should_not be_nil
471
+ @doc.run_before_save.should be_true
472
+ end
473
+ it "should run the before create filter" do
474
+ @doc.run_before_create.should be_nil
475
+ @doc.create.should_not be_nil
476
+ @doc.create
477
+ @doc.run_before_create.should be_true
478
+ end
479
+ it "should run the after create filter" do
480
+ @doc.run_after_create.should be_nil
481
+ @doc.create.should_not be_nil
482
+ @doc.create
483
+ @doc.run_after_create.should be_true
484
+ end
485
+ end
486
+ describe "update" do
487
+
488
+ before(:each) do
489
+ @doc.save
490
+ end
491
+ it "should run the before update filter when updating an existing document" do
492
+ @doc.run_before_update.should be_nil
493
+ @doc.update
494
+ @doc.run_before_update.should be_true
495
+ end
496
+ it "should run the after update filter when updating an existing document" do
497
+ @doc.run_after_update.should be_nil
498
+ @doc.update
499
+ @doc.run_after_update.should be_true
500
+ end
501
+ it "should run the before update filter when saving an existing document" do
502
+ @doc.run_before_update.should be_nil
503
+ @doc.save
504
+ @doc.run_before_update.should be_true
505
+ end
506
+
507
+ end
508
+ end
509
+ end