riak-client 0.8.3 → 0.9.0.beta

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.
@@ -23,6 +23,13 @@ module Riak
23
23
  def escape(bucket_or_key)
24
24
  CGI.escape(bucket_or_key.to_s).gsub("+", "%20")
25
25
  end
26
+
27
+ # CGI-unescapes bucket or key names
28
+ # @param [String] bucket_or_key the bucket or key name
29
+ # @return [String] the unescaped name
30
+ def unescape(bucket_or_key)
31
+ CGI.unescape(bucket_or_key.to_s)
32
+ end
26
33
  end
27
34
  end
28
35
  end
@@ -2,19 +2,19 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{riak-client}
5
- s.version = "0.8.3"
5
+ s.version = "0.9.0.beta"
6
6
 
7
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
7
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Sean Cribbs"]
9
- s.date = %q{2010-12-13}
9
+ s.date = %q{2010-12-29}
10
10
  s.description = %q{riak-client is a rich client for Riak, the distributed database by Basho. It supports the full HTTP interface including storage operations, bucket configuration, link-walking and map-reduce.}
11
11
  s.email = %q{sean@basho.com}
12
- s.files = ["erl_src/riak_kv_test_backend.beam", "erl_src/riak_kv_test_backend.erl", "Gemfile", "lib/active_support/cache/riak_store.rb", "lib/riak/bucket.rb", "lib/riak/cache_store.rb", "lib/riak/client/curb_backend.rb", "lib/riak/client/excon_backend.rb", "lib/riak/client/http_backend.rb", "lib/riak/client/net_http_backend.rb", "lib/riak/client.rb", "lib/riak/core_ext/blank.rb", "lib/riak/core_ext/extract_options.rb", "lib/riak/core_ext/slice.rb", "lib/riak/core_ext/stringify_keys.rb", "lib/riak/core_ext/symbolize_keys.rb", "lib/riak/core_ext/to_param.rb", "lib/riak/core_ext.rb", "lib/riak/failed_request.rb", "lib/riak/i18n.rb", "lib/riak/invalid_response.rb", "lib/riak/link.rb", "lib/riak/locale/en.yml", "lib/riak/map_reduce.rb", "lib/riak/map_reduce_error.rb", "lib/riak/robject.rb", "lib/riak/search.rb", "lib/riak/test_server.rb", "lib/riak/util/escape.rb", "lib/riak/util/fiber1.8.rb", "lib/riak/util/headers.rb", "lib/riak/util/multipart.rb", "lib/riak/util/tcp_socket_extensions.rb", "lib/riak/util/translation.rb", "lib/riak/walk_spec.rb", "lib/riak.rb", "Rakefile", "riak-client.gemspec", "spec/fixtures/cat.jpg", "spec/fixtures/multipart-blank.txt", "spec/fixtures/multipart-with-body.txt", "spec/integration/riak/cache_store_spec.rb", "spec/integration/riak/test_server_spec.rb", "spec/riak/bucket_spec.rb", "spec/riak/client_spec.rb", "spec/riak/curb_backend_spec.rb", "spec/riak/escape_spec.rb", "spec/riak/excon_backend_spec.rb", "spec/riak/headers_spec.rb", "spec/riak/http_backend_spec.rb", "spec/riak/link_spec.rb", "spec/riak/map_reduce_spec.rb", "spec/riak/multipart_spec.rb", "spec/riak/net_http_backend_spec.rb", "spec/riak/object_spec.rb", "spec/riak/search_spec.rb", "spec/riak/walk_spec_spec.rb", "spec/spec_helper.rb", "spec/support/drb_mock_server.rb", "spec/support/http_backend_implementation_examples.rb", "spec/support/mock_server.rb", "spec/support/mocks.rb", "spec/support/test_server.yml.example"]
12
+ s.files = ["erl_src/riak_kv_test_backend.beam", "erl_src/riak_kv_test_backend.erl", "Gemfile", "lib/active_support/cache/riak_store.rb", "lib/riak/bucket.rb", "lib/riak/cache_store.rb", "lib/riak/client/curb_backend.rb", "lib/riak/client/excon_backend.rb", "lib/riak/client/http_backend/configuration.rb", "lib/riak/client/http_backend/object_methods.rb", "lib/riak/client/http_backend/request_headers.rb", "lib/riak/client/http_backend/transport_methods.rb", "lib/riak/client/http_backend.rb", "lib/riak/client/net_http_backend.rb", "lib/riak/client.rb", "lib/riak/core_ext/blank.rb", "lib/riak/core_ext/extract_options.rb", "lib/riak/core_ext/slice.rb", "lib/riak/core_ext/stringify_keys.rb", "lib/riak/core_ext/symbolize_keys.rb", "lib/riak/core_ext/to_param.rb", "lib/riak/core_ext.rb", "lib/riak/failed_request.rb", "lib/riak/i18n.rb", "lib/riak/invalid_response.rb", "lib/riak/link.rb", "lib/riak/locale/en.yml", "lib/riak/map_reduce/filter_builder.rb", "lib/riak/map_reduce/phase.rb", "lib/riak/map_reduce.rb", "lib/riak/map_reduce_error.rb", "lib/riak/robject.rb", "lib/riak/search.rb", "lib/riak/test_server.rb", "lib/riak/util/escape.rb", "lib/riak/util/fiber1.8.rb", "lib/riak/util/headers.rb", "lib/riak/util/multipart.rb", "lib/riak/util/tcp_socket_extensions.rb", "lib/riak/util/translation.rb", "lib/riak/walk_spec.rb", "lib/riak.rb", "Rakefile", "riak-client.gemspec", "spec/fixtures/cat.jpg", "spec/fixtures/multipart-blank.txt", "spec/fixtures/multipart-with-body.txt", "spec/integration/riak/cache_store_spec.rb", "spec/integration/riak/test_server_spec.rb", "spec/riak/bucket_spec.rb", "spec/riak/client_spec.rb", "spec/riak/curb_backend_spec.rb", "spec/riak/escape_spec.rb", "spec/riak/excon_backend_spec.rb", "spec/riak/headers_spec.rb", "spec/riak/http_backend/object_methods_spec.rb", "spec/riak/http_backend_spec.rb", "spec/riak/link_spec.rb", "spec/riak/map_reduce_spec.rb", "spec/riak/multipart_spec.rb", "spec/riak/net_http_backend_spec.rb", "spec/riak/object_spec.rb", "spec/riak/search_spec.rb", "spec/riak/walk_spec_spec.rb", "spec/spec_helper.rb", "spec/support/drb_mock_server.rb", "spec/support/http_backend_implementation_examples.rb", "spec/support/mock_server.rb", "spec/support/mocks.rb", "spec/support/test_server.yml.example"]
13
13
  s.homepage = %q{http://seancribbs.github.com/ripple}
14
14
  s.require_paths = ["lib"]
15
15
  s.rubygems_version = %q{1.3.7}
16
16
  s.summary = %q{riak-client is a rich client for Riak, the distributed database by Basho.}
17
- s.test_files = ["lib/riak/walk_spec.rb", "spec/integration/riak/cache_store_spec.rb", "spec/integration/riak/test_server_spec.rb", "spec/riak/bucket_spec.rb", "spec/riak/client_spec.rb", "spec/riak/curb_backend_spec.rb", "spec/riak/escape_spec.rb", "spec/riak/excon_backend_spec.rb", "spec/riak/headers_spec.rb", "spec/riak/http_backend_spec.rb", "spec/riak/link_spec.rb", "spec/riak/map_reduce_spec.rb", "spec/riak/multipart_spec.rb", "spec/riak/net_http_backend_spec.rb", "spec/riak/object_spec.rb", "spec/riak/search_spec.rb", "spec/riak/walk_spec_spec.rb"]
17
+ s.test_files = ["lib/riak/walk_spec.rb", "spec/integration/riak/cache_store_spec.rb", "spec/integration/riak/test_server_spec.rb", "spec/riak/bucket_spec.rb", "spec/riak/client_spec.rb", "spec/riak/curb_backend_spec.rb", "spec/riak/escape_spec.rb", "spec/riak/excon_backend_spec.rb", "spec/riak/headers_spec.rb", "spec/riak/http_backend/object_methods_spec.rb", "spec/riak/http_backend_spec.rb", "spec/riak/link_spec.rb", "spec/riak/map_reduce_spec.rb", "spec/riak/multipart_spec.rb", "spec/riak/net_http_backend_spec.rb", "spec/riak/object_spec.rb", "spec/riak/search_spec.rb", "spec/riak/walk_spec_spec.rb"]
18
18
 
19
19
  if s.respond_to? :specification_version then
20
20
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
@@ -16,24 +16,11 @@ require File.expand_path("../spec_helper", File.dirname(__FILE__))
16
16
  describe Riak::Bucket do
17
17
  before :each do
18
18
  @client = Riak::Client.new
19
+ @backend = mock("Backend")
20
+ @client.stub!(:backend).and_return(@backend)
19
21
  @bucket = Riak::Bucket.new(@client, "foo")
20
22
  end
21
23
 
22
- def do_load(overrides={})
23
- @bucket.load({
24
- :body => '{"props":{"name":"foo","n_val":3,"allow_mult":false,"last_write_wins":false,"precommit":[],"postcommit":[],"chash_keyfun":{"mod":"riak_core_util","fun":"chash_std_keyfun"},"linkfun":{"mod":"riak_kv_wm_link_walker","fun":"mapreduce_linkfun"},"old_vclock":86400,"young_vclock":20,"big_vclock":50,"small_vclock":10,"r":"quorum","w":"quorum","dw":"quorum","rw":"quorum"},"keys":["bar"]}',
25
- :headers => {
26
- "vary" => ["Accept-Encoding"],
27
- "server" => ["MochiWeb/1.1 WebMachine/1.5.1 (hack the charles gibson)"],
28
- "link" => ['</riak/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
24
  describe "when initializing" do
38
25
  it "should require a client and a name" do
39
26
  lambda { Riak::Bucket.new }.should raise_error
@@ -50,116 +37,69 @@ describe Riak::Bucket do
50
37
  end
51
38
  end
52
39
 
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", "n_val"=>3, "allow_mult"=>false, "last_write_wins"=>false, "precommit"=>[], "postcommit"=>[], "chash_keyfun"=>{"mod"=>"riak_core_util", "fun"=>"chash_std_keyfun"}, "linkfun"=>{"mod"=>"riak_kv_wm_link_walker", "fun"=>"mapreduce_linkfun"}, "old_vclock"=>86400, "young_vclock"=>20, "big_vclock"=>50, "small_vclock"=>10, "r"=>"quorum", "w"=>"quorum", "dw"=>"quorum", "rw"=>"quorum"}
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
40
  describe "accessing keys" do
77
- before :each do
78
- @http = mock("HTTPBackend")
79
- @client.stub!(:http).and_return(@http)
80
- end
81
-
82
41
  it "should load the keys if not present" do
83
- @http.should_receive(:get).with(200, "/riak/", "foo", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
42
+ @backend.should_receive(:list_keys).with(@bucket).and_return(["bar"])
84
43
  @bucket.keys.should == ["bar"]
85
44
  end
86
45
 
87
46
  it "should allow reloading of the keys" do
88
- @http.should_receive(:get).with(200, "/riak/","foo", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
89
- do_load # Ensures they're already loaded
47
+ @backend.should_receive(:list_keys).with(@bucket).and_return(["bar"])
48
+ @bucket.instance_variable_set(:@keys, ["foo"])
90
49
  @bucket.keys(:reload => true).should == ["bar"]
91
50
  end
92
51
 
93
52
  it "should allow streaming keys through block" do
94
- # pending "Needs support in the raw_http_resource"
95
- @http.should_receive(:get).with(200, "/riak/","foo", {:props => false, :keys => "stream"}, {}).and_yield("{}").and_yield('{"keys":[]}').and_yield('{"keys":["bar"]}').and_yield('{"keys":["baz"]}')
53
+ @backend.should_receive(:list_keys).with(@bucket).and_yield([]).and_yield(["bar"]).and_yield(["baz"])
96
54
  all_keys = []
97
55
  @bucket.keys do |list|
98
56
  all_keys.concat(list)
99
57
  end
100
58
  all_keys.should == ["bar", "baz"]
101
59
  end
102
-
103
- it "should unescape key names" do
104
- @http.should_receive(:get).with(200, "/riak/","foo", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar%20baz"]}'})
105
- @bucket.keys.should == ["bar baz"]
106
- end
107
-
108
- it "should escape the bucket name" do
109
- @bucket.instance_variable_set :@name, "unescaped "
110
- @http.should_receive(:get).with(200, "/riak/","unescaped%20", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
111
- @bucket.keys.should == ["bar"]
112
- end
113
60
  end
114
61
 
115
62
  describe "setting the bucket properties" do
116
- before :each do
117
- @http = mock("HTTPBackend")
118
- @client.stub!(:http).and_return(@http)
63
+ it "should prefetch the properties when they are not present" do
64
+ @backend.stub!(:set_bucket_props)
65
+ @backend.should_receive(:get_bucket_props).with(@bucket).and_return({"name" => "foo"})
66
+ @bucket.props = {"precommit" => []}
119
67
  end
120
68
 
121
- it "should PUT the new properties to the bucket" do
122
- @http.should_receive(:put).with(204, "/riak/","foo", '{"props":{"name":"foo"}}', {"Content-Type" => "application/json"}).and_return({:body => "", :headers => {}})
69
+ it "should set the new properties on the bucket" do
70
+ @bucket.instance_variable_set(:@props, {}) # Pretend they are there
71
+ @backend.should_receive(:set_bucket_props).with(@bucket, { :name => "foo" })
123
72
  @bucket.props = { :name => "foo" }
124
73
  end
125
74
 
126
- it "should raise an error if an invalid property is given" do
75
+ it "should raise an error if an invalid type is given" do
127
76
  lambda { @bucket.props = "blah" }.should raise_error(ArgumentError)
128
77
  end
129
-
130
- it "should escape the bucket name" do
131
- @bucket.instance_variable_set :@name, "foo bar"
132
- @http.should_receive(:put).with(204, "/riak/","foo%20bar", '{"props":{"n_val":2}}', {"Content-Type" => "application/json"}).and_return({:body => "", :headers => {}})
133
- @bucket.props = {:n_val => 2}
134
- end
135
78
  end
136
79
 
137
- describe "fetching an object" do
138
- before :each do
139
- @http = mock("HTTPBackend")
140
- @client.stub!(:http).and_return(@http)
141
- end
142
-
143
- it "should load the object from the server as a Riak::RObject" do
144
- @http.should_receive(:get).with(200, "/riak/","foo", "db", {}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
145
- @bucket.get("db").should be_kind_of(Riak::RObject)
80
+ describe "fetching the bucket properties" do
81
+ it "should fetch properties on first access" do
82
+ @bucket.instance_variable_get(:@props).should be_nil
83
+ @backend.should_receive(:get_bucket_props).with(@bucket).and_return({"name" => "foo"})
84
+ @bucket.props.should == {"name" => "foo"}
146
85
  end
147
86
 
148
- it "should use the given query parameters (for R value, etc)" do
149
- @http.should_receive(:get).with(200, "/riak/","foo", "db", {:r => 2}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
150
- @bucket.get("db", :r => 2).should be_kind_of(Riak::RObject)
87
+ it "should memoize fetched properties" do
88
+ @backend.should_receive(:get_bucket_props).once.with(@bucket).and_return({"name" => "foo"})
89
+ @bucket.props.should == {"name" => "foo"}
90
+ @bucket.props.should == {"name" => "foo"}
151
91
  end
92
+ end
152
93
 
153
- it "should allow 300 responses if allow_mult is set" do
154
- @bucket.instance_variable_set(:@props, {'allow_mult' => true})
155
- @http.should_receive(:get).with([200,300], "/riak/","foo", "db", {}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
156
- @bucket.get('db')
94
+ describe "fetching an object" do
95
+ it "should fetch the object via the backend" do
96
+ @backend.should_receive(:fetch_object).with(@bucket, "db", nil).and_return(nil)
97
+ @bucket.get("db")
157
98
  end
158
99
 
159
- it "should escape the bucket and key names" do
160
- @bucket.instance_variable_set(:@name, "foo ")
161
- @http.should_receive(:get).with(200, "/riak/","foo%20", "%20bar", {}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
162
- @bucket.get(' bar')
100
+ it "should use the specified R quroum" do
101
+ @backend.should_receive(:fetch_object).with(@bucket, "db", 2).and_return(nil)
102
+ @bucket.get("db", :r => 2)
163
103
  end
164
104
  end
165
105
 
@@ -173,34 +113,34 @@ describe Riak::Bucket do
173
113
  end
174
114
 
175
115
  describe "fetching or creating a new object" do
176
- before :each do
177
- @http = mock("HTTPBackend")
178
- @client.stub!(:http).and_return(@http)
179
- end
180
-
181
116
  it "should return the existing object if present" do
182
- @http.should_receive(:get).with(200, "/riak/","foo", "db", {}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"name":"Riak","company":"Basho"}'})
183
- obj = @bucket.get_or_new('db')
184
- obj.key.should == 'db'
185
- obj.data['name'].should == "Riak"
117
+ @object = mock("RObject")
118
+ @backend.should_receive(:fetch_object).with(@bucket,"db", nil).and_return(@object)
119
+ @bucket.get_or_new('db').should == @object
186
120
  end
187
121
 
188
122
  it "should create a new blank object if the key does not exist" do
189
- @http.should_receive(:get).and_raise(Riak::FailedRequest.new(:get, 200, 404, {}, "File not found"))
123
+ @backend.should_receive(:fetch_object).and_raise(Riak::FailedRequest.new(:get, 200, 404, {}, "File not found"))
190
124
  obj = @bucket.get_or_new('db')
191
125
  obj.key.should == 'db'
192
126
  obj.data.should be_blank
193
127
  end
194
128
 
195
129
  it "should bubble up non-ok non-missing errors" do
196
- @http.should_receive(:get).and_raise(Riak::FailedRequest.new(:get, 200, 500, {}, "File not found"))
130
+ @backend.should_receive(:fetch_object).and_raise(Riak::FailedRequest.new(:get, 200, 500, {}, "File not found"))
197
131
  lambda { @bucket.get_or_new('db') }.should raise_error(Riak::FailedRequest)
198
132
  end
133
+
134
+ it "should pass along the given R quorum parameter" do
135
+ @object = mock("RObject")
136
+ @backend.should_receive(:fetch_object).with(@bucket,"db", "all").and_return(@object)
137
+ @bucket.get_or_new('db', :r => "all").should == @object
138
+ end
199
139
  end
200
140
 
201
141
  describe "get/set allow_mult property" do
202
142
  before :each do
203
- do_load
143
+ @backend.stub!(:get_bucket_props).and_return({"allow_mult" => false})
204
144
  end
205
145
 
206
146
  it "should extract the allow_mult property" do
@@ -215,7 +155,7 @@ describe Riak::Bucket do
215
155
 
216
156
  describe "get/set the N value" do
217
157
  before :each do
218
- do_load
158
+ @backend.stub!(:get_bucket_props).and_return({"n_val" => 3})
219
159
  end
220
160
 
221
161
  it "should extract the N value" do
@@ -231,7 +171,7 @@ describe Riak::Bucket do
231
171
  [:r, :w, :dw, :rw].each do |q|
232
172
  describe "get/set the default #{q} quorum" do
233
173
  before :each do
234
- do_load
174
+ @backend.stub!(:get_bucket_props).and_return({"r" => "quorum", "w" => "quorum", "dw" => "quorum", "rw" => "quorum"})
235
175
  end
236
176
 
237
177
  it "should extract the default #{q} quorum" do
@@ -245,21 +185,27 @@ describe Riak::Bucket do
245
185
  end
246
186
  end
247
187
 
248
-
249
188
  describe "checking whether a key exists" do
250
189
  it "should return true if the object does exist" do
251
- @client.http.should_receive(:head).and_return(:code => 200)
190
+ @backend.should_receive(:fetch_object).and_return(mock)
252
191
  @bucket.exists?("foo").should be_true
253
192
  end
254
193
 
255
194
  it "should return false if the object doesn't exist" do
256
- @client.http.should_receive(:head).and_return(:code => 404)
195
+ @backend.should_receive(:fetch_object).and_raise(Riak::FailedRequest.new(:get, [200,300], 404, {}, "not found"))
257
196
  @bucket.exists?("foo").should be_false
258
197
  end
259
198
  end
260
199
 
261
- it "should delete a key from within the bucket" do
262
- @client.http.should_receive(:delete).with([204,404], @client.prefix, @bucket.name, 'bar',{},{}).and_return(:code => 204)
263
- @bucket.delete('bar')
200
+ describe "deleting an object" do
201
+ it "should delete a key from within the bucket" do
202
+ @backend.should_receive(:delete_object).with(@bucket, "bar", nil)
203
+ @bucket.delete('bar')
204
+ end
205
+
206
+ it "should use the specified RW quorum" do
207
+ @backend.should_receive(:delete_object).with(@bucket, "bar", "all")
208
+ @bucket.delete('bar', :rw => "all")
209
+ end
264
210
  end
265
211
  end
@@ -112,7 +112,7 @@ describe Riak::Client do
112
112
  end
113
113
  end
114
114
 
115
- it "should allow setting the prefix (although we prefer the raw interface)" do
115
+ it "should allow setting the prefix" do
116
116
  @client.should respond_to(:prefix=)
117
117
  @client.prefix = "/another-prefix"
118
118
  @client.prefix.should == "/another-prefix"
@@ -165,108 +165,127 @@ describe Riak::Client do
165
165
  describe "retrieving a bucket" do
166
166
  before :each do
167
167
  @client = Riak::Client.new
168
- @http = mock(Riak::Client::HTTPBackend)
169
- @client.stub!(:http).and_return(@http)
170
- @payload = {:headers => {"content-type" => ["application/json"]}, :body => "{}"}
171
- @http.stub!(:get).and_return(@payload)
168
+ @backend = mock("Backend")
169
+ @client.stub!(:backend).and_return(@backend)
172
170
  end
173
171
 
174
- it "should send a GET request to the bucket name and return a Riak::Bucket" do
175
- @http.should_receive(:get).with(200, "/riak/", "foo", {:keys => false}, {}).and_return(@payload)
172
+ it "should return a bucket object" do
176
173
  @client.bucket("foo").should be_kind_of(Riak::Bucket)
177
174
  end
178
175
 
179
- it "should allow requesting bucket properties with the keys" do
180
- @http.should_receive(:get).with(200, "/riak/", "foo", {:keys => true}, {}).and_return(@payload)
176
+ it "should fetch bucket properties if asked" do
177
+ @backend.should_receive(:get_bucket_props) {|b| b.name.should == "foo"; {} }
178
+ @client.bucket("foo", :props => true)
179
+ end
180
+
181
+ it "should fetch keys if asked" do
182
+ @backend.should_receive(:list_keys) {|b| b.name.should == "foo"; ["bar"] }
181
183
  @client.bucket("foo", :keys => true)
182
184
  end
183
185
 
184
- it "should escape bucket names with invalid characters" do
185
- @http.should_receive(:get).with(200, "/riak/", "foo%2Fbar%20", {:keys => false}, {}).and_return(@payload)
186
- @client.bucket("foo/bar ", :keys => false)
186
+
187
+ it "should memoize bucket parameters" do
188
+ @bucket = mock("Bucket")
189
+ Riak::Bucket.should_receive(:new).with(@client, "baz").once.and_return(@bucket)
190
+ @client.bucket("baz").should == @bucket
191
+ @client.bucket("baz").should == @bucket
187
192
  end
188
193
  end
189
194
 
190
- describe "storing a file" do
191
- before :each do
192
- @client = Riak::Client.new
193
- @http = mock(Riak::Client::HTTPBackend)
194
- @client.stub!(:http).and_return(@http)
195
- end
195
+ it "should list buckets" do
196
+ @client = Riak::Client.new
197
+ @backend = mock("Backend")
198
+ @client.stub!(:backend).and_return(@backend)
199
+ @backend.should_receive(:list_buckets).and_return(%w{test test2})
200
+ buckets = @client.buckets
201
+ buckets.should have(2).items
202
+ buckets.should be_all {|b| b.is_a?(Riak::Bucket) }
203
+ buckets[0].name.should == "test"
204
+ buckets[1].name.should == "test2"
205
+ end
196
206
 
197
- it "should store the file in Luwak and return the key/filename when no filename is given" do
198
- @http.should_receive(:post).with(201, "/luwak", anything, {"Content-Type" => "text/plain"}).and_return(:code => 201, :headers => {"location" => ["/luwak/123456789"]})
199
- @client.store_file("text/plain", "Hello, world").should == "123456789"
207
+ describe "Luwak (large-files) support" do
208
+ describe "storing a file" do
209
+ before :each do
210
+ @client = Riak::Client.new
211
+ @http = mock(Riak::Client::HTTPBackend)
212
+ @client.stub!(:http).and_return(@http)
213
+ end
214
+
215
+ it "should store the file in Luwak and return the key/filename when no filename is given" do
216
+ @http.should_receive(:post).with(201, "/luwak", anything, {"Content-Type" => "text/plain"}).and_return(:code => 201, :headers => {"location" => ["/luwak/123456789"]})
217
+ @client.store_file("text/plain", "Hello, world").should == "123456789"
218
+ end
219
+
220
+ it "should store the file in Luwak and return the key/filename when the filename is given" do
221
+ @http.should_receive(:put).with(204, "/luwak", "greeting.txt", anything, {"Content-Type" => "text/plain"}).and_return(:code => 204, :headers => {})
222
+ @client.store_file("greeting.txt", "text/plain", "Hello, world").should == "greeting.txt"
223
+ end
200
224
  end
201
225
 
202
- it "should store the file in Luwak and return the key/filename when the filename is given" do
203
- @http.should_receive(:put).with(204, "/luwak", "greeting.txt", anything, {"Content-Type" => "text/plain"}).and_return(:code => 204, :headers => {})
204
- @client.store_file("greeting.txt", "text/plain", "Hello, world").should == "greeting.txt"
226
+ describe "retrieving a file" do
227
+ before :each do
228
+ @client = Riak::Client.new
229
+ @http = mock(Riak::Client::HTTPBackend)
230
+ @client.stub!(:http).and_return(@http)
231
+ @http.should_receive(:get).with(200, "/luwak", "greeting.txt").and_yield("Hello,").and_yield(" world!").and_return({:code => 200, :headers => {"content-type" => ["text/plain"]}})
232
+ end
233
+
234
+ it "should stream the data to a temporary file" do
235
+ file = @client.get_file("greeting.txt")
236
+ file.open {|f| f.read.should == "Hello, world!" }
237
+ end
238
+
239
+ it "should stream the data through the given block, returning nil" do
240
+ string = ""
241
+ result = @client.get_file("greeting.txt"){|chunk| string << chunk }
242
+ result.should be_nil
243
+ string.should == "Hello, world!"
244
+ end
245
+
246
+ it "should expose the original key and content-type on the temporary file" do
247
+ file = @client.get_file("greeting.txt")
248
+ file.content_type.should == "text/plain"
249
+ file.original_filename.should == "greeting.txt"
250
+ end
205
251
  end
206
- end
207
252
 
208
- describe "retrieving a file" do
209
- before :each do
253
+ it "should delete a file" do
210
254
  @client = Riak::Client.new
211
255
  @http = mock(Riak::Client::HTTPBackend)
212
256
  @client.stub!(:http).and_return(@http)
213
- @http.should_receive(:get).with(200, "/luwak", "greeting.txt").and_yield("Hello,").and_yield(" world!").and_return({:code => 200, :headers => {"content-type" => ["text/plain"]}})
257
+ @http.should_receive(:delete).with([204,404], "/luwak", "greeting.txt")
258
+ @client.delete_file("greeting.txt")
214
259
  end
215
260
 
216
- it "should stream the data to a temporary file" do
217
- file = @client.get_file("greeting.txt")
218
- file.open {|f| f.read.should == "Hello, world!" }
261
+ it "should return true if the file exists" do
262
+ @client = Riak::Client.new
263
+ @client.http.should_receive(:head).and_return(:code => 200)
264
+ @client.file_exists?("foo").should be_true
219
265
  end
220
266
 
221
- it "should stream the data through the given block, returning nil" do
222
- string = ""
223
- result = @client.get_file("greeting.txt"){|chunk| string << chunk }
224
- result.should be_nil
225
- string.should == "Hello, world!"
267
+ it "should return false if the file doesn't exist" do
268
+ @client = Riak::Client.new
269
+ @client.http.should_receive(:head).and_return(:code => 404)
270
+ @client.file_exists?("foo").should be_false
226
271
  end
227
272
 
228
- it "should expose the original key and content-type on the temporary file" do
229
- file = @client.get_file("greeting.txt")
230
- file.content_type.should == "text/plain"
231
- file.original_filename.should == "greeting.txt"
273
+ it "should escape the filename when storing, retrieving or deleting files" do
274
+ @client = Riak::Client.new
275
+ @http = mock(Riak::Client::HTTPBackend)
276
+ @client.stub!(:http).and_return(@http)
277
+ # Delete escapes keys
278
+ @http.should_receive(:delete).with([204,404], "/luwak", "docs%2FA%20Big%20PDF.pdf")
279
+ @client.delete_file("docs/A Big PDF.pdf")
280
+ # Get escapes keys
281
+ @http.should_receive(:get).with(200, "/luwak", "docs%2FA%20Big%20PDF.pdf").and_yield("foo").and_return(:headers => {"content-type" => ["text/plain"]}, :code => 200)
282
+ @client.get_file("docs/A Big PDF.pdf")
283
+ # Streamed get escapes keys
284
+ @http.should_receive(:get).with(200, "/luwak", "docs%2FA%20Big%20PDF.pdf").and_yield("foo").and_return(:headers => {"content-type" => ["text/plain"]}, :code => 200)
285
+ @client.get_file("docs/A Big PDF.pdf"){|chunk| chunk }
286
+ # Put escapes keys
287
+ @http.should_receive(:put).with(204, "/luwak", "docs%2FA%20Big%20PDF.pdf", "foo", {"Content-Type" => "text/plain"})
288
+ @client.store_file("docs/A Big PDF.pdf", "text/plain", "foo")
232
289
  end
233
290
  end
234
-
235
- it "should delete a file" do
236
- @client = Riak::Client.new
237
- @http = mock(Riak::Client::HTTPBackend)
238
- @client.stub!(:http).and_return(@http)
239
- @http.should_receive(:delete).with([204,404], "/luwak", "greeting.txt")
240
- @client.delete_file("greeting.txt")
241
- end
242
-
243
- it "should return true if the file exists" do
244
- @client = Riak::Client.new
245
- @client.http.should_receive(:head).and_return(:code => 200)
246
- @client.file_exists?("foo").should be_true
247
- end
248
-
249
- it "should return false if the file doesn't exist" do
250
- @client = Riak::Client.new
251
- @client.http.should_receive(:head).and_return(:code => 404)
252
- @client.file_exists?("foo").should be_false
253
- end
254
-
255
- it "should escape the filename when storing, retrieving or deleting files" do
256
- @client = Riak::Client.new
257
- @http = mock(Riak::Client::HTTPBackend)
258
- @client.stub!(:http).and_return(@http)
259
- # Delete escapes keys
260
- @http.should_receive(:delete).with([204,404], "/luwak", "docs%2FA%20Big%20PDF.pdf")
261
- @client.delete_file("docs/A Big PDF.pdf")
262
- # Get escapes keys
263
- @http.should_receive(:get).with(200, "/luwak", "docs%2FA%20Big%20PDF.pdf").and_yield("foo").and_return(:headers => {"content-type" => ["text/plain"]}, :code => 200)
264
- @client.get_file("docs/A Big PDF.pdf")
265
- # Streamed get escapes keys
266
- @http.should_receive(:get).with(200, "/luwak", "docs%2FA%20Big%20PDF.pdf").and_yield("foo").and_return(:headers => {"content-type" => ["text/plain"]}, :code => 200)
267
- @client.get_file("docs/A Big PDF.pdf"){|chunk| chunk }
268
- # Put escapes keys
269
- @http.should_receive(:put).with(204, "/luwak", "docs%2FA%20Big%20PDF.pdf", "foo", {"Content-Type" => "text/plain"})
270
- @client.store_file("docs/A Big PDF.pdf", "text/plain", "foo")
271
- end
272
291
  end