ripple 0.5.0

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 (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