riak-client 0.7.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 (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