jchris-couchrest 0.2.2 → 0.7.99

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/README +39 -0
  2. data/Rakefile +3 -54
  3. data/lib/couchrest.rb +39 -161
  4. data/lib/database.rb +83 -0
  5. data/script/couchdir +58 -0
  6. data/script/couchview +158 -0
  7. data/spec/couchrest_spec.rb +86 -0
  8. data/spec/database_spec.rb +407 -0
  9. metadata +15 -138
  10. data/LICENSE +0 -176
  11. data/README.md +0 -93
  12. data/THANKS.md +0 -18
  13. data/examples/model/example.rb +0 -144
  14. data/examples/word_count/markov +0 -38
  15. data/examples/word_count/views/books/chunked-map.js +0 -3
  16. data/examples/word_count/views/books/united-map.js +0 -1
  17. data/examples/word_count/views/markov/chain-map.js +0 -6
  18. data/examples/word_count/views/markov/chain-reduce.js +0 -7
  19. data/examples/word_count/views/word_count/count-map.js +0 -6
  20. data/examples/word_count/views/word_count/count-reduce.js +0 -3
  21. data/examples/word_count/word_count.rb +0 -46
  22. data/examples/word_count/word_count_query.rb +0 -40
  23. data/examples/word_count/word_count_views.rb +0 -26
  24. data/lib/couchrest/commands/generate.rb +0 -71
  25. data/lib/couchrest/commands/push.rb +0 -103
  26. data/lib/couchrest/core/database.rb +0 -318
  27. data/lib/couchrest/core/design.rb +0 -89
  28. data/lib/couchrest/core/document.rb +0 -96
  29. data/lib/couchrest/core/response.rb +0 -16
  30. data/lib/couchrest/core/server.rb +0 -88
  31. data/lib/couchrest/core/view.rb +0 -4
  32. data/lib/couchrest/helper/pager.rb +0 -103
  33. data/lib/couchrest/helper/streamer.rb +0 -44
  34. data/lib/couchrest/helper/upgrade.rb +0 -51
  35. data/lib/couchrest/mixins.rb +0 -4
  36. data/lib/couchrest/mixins/attachments.rb +0 -31
  37. data/lib/couchrest/mixins/callbacks.rb +0 -483
  38. data/lib/couchrest/mixins/design_doc.rb +0 -64
  39. data/lib/couchrest/mixins/document_queries.rb +0 -48
  40. data/lib/couchrest/mixins/extended_attachments.rb +0 -68
  41. data/lib/couchrest/mixins/extended_document_mixins.rb +0 -6
  42. data/lib/couchrest/mixins/properties.rb +0 -125
  43. data/lib/couchrest/mixins/validation.rb +0 -234
  44. data/lib/couchrest/mixins/views.rb +0 -168
  45. data/lib/couchrest/monkeypatches.rb +0 -119
  46. data/lib/couchrest/more/casted_model.rb +0 -28
  47. data/lib/couchrest/more/extended_document.rb +0 -217
  48. data/lib/couchrest/more/property.rb +0 -40
  49. data/lib/couchrest/support/blank.rb +0 -42
  50. data/lib/couchrest/support/class.rb +0 -191
  51. data/lib/couchrest/validation/auto_validate.rb +0 -163
  52. data/lib/couchrest/validation/contextual_validators.rb +0 -78
  53. data/lib/couchrest/validation/validation_errors.rb +0 -118
  54. data/lib/couchrest/validation/validators/absent_field_validator.rb +0 -74
  55. data/lib/couchrest/validation/validators/confirmation_validator.rb +0 -99
  56. data/lib/couchrest/validation/validators/format_validator.rb +0 -117
  57. data/lib/couchrest/validation/validators/formats/email.rb +0 -66
  58. data/lib/couchrest/validation/validators/formats/url.rb +0 -43
  59. data/lib/couchrest/validation/validators/generic_validator.rb +0 -120
  60. data/lib/couchrest/validation/validators/length_validator.rb +0 -134
  61. data/lib/couchrest/validation/validators/method_validator.rb +0 -89
  62. data/lib/couchrest/validation/validators/numeric_validator.rb +0 -104
  63. data/lib/couchrest/validation/validators/required_field_validator.rb +0 -109
  64. data/spec/couchrest/core/couchrest_spec.rb +0 -201
  65. data/spec/couchrest/core/database_spec.rb +0 -745
  66. data/spec/couchrest/core/design_spec.rb +0 -131
  67. data/spec/couchrest/core/document_spec.rb +0 -311
  68. data/spec/couchrest/core/server_spec.rb +0 -35
  69. data/spec/couchrest/helpers/pager_spec.rb +0 -122
  70. data/spec/couchrest/helpers/streamer_spec.rb +0 -23
  71. data/spec/couchrest/more/casted_extended_doc_spec.rb +0 -40
  72. data/spec/couchrest/more/casted_model_spec.rb +0 -98
  73. data/spec/couchrest/more/extended_doc_attachment_spec.rb +0 -130
  74. data/spec/couchrest/more/extended_doc_spec.rb +0 -509
  75. data/spec/couchrest/more/extended_doc_view_spec.rb +0 -207
  76. data/spec/couchrest/more/property_spec.rb +0 -130
  77. data/spec/couchrest/support/class_spec.rb +0 -59
  78. data/spec/fixtures/attachments/README +0 -3
  79. data/spec/fixtures/attachments/couchdb.png +0 -0
  80. data/spec/fixtures/attachments/test.html +0 -11
  81. data/spec/fixtures/more/article.rb +0 -34
  82. data/spec/fixtures/more/card.rb +0 -20
  83. data/spec/fixtures/more/course.rb +0 -14
  84. data/spec/fixtures/more/event.rb +0 -6
  85. data/spec/fixtures/more/invoice.rb +0 -17
  86. data/spec/fixtures/more/person.rb +0 -8
  87. data/spec/fixtures/more/question.rb +0 -6
  88. data/spec/fixtures/more/service.rb +0 -12
  89. data/spec/fixtures/views/lib.js +0 -3
  90. data/spec/fixtures/views/test_view/lib.js +0 -3
  91. data/spec/fixtures/views/test_view/only-map.js +0 -4
  92. data/spec/fixtures/views/test_view/test-map.js +0 -3
  93. data/spec/fixtures/views/test_view/test-reduce.js +0 -3
  94. data/spec/spec.opts +0 -6
  95. data/spec/spec_helper.rb +0 -26
  96. data/utils/remap.rb +0 -27
  97. data/utils/subset.rb +0 -30
data/script/couchview ADDED
@@ -0,0 +1,158 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ commands = %w{pull push}
4
+
5
+ command = ARGV[0]
6
+
7
+ if !commands.include?(command)
8
+ puts <<-USAGE
9
+ Usage: couchview (pull|push) my-database-name
10
+ For help on pull and push run script/views (pull|push) without a database name.
11
+ USAGE
12
+ exit
13
+ end
14
+
15
+ if ARGV.length == 1
16
+ case command
17
+ when "pull"
18
+ puts <<-PULL
19
+ couchview pull my-database-name
20
+
21
+ I will automagically create a "views" directory in your current working directory if none exists.
22
+ Then I copy the design documents and views into a directory structure like:
23
+
24
+ ./views/my-design-doc/view-name-map.js
25
+ ./views/my-design-doc/view-name-reduce.js
26
+
27
+ If your view names don't end in "map" or "reduce" I'll add those suffixes as a pull. On push I'll put them in new locations corresponding to these new names (overwriting the old design documents). I'm opinionated, but if these conventions don't work for you, the source code is right here.
28
+
29
+ PULL
30
+ when "push"
31
+ puts <<-PUSH
32
+ couchview push my-database-name
33
+
34
+ I'll push all the files in your views directory to the specified database. Because CouchDB caches the results of view calculation by function content, there's no performance penalty for duplicating the map function twice, which I'll do if you have a reduce function. This makes it possible to browse the results of just the map, which can be useful for both queries and debugging.
35
+
36
+ ./views/my-design-doc/view-name-map.js
37
+ ./views/my-design-doc/view-name-reduce.js
38
+
39
+ Pushed to =>
40
+
41
+ http://localhost:5984/my-database-name/_design/my-design-doc
42
+ {
43
+ "views" : {
44
+ "view-name-map" : {
45
+ "map" : "### contents of view-name-map.js ###"
46
+ },
47
+ "view-name-reduce" : {
48
+ "map" : "### contents of view-name-map.js ###",
49
+ "reduce" : "### contents of view-name-reduce.js ###"
50
+ },
51
+ }
52
+ }
53
+ PUSH
54
+ end
55
+ exit
56
+ end
57
+
58
+ dbname = ARGV[1]
59
+ dirname = ARGV[2] || "views"
60
+
61
+ puts "Running #{command} on #{dbname} from directory #{dirname}."
62
+
63
+ require File.expand_path(File.dirname(__FILE__)) + '/../couchrest'
64
+ require 'fileutils'
65
+
66
+ module Enumerable
67
+ def group_by
68
+ inject({}) do |groups, element|
69
+ (groups[yield(element)] ||= []) << element
70
+ groups
71
+ end
72
+ end if RUBY_VERSION < '1.9'
73
+ end
74
+
75
+ # connect to couchdb
76
+ cr = CouchRest.new("http://localhost:5984")
77
+ db = cr.database(dbname)
78
+
79
+ def readjs(file, libs=nil)
80
+ st = open(file).read
81
+ st.sub!(/\/\/include-lib/,libs) if libs
82
+ st
83
+ end
84
+
85
+ case command
86
+ when "push" # files to views
87
+ views = {}
88
+ viewfiles = Dir.glob(File.join(dirname,"**","*.js")) # todo support non-js views
89
+ libfiles = viewfiles.select{|f|/lib\.js/.match(f)}
90
+ libs = open(libfiles[0]).read if libfiles[0]
91
+ all = (viewfiles-libfiles).collect do |file|
92
+ fileparts = file.split('/')
93
+ filename = /(\w.*)-(\w.*)\.js/.match file.split('/').pop
94
+ design = fileparts[1]
95
+ view = filename[1]
96
+ func = filename[2]
97
+ path = file
98
+ [design,view,func,path]
99
+ end
100
+ designs = all.group_by{|f|f[0]}
101
+ designs.each do |design,parts|
102
+ # puts "replace _design/#{design}? (enter to proceed, 'n' to skip)"
103
+ # rep = $stdin.gets.chomp
104
+ # next if rep == 'n'
105
+ dviews = {}
106
+ parts.group_by{|p|p[1]}.each do |view,fs|
107
+ fs.each do |f|
108
+ dviews["#{view}-reduce"] ||= {}
109
+ dviews["#{view}-reduce"][f[2]] = readjs(f.last,libs)
110
+ end
111
+ dviews["#{view}-map"] = {'map' => dviews["#{view}-reduce"]['map']}
112
+ dviews.delete("#{view}-reduce") unless dviews["#{view}-reduce"]["reduce"]
113
+ end
114
+ # save them to the db
115
+ view = db.get("_design/#{design}") rescue nil
116
+ if (view && view['views'] == dviews)
117
+ puts "no change to _design/#{design}. skipping..."
118
+ else
119
+ puts "replacing _design/#{design}"
120
+ db.delete(view) rescue nil
121
+ db.save({
122
+ "_id" => "_design/#{design}",
123
+ :views => dviews
124
+ })
125
+ end
126
+ end
127
+
128
+ when "pull" # views to files
129
+ ds = db.documents(:startkey => '_design/', :endkey => '_design/ZZZZZZZZZ')
130
+ ds['rows'].collect{|r|r['id']}.each do |id|
131
+ puts directory = id.split('/').last
132
+ FileUtils.mkdir_p(File.join("views",directory))
133
+ views = db.get(id)['views']
134
+
135
+ vgroups = views.keys.group_by{|k|k.sub(/\-(map|reduce)$/,'')}
136
+ vgroups.each do|g,vs|
137
+ mapname = vs.find {|v|views[v]["map"]}
138
+ if mapname
139
+ # save map
140
+ mapfunc = views[mapname]["map"]
141
+ mapfile = File.join(dirname,directory,"#{g}-map.js") # todo support non-js views
142
+ File.open(mapfile,'w') do |f|
143
+ f.write mapfunc
144
+ end
145
+ end
146
+
147
+ reducename = vs.find {|v|views[v]["reduce"]}
148
+ if reducename
149
+ # save reduce
150
+ reducefunc = views[reducename]["reduce"]
151
+ reducefile = File.join(dirname,directory,"#{g}-reduce.js") # todo support non-js views
152
+ File.open(reducefile,'w') do |f|
153
+ f.write reducefunc
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,86 @@
1
+ require File.dirname(__FILE__) + '/../lib/couchrest'
2
+
3
+ describe CouchRest do
4
+
5
+ before(:each) do
6
+ @cr = CouchRest.new("http://localhost:5984")
7
+ @db = @cr.database('couchrest-test')
8
+ begin
9
+ @db.delete!
10
+ rescue RestClient::Request::RequestFailed
11
+ end
12
+ end
13
+
14
+ after(:each) do
15
+ begin
16
+ @db.delete!
17
+ rescue RestClient::Request::RequestFailed
18
+ end
19
+ end
20
+
21
+ describe "tested against the current CouchDB svn revision" do
22
+ it "should be up to date" do
23
+ v = @cr.info["version"]
24
+ if /incubating/.match(v)
25
+ v.should include('0.8.0')
26
+ else
27
+ vi = v.split(/a/).pop.to_i
28
+ vi.should be >= 661484 # versions older than this will likely fail many specs
29
+ vi.should be <= 663797 # versions newer than this haven't been tried
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "getting info" do
35
+ it "should list databases" do
36
+ @cr.databases.should be_an_instance_of(Array)
37
+ end
38
+ it "should get info" do
39
+ @cr.info["couchdb"].should == "Welcome"
40
+ @cr.info.class.should == Hash
41
+ end
42
+ end
43
+
44
+ describe "description" do
45
+ it "should restart" do
46
+ @cr.restart!
47
+ end
48
+ end
49
+
50
+ describe "initializing a database" do
51
+ it "should return a db" do
52
+ db = @cr.database('couchrest-test')
53
+ db.should be_an_instance_of(CouchRest::Database)
54
+ db.host.should == @cr.uri
55
+ end
56
+ end
57
+
58
+ describe "successfully creating a database" do
59
+ it "should start without a database" do
60
+ @cr.databases.should_not include('couchrest-test')
61
+ end
62
+ it "should return the created database" do
63
+ db = @cr.create_db('couchrest-test')
64
+ db.should be_an_instance_of(CouchRest::Database)
65
+ end
66
+ it "should create the database" do
67
+ db = @cr.create_db('couchrest-test')
68
+ @cr.databases.should include('couchrest-test')
69
+ end
70
+ end
71
+
72
+ describe "failing to create a database because the name is taken" do
73
+ before(:each) do
74
+ db = @cr.create_db('couchrest-test')
75
+ end
76
+ it "should start with the test database" do
77
+ @cr.databases.should include('couchrest-test')
78
+ end
79
+ it "should PUT the database and raise an error" do
80
+ lambda{
81
+ @cr.create_db('couchrest-test')
82
+ }.should raise_error(RestClient::Request::RequestFailed)
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,407 @@
1
+ require File.dirname(__FILE__) + '/../lib/couchrest'
2
+
3
+ describe CouchRest::Database do
4
+ before(:each) do
5
+ @cr = CouchRest.new("http://localhost:5984")
6
+ begin
7
+ @db = @cr.create_db('couchrest-test')
8
+ rescue RestClient::Request::RequestFailed
9
+ end
10
+ end
11
+
12
+ after(:each) do
13
+ begin
14
+ @db.delete!
15
+ rescue RestClient::Request::RequestFailed
16
+ end
17
+ end
18
+
19
+ describe "map query with _temp_view in Javascript" do
20
+ before(:each) do
21
+ @db.bulk_save([
22
+ {"wild" => "and random"},
23
+ {"mild" => "yet local"},
24
+ {"another" => ["set","of","keys"]}
25
+ ])
26
+ @temp_view = {:map => "function(doc){for(var w in doc){ if(!w.match(/^_/))emit(w,doc[w])}}"}
27
+ end
28
+ it "should return the result of the temporary function" do
29
+ rs = @db.temp_view(@temp_view)
30
+ rs['rows'].select{|r|r['key'] == 'wild' && r['value'] == 'and random'}.length.should == 1
31
+ end
32
+ it "should work with a range" do
33
+ rs = @db.temp_view(@temp_view,{:startkey => "b", :endkey => "z"})
34
+ rs['rows'].length.should == 2
35
+ end
36
+ it "should work with a key" do
37
+ rs = @db.temp_view(@temp_view,{:key => "wild"})
38
+ rs['rows'].length.should == 1
39
+ end
40
+ it "should work with a count" do
41
+ rs = @db.temp_view(@temp_view,{:count => 1})
42
+ rs['rows'].length.should == 1
43
+ end
44
+ end
45
+
46
+ describe "map/reduce query with _temp_view in Javascript" do
47
+ before(:each) do
48
+ @db.bulk_save([
49
+ {"beverage" => "beer", :count => 4},
50
+ {"beverage" => "beer", :count => 2},
51
+ {"beverage" => "tea", :count => 3}
52
+ ])
53
+ end
54
+ it "should return the result of the temporary function" do
55
+ rs = @db.temp_view(:map => "function(doc){emit(doc.beverage, doc.count)}", :reduce => "function(beverage,counts){return sum(counts)}")
56
+ # rs.should == 'x'
57
+ rs['rows'][0]['value'].should == 9
58
+ end
59
+ end
60
+
61
+ describe "saving a view" do
62
+ before(:each) do
63
+ @view = {'test' => {'map' => 'function(doc) {
64
+ if (doc.word && !/\W/.test(doc.word)) {
65
+ emit(doc.word,null);
66
+ }
67
+ }'}}
68
+ @db.save({
69
+ "_id" => "_design/test",
70
+ :views => @view
71
+ })
72
+ end
73
+ it "should work properly" do
74
+ @db.bulk_save([
75
+ {"word" => "once"},
76
+ {"word" => "and again"}
77
+ ])
78
+ @db.view('test/test')['total_rows'].should == 1
79
+ end
80
+ it "should round trip" do
81
+ @db.get("_design/test")['views'].should == @view
82
+ end
83
+ end
84
+
85
+ describe "select from an existing view" do
86
+ before(:each) do
87
+ r = @db.save({
88
+ "_id" => "_design/first",
89
+ :views => {
90
+ :test => {
91
+ :map => "function(doc){for(var w in doc){ if(!w.match(/^_/))emit(w,doc[w])}}"
92
+ }
93
+ }
94
+ })
95
+ @db.bulk_save([
96
+ {"wild" => "and random"},
97
+ {"mild" => "yet local"},
98
+ {"another" => ["set","of","keys"]}
99
+ ])
100
+ end
101
+ it "should have the view" do
102
+ @db.get('_design/first')['views']['test']['map'].should include("for(var w in doc)")
103
+ end
104
+ it "should list from the view" do
105
+ rs = @db.view('first/test')
106
+ rs['rows'].select{|r|r['key'] == 'wild' && r['value'] == 'and random'}.length.should == 1
107
+ end
108
+ it "should work with a range" do
109
+ rs = @db.view('first/test',{:startkey => "b", :endkey => "z"})
110
+ rs['rows'].length.should == 2
111
+ end
112
+ it "should work with a key" do
113
+ rs = @db.view('first/test',{:key => "wild"})
114
+ rs['rows'].length.should == 1
115
+ end
116
+ it "should work with a count" do
117
+ rs = @db.view('first/test',{:count => 1})
118
+ rs['rows'].length.should == 1
119
+ end
120
+ end
121
+
122
+ describe "GET (document by id) when the doc exists" do
123
+ before(:each) do
124
+ @r = @db.save({'lemons' => 'from texas', 'and' => 'spain'})
125
+ @docid = "http://example.com/stuff.cgi?things=and%20stuff"
126
+ @db.save({'_id' => @docid, 'will-exist' => 'here'})
127
+ end
128
+ it "should get the document" do
129
+ doc = @db.get(@r['id'])
130
+ doc['lemons'].should == 'from texas'
131
+ end
132
+ it "should work with a funky id" do
133
+ @db.get(@docid)['will-exist'].should == 'here'
134
+ end
135
+ end
136
+
137
+ describe "POST (adding bulk documents)" do
138
+ it "should add them without ids" do
139
+ rs = @db.bulk_save([
140
+ {"wild" => "and random"},
141
+ {"mild" => "yet local"},
142
+ {"another" => ["set","of","keys"]}
143
+ ])
144
+ rs['new_revs'].each do |r|
145
+ @db.get(r['id'])
146
+ end
147
+ end
148
+ it "should add them with uniq ids" do
149
+ rs = @db.bulk_save([
150
+ {"_id" => "oneB", "wild" => "and random"},
151
+ {"_id" => "twoB", "mild" => "yet local"},
152
+ {"another" => ["set","of","keys"]}
153
+ ])
154
+ rs['new_revs'].each do |r|
155
+ @db.get(r['id'])
156
+ end
157
+ end
158
+ it "in the case of an id conflict should not insert anything" do
159
+ @r = @db.save({'lemons' => 'from texas', 'and' => 'how', "_id" => "oneB"})
160
+
161
+ lambda do
162
+ rs = @db.bulk_save([
163
+ {"_id" => "oneB", "wild" => "and random"},
164
+ {"_id" => "twoB", "mild" => "yet local"},
165
+ {"another" => ["set","of","keys"]}
166
+ ])
167
+ end.should raise_error(RestClient::Request::RequestFailed)
168
+
169
+ lambda do
170
+ @db.get('twoB')
171
+ end.should raise_error(RestClient::Request::RequestFailed)
172
+ end
173
+ end
174
+
175
+ describe "POST (new document without an id)" do
176
+ it "should start empty" do
177
+ @db.documents["total_rows"].should == 0
178
+ end
179
+ it "should create the document and return the id" do
180
+ r = @db.save({'lemons' => 'from texas', 'and' => 'spain'})
181
+ r2 = @db.get(r['id'])
182
+ r2["lemons"].should == "from texas"
183
+ end
184
+ end
185
+
186
+ describe "PUT document with attachment" do
187
+ before(:each) do
188
+ @attach = "<html><head><title>My Doc</title></head><body><p>Has words.</p></body></html>"
189
+ @doc = {
190
+ "_id" => "mydocwithattachment",
191
+ "field" => ["some value"],
192
+ "_attachments" => {
193
+ "test.html" => {
194
+ "type" => "text/html",
195
+ "data" => @attach
196
+ }
197
+ }
198
+ }
199
+ @db.save(@doc)
200
+ end
201
+ it "should save and be indicated" do
202
+ doc = @db.get("mydocwithattachment")
203
+ doc['_attachments']['test.html']['length'].should == @attach.length
204
+ end
205
+ it "should be there" do
206
+ attachment = @db.fetch_attachment("mydocwithattachment","test.html")
207
+ attachment.should == @attach
208
+ end
209
+ end
210
+
211
+ describe "PUT document with attachment stub" do
212
+ before(:each) do
213
+ @attach = "<html><head><title>My Doc</title></head><body><p>Has words.</p></body></html>"
214
+ doc = {
215
+ '_id' => 'mydocwithattachment',
216
+ 'field' => ['some_value'],
217
+ '_attachments' => {
218
+ 'test.html' => {
219
+ 'type' => 'text/html', 'data' => @attach
220
+ }
221
+ }
222
+ }
223
+ @db.save(doc)
224
+ doc = @db.get('mydocwithattachment')
225
+ doc['field'] << 'another value'
226
+ @db.save(doc)
227
+ end
228
+
229
+ it 'should be there' do
230
+ attachment = @db.fetch_attachment('mydocwithattachment', 'test.html')
231
+ attachment.should == @attach
232
+ end
233
+ end
234
+
235
+ describe "PUT document with multiple attachments" do
236
+ before(:each) do
237
+ @attach = "<html><head><title>My Doc</title></head><body><p>Has words.</p></body></html>"
238
+ @attach2 = "<html><head><title>Other Doc</title></head><body><p>Has more words.</p></body></html>"
239
+ @doc = {
240
+ "_id" => "mydocwithattachment",
241
+ "field" => ["some value"],
242
+ "_attachments" => {
243
+ "test.html" => {
244
+ "type" => "text/html",
245
+ "data" => @attach
246
+ },
247
+ "other.html" => {
248
+ "type" => "text/html",
249
+ "data" => @attach2
250
+ }
251
+ }
252
+ }
253
+ @db.save(@doc)
254
+ end
255
+ it "should save and be indicated" do
256
+ doc = @db.get("mydocwithattachment")
257
+ doc['_attachments']['test.html']['length'].should == @attach.length
258
+ doc['_attachments']['other.html']['length'].should == @attach2.length
259
+ end
260
+ it "should be there" do
261
+ attachment = @db.fetch_attachment("mydocwithattachment","test.html")
262
+ attachment.should == @attach
263
+ end
264
+ it "should be there" do
265
+ attachment = @db.fetch_attachment("mydocwithattachment","other.html")
266
+ attachment.should == @attach2
267
+ end
268
+ end
269
+
270
+ describe "POST document with attachment (with funky name)" do
271
+ before(:each) do
272
+ @attach = "<html><head><title>My Funky Doc</title></head><body><p>Has words.</p></body></html>"
273
+ @doc = {
274
+ "field" => ["some other value"],
275
+ "_attachments" => {
276
+ "http://example.com/stuff.cgi?things=and%20stuff" => {
277
+ "type" => "text/html",
278
+ "data" => @attach
279
+ }
280
+ }
281
+ }
282
+ @docid = @db.save(@doc)['id']
283
+ end
284
+ it "should save and be indicated" do
285
+ doc = @db.get(@docid)
286
+ doc['_attachments']['http://example.com/stuff.cgi?things=and%20stuff']['length'].should == @attach.length
287
+ end
288
+ it "should be there" do
289
+ attachment = @db.fetch_attachment(@docid,"http://example.com/stuff.cgi?things=and%20stuff")
290
+ attachment.should == @attach
291
+ end
292
+ end
293
+
294
+ describe "PUT (new document with url id)" do
295
+ it "should create the document" do
296
+ @docid = "http://example.com/stuff.cgi?things=and%20stuff"
297
+ @db.save({'_id' => @docid, 'will-exist' => 'here'})
298
+ lambda{@db.save({'_id' => @docid})}.should raise_error(RestClient::Request::RequestFailed)
299
+ @db.get(@docid)['will-exist'].should == 'here'
300
+ end
301
+ end
302
+
303
+ describe "PUT (new document with id)" do
304
+ it "should start without the document" do
305
+ # r = @db.save({'lemons' => 'from texas', 'and' => 'spain'})
306
+ @db.documents['rows'].each do |doc|
307
+ doc['id'].should_not == 'my-doc'
308
+ end
309
+ # should_not include({'_id' => 'my-doc'})
310
+ # this needs to be a loop over docs on content with the post
311
+ # or instead make it return something with a fancy <=> method
312
+ end
313
+ it "should create the document" do
314
+ @db.save({'_id' => 'my-doc', 'will-exist' => 'here'})
315
+ lambda{@db.save({'_id' => 'my-doc'})}.should raise_error(RestClient::Request::RequestFailed)
316
+ end
317
+ end
318
+
319
+ describe "PUT (existing document with rev)" do
320
+ before(:each) do
321
+ @db.save({'_id' => 'my-doc', 'will-exist' => 'here'})
322
+ @doc = @db.get('my-doc')
323
+ @docid = "http://example.com/stuff.cgi?things=and%20stuff"
324
+ @db.save({'_id' => @docid, 'now' => 'save'})
325
+ end
326
+ it "should start with the document" do
327
+ @doc['will-exist'].should == 'here'
328
+ @db.get(@docid)['now'].should == 'save'
329
+ end
330
+ it "should save with url id" do
331
+ doc = @db.get(@docid)
332
+ doc['yaml'] = ['json', 'word.']
333
+ @db.save doc
334
+ @db.get(@docid)['yaml'].should == ['json', 'word.']
335
+ end
336
+ it "should fail to resave without the rev" do
337
+ @doc['them-keys'] = 'huge'
338
+ @doc['_rev'] = 'wrong'
339
+ # @db.save(@doc)
340
+ lambda {@db.save(@doc)}.should raise_error
341
+ end
342
+ it "should update the document" do
343
+ @doc['them-keys'] = 'huge'
344
+ @db.save(@doc)
345
+ now = @db.get('my-doc')
346
+ now['them-keys'].should == 'huge'
347
+ end
348
+ end
349
+
350
+ describe "DELETE existing document" do
351
+ before(:each) do
352
+ @r = @db.save({'lemons' => 'from texas', 'and' => 'spain'})
353
+ @docid = "http://example.com/stuff.cgi?things=and%20stuff"
354
+ @db.save({'_id' => @docid, 'will-exist' => 'here'})
355
+ end
356
+ it "should work" do
357
+ doc = @db.get(@r['id'])
358
+ doc['and'].should == 'spain'
359
+ @db.delete doc
360
+ lambda{@db.get @r['id']}.should raise_error
361
+ end
362
+ it "should work with uri id" do
363
+ doc = @db.get(@docid)
364
+ @db.delete doc
365
+ lambda{@db.get @docid}.should raise_error
366
+ end
367
+ end
368
+
369
+ it "should list documents" do
370
+ 5.times do
371
+ @db.save({'another' => 'doc', 'will-exist' => 'anywhere'})
372
+ end
373
+ ds = @db.documents
374
+ ds['rows'].should be_an_instance_of(Array)
375
+ ds['rows'][0]['id'].should_not be_nil
376
+ ds['total_rows'].should == 5
377
+ end
378
+
379
+ it "should list documents with keys and such" do
380
+ 9.times do |i|
381
+ @db.save({'_id' => "doc#{i}",'another' => 'doc', 'will-exist' => 'here'})
382
+ end
383
+ ds = @db.documents
384
+ ds['rows'].should be_an_instance_of(Array)
385
+ ds['rows'][0]['id'].should == "doc0"
386
+ ds['total_rows'].should == 9
387
+ ds = @db.documents(:startkey => 'doc0', :endkey => 'doc3')
388
+ ds['rows'].length.should == 4
389
+ ds = @db.documents(:key => 'doc0')
390
+ ds['rows'].length.should == 1
391
+ end
392
+
393
+ describe "deleting a database" do
394
+ it "should start with the test database" do
395
+ @cr.databases.should include('couchrest-test')
396
+ end
397
+ it "should delete the database" do
398
+ db = @cr.database('couchrest-test')
399
+ # r =
400
+ db.delete!
401
+ # r['ok'].should == true
402
+ @cr.databases.should_not include('couchrest-test')
403
+ end
404
+ end
405
+
406
+
407
+ end