riak-client 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/Rakefile +74 -0
  2. data/lib/riak.rb +49 -0
  3. data/lib/riak/bucket.rb +176 -0
  4. data/lib/riak/cache_store.rb +82 -0
  5. data/lib/riak/client.rb +139 -0
  6. data/lib/riak/client/curb_backend.rb +82 -0
  7. data/lib/riak/client/http_backend.rb +209 -0
  8. data/lib/riak/client/net_http_backend.rb +49 -0
  9. data/lib/riak/failed_request.rb +37 -0
  10. data/lib/riak/i18n.rb +20 -0
  11. data/lib/riak/invalid_response.rb +25 -0
  12. data/lib/riak/link.rb +73 -0
  13. data/lib/riak/locale/en.yml +37 -0
  14. data/lib/riak/map_reduce.rb +248 -0
  15. data/lib/riak/map_reduce_error.rb +20 -0
  16. data/lib/riak/robject.rb +267 -0
  17. data/lib/riak/util/escape.rb +12 -0
  18. data/lib/riak/util/fiber1.8.rb +48 -0
  19. data/lib/riak/util/headers.rb +44 -0
  20. data/lib/riak/util/multipart.rb +52 -0
  21. data/lib/riak/util/translation.rb +29 -0
  22. data/lib/riak/walk_spec.rb +117 -0
  23. data/spec/fixtures/cat.jpg +0 -0
  24. data/spec/fixtures/multipart-blank.txt +7 -0
  25. data/spec/fixtures/multipart-with-body.txt +16 -0
  26. data/spec/integration/riak/cache_store_spec.rb +129 -0
  27. data/spec/riak/bucket_spec.rb +247 -0
  28. data/spec/riak/client_spec.rb +174 -0
  29. data/spec/riak/curb_backend_spec.rb +53 -0
  30. data/spec/riak/escape_spec.rb +21 -0
  31. data/spec/riak/headers_spec.rb +34 -0
  32. data/spec/riak/http_backend_spec.rb +131 -0
  33. data/spec/riak/link_spec.rb +82 -0
  34. data/spec/riak/map_reduce_spec.rb +352 -0
  35. data/spec/riak/multipart_spec.rb +36 -0
  36. data/spec/riak/net_http_backend_spec.rb +28 -0
  37. data/spec/riak/object_spec.rb +538 -0
  38. data/spec/riak/walk_spec_spec.rb +208 -0
  39. data/spec/spec_helper.rb +30 -0
  40. data/spec/support/http_backend_implementation_examples.rb +215 -0
  41. data/spec/support/mock_server.rb +61 -0
  42. data/spec/support/mocks.rb +3 -0
  43. metadata +187 -0
@@ -0,0 +1,74 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+
4
+ gemspec = Gem::Specification.new do |gem|
5
+ gem.name = "riak-client"
6
+ gem.summary = %Q{riak-client is a rich client for Riak, the distributed database by Basho.}
7
+ gem.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.}
8
+ gem.version = File.read('../VERSION').strip
9
+ gem.email = "seancribbs@gmail.com"
10
+ gem.homepage = "http://seancribbs.github.com/ripple"
11
+ gem.authors = ["Sean Cribbs"]
12
+ gem.add_development_dependency "rspec", "~>2.0.0.beta.6"
13
+ gem.add_development_dependency "fakeweb", ">=1.2"
14
+ gem.add_development_dependency "rack", ">=1.0"
15
+ gem.add_development_dependency "curb", ">=0.6"
16
+ gem.add_dependency "activesupport", ">= 2.3.5"
17
+ gem.requirements << "`gem install curb` for better HTTP performance"
18
+
19
+ files = FileList["**/*"]
20
+ files.exclude /\.DS_Store/
21
+ files.exclude /\#/
22
+ files.exclude /~/
23
+ files.exclude /\.swp/
24
+ files.exclude '**/._*'
25
+ files.exclude '**/*.orig'
26
+ files.exclude '**/*.rej'
27
+ files.exclude /^pkg/
28
+ files.exclude 'riak-client.gemspec'
29
+
30
+ gem.files = files.to_a
31
+
32
+ gem.test_files = FileList["spec/**/*.rb"].to_a
33
+ end
34
+
35
+ # Gem packaging tasks
36
+ Rake::GemPackageTask.new(gemspec) do |pkg|
37
+ pkg.need_zip = false
38
+ pkg.need_tar = false
39
+ end
40
+
41
+ task :gem => :gemspec
42
+
43
+ desc %{Build the gemspec file.}
44
+ task :gemspec do
45
+ gemspec.validate
46
+ File.open("#{gemspec.name}.gemspec", 'w'){|f| f.write gemspec.to_ruby }
47
+ end
48
+
49
+ desc %{Release the gem to RubyGems.org}
50
+ task :release => :gem do
51
+ "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
52
+ end
53
+
54
+ require 'rspec/core'
55
+ require 'rspec/core/rake_task'
56
+
57
+ desc "Run Unit Specs Only"
58
+ Rspec::Core::RakeTask.new(:spec) do |spec|
59
+ spec.pattern = "spec/riak/**/*_spec.rb"
60
+ end
61
+
62
+ namespace :spec do
63
+ desc "Run Integration Specs Only"
64
+ Rspec::Core::RakeTask.new(:integration) do |spec|
65
+ spec.pattern = "spec/integration/**/*_spec.rb"
66
+ end
67
+
68
+ desc "Run All Specs"
69
+ Rspec::Core::RakeTask.new(:all) do |spec|
70
+ spec.pattern = "spec/**/*_spec.rb"
71
+ end
72
+ end
73
+
74
+ task :default => :spec
@@ -0,0 +1,49 @@
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
+ $KCODE = "UTF8" if RUBY_VERSION < "1.9"
15
+
16
+ require 'active_support/all'
17
+ require 'active_support/json'
18
+ require 'base64'
19
+ require 'uri'
20
+ require 'net/http'
21
+ require 'yaml'
22
+ require 'riak/i18n'
23
+
24
+ # The Riak module contains all aspects of the HTTP client interface
25
+ # to Riak.
26
+ module Riak
27
+ # Domain objects
28
+ autoload :Bucket, "riak/bucket"
29
+ autoload :Client, "riak/client"
30
+ autoload :Link, "riak/link"
31
+ autoload :WalkSpec, "riak/walk_spec"
32
+ autoload :RObject, "riak/robject"
33
+ autoload :MapReduce, "riak/map_reduce"
34
+
35
+ # Cache store
36
+ autoload :CacheStore, "riak/cache_store"
37
+
38
+ # Exceptions
39
+ autoload :FailedRequest, "riak/failed_request"
40
+ autoload :InvalidResponse, "riak/invalid_response"
41
+ autoload :MapReduceError, "riak/map_reduce_error"
42
+
43
+ module Util
44
+ autoload :Escape, "riak/util/escape"
45
+ autoload :Headers, "riak/util/headers"
46
+ autoload :Multipart, "riak/util/multipart"
47
+ autoload :Translation, "riak/util/translation"
48
+ end
49
+ end
@@ -0,0 +1,176 @@
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 'riak'
15
+
16
+ module Riak
17
+ # Represents and encapsulates operations on a Riak bucket. You may retrieve a bucket
18
+ # using {Client#bucket}, or create it manually and retrieve its meta-information later.
19
+ class Bucket
20
+ include Util::Translation
21
+ include Util::Escape
22
+
23
+ # @return [Riak::Client] the associated client
24
+ attr_reader :client
25
+
26
+ # @return [String] the bucket name
27
+ attr_reader :name
28
+
29
+ # @return [Hash] Internal Riak bucket properties.
30
+ attr_reader :props
31
+ alias_attribute :properties, :props
32
+
33
+ # Create a Riak bucket manually.
34
+ # @param [Client] client the {Riak::Client} for this bucket
35
+ # @param [String] name the name of the bucket
36
+ def initialize(client, name)
37
+ raise ArgumentError, t("client_type", :client => client.inspect) unless Client === client
38
+ raise ArgumentError, t("string_type", :string => name.inspect) unless String === name
39
+ @client, @name, @props = client, name, {}
40
+ end
41
+
42
+ # Load information for the bucket from a response given by the {Riak::Client::HTTPBackend}.
43
+ # Used mostly internally - use {Riak::Client#bucket} to get a {Bucket} instance.
44
+ # @param [Hash] response a response from {Riak::Client::HTTPBackend}
45
+ # @return [Bucket] self
46
+ # @see Client#bucket
47
+ def load(response={})
48
+ unless response.try(:[], :headers).try(:[],'content-type').try(:first) =~ /json$/
49
+ raise Riak::InvalidResponse.new({"content-type" => ["application/json"]}, response[:headers], t("loading_bucket", :name => name))
50
+ end
51
+ payload = ActiveSupport::JSON.decode(response[:body])
52
+ @keys = payload['keys'].map {|k| URI.unescape(k) } if payload['keys']
53
+ @props = payload['props'] if payload['props']
54
+ self
55
+ end
56
+
57
+ # Accesses or retrieves a list of keys in this bucket.
58
+ # If a block is given, keys will be streamed through
59
+ # the block (useful for large buckets). When streaming,
60
+ # results of the operation will not be retained in the local Bucket object.
61
+ # @param [Hash] options extra options
62
+ # @yield [Array<String>] a list of keys from the current chunk
63
+ # @option options [Boolean] :reload (false) If present, will force reloading of the bucket's keys from Riak
64
+ # @return [Array<String>] Keys in this bucket
65
+ def keys(options={})
66
+ if block_given?
67
+ @client.http.get(200, @client.prefix, escape(name), {:props => false, :keys => 'stream'}, {}) do |chunk|
68
+ obj = ActiveSupport::JSON.decode(chunk) rescue {}
69
+ yield obj['keys'].map {|k| URI.unescape(k) } if obj['keys']
70
+ end
71
+ elsif @keys.nil? || options[:reload]
72
+ response = @client.http.get(200, @client.prefix, escape(name), {:props => false}, {})
73
+ load(response)
74
+ end
75
+ @keys
76
+ end
77
+
78
+ # Sets internal properties on the bucket
79
+ # Note: this results in a request to the Riak server!
80
+ # @param [Hash] properties new properties for the bucket
81
+ # @return [Hash] the properties that were accepted
82
+ # @raise [FailedRequest] if the new properties were not accepted by the Riak server
83
+ def props=(properties)
84
+ raise ArgumentError, t("hash_type", :hash => properties.inspect) unless Hash === properties
85
+ body = {'props' => properties}.to_json
86
+ @client.http.put(204, @client.prefix, escape(name), body, {"Content-Type" => "application/json"})
87
+ @props = properties
88
+ end
89
+
90
+ # Retrieve an object from within the bucket.
91
+ # @param [String] key the key of the object to retrieve
92
+ # @param [Hash] options query parameters for the request
93
+ # @option options [Fixnum] :r - the read quorum for the request - how many nodes should concur on the read
94
+ # @return [Riak::RObject] the object
95
+ # @raise [FailedRequest] if the object is not found or some other error occurs
96
+ def get(key, options={})
97
+ code = allow_mult ? [200,300] : 200
98
+ response = @client.http.get(code, @client.prefix, escape(name), escape(key), options, {})
99
+ RObject.new(self, key).load(response)
100
+ end
101
+ alias :[] :get
102
+
103
+ # Create a new blank object
104
+ # @param [String] key the key of the new object
105
+ # @return [RObject] the new, unsaved object
106
+ def new(key=nil)
107
+ RObject.new(self, key).tap do |obj|
108
+ obj.content_type = "application/json"
109
+ end
110
+ end
111
+
112
+ # Fetches an object if it exists, otherwise creates a new one with the given key
113
+ # @param [String] key the key to fetch or create
114
+ # @return [RObject] the new or existing object
115
+ def get_or_new(key, options={})
116
+ begin
117
+ get(key, options)
118
+ rescue Riak::FailedRequest => fr
119
+ if fr.code.to_i == 404
120
+ new(key)
121
+ else
122
+ raise fr
123
+ end
124
+ end
125
+ end
126
+
127
+ # Checks whether an object exists in Riak.
128
+ # @param [String] key the key to check
129
+ # @param [Hash] options quorum options
130
+ # @option options [Fixnum] :r - the read quorum value for the request (R)
131
+ # @return [true, false] whether the key exists in this bucket
132
+ def exists?(key, options={})
133
+ result = client.http.head([200,404], client.prefix, escape(name), escape(key), options, {})
134
+ result[:code] == 200
135
+ end
136
+ alias :exist? :exists?
137
+
138
+ # Deletes a key from the bucket
139
+ # @param [String] key the key to delete
140
+ # @param [Hash] options quorum options
141
+ # @option options [Fixnum] :rw - the read/write quorum for the delete
142
+ def delete(key, options={})
143
+ client.http.delete([204,404], client.prefix, escape(name), escape(key), options, {})
144
+ end
145
+
146
+ # @return [true, false] whether the bucket allows divergent siblings
147
+ def allow_mult
148
+ props['allow_mult']
149
+ end
150
+
151
+ # Set the allow_mult property. *NOTE* This will result in a PUT request to Riak.
152
+ # @param [true, false] value whether the bucket should allow siblings
153
+ def allow_mult=(value)
154
+ self.props = {'allow_mult' => value}
155
+ value
156
+ end
157
+
158
+ # @return [Fixnum] the N value, or number of replicas for this bucket
159
+ def n_value
160
+ props['n_val']
161
+ end
162
+
163
+ # Set the N value (number of replicas). *NOTE* This will result in a PUT request to Riak.
164
+ # Setting this value after the bucket has objects stored in it may have unpredictable results.
165
+ # @param [Fixnum] value the number of replicas the bucket should keep of each object
166
+ def n_value=(value)
167
+ self.props = {'n_val' => value}
168
+ value
169
+ end
170
+
171
+ # @return [String] a representation suitable for IRB and debugging output
172
+ def inspect
173
+ "#<Riak::Bucket #{client.http.path(client.prefix, escape(name)).to_s}#{" keys=[#{keys.join(',')}]" if defined?(@keys)}>"
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,82 @@
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 'riak'
15
+ module Riak
16
+ class CacheStore < ActiveSupport::Cache::Store
17
+ attr_accessor :client
18
+
19
+ def initialize(options = {})
20
+ @bucket_name = options.delete(:bucket) || '_cache'
21
+ @n_value = options.delete(:n_value) || 2
22
+ @r = [options.delete(:r) || 1, @n_value].min
23
+ @w = [options.delete(:w) || 1, @n_value].min
24
+ @dw = [options.delete(:dw) || 0, @n_value].min
25
+ @rw = [options.delete(:rw) || 1, @n_value].min
26
+ @client = Riak::Client.new(options)
27
+ end
28
+
29
+ def bucket
30
+ @bucket ||= @client.bucket(@bucket_name, :keys => false).tap do |b|
31
+ begin
32
+ b.n_value = @n_value unless b.n_value == @n_value
33
+ rescue
34
+ end
35
+ end
36
+ end
37
+
38
+ def write(key, value, options={})
39
+ super do
40
+ object = bucket.get_or_new(key, :r => @r)
41
+ object.content_type = 'application/yaml'
42
+ object.data = value
43
+ object.store(:r => @r, :w => @w, :dw => @dw)
44
+ end
45
+ end
46
+
47
+ def read(key, options={})
48
+ super do
49
+ begin
50
+ bucket.get(key, :r => @r).data
51
+ rescue Riak::FailedRequest => fr
52
+ raise fr unless fr.code == 404
53
+ nil
54
+ end
55
+ end
56
+ end
57
+
58
+ def exist?(key)
59
+ super do
60
+ bucket.exists?(key, :r => @r)
61
+ end
62
+ end
63
+
64
+ def delete_matched(matcher, options={})
65
+ super do
66
+ bucket.keys do |keys|
67
+ keys.grep(matcher).each do |k|
68
+ bucket.delete(k, :rw => @rw)
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ def delete(key, options={})
75
+ super do
76
+ bucket.delete(key, :rw => @rw)
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ ActiveSupport::Cache::RiakStore = Riak::CacheStore unless defined?(ActiveSupport::Cache::RiakStore)
@@ -0,0 +1,139 @@
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 'riak'
15
+
16
+ module Riak
17
+ # A client connection to Riak.
18
+ class Client
19
+ include Util::Translation
20
+ include Util::Escape
21
+
22
+ autoload :HTTPBackend, "riak/client/http_backend"
23
+ autoload :NetHTTPBackend, "riak/client/net_http_backend"
24
+ autoload :CurbBackend, "riak/client/curb_backend"
25
+
26
+ # When using integer client IDs, the exclusive upper-bound of valid values.
27
+ MAX_CLIENT_ID = 4294967296
28
+
29
+ # Regexp for validating hostnames, lifted from uri.rb in Ruby 1.8.6
30
+ HOST_REGEX = /^(?:(?:(?:[a-zA-Z\d](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-fA-F\d]{1,4}:)*[a-fA-F\d]{1,4})?::(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)\])$/n
31
+
32
+ # @return [String] The host or IP address for the Riak endpoint
33
+ attr_reader :host
34
+
35
+ # @return [Fixnum] The port of the Riak HTTP endpoint
36
+ attr_reader :port
37
+
38
+ # @return [String] The internal client ID used by Riak to route responses
39
+ attr_reader :client_id
40
+
41
+ # @return [String] The URL path prefix to the "raw" HTTP endpoint
42
+ attr_accessor :prefix
43
+
44
+ # @return [String] The URL path to the map-reduce HTTP endpoint
45
+ attr_accessor :mapred
46
+
47
+ # Creates a client connection to Riak
48
+ # @param [Hash] options configuration options for the client
49
+ # @option options [String] :host ('127.0.0.1') The host or IP address for the Riak endpoint
50
+ # @option options [Fixnum] :port (8098) The port of the Riak HTTP endpoint
51
+ # @option options [String] :prefix ('/riak/') The URL path prefix to the main HTTP endpoint
52
+ # @option options [String] :mapred ('/mapred') The path to the map-reduce HTTP endpoint
53
+ # @option options [Fixnum, String] :client_id (rand(MAX_CLIENT_ID)) The internal client ID used by Riak to route responses
54
+ # @raise [ArgumentError] raised if any options are invalid
55
+ def initialize(options={})
56
+ options.assert_valid_keys(:host, :port, :prefix, :client_id, :mapred)
57
+ self.host = options[:host] || "127.0.0.1"
58
+ self.port = options[:port] || 8098
59
+ self.client_id = options[:client_id] || make_client_id
60
+ self.prefix = options[:prefix] || "/riak/"
61
+ self.mapred = options[:mapred] || "/mapred"
62
+ raise ArgumentError, t("missing_host_and_port") unless @host && @port
63
+ end
64
+
65
+ # Set the client ID for this client. Must be a string or Fixnum value 0 =< value < MAX_CLIENT_ID.
66
+ # @param [String, Fixnum] value The internal client ID used by Riak to route responses
67
+ # @raise [ArgumentError] when an invalid client ID is given
68
+ # @return [String] the assigned client ID
69
+ def client_id=(value)
70
+ @client_id = case value
71
+ when 0...MAX_CLIENT_ID
72
+ b64encode(value)
73
+ when String
74
+ value
75
+ else
76
+ raise ArgumentError, t("invalid_client_id", :max_id => MAX_CLIENT_ID)
77
+ end
78
+ end
79
+
80
+ # Set the hostname of the Riak endpoint. Must be an IPv4, IPv6, or valid hostname
81
+ # @param [String] value The host or IP address for the Riak endpoint
82
+ # @raise [ArgumentError] if an invalid hostname is given
83
+ # @return [String] the assigned hostname
84
+ def host=(value)
85
+ raise ArgumentError, t("hostname_invalid") unless String === value && value.present? && value =~ HOST_REGEX
86
+ @host = value
87
+ end
88
+
89
+ # Set the port number of the Riak endpoint. This must be an integer between 0 and 65535.
90
+ # @param [Fixnum] value The port number of the Riak endpoint
91
+ # @raise [ArgumentError] if an invalid port number is given
92
+ # @return [Fixnum] the assigned port number
93
+ def port=(value)
94
+ raise ArgumentError, t("port_invalid") unless (0..65535).include?(value)
95
+ @port = value
96
+ end
97
+
98
+ # Automatically detects and returns an appropriate HTTP backend.
99
+ # The HTTP backend is used internally by the Riak client, but can also
100
+ # be used to access the server directly.
101
+ # @return [HTTPBackend] the HTTP backend for this client
102
+ def http
103
+ @http ||= begin
104
+ require 'curb'
105
+ CurbBackend.new(self)
106
+ rescue LoadError, NameError
107
+ warn t("install_curb")
108
+ NetHTTPBackend.new(self)
109
+ end
110
+ end
111
+
112
+ # Retrieves a bucket from Riak.
113
+ # @param [String] bucket the bucket to retrieve
114
+ # @param [Hash] options options for retrieving the bucket
115
+ # @option options [Boolean] :keys (true) whether to retrieve the bucket keys
116
+ # @option options [Boolean] :props (true) whether to retreive the bucket properties
117
+ # @return [Bucket] the requested bucket
118
+ def bucket(name, options={})
119
+ options.assert_valid_keys(:keys, :props)
120
+ response = http.get(200, prefix, escape(name), options, {})
121
+ Bucket.new(self, name).load(response)
122
+ end
123
+ alias :[] :bucket
124
+
125
+ # @return [String] A representation suitable for IRB and debugging output.
126
+ def inspect
127
+ "#<Riak::Client #{http.root_uri.to_s}>"
128
+ end
129
+
130
+ private
131
+ def make_client_id
132
+ b64encode(rand(MAX_CLIENT_ID))
133
+ end
134
+
135
+ def b64encode(n)
136
+ Base64.encode64([n].pack("N")).chomp
137
+ end
138
+ end
139
+ end