riak-client 0.8.3 → 0.9.0.beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -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