ripple 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/.document +5 -0
  2. data/.gitignore +26 -0
  3. data/LICENSE +13 -0
  4. data/README.textile +126 -0
  5. data/RELEASE_NOTES.textile +24 -0
  6. data/Rakefile +61 -0
  7. data/VERSION +1 -0
  8. data/lib/riak.rb +45 -0
  9. data/lib/riak/bucket.rb +105 -0
  10. data/lib/riak/client.rb +138 -0
  11. data/lib/riak/client/curb_backend.rb +63 -0
  12. data/lib/riak/client/http_backend.rb +209 -0
  13. data/lib/riak/client/net_http_backend.rb +49 -0
  14. data/lib/riak/failed_request.rb +37 -0
  15. data/lib/riak/i18n.rb +15 -0
  16. data/lib/riak/invalid_response.rb +25 -0
  17. data/lib/riak/link.rb +54 -0
  18. data/lib/riak/locale/en.yml +37 -0
  19. data/lib/riak/map_reduce.rb +240 -0
  20. data/lib/riak/map_reduce_error.rb +20 -0
  21. data/lib/riak/robject.rb +234 -0
  22. data/lib/riak/util/headers.rb +44 -0
  23. data/lib/riak/util/multipart.rb +52 -0
  24. data/lib/riak/util/translation.rb +29 -0
  25. data/lib/riak/walk_spec.rb +113 -0
  26. data/lib/ripple.rb +48 -0
  27. data/lib/ripple/core_ext/casting.rb +96 -0
  28. data/lib/ripple/document.rb +60 -0
  29. data/lib/ripple/document/attribute_methods.rb +111 -0
  30. data/lib/ripple/document/attribute_methods/dirty.rb +52 -0
  31. data/lib/ripple/document/attribute_methods/query.rb +49 -0
  32. data/lib/ripple/document/attribute_methods/read.rb +38 -0
  33. data/lib/ripple/document/attribute_methods/write.rb +36 -0
  34. data/lib/ripple/document/bucket_access.rb +38 -0
  35. data/lib/ripple/document/finders.rb +84 -0
  36. data/lib/ripple/document/persistence.rb +93 -0
  37. data/lib/ripple/document/persistence/callbacks.rb +48 -0
  38. data/lib/ripple/document/properties.rb +85 -0
  39. data/lib/ripple/document/validations.rb +44 -0
  40. data/lib/ripple/embedded_document.rb +38 -0
  41. data/lib/ripple/embedded_document/persistence.rb +46 -0
  42. data/lib/ripple/i18n.rb +15 -0
  43. data/lib/ripple/locale/en.yml +16 -0
  44. data/lib/ripple/property_type_mismatch.rb +23 -0
  45. data/lib/ripple/translation.rb +24 -0
  46. data/ripple.gemspec +159 -0
  47. data/spec/fixtures/cat.jpg +0 -0
  48. data/spec/fixtures/multipart-blank.txt +7 -0
  49. data/spec/fixtures/multipart-with-body.txt +16 -0
  50. data/spec/riak/bucket_spec.rb +141 -0
  51. data/spec/riak/client_spec.rb +169 -0
  52. data/spec/riak/curb_backend_spec.rb +50 -0
  53. data/spec/riak/headers_spec.rb +34 -0
  54. data/spec/riak/http_backend_spec.rb +136 -0
  55. data/spec/riak/link_spec.rb +50 -0
  56. data/spec/riak/map_reduce_spec.rb +347 -0
  57. data/spec/riak/multipart_spec.rb +36 -0
  58. data/spec/riak/net_http_backend_spec.rb +28 -0
  59. data/spec/riak/object_spec.rb +444 -0
  60. data/spec/riak/walk_spec_spec.rb +208 -0
  61. data/spec/ripple/attribute_methods_spec.rb +149 -0
  62. data/spec/ripple/bucket_access_spec.rb +48 -0
  63. data/spec/ripple/callbacks_spec.rb +86 -0
  64. data/spec/ripple/document_spec.rb +35 -0
  65. data/spec/ripple/embedded_document_spec.rb +52 -0
  66. data/spec/ripple/finders_spec.rb +146 -0
  67. data/spec/ripple/persistence_spec.rb +89 -0
  68. data/spec/ripple/properties_spec.rb +195 -0
  69. data/spec/ripple/ripple_spec.rb +43 -0
  70. data/spec/ripple/validations_spec.rb +64 -0
  71. data/spec/spec.opts +1 -0
  72. data/spec/spec_helper.rb +32 -0
  73. data/spec/support/http_backend_implementation_examples.rb +215 -0
  74. data/spec/support/mock_server.rb +58 -0
  75. metadata +221 -0
Binary file
@@ -0,0 +1,7 @@
1
+
2
+ --73NmmA8dJxSB5nL2dVerpFIi8ze
3
+ Content-Type: multipart/mixed; boundary=8fPXq9XfV15txMoV1IbA3hovEij
4
+
5
+ --8fPXq9XfV15txMoV1IbA3hovEij--
6
+
7
+ --73NmmA8dJxSB5nL2dVerpFIi8ze--
@@ -0,0 +1,16 @@
1
+
2
+ --5EiMOjuGavQ2IbXAqsJPLLfJNlA
3
+ Content-Type: multipart/mixed; boundary=7extjTzvYIKVMVHowUiTn0LfvSs
4
+
5
+ --7extjTzvYIKVMVHowUiTn0LfvSs
6
+ X-Riak-Vclock: a85hYGBgyWDKBVHMr9s3ZzAlMuaxMtyZcPAIH1RYyObHDqiwxIZjcOG1M98chAq3bUQIz7SSFQEKM4FUbwMKZwEA
7
+ Location: /raw/foo/baz
8
+ Content-Type: text/plain
9
+ Link: </raw/foo>; rel="up"
10
+ Etag: 6JdI51eFrvv5lDwY6un7a2
11
+ Last-Modified: Sat, 16 Jan 2010 22:13:44 GMT
12
+
13
+ SCP sloooow....
14
+ --7extjTzvYIKVMVHowUiTn0LfvSs--
15
+
16
+ --5EiMOjuGavQ2IbXAqsJPLLfJNlA--
@@ -0,0 +1,141 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require File.expand_path("../spec_helper", File.dirname(__FILE__))
15
+
16
+ describe Riak::Bucket do
17
+ before :each do
18
+ @client = Riak::Client.new
19
+ @bucket = Riak::Bucket.new(@client, "foo")
20
+ end
21
+
22
+ def do_load(overrides={})
23
+ @bucket.load({
24
+ :body => '{"props":{"name":"foo","allow_mult":false,"big_vclock":50,"chash_keyfun":{"mod":"riak_util","fun":"chash_std_keyfun"},"linkfun":{"mod":"jiak_object","fun":"mapreduce_linkfun"},"n_val":3,"old_vclock":86400,"small_vclock":10,"young_vclock":20},"keys":["bar"]}',
25
+ :headers => {
26
+ "vary" => ["Accept-Encoding"],
27
+ "server" => ["MochiWeb/1.1 WebMachine/1.5.1 (hack the charles gibson)"],
28
+ "link" => ['</raw/foo/bar>; riaktag="contained"'],
29
+ "date" => ["Tue, 12 Jan 2010 15:30:43 GMT"],
30
+ "content-type" => ["application/json"],
31
+ "content-length" => ["257"]
32
+ }
33
+ }.merge(overrides))
34
+ end
35
+
36
+
37
+ describe "when initializing" do
38
+ it "should require a client and a name" do
39
+ lambda { Riak::Bucket.new }.should raise_error
40
+ lambda { Riak::Bucket.new(@client) }.should raise_error
41
+ lambda { Riak::Bucket.new("foo") }.should raise_error
42
+ lambda { Riak::Bucket.new("foo", @client) }.should raise_error
43
+ lambda { Riak::Bucket.new(@client, "foo") }.should_not raise_error
44
+ end
45
+
46
+ it "should set the client and name attributes" do
47
+ bucket = Riak::Bucket.new(@client, "foo")
48
+ bucket.client.should == @client
49
+ bucket.name.should == "foo"
50
+ end
51
+ end
52
+
53
+ describe "when loading data from an HTTP response" do
54
+ it "should load the bucket properties from the response body" do
55
+ do_load
56
+ @bucket.props.should == {"name"=>"foo","allow_mult" => false,"big_vclock" => 50,"chash_keyfun" => {"mod" =>"riak_util","fun"=>"chash_std_keyfun"},"linkfun"=>{"mod"=>"jiak_object","fun"=>"mapreduce_linkfun"},"n_val"=>3,"old_vclock"=>86400,"small_vclock"=>10,"young_vclock"=>20}
57
+ end
58
+
59
+ it "should load the keys from the response body" do
60
+ do_load
61
+ @bucket.keys.should == ["bar"]
62
+ end
63
+
64
+ it "should raise an error for a response that is not JSON" do
65
+ lambda do
66
+ do_load(:headers => {"content-type" => ["text/plain"]})
67
+ end.should raise_error(Riak::InvalidResponse)
68
+ end
69
+
70
+ it "should unescape key names" do
71
+ do_load(:body => '{"keys":["foo", "bar%20baz"]}')
72
+ @bucket.keys.should == ["foo", "bar baz"]
73
+ end
74
+ end
75
+
76
+ describe "accessing keys" do
77
+ before :each do
78
+ @http = mock("HTTPBackend")
79
+ @client.stub!(:http).and_return(@http)
80
+ end
81
+
82
+ it "should load the keys if not present" do
83
+ @http.should_receive(:get).with(200, "/raw/", "foo", {:props => false}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
84
+ @bucket.keys.should == ["bar"]
85
+ end
86
+
87
+ it "should allow reloading of the keys" do
88
+ @http.should_receive(:get).with(200, "/raw/","foo", {:props => false}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
89
+ do_load # Ensures they're already loaded
90
+ @bucket.keys(:reload => true).should == ["bar"]
91
+ end
92
+
93
+ it "should allow streaming keys through block" do
94
+ # pending "Needs support in the raw_http_resource"
95
+ @http.should_receive(:get).with(200, "/raw/","foo", {:props => false}, {}).and_yield("{}").and_yield('{"keys":[]}').and_yield('{"keys":["bar"]}').and_yield('{"keys":["baz"]}')
96
+ all_keys = []
97
+ @bucket.keys do |list|
98
+ all_keys.concat(list)
99
+ end
100
+ all_keys.should == ["bar", "baz"]
101
+ end
102
+
103
+ it "should unescape key names" do
104
+ @http.should_receive(:get).with(200, "/raw/","foo", {:props => false}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar%20baz"]}'})
105
+ @bucket.keys.should == ["bar baz"]
106
+ end
107
+ end
108
+
109
+ describe "setting the bucket properties" do
110
+ before :each do
111
+ @http = mock("HTTPBackend")
112
+ @client.stub!(:http).and_return(@http)
113
+ end
114
+
115
+ it "should PUT the new properties to the bucket" do
116
+ @http.should_receive(:put).with(204, "/raw/","foo", '{"props":{"name":"foo"}}', {"Content-Type" => "application/json"}).and_return({:body => "", :headers => {}})
117
+ @bucket.props = { :name => "foo" }
118
+ end
119
+
120
+ it "should raise an error if an invalid property is given" do
121
+ lambda { @bucket.props = "blah" }.should raise_error(ArgumentError)
122
+ end
123
+ end
124
+
125
+ describe "fetching an object" do
126
+ before :each do
127
+ @http = mock("HTTPBackend")
128
+ @client.stub!(:http).and_return(@http)
129
+ end
130
+
131
+ it "should load the object from the server as a Riak::RObject" do
132
+ @http.should_receive(:get).with(200, "/raw/","foo", "db", {}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
133
+ @bucket.get("db").should be_kind_of(Riak::RObject)
134
+ end
135
+
136
+ it "should use the given query parameters (for R value, etc)" do
137
+ @http.should_receive(:get).with(200, "/raw/","foo", "db", {:r => 2}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
138
+ @bucket.get("db", :r => 2).should be_kind_of(Riak::RObject)
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,169 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require File.expand_path("../spec_helper", File.dirname(__FILE__))
15
+
16
+ describe Riak::Client do
17
+ describe "when initializing" do
18
+ it "should default to the local interface on port 8098" do
19
+ client = Riak::Client.new
20
+ client.host.should == "127.0.0.1"
21
+ client.port.should == 8098
22
+ end
23
+
24
+ it "should accept a host" do
25
+ client = Riak::Client.new :host => "riak.basho.com"
26
+ client.host.should == "riak.basho.com"
27
+ end
28
+
29
+ it "should accept a port" do
30
+ client = Riak::Client.new :port => 9000
31
+ client.port.should == 9000
32
+ end
33
+
34
+ it "should accept a client ID" do
35
+ client = Riak::Client.new :client_id => "AAAAAA=="
36
+ client.client_id.should == "AAAAAA=="
37
+ end
38
+
39
+ it "should turn an integer client ID into a base64-encoded string" do
40
+ client = Riak::Client.new :client_id => 1
41
+ client.client_id.should == "AAAAAQ=="
42
+ end
43
+
44
+ it "should create a client ID if not specified" do
45
+ Riak::Client.new.client_id.should be_kind_of(String)
46
+ end
47
+
48
+ it "should accept a path prefix" do
49
+ client = Riak::Client.new(:prefix => "/jiak/")
50
+ client.prefix.should == "/jiak/"
51
+ end
52
+
53
+ it "should default the prefix to /raw/ if not specified" do
54
+ Riak::Client.new.prefix.should == "/raw/"
55
+ end
56
+
57
+ it "should accept a mapreduce path" do
58
+ client = Riak::Client.new(:mapred => "/mr")
59
+ client.mapred.should == "/mr"
60
+ end
61
+
62
+ it "should default the mapreduce path to /mapred if not specified" do
63
+ Riak::Client.new.mapred.should == "/mapred"
64
+ end
65
+ end
66
+
67
+ describe "reconfiguring" do
68
+ before :each do
69
+ @client = Riak::Client.new
70
+ end
71
+
72
+ describe "setting the host" do
73
+ it "should allow setting the host" do
74
+ @client.should respond_to(:host=)
75
+ @client.host = "riak.basho.com"
76
+ @client.host.should == "riak.basho.com"
77
+ end
78
+
79
+ it "should require the host to be an IP or hostname" do
80
+ [238472384972, ""].each do |invalid|
81
+ lambda { @client.host = invalid }.should raise_error(ArgumentError)
82
+ end
83
+ ["127.0.0.1", "10.0.100.5", "localhost", "otherhost.local", "riak.basho.com"].each do |valid|
84
+ lambda { @client.host = valid }.should_not raise_error
85
+ end
86
+ end
87
+ end
88
+
89
+ describe "setting the port" do
90
+ it "should allow setting the port" do
91
+ @client.should respond_to(:port=)
92
+ @client.port = 9000
93
+ @client.port.should == 9000
94
+ end
95
+
96
+ it "should require the port to be a valid number" do
97
+ [-1,65536,"foo"].each do |invalid|
98
+ lambda { @client.port = invalid }.should raise_error(ArgumentError)
99
+ end
100
+ [0,1,65535,8098].each do |valid|
101
+ lambda { @client.port = valid }.should_not raise_error
102
+ end
103
+ end
104
+ end
105
+
106
+ it "should allow setting the prefix (although we prefer the raw interface)" do
107
+ @client.should respond_to(:prefix=)
108
+ @client.prefix = "/another-prefix"
109
+ @client.prefix.should == "/another-prefix"
110
+ end
111
+
112
+ describe "setting the client id" do
113
+ it "should accept a string unmodified" do
114
+ @client.client_id = "foo"
115
+ @client.client_id.should == "foo"
116
+ end
117
+
118
+ it "should base64-encode an integer" do
119
+ @client.client_id = 1
120
+ @client.client_id.should == "AAAAAQ=="
121
+ end
122
+
123
+ it "should reject an integer equal to the maximum client id" do
124
+ lambda { @client.client_id = Riak::Client::MAX_CLIENT_ID }.should raise_error(ArgumentError)
125
+ end
126
+
127
+ it "should reject an integer larger than the maximum client id" do
128
+ lambda { @client.client_id = Riak::Client::MAX_CLIENT_ID + 1 }.should raise_error(ArgumentError)
129
+ end
130
+ end
131
+ end
132
+
133
+ describe "choosing an HTTP backend" do
134
+ before :each do
135
+ @client = Riak::Client.new
136
+ end
137
+
138
+ it "should choose the Curb backend if Curb is available" do
139
+ @client.should_receive(:require).with('curb').and_return(true)
140
+ @client.http.should be_instance_of(Riak::Client::CurbBackend)
141
+ end
142
+
143
+ it "should choose the Net::HTTP backend if Curb is unavailable" do
144
+ @client.should_receive(:require).with('curb').and_raise(LoadError)
145
+ @client.should_receive(:warn).and_return(true)
146
+ @client.http.should be_instance_of(Riak::Client::NetHTTPBackend)
147
+ end
148
+ end
149
+
150
+ describe "retrieving a bucket" do
151
+ before :each do
152
+ @client = Riak::Client.new
153
+ @http = mock(Riak::Client::HTTPBackend)
154
+ @client.stub!(:http).and_return(@http)
155
+ @payload = {:headers => {"content-type" => ["application/json"]}, :body => "{}"}
156
+ @http.stub!(:get).and_return(@payload)
157
+ end
158
+
159
+ it "should send a GET request to the bucket name and return a Riak::Bucket" do
160
+ @http.should_receive(:get).with(200, "/raw/", "foo", {}, {}).and_return(@payload)
161
+ @client.bucket("foo").should be_kind_of(Riak::Bucket)
162
+ end
163
+
164
+ it "should allow requesting bucket properties without the keys" do
165
+ @http.should_receive(:get).with(200, "/raw/", "foo", {:keys => false}, {}).and_return(@payload)
166
+ @client.bucket("foo", :keys => false)
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,50 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require File.expand_path("../spec_helper", File.dirname(__FILE__))
15
+
16
+ begin
17
+ require 'curb'
18
+ rescue LoadError
19
+ warn "Skipping CurbBackend specs, curb library not found."
20
+ else
21
+ describe Riak::Client::CurbBackend do
22
+ def setup_http_mock(method, uri, options={})
23
+ method = method.to_s.upcase
24
+ uri = URI.parse(uri)
25
+ path = uri.path || "/"
26
+ query = uri.query || ""
27
+ status = options[:status] ? Array(options[:status]).first.to_i : 200
28
+ body = options[:body] || []
29
+ headers = options[:headers] || {}
30
+ headers['Content-Type'] ||= "text/plain"
31
+ $server.attach do |env|
32
+ env["REQUEST_METHOD"].should == method
33
+ env["PATH_INFO"].should == path
34
+ env["QUERY_STRING"].should == query
35
+ [status, headers, Array(body)]
36
+ end
37
+ end
38
+
39
+ before :each do
40
+ @client = Riak::Client.new(:port => 4000) # Point to our mock
41
+ @backend = Riak::Client::CurbBackend.new(@client)
42
+ end
43
+
44
+ it_should_behave_like "HTTP backend"
45
+
46
+ after :each do
47
+ $server.detach
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,34 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ require File.expand_path("../spec_helper", File.dirname(__FILE__))
15
+
16
+ describe Riak::Util::Headers do
17
+ it "should include the Net::HTTPHeader module" do
18
+ Riak::Util::Headers.included_modules.should include(Net::HTTPHeader)
19
+ end
20
+
21
+ it "should be initially empty" do
22
+ Riak::Util::Headers.new.to_hash.should == {}
23
+ end
24
+
25
+ it "should parse a header line into the key and value" do
26
+ Riak::Util::Headers.parse("Content-Type: text/plain\n").should == ["Content-Type", "text/plain"]
27
+ end
28
+
29
+ it "should parse a header line and add it to the collection" do
30
+ h = Riak::Util::Headers.new
31
+ h.parse("Content-Type: text/plain\n")
32
+ h.to_hash.should == {"content-type" => ["text/plain"]}
33
+ end
34
+ end
@@ -0,0 +1,136 @@
1
+ # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License
14
+ require File.expand_path("../spec_helper", File.dirname(__FILE__))
15
+
16
+ describe Riak::Client::HTTPBackend do
17
+ before :each do
18
+ @client = Riak::Client.new
19
+ @backend = Riak::Client::HTTPBackend.new(@client)
20
+ end
21
+
22
+ it "should take the Riak::Client when creating" do
23
+ lambda { Riak::Client::HTTPBackend.new(nil) }.should raise_error(ArgumentError)
24
+ lambda { Riak::Client::HTTPBackend.new(@client) }.should_not raise_error
25
+ end
26
+
27
+ it "should make the client accessible" do
28
+ @backend.client.should == @client
29
+ end
30
+
31
+ it "should generate default headers for requests based on the client settings" do
32
+ @client.client_id = "testing"
33
+ @backend.default_headers.should == {"X-Riak-ClientId" => "testing", "Accept" => "multipart/mixed, application/json;q=0.7, */*;q=0.5"}
34
+ end
35
+
36
+ it "should generate a root URI based on the client settings" do
37
+ @backend.root_uri.should be_kind_of(URI)
38
+ @backend.root_uri.to_s.should == "http://127.0.0.1:8098"
39
+ end
40
+
41
+ it "should compute a URI from a relative resource path" do
42
+ @backend.path("baz").should be_kind_of(URI)
43
+ @backend.path("foo").to_s.should == "http://127.0.0.1:8098/foo"
44
+ @backend.path("foo", "bar").to_s.should == "http://127.0.0.1:8098/foo/bar"
45
+ @backend.path("/foo/bar").to_s.should == "http://127.0.0.1:8098/foo/bar"
46
+ end
47
+
48
+ it "should escape appropriate characters in a relative resource path" do
49
+ @backend.path("foo bar").to_s.should == "http://127.0.0.1:8098/foo%20bar"
50
+ @backend.path("foo", "bar", {"param" => "a string"}).to_s.should == "http://127.0.0.1:8098/foo/bar?param=a+string"
51
+ end
52
+
53
+ it "should compute a URI from a relative resource path with a hash of query parameters" do
54
+ @backend.path("baz", :r => 2).to_s.should == "http://127.0.0.1:8098/baz?r=2"
55
+ end
56
+
57
+ it "should raise an error if a resource path is too short" do
58
+ lambda { @backend.verify_path!(["/raw/"]) }.should raise_error(ArgumentError)
59
+ lambda { @backend.verify_path!(["/raw/", "foo"]) }.should_not raise_error
60
+ lambda { @backend.verify_path!(["/mapred"]) }.should_not raise_error
61
+ end
62
+
63
+ describe "verify_path_and_body!" do
64
+ it "should separate the path and body from given arguments" do
65
+ uri, data = @backend.verify_path_and_body!(["/raw/", "foo", "This is the body."])
66
+ uri.should == ["/raw/", "foo"]
67
+ data.should == "This is the body."
68
+ end
69
+
70
+ it "should raise an error if the body is not a string or IO" do
71
+ lambda { @backend.verify_path_and_body!(["/raw/", "foo", nil]) }.should raise_error(ArgumentError)
72
+ lambda { @backend.verify_path_and_body!(["/raw/", "foo", File.open("spec/fixtures/cat.jpg")]) }.should_not raise_error(ArgumentError)
73
+ end
74
+
75
+ it "should raise an error if a body is not given" do
76
+ lambda { @backend.verify_path_and_body!(["/raw/", "foo"])}.should raise_error(ArgumentError)
77
+ end
78
+
79
+ it "should raise an error if a path is not given" do
80
+ lambda { @backend.verify_path_and_body!(["/raw/"])}.should raise_error(ArgumentError)
81
+ end
82
+ end
83
+
84
+ describe "detecting valid response codes" do
85
+ it "should accept strings or integers for either argument" do
86
+ @backend.should be_valid_response("300", "300")
87
+ @backend.should be_valid_response(300, "300")
88
+ @backend.should be_valid_response("300", 300)
89
+ end
90
+
91
+ it "should accept an array of strings or integers for the expected code" do
92
+ @backend.should be_valid_response([200,304], "200")
93
+ @backend.should be_valid_response(["200",304], "200")
94
+ @backend.should be_valid_response([200,"304"], "200")
95
+ @backend.should be_valid_response(["200","304"], "200")
96
+ @backend.should be_valid_response([200,304], 200)
97
+ end
98
+
99
+ it "should be false when none of the response codes match" do
100
+ @backend.should_not be_valid_response(200, 404)
101
+ @backend.should_not be_valid_response(["200","304"], 404)
102
+ @backend.should_not be_valid_response([200,304], 404)
103
+ end
104
+ end
105
+
106
+ describe "detecting whether a body should be returned" do
107
+ it "should be false when the method is :head" do
108
+ @backend.should_not be_return_body(:head, 200, false)
109
+ end
110
+
111
+ it "should be false when the response code is 204, 205, or 304" do
112
+ @backend.should_not be_return_body(:get, 204, false)
113
+ @backend.should_not be_return_body(:get, 205, false)
114
+ @backend.should_not be_return_body(:get, 304, false)
115
+ end
116
+
117
+ it "should be false when a streaming block was passed" do
118
+ @backend.should_not be_return_body(:get, 200, true)
119
+ end
120
+
121
+ it "should be true when the method is not head, a code other than 204, 205, or 304 was given, and there was no streaming block" do
122
+ [:get, :put, :post, :delete].each do |method|
123
+ [100,101,200,201,202,203,206,300,301,302,303,305,307,400,401,
124
+ 402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,
125
+ 500,501,502,503,504,505].each do |code|
126
+ @backend.should be_return_body(method, code, false)
127
+ @backend.should be_return_body(method, code.to_s, false)
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ it "should force subclasses to implement the perform method" do
134
+ lambda { @backend.send(:perform, :get, "/foo", {}, 200) }.should raise_error(NotImplementedError)
135
+ end
136
+ end