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.
- data/Rakefile +10 -3
- data/lib/riak/bucket.rb +31 -46
- data/lib/riak/client.rb +18 -4
- data/lib/riak/client/excon_backend.rb +15 -1
- data/lib/riak/client/http_backend.rb +169 -196
- data/lib/riak/client/http_backend/configuration.rb +56 -0
- data/lib/riak/client/http_backend/object_methods.rb +101 -0
- data/lib/riak/client/http_backend/request_headers.rb +46 -0
- data/lib/riak/client/http_backend/transport_methods.rb +208 -0
- data/lib/riak/core_ext/blank.rb +14 -2
- data/lib/riak/link.rb +9 -4
- data/lib/riak/locale/en.yml +2 -0
- data/lib/riak/map_reduce.rb +37 -103
- data/lib/riak/map_reduce/filter_builder.rb +106 -0
- data/lib/riak/map_reduce/phase.rb +108 -0
- data/lib/riak/robject.rb +19 -96
- data/lib/riak/util/escape.rb +7 -0
- data/riak-client.gemspec +5 -5
- data/spec/riak/bucket_spec.rb +57 -111
- data/spec/riak/client_spec.rb +97 -78
- data/spec/riak/escape_spec.rb +7 -0
- data/spec/riak/http_backend/object_methods_spec.rb +218 -0
- data/spec/riak/http_backend_spec.rb +204 -65
- data/spec/riak/link_spec.rb +8 -0
- data/spec/riak/map_reduce_spec.rb +26 -151
- data/spec/riak/object_spec.rb +36 -350
- data/spec/riak/search_spec.rb +5 -16
- data/spec/spec_helper.rb +1 -1
- metadata +29 -8
data/Rakefile
CHANGED
@@ -26,6 +26,10 @@ gemspec = Gem::Specification.new do |gem|
|
|
26
26
|
lib/riak/cache_store.rb
|
27
27
|
lib/riak/client/curb_backend.rb
|
28
28
|
lib/riak/client/excon_backend.rb
|
29
|
+
lib/riak/client/http_backend/configuration.rb
|
30
|
+
lib/riak/client/http_backend/object_methods.rb
|
31
|
+
lib/riak/client/http_backend/request_headers.rb
|
32
|
+
lib/riak/client/http_backend/transport_methods.rb
|
29
33
|
lib/riak/client/http_backend.rb
|
30
34
|
lib/riak/client/net_http_backend.rb
|
31
35
|
lib/riak/client.rb
|
@@ -42,6 +46,8 @@ gemspec = Gem::Specification.new do |gem|
|
|
42
46
|
lib/riak/link.rb
|
43
47
|
lib/riak/locale
|
44
48
|
lib/riak/locale/en.yml
|
49
|
+
lib/riak/map_reduce/filter_builder.rb
|
50
|
+
lib/riak/map_reduce/phase.rb
|
45
51
|
lib/riak/map_reduce.rb
|
46
52
|
lib/riak/map_reduce_error.rb
|
47
53
|
lib/riak/robject.rb
|
@@ -68,6 +74,7 @@ gemspec = Gem::Specification.new do |gem|
|
|
68
74
|
spec/riak/escape_spec.rb
|
69
75
|
spec/riak/excon_backend_spec.rb
|
70
76
|
spec/riak/headers_spec.rb
|
77
|
+
spec/riak/http_backend/object_methods_spec.rb
|
71
78
|
spec/riak/http_backend_spec.rb
|
72
79
|
spec/riak/link_spec.rb
|
73
80
|
spec/riak/map_reduce_spec.rb
|
@@ -110,18 +117,18 @@ require 'rspec/core'
|
|
110
117
|
require 'rspec/core/rake_task'
|
111
118
|
|
112
119
|
desc "Run Unit Specs Only"
|
113
|
-
|
120
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
114
121
|
spec.pattern = "spec/riak/**/*_spec.rb"
|
115
122
|
end
|
116
123
|
|
117
124
|
namespace :spec do
|
118
125
|
desc "Run Integration Specs Only"
|
119
|
-
|
126
|
+
RSpec::Core::RakeTask.new(:integration) do |spec|
|
120
127
|
spec.pattern = "spec/integration/**/*_spec.rb"
|
121
128
|
end
|
122
129
|
|
123
130
|
desc "Run All Specs"
|
124
|
-
|
131
|
+
RSpec::Core::RakeTask.new(:all) do |spec|
|
125
132
|
spec.pattern = "spec/**/*_spec.rb"
|
126
133
|
end
|
127
134
|
end
|
data/lib/riak/bucket.rb
CHANGED
@@ -32,23 +32,7 @@ module Riak
|
|
32
32
|
def initialize(client, name)
|
33
33
|
raise ArgumentError, t("client_type", :client => client.inspect) unless Client === client
|
34
34
|
raise ArgumentError, t("string_type", :string => name.inspect) unless String === name
|
35
|
-
@client, @name
|
36
|
-
end
|
37
|
-
|
38
|
-
# Load information for the bucket from a response given by the {Riak::Client::HTTPBackend}.
|
39
|
-
# Used mostly internally - use {Riak::Client#bucket} to get a {Bucket} instance.
|
40
|
-
# @param [Hash] response a response from {Riak::Client::HTTPBackend}
|
41
|
-
# @return [Bucket] self
|
42
|
-
# @see Client#bucket
|
43
|
-
def load(response={})
|
44
|
-
content_type = response[:headers]['content-type'].first rescue ""
|
45
|
-
unless content_type =~ /json$/
|
46
|
-
raise Riak::InvalidResponse.new({"content-type" => ["application/json"]}, response[:headers], t("loading_bucket", :name => name))
|
47
|
-
end
|
48
|
-
payload = JSON.parse(response[:body])
|
49
|
-
@keys = payload['keys'].map {|k| CGI.unescape(k) } if payload['keys']
|
50
|
-
@props = payload['props'] if payload['props']
|
51
|
-
self
|
35
|
+
@client, @name = client, name
|
52
36
|
end
|
53
37
|
|
54
38
|
# Accesses or retrieves a list of keys in this bucket.
|
@@ -59,21 +43,15 @@ module Riak
|
|
59
43
|
# @yield [Array<String>] a list of keys from the current chunk
|
60
44
|
# @option options [Boolean] :reload (false) If present, will force reloading of the bucket's keys from Riak
|
61
45
|
# @return [Array<String>] Keys in this bucket
|
62
|
-
def keys(options={})
|
46
|
+
def keys(options={}, &block)
|
63
47
|
if block_given?
|
64
|
-
@client.
|
65
|
-
obj = JSON.parse(chunk) rescue nil
|
66
|
-
next unless obj and obj['keys']
|
67
|
-
yield obj['keys'].map {|k| CGI.unescape(k) } if obj['keys']
|
68
|
-
end
|
48
|
+
@client.backend.list_keys(self, &block)
|
69
49
|
elsif @keys.nil? || options[:reload]
|
70
|
-
|
71
|
-
load(response)
|
50
|
+
@keys = @client.backend.list_keys(self)
|
72
51
|
end
|
73
52
|
@keys
|
74
53
|
end
|
75
54
|
|
76
|
-
|
77
55
|
# Sets internal properties on the bucket
|
78
56
|
# Note: this results in a request to the Riak server!
|
79
57
|
# @param [Hash] properties new properties for the bucket
|
@@ -95,18 +73,18 @@ module Riak
|
|
95
73
|
# @see #n_value, #allow_mult, #r, #w, #dw, #rw
|
96
74
|
def props=(properties)
|
97
75
|
raise ArgumentError, t("hash_type", :hash => properties.inspect) unless Hash === properties
|
98
|
-
|
99
|
-
@client.
|
100
|
-
|
76
|
+
props.merge!(properties)
|
77
|
+
@client.backend.set_bucket_props(self, properties)
|
78
|
+
props
|
101
79
|
end
|
102
|
-
alias properties= props=
|
80
|
+
alias :'properties=' :'props='
|
103
81
|
|
104
82
|
# @return [Hash] Internal Riak bucket properties.
|
105
83
|
# @see #props=
|
106
84
|
def props
|
107
|
-
@props
|
85
|
+
@props ||= @client.backend.get_bucket_props(self)
|
108
86
|
end
|
109
|
-
alias properties props
|
87
|
+
alias :properties :props
|
110
88
|
|
111
89
|
# Retrieve an object from within the bucket.
|
112
90
|
# @param [String] key the key of the object to retrieve
|
@@ -115,9 +93,7 @@ module Riak
|
|
115
93
|
# @return [Riak::RObject] the object
|
116
94
|
# @raise [FailedRequest] if the object is not found or some other error occurs
|
117
95
|
def get(key, options={})
|
118
|
-
|
119
|
-
response = @client.http.get(code, @client.prefix, escape(name), escape(key), options, {})
|
120
|
-
RObject.new(self, key).load(response)
|
96
|
+
@client.backend.fetch_object(self, key, options[:r])
|
121
97
|
end
|
122
98
|
alias :[] :get
|
123
99
|
|
@@ -151,8 +127,12 @@ module Riak
|
|
151
127
|
# @option options [Fixnum] :r - the read quorum value for the request (R)
|
152
128
|
# @return [true, false] whether the key exists in this bucket
|
153
129
|
def exists?(key, options={})
|
154
|
-
|
155
|
-
|
130
|
+
begin
|
131
|
+
get(key, options)
|
132
|
+
true
|
133
|
+
rescue Riak::FailedRequest
|
134
|
+
false
|
135
|
+
end
|
156
136
|
end
|
157
137
|
alias :exist? :exists?
|
158
138
|
|
@@ -161,7 +141,7 @@ module Riak
|
|
161
141
|
# @param [Hash] options quorum options
|
162
142
|
# @option options [Fixnum] :rw - the read/write quorum for the delete
|
163
143
|
def delete(key, options={})
|
164
|
-
client.
|
144
|
+
client.backend.delete_object(self, key, options[:rw])
|
165
145
|
end
|
166
146
|
|
167
147
|
# @return [true, false] whether the bucket allows divergent siblings
|
@@ -181,7 +161,7 @@ module Riak
|
|
181
161
|
props['n_val']
|
182
162
|
end
|
183
163
|
alias :n_val :n_value
|
184
|
-
|
164
|
+
|
185
165
|
# Set the N value (number of replicas). *NOTE* This will result in a PUT request to Riak.
|
186
166
|
# Setting this value after the bucket has objects stored in it may have unpredictable results.
|
187
167
|
# @param [Fixnum] value the number of replicas the bucket should keep of each object
|
@@ -189,8 +169,8 @@ module Riak
|
|
189
169
|
self.props = {'n_val' => value}
|
190
170
|
value
|
191
171
|
end
|
192
|
-
alias :n_val= :n_value=
|
193
|
-
|
172
|
+
alias :'n_val=' :'n_value='
|
173
|
+
|
194
174
|
[:r,:w,:dw,:rw].each do |q|
|
195
175
|
class_eval <<-CODE
|
196
176
|
def #{q}
|
@@ -202,11 +182,16 @@ module Riak
|
|
202
182
|
value
|
203
183
|
end
|
204
184
|
CODE
|
205
|
-
|
185
|
+
end
|
206
186
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
187
|
+
# @return [String] a representation suitable for IRB and debugging output
|
188
|
+
def inspect
|
189
|
+
"#<Riak::Bucket {#{name}}#{" keys=[#{keys.join(',')}]" if defined?(@keys)}>"
|
190
|
+
end
|
191
|
+
|
192
|
+
# @return [true,false] whether the other is equivalent
|
193
|
+
def ==(other)
|
194
|
+
Bucket === other && other.client == client && other.name == name
|
211
195
|
end
|
212
196
|
end
|
197
|
+
end
|
data/lib/riak/client.rb
CHANGED
@@ -130,21 +130,35 @@ module Riak
|
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
|
+
alias :backend :http
|
134
|
+
|
133
135
|
# Retrieves a bucket from Riak.
|
134
136
|
# @param [String] bucket the bucket to retrieve
|
135
137
|
# @param [Hash] options options for retrieving the bucket
|
136
|
-
# @option options [Boolean] :keys (
|
137
|
-
# @option options [Boolean] :props (
|
138
|
+
# @option options [Boolean] :keys (false whether to retrieve the bucket keys
|
139
|
+
# @option options [Boolean] :props (false) whether to retreive the bucket properties
|
138
140
|
# @return [Bucket] the requested bucket
|
139
141
|
def bucket(name, options={})
|
140
142
|
unless (options.keys - [:keys, :props]).empty?
|
141
143
|
raise ArgumentError, "invalid options"
|
142
144
|
end
|
143
|
-
|
144
|
-
Bucket.new(self, name).
|
145
|
+
@bucket_cache ||= {}
|
146
|
+
(@bucket_cache[name] ||= Bucket.new(self, name)).tap do |b|
|
147
|
+
b.props if options[:props]
|
148
|
+
b.keys if options[:keys]
|
149
|
+
end
|
145
150
|
end
|
146
151
|
alias :[] :bucket
|
147
152
|
|
153
|
+
# Lists buckets which have keys stored in them.
|
154
|
+
# @note This is an expensive operation and should be used only
|
155
|
+
# in development.
|
156
|
+
# @return [Array<Bucket>] a list of buckets
|
157
|
+
def buckets
|
158
|
+
backend.list_buckets.map {|name| Bucket.new(self, name) }
|
159
|
+
end
|
160
|
+
alias :list_buckets :buckets
|
161
|
+
|
148
162
|
# Stores a large file/IO object in Riak via the "Luwak" interface.
|
149
163
|
# @overload store_file(filename, content_type, data)
|
150
164
|
# Stores the file at the given key/filename
|
@@ -21,6 +21,11 @@ module Riak
|
|
21
21
|
class ExconBackend < HTTPBackend
|
22
22
|
def self.configured?
|
23
23
|
begin
|
24
|
+
begin
|
25
|
+
require 'fiber'
|
26
|
+
rescue LoadError
|
27
|
+
require 'riak/util/fiber1.8'
|
28
|
+
end
|
24
29
|
require 'excon'
|
25
30
|
Excon::VERSION >= "0.3.4"
|
26
31
|
rescue LoadError
|
@@ -38,7 +43,16 @@ module Riak
|
|
38
43
|
params[:query] = uri.query if uri.query
|
39
44
|
params[:body] = data if [:put,:post].include?(method)
|
40
45
|
params[:idempotent] = (method != :post)
|
41
|
-
|
46
|
+
if block_given?
|
47
|
+
passed_block = block
|
48
|
+
Fiber.new {
|
49
|
+
f = Fiber.current
|
50
|
+
block = lambda {|chunk| f.resume(chunk) }
|
51
|
+
loop do
|
52
|
+
passed_block.call Fiber.yield
|
53
|
+
end
|
54
|
+
}.resume
|
55
|
+
end
|
42
56
|
response = connection.request(params, &block)
|
43
57
|
if valid_response?(expect, response.status)
|
44
58
|
response_headers.initialize_http_header(response.headers)
|
@@ -13,11 +13,27 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
require 'riak'
|
15
15
|
|
16
|
+
|
16
17
|
module Riak
|
17
18
|
class Client
|
18
|
-
# The parent class for all backends that connect to Riak via
|
19
|
+
# The parent class for all backends that connect to Riak via
|
20
|
+
# HTTP. This class implements all of the universal backend API
|
21
|
+
# methods on behalf of subclasses, which need only implement the
|
22
|
+
# {TransportMethods#perform} method for library-specific
|
23
|
+
# semantics.
|
19
24
|
class HTTPBackend
|
25
|
+
autoload :RequestHeaders, "riak/client/http_backend/request_headers"
|
26
|
+
autoload :TransportMethods, "riak/client/http_backend/transport_methods"
|
27
|
+
autoload :ObjectMethods, "riak/client/http_backend/object_methods"
|
28
|
+
autoload :Configuration, "riak/client/http_backend/configuration"
|
29
|
+
|
30
|
+
include TransportMethods
|
31
|
+
include ObjectMethods
|
32
|
+
include Configuration
|
33
|
+
|
34
|
+
include Util::Escape
|
20
35
|
include Util::Translation
|
36
|
+
|
21
37
|
# The Riak::Client that uses this backend
|
22
38
|
attr_reader :client
|
23
39
|
|
@@ -28,212 +44,169 @@ module Riak
|
|
28
44
|
@client = client
|
29
45
|
end
|
30
46
|
|
31
|
-
#
|
32
|
-
# @return [
|
33
|
-
def
|
34
|
-
{
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
#
|
41
|
-
# @param [
|
42
|
-
#
|
43
|
-
# @
|
44
|
-
# @
|
45
|
-
#
|
46
|
-
#
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
# @overload get(expect, *resource, headers={})
|
63
|
-
# Stream the response body through the supplied block
|
64
|
-
# @param [Hash] headers custom headers to send with the request
|
65
|
-
# @yield [chunk] yields successive chunks of the response body as strings
|
66
|
-
# @return [Hash] response data, containing only the :headers and :code keys
|
67
|
-
# @return [Hash] response data, containing :headers, :body, and :code keys
|
68
|
-
# @raise [FailedRequest] if the response code doesn't match the expected response
|
69
|
-
def get(expect, *resource, &block)
|
70
|
-
headers = default_headers.merge(resource.extract_options!)
|
71
|
-
verify_path!(resource)
|
72
|
-
perform(:get, path(*resource), headers, expect, &block)
|
73
|
-
end
|
74
|
-
|
75
|
-
# Performs a PUT request to the specified resource on the Riak server.
|
76
|
-
# @param [Fixnum, Array] expect the expected HTTP response code(s) from Riak
|
77
|
-
# @param [String, Array<String,Hash>] resource a relative path or array of path segments and optional query params Hash that will be joined to the root URI
|
78
|
-
# @param [String] body the request body to send to the server
|
79
|
-
# @overload put(expect, *resource, body)
|
80
|
-
# @overload put(expect, *resource, body, headers)
|
81
|
-
# Send the request with custom headers
|
82
|
-
# @param [Hash] headers custom headers to send with the request
|
83
|
-
# @overload put(expect, *resource, body, headers={})
|
84
|
-
# Stream the response body through the supplied block
|
85
|
-
# @param [Hash] headers custom headers to send with the request
|
86
|
-
# @yield [chunk] yields successive chunks of the response body as strings
|
87
|
-
# @return [Hash] response data, containing only the :headers and :code keys
|
88
|
-
# @return [Hash] response data, containing :headers, :code, and :body keys
|
89
|
-
# @raise [FailedRequest] if the response code doesn't match the expected response
|
90
|
-
def put(expect, *resource, &block)
|
91
|
-
headers = default_headers.merge(resource.extract_options!)
|
92
|
-
uri, data = verify_path_and_body!(resource)
|
93
|
-
perform(:put, path(*uri), headers, expect, data, &block)
|
94
|
-
end
|
95
|
-
|
96
|
-
# Performs a POST request to the specified resource on the Riak server.
|
97
|
-
# @param [Fixnum, Array] expect the expected HTTP response code(s) from Riak
|
98
|
-
# @param [String, Array<String>] resource a relative path or array of path segments that will be joined to the root URI
|
99
|
-
# @param [String] body the request body to send to the server
|
100
|
-
# @overload post(expect, *resource, body)
|
101
|
-
# @overload post(expect, *resource, body, headers)
|
102
|
-
# Send the request with custom headers
|
103
|
-
# @param [Hash] headers custom headers to send with the request
|
104
|
-
# @overload post(expect, *resource, body, headers={})
|
105
|
-
# Stream the response body through the supplied block
|
106
|
-
# @param [Hash] headers custom headers to send with the request
|
107
|
-
# @yield [chunk] yields successive chunks of the response body as strings
|
108
|
-
# @return [Hash] response data, containing only the :headers and :code keys
|
109
|
-
# @return [Hash] response data, containing :headers, :code and :body keys
|
110
|
-
# @raise [FailedRequest] if the response code doesn't match the expected response
|
111
|
-
def post(expect, *resource, &block)
|
112
|
-
headers = default_headers.merge(resource.extract_options!)
|
113
|
-
uri, data = verify_path_and_body!(resource)
|
114
|
-
perform(:post, path(*uri), headers, expect, data, &block)
|
115
|
-
end
|
116
|
-
|
117
|
-
# Performs a DELETE request to the specified resource on the Riak server.
|
118
|
-
# @param [Fixnum, Array] expect the expected HTTP response code(s) from Riak
|
119
|
-
# @param [String, Array<String,Hash>] resource a relative path or array of path segments and optional query params Hash that will be joined to the root URI
|
120
|
-
# @overload delete(expect, *resource)
|
121
|
-
# @overload delete(expect, *resource, headers)
|
122
|
-
# Send the request with custom headers
|
123
|
-
# @param [Hash] headers custom headers to send with the request
|
124
|
-
# @overload delete(expect, *resource, headers={})
|
125
|
-
# Stream the response body through the supplied block
|
126
|
-
# @param [Hash] headers custom headers to send with the request
|
127
|
-
# @yield [chunk] yields successive chunks of the response body as strings
|
128
|
-
# @return [Hash] response data, containing only the :headers and :code keys
|
129
|
-
# @return [Hash] response data, containing :headers, :code and :body keys
|
130
|
-
# @raise [FailedRequest] if the response code doesn't match the expected response
|
131
|
-
def delete(expect, *resource, &block)
|
132
|
-
headers = default_headers.merge(resource.extract_options!)
|
133
|
-
verify_path!(resource)
|
134
|
-
perform(:delete, path(*resource), headers, expect, &block)
|
135
|
-
end
|
136
|
-
|
137
|
-
# @return [URI] The calculated root URI for the Riak HTTP endpoint
|
138
|
-
def root_uri
|
139
|
-
URI.parse("http://#{@client.host}:#{@client.port}")
|
140
|
-
end
|
141
|
-
|
142
|
-
# Calculates an absolute URI from a relative path specification
|
143
|
-
# @param [Array<String,Hash>] segments a relative path or sequence of path segments and optional query params Hash that will be joined to the root URI
|
144
|
-
# @return [URI] an absolute URI for the resource
|
145
|
-
def path(*segments)
|
146
|
-
query = segments.extract_options!.to_param
|
147
|
-
root_uri.merge(segments.join("/").gsub(/\/+/, "/").sub(/^\//, '')).tap do |uri|
|
148
|
-
uri.query = query if query.present?
|
47
|
+
# Pings the server
|
48
|
+
# @return [true,false] whether the server is available
|
49
|
+
def ping
|
50
|
+
get(200, riak_kv_wm_ping, {}, {})
|
51
|
+
true
|
52
|
+
rescue
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
# Fetches an object by bucket/key
|
57
|
+
# @param [Bucket, String] bucket the bucket where the object is
|
58
|
+
# stored
|
59
|
+
# @param [String] key the key of the object
|
60
|
+
# @param [Fixnum, String, Symbol] r the read quorum for the
|
61
|
+
# request - how many nodes should concur on the read
|
62
|
+
# @return [RObject] the fetched object
|
63
|
+
def fetch_object(bucket, key, r=nil)
|
64
|
+
bucket = Bucket.new(client, bucket) if String === bucket
|
65
|
+
options = r ? {:r => r} : {}
|
66
|
+
response = get([200,300],riak_kv_wm_raw, escape(bucket.name), escape(key), options, {})
|
67
|
+
load_object(RObject.new(bucket, key), response)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Reloads the data for a given RObject, a special case of {#fetch}.
|
71
|
+
def reload_object(robject, r = nil)
|
72
|
+
options = r ? {:r => r} : {}
|
73
|
+
response = get([200,300,304], riak_kv_wm_raw, escape(robject.bucket.name), escape(robject.key), options, reload_headers(robject))
|
74
|
+
if response[:code].to_i == 304
|
75
|
+
robject
|
76
|
+
else
|
77
|
+
load_object(robject, response)
|
149
78
|
end
|
150
79
|
end
|
151
80
|
|
152
|
-
#
|
153
|
-
# @param [
|
154
|
-
# @
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
81
|
+
# Stores an object
|
82
|
+
# @param [RObject] robject the object to store
|
83
|
+
# @param [true,false] returnbody (false) whether to update the object
|
84
|
+
# after write with the new value
|
85
|
+
# @param [Fixnum, String, Symbol] w the write quorum
|
86
|
+
# @param [Fixnum, String, Symbol] dw the durable write quorum
|
87
|
+
def store_object(robject, returnbody=false, w=nil, dw=nil)
|
88
|
+
query = {}.tap do |q|
|
89
|
+
q[:returnbody] = returnbody unless returnbody.nil?
|
90
|
+
q[:w] = w unless w.nil?
|
91
|
+
q[:dw] = dw unless dw.nil?
|
161
92
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
#
|
176
|
-
#
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
#
|
185
|
-
#
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
#
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
#
|
202
|
-
# @
|
203
|
-
#
|
204
|
-
# @
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
initialize_http_header(hash)
|
93
|
+
method, codes, path = if robject.key.present?
|
94
|
+
[:put, [200,204,300], "#{escape(robject.bucket.name)}/#{escape(robject.key)}"]
|
95
|
+
else
|
96
|
+
[:post, 201, escape(robject.bucket.name)]
|
97
|
+
end
|
98
|
+
response = send(method, codes, riak_kv_wm_raw, path, query, robject.raw_data, store_headers(robject))
|
99
|
+
load_object(robject, response) if returnbody
|
100
|
+
end
|
101
|
+
|
102
|
+
# Deletes an object
|
103
|
+
# @param [Bucket, String] bucket the bucket where the object
|
104
|
+
# lives
|
105
|
+
# @param [String] key the key where the object lives
|
106
|
+
# @param [Fixnum, String, Symbol] rw the read/write quorum for
|
107
|
+
# the request
|
108
|
+
def delete_object(bucket, key, rw=nil)
|
109
|
+
bucket = bucket.name if Bucket === bucket
|
110
|
+
options = rw ? {:rw => rw} : {}
|
111
|
+
delete([204, 404], riak_kv_wm_raw, escape(bucket), escape(key), options, {})
|
112
|
+
end
|
113
|
+
|
114
|
+
# Fetches bucket properties
|
115
|
+
# @param [Bucket, String] bucket the bucket properties to fetch
|
116
|
+
# @return [Hash] bucket properties
|
117
|
+
def get_bucket_props(bucket)
|
118
|
+
bucket = bucket.name if Bucket === bucket
|
119
|
+
response = get(200, riak_kv_wm_raw, escape(bucket), {:keys => false, :props => true}, {})
|
120
|
+
JSON.parse(response[:body])['props']
|
121
|
+
end
|
122
|
+
|
123
|
+
# Sets bucket properties
|
124
|
+
# @param [Bucket, String] bucket the bucket to set properties on
|
125
|
+
# @param [Hash] properties the properties to set
|
126
|
+
def set_bucket_props(bucket, props)
|
127
|
+
bucket = bucket.name if Bucket === bucket
|
128
|
+
body = {'props' => props}.to_json
|
129
|
+
put(204, riak_kv_wm_raw, escape(bucket), body, {"Content-Type" => "application/json"})
|
130
|
+
end
|
131
|
+
|
132
|
+
# List keys in a bucket
|
133
|
+
# @param [Bucket, String] bucket the bucket to fetch the keys
|
134
|
+
# for
|
135
|
+
# @yield [Array<String>] a list of keys from the current
|
136
|
+
# streamed chunk
|
137
|
+
# @return [Array<String>] the list of keys, if no block was given
|
138
|
+
def list_keys(bucket, &block)
|
139
|
+
bucket = bucket.name if Bucket === bucket
|
140
|
+
if block_given?
|
141
|
+
get(200, riak_kv_wm_raw, escape(bucket), {:props => false, :keys => 'stream'}, {}) do |chunk|
|
142
|
+
obj = JSON.parse(chunk) rescue nil
|
143
|
+
next unless obj && obj['keys']
|
144
|
+
yield obj['keys'].map {|k| unescape(k) }
|
145
|
+
end
|
146
|
+
else
|
147
|
+
response = get(200, riak_kv_wm_raw, escape(bucket), {:props => false, :keys => true}, {})
|
148
|
+
obj = JSON.parse(response[:body])
|
149
|
+
obj && obj['keys'].map {|k| unescape(k) }
|
220
150
|
end
|
151
|
+
end
|
221
152
|
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
153
|
+
# Lists known buckets
|
154
|
+
# @return [Array<String>] the list of buckets
|
155
|
+
def list_buckets
|
156
|
+
response = get(200, riak_kv_wm_raw, {:buckets => true}, {})
|
157
|
+
JSON.parse(response[:body])['buckets']
|
158
|
+
end
|
159
|
+
|
160
|
+
# Performs a MapReduce query.
|
161
|
+
# @param [MapReduce] mr the query to perform
|
162
|
+
# @yield [Fixnum, Object] the phase number and single result
|
163
|
+
# from the phase
|
164
|
+
# @return [Array<Object>] the list of results, if no block was
|
165
|
+
# given
|
166
|
+
def mapred(mr)
|
167
|
+
response = post(200, riak_kv_wm_mapred, mr.to_json, {"Content-Type" => "application/json", "Accept" => "application/json"})
|
168
|
+
data = begin
|
169
|
+
JSON.parse(response[:body])
|
170
|
+
rescue
|
171
|
+
response
|
172
|
+
end
|
173
|
+
# This fakes streaming until the streaming MIME parser works.
|
174
|
+
if block_given?
|
175
|
+
data = [data] if mr.query.count {|p| p.keep } == 1
|
176
|
+
data.each_with_index do |phase, idx|
|
177
|
+
phase.each {|obj| yield idx, obj }
|
227
178
|
end
|
179
|
+
else
|
180
|
+
data
|
228
181
|
end
|
182
|
+
end
|
229
183
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
184
|
+
# Gets health statistics
|
185
|
+
# @return [Hash] information about the server, including stats
|
186
|
+
def stats
|
187
|
+
response = get(200, riak_kv_wm_stats, {}, {})
|
188
|
+
JSON.parse(response[:body])
|
189
|
+
end
|
190
|
+
|
191
|
+
# Performs a link-walking query
|
192
|
+
# @param [RObject] robject the object to start at
|
193
|
+
# @param [Array<WalkSpec>] walk_specs a list of walk
|
194
|
+
# specifications to process
|
195
|
+
# @return [Array<Array<RObject>>] a list of the matched objects,
|
196
|
+
# grouped by phase
|
197
|
+
def link_walk(robject, walk_specs)
|
198
|
+
response = get(200, riak_kv_wm_link_walker, escape(robject.bucket.name), escape(robject.key), walk_specs.join("/"))
|
199
|
+
if boundary = Util::Multipart.extract_boundary(response[:headers]['content-type'].first)
|
200
|
+
Util::Multipart.parse(response[:body], boundary).map do |group|
|
201
|
+
group.map do |obj|
|
202
|
+
if obj[:headers] && obj[:body] && obj[:headers]['location']
|
203
|
+
bucket, key = $1, $2 if obj[:headers]['location'].first =~ %r{/.*/(.*)/(.*)$}
|
204
|
+
load_object(RObject.new(client.bucket(bucket), key), obj)
|
205
|
+
end
|
235
206
|
end
|
236
207
|
end
|
208
|
+
else
|
209
|
+
[]
|
237
210
|
end
|
238
211
|
end
|
239
212
|
end
|