riak-client 0.9.0.beta → 0.9.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +10 -7
- data/Rakefile +21 -3
- data/erl_src/riak_kv_test_backend.beam +0 -0
- data/erl_src/riak_kv_test_backend.erl +29 -13
- data/lib/riak/bucket.rb +1 -1
- data/lib/riak/cache_store.rb +1 -1
- data/lib/riak/client.rb +119 -8
- data/lib/riak/client/beefcake/messages.rb +162 -0
- data/lib/riak/client/beefcake/object_methods.rb +92 -0
- data/lib/riak/client/beefcake_protobuffs_backend.rb +186 -0
- data/lib/riak/client/curb_backend.rb +10 -16
- data/lib/riak/client/excon_backend.rb +14 -18
- data/lib/riak/client/http_backend.rb +13 -13
- data/lib/riak/client/http_backend/object_methods.rb +1 -1
- data/lib/riak/client/http_backend/transport_methods.rb +6 -2
- data/lib/riak/client/net_http_backend.rb +33 -20
- data/lib/riak/client/protobuffs_backend.rb +103 -0
- data/lib/riak/client/pump.rb +44 -0
- data/lib/riak/failed_request.rb +58 -3
- data/lib/riak/locale/en.yml +11 -3
- data/lib/riak/map_reduce.rb +15 -6
- data/lib/riak/map_reduce/filter_builder.rb +4 -4
- data/lib/riak/test_server.rb +5 -1
- data/lib/riak/util/multipart.rb +30 -16
- data/lib/riak/util/multipart/stream_parser.rb +74 -0
- data/riak-client.gemspec +14 -12
- data/spec/fixtures/server.cert.crt +15 -0
- data/spec/fixtures/server.cert.key +15 -0
- data/spec/fixtures/test.pem +1 -0
- data/spec/integration/riak/http_backends_spec.rb +45 -0
- data/spec/integration/riak/protobuffs_backends_spec.rb +45 -0
- data/spec/integration/riak/test_server_spec.rb +2 -2
- data/spec/riak/bucket_spec.rb +4 -4
- data/spec/riak/client_spec.rb +209 -3
- data/spec/riak/excon_backend_spec.rb +8 -7
- data/spec/riak/http_backend/configuration_spec.rb +64 -0
- data/spec/riak/http_backend/object_methods_spec.rb +1 -1
- data/spec/riak/http_backend/transport_methods_spec.rb +129 -0
- data/spec/riak/http_backend_spec.rb +13 -1
- data/spec/riak/map_reduce/filter_builder_spec.rb +45 -0
- data/spec/riak/map_reduce/phase_spec.rb +149 -0
- data/spec/riak/map_reduce_spec.rb +5 -5
- data/spec/riak/net_http_backend_spec.rb +1 -0
- data/spec/riak/{object_spec.rb → robject_spec.rb} +1 -1
- data/spec/riak/stream_parser_spec.rb +66 -0
- data/spec/support/drb_mock_server.rb +2 -2
- data/spec/support/http_backend_implementation_examples.rb +27 -0
- data/spec/support/mock_server.rb +22 -1
- data/spec/support/unified_backend_examples.rb +255 -0
- metadata +43 -54
@@ -104,7 +104,7 @@ describe Riak::MapReduce do
|
|
104
104
|
|
105
105
|
it "should accept a list of key-filters along with a bucket" do
|
106
106
|
@mr.add("foo", [[:tokenize, "-", 3], [:string_to_int], [:between, 2009, 2010]])
|
107
|
-
@mr.inputs.should == {:bucket => "foo", :
|
107
|
+
@mr.inputs.should == {:bucket => "foo", :key_filters => [[:tokenize, "-", 3], [:string_to_int], [:between, 2009, 2010]]}
|
108
108
|
end
|
109
109
|
|
110
110
|
it "should add a bucket and filter list via a builder block" do
|
@@ -113,7 +113,7 @@ describe Riak::MapReduce do
|
|
113
113
|
string_to_int
|
114
114
|
between 2009, 2010
|
115
115
|
end
|
116
|
-
@mr.inputs.should == {:bucket => "foo", :
|
116
|
+
@mr.inputs.should == {:bucket => "foo", :key_filters => [[:tokenize, "-", 3], [:string_to_int], [:between, 2009, 2010]]}
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
@@ -250,19 +250,19 @@ describe Riak::MapReduce do
|
|
250
250
|
end
|
251
251
|
|
252
252
|
it "should interpret failed requests with JSON content-types as map reduce errors" do
|
253
|
-
@backend.stub!(:mapred).and_raise(Riak::
|
253
|
+
@backend.stub!(:mapred).and_raise(Riak::HTTPFailedRequest.new(:post, 200, 500, {"content-type" => ["application/json"]}, '{"error":"syntax error"}'))
|
254
254
|
lambda { @mr.run }.should raise_error(Riak::MapReduceError)
|
255
255
|
begin
|
256
256
|
@mr.run
|
257
257
|
rescue Riak::MapReduceError => mre
|
258
|
-
mre.message.should
|
258
|
+
mre.message.should include('{"error":"syntax error"}')
|
259
259
|
else
|
260
260
|
fail "No exception raised!"
|
261
261
|
end
|
262
262
|
end
|
263
263
|
|
264
264
|
it "should re-raise non-JSON error responses" do
|
265
|
-
@backend.stub!(:mapred).and_raise(Riak::
|
265
|
+
@backend.stub!(:mapred).and_raise(Riak::HTTPFailedRequest.new(:post, 200, 500, {"content-type" => ["text/plain"]}, 'Oops, you bwoke it.'))
|
266
266
|
lambda { @mr.run }.should raise_error(Riak::FailedRequest)
|
267
267
|
end
|
268
268
|
end
|
@@ -374,7 +374,7 @@ describe Riak::RObject do
|
|
374
374
|
end
|
375
375
|
|
376
376
|
it "should pass through a failed request exception" do
|
377
|
-
@backend.should_receive(:delete_object).and_raise(Riak::
|
377
|
+
@backend.should_receive(:delete_object).and_raise(Riak::HTTPFailedRequest.new(:delete, [204,404], 500, {}, ""))
|
378
378
|
lambda { @object.delete }.should raise_error(Riak::FailedRequest)
|
379
379
|
end
|
380
380
|
end
|
@@ -0,0 +1,66 @@
|
|
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 File.expand_path("../spec_helper", File.dirname(__FILE__))
|
15
|
+
|
16
|
+
describe Riak::Util::Multipart::StreamParser do
|
17
|
+
let(:klass) { Riak::Util::Multipart::StreamParser }
|
18
|
+
let(:block) { mock }
|
19
|
+
it "should detect the initial boundary" do
|
20
|
+
text = "--boundary1\r\nContent-Type: text/plain\r\n\r\nfoo\r\n--boundary1--\r\n"
|
21
|
+
parser = klass.new do |result|
|
22
|
+
result[:headers]['content-type'].should include("text/plain")
|
23
|
+
result[:body].should == "foo"
|
24
|
+
end
|
25
|
+
parser.accept text
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should detect inner multipart bodies" do
|
29
|
+
block.should_receive(:ping).once.and_return(true)
|
30
|
+
parser = klass.new do |result|
|
31
|
+
block.ping
|
32
|
+
result.should have(1).item
|
33
|
+
result.first[:headers]['content-type'].should include("text/plain")
|
34
|
+
result.first[:body].should == "SCP sloooow...."
|
35
|
+
end
|
36
|
+
File.open("spec/fixtures/multipart-with-body.txt", "r") do |f|
|
37
|
+
while chunk = f.read(16)
|
38
|
+
parser.accept chunk
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should yield successive complete chunks to the block" do
|
44
|
+
block.should_receive(:ping).twice.and_return(true)
|
45
|
+
parser = klass.new do |result|
|
46
|
+
block.ping
|
47
|
+
result[:headers]['content-type'].should include("application/json")
|
48
|
+
lambda { JSON.parse(result[:body]) }.should_not raise_error
|
49
|
+
end
|
50
|
+
File.open("spec/fixtures/multipart-mapreduce.txt", "r") do |f|
|
51
|
+
while chunk = f.read(16)
|
52
|
+
parser.accept chunk
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should yield successive complete bodies to the block, even when multiple bodies are accepted in a single chunk" do
|
58
|
+
block.should_receive(:ping).twice.and_return(true)
|
59
|
+
parser = klass.new do |result|
|
60
|
+
block.ping
|
61
|
+
result[:headers]['content-type'].should include("application/json")
|
62
|
+
lambda { JSON.parse(result[:body]) }.should_not raise_error
|
63
|
+
end
|
64
|
+
parser.accept File.read("spec/fixtures/multipart-mapreduce.txt")
|
65
|
+
end
|
66
|
+
end
|
@@ -7,7 +7,7 @@ module DrbMockServer
|
|
7
7
|
def start_client
|
8
8
|
# JRuby doesn't support fork
|
9
9
|
if defined? JRUBY_VERSION
|
10
|
-
@server = MockServer.new
|
10
|
+
@server = MockServer.new(2)
|
11
11
|
at_exit { @server.stop }
|
12
12
|
else
|
13
13
|
child_pid = Process.fork do
|
@@ -17,6 +17,7 @@ module DrbMockServer
|
|
17
17
|
at_exit { Process.kill("HUP", child_pid); Process.wait2 }
|
18
18
|
DRb.start_service
|
19
19
|
@server = DRbObject.new_with_uri(DRBURI)
|
20
|
+
sleep 1
|
20
21
|
end
|
21
22
|
true
|
22
23
|
end
|
@@ -29,7 +30,6 @@ module DrbMockServer
|
|
29
30
|
@server.send(meth, *args, &block)
|
30
31
|
end
|
31
32
|
|
32
|
-
private
|
33
33
|
def start_server
|
34
34
|
server = MockServer.new
|
35
35
|
DRb.start_service(DRBURI, server)
|
@@ -212,4 +212,31 @@ shared_examples_for "HTTP backend" do
|
|
212
212
|
end
|
213
213
|
end
|
214
214
|
end
|
215
|
+
|
216
|
+
describe "SSL" do
|
217
|
+
it "should be supported" do
|
218
|
+
@client.port = $mock_server.port + 1 unless @client.http_backend == :NetHTTP
|
219
|
+
@client.ssl = true
|
220
|
+
setup_http_mock(:get, @backend.path("/riak/","ssl").to_s, :body => "Success!")
|
221
|
+
response = @backend.get(200, "/riak/","ssl")
|
222
|
+
response[:code].should == 200
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe "HTTP Basic Authentication", :basic_auth => true do
|
227
|
+
it "should add the http basic auth header" do
|
228
|
+
@client.basic_auth = "ripple:rocks"
|
229
|
+
if @client.http_backend == :NetHTTP
|
230
|
+
setup_http_mock(:get, "http://ripple:rocks@127.0.0.1:8098/riak/auth", :body => 'Success!')
|
231
|
+
else
|
232
|
+
@_mock_set = "Basic #{Base64::encode64("ripple:rocks").strip}"
|
233
|
+
$mock_server.attach do |env|
|
234
|
+
$mock_server.satisfied = env['HTTP_AUTHORIZATION'] == @_mock_set
|
235
|
+
[200, {}, Array('Success!')]
|
236
|
+
end
|
237
|
+
end
|
238
|
+
response = @backend.get(200, "/riak/", "auth")
|
239
|
+
response[:code].should == 200
|
240
|
+
end
|
241
|
+
end
|
215
242
|
end
|
data/spec/support/mock_server.rb
CHANGED
@@ -16,6 +16,9 @@
|
|
16
16
|
# Based on code from Rob Styles and Chris Tierney found at:
|
17
17
|
# http://dynamicorange.com/2009/02/18/ruby-mock-web-server/
|
18
18
|
require 'rack'
|
19
|
+
require 'openssl'
|
20
|
+
require 'webrick/https'
|
21
|
+
require 'rack/handler/webrick'
|
19
22
|
|
20
23
|
class MockServer
|
21
24
|
attr_accessor :port
|
@@ -25,14 +28,24 @@ class MockServer
|
|
25
28
|
self.port = 4000 + rand(61535)
|
26
29
|
@block = nil
|
27
30
|
@parent_thread = Thread.current
|
31
|
+
options = {:AccessLog => [], :Logger => NullLogger.new, :Host => '127.0.0.1'}
|
28
32
|
@thread = Thread.new do
|
29
|
-
Rack::Handler::WEBrick.run(self, :Port => port
|
33
|
+
Rack::Handler::WEBrick.run(self, options.merge(:Port => port))
|
34
|
+
end
|
35
|
+
@ssl_thread = Thread.new do
|
36
|
+
Rack::Handler::WEBrick.run(self, options.merge(:Port => port+1,
|
37
|
+
:SSLEnable => true,
|
38
|
+
:SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
|
39
|
+
:SSLCertificate => read_cert,
|
40
|
+
:SSLPrivateKey => read_pkey,
|
41
|
+
:SSLCertName => [ [ "CN",'127.0.0.1' ] ]))
|
30
42
|
end
|
31
43
|
sleep pause # give the server time to fire up… YUK!
|
32
44
|
end
|
33
45
|
|
34
46
|
def stop
|
35
47
|
Thread.kill(@thread)
|
48
|
+
Thread.kill(@ssl_thread)
|
36
49
|
end
|
37
50
|
|
38
51
|
def expect(status, headers, method, path, query, body)
|
@@ -64,6 +77,14 @@ class MockServer
|
|
64
77
|
end
|
65
78
|
end
|
66
79
|
|
80
|
+
def read_pkey
|
81
|
+
OpenSSL::PKey::RSA.new(File.read(File.expand_path(File.dirname(__FILE__) + '/../fixtures/server.cert.key')), 'ripple')
|
82
|
+
end
|
83
|
+
|
84
|
+
def read_cert
|
85
|
+
OpenSSL::X509::Certificate.new(File.read((File.expand_path(File.dirname(__FILE__) + '/../fixtures/server.cert.crt'))))
|
86
|
+
end
|
87
|
+
|
67
88
|
class NullLogger
|
68
89
|
def fatal(msg) end
|
69
90
|
def error(msg) end
|
@@ -0,0 +1,255 @@
|
|
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
|
+
|
15
|
+
shared_examples_for "Unified backend API" do
|
16
|
+
# ping
|
17
|
+
it "should ping the server" do
|
18
|
+
@backend.ping.should be_true
|
19
|
+
end
|
20
|
+
|
21
|
+
# fetch_object
|
22
|
+
context "fetching an object" do
|
23
|
+
before do
|
24
|
+
@robject = Riak::RObject.new(@client.bucket("test"), "fetch")
|
25
|
+
@robject.content_type = "application/json"
|
26
|
+
@robject.data = { "test" => "pass" }
|
27
|
+
@backend.store_object(@robject)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should find a stored object" do
|
31
|
+
robj = @backend.fetch_object("test", "fetch")
|
32
|
+
robj.should be_kind_of(Riak::RObject)
|
33
|
+
robj.data.should == { "test" => "pass" }
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should raise an error when the object is not found" do
|
37
|
+
begin
|
38
|
+
@backend.fetch_object("test", "notfound")
|
39
|
+
rescue Riak::FailedRequest => exception
|
40
|
+
@exception = exception
|
41
|
+
end
|
42
|
+
@exception.should be_kind_of(Riak::FailedRequest)
|
43
|
+
@exception.should be_not_found
|
44
|
+
end
|
45
|
+
|
46
|
+
[1,2,3,:one,:quorum,:all,:default].each do |q|
|
47
|
+
it "should accept a R value of #{q.inspect} for the request" do
|
48
|
+
robj = @backend.fetch_object("test", "fetch", q)
|
49
|
+
robj.should be_kind_of(Riak::RObject)
|
50
|
+
robj.data.should == { "test" => "pass" }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# reload_object
|
56
|
+
context "reloading an existing object" do
|
57
|
+
before do
|
58
|
+
@robject = Riak::RObject.new(@client.bucket('test'), 'reload')
|
59
|
+
@robject.content_type = "application/json"
|
60
|
+
@robject.data = {"test" => "pass"}
|
61
|
+
@backend.store_object(@robject)
|
62
|
+
@robject2 = @backend.fetch_object("test", "reload")
|
63
|
+
@robject2.data["test"] = "second"
|
64
|
+
@backend.store_object(@robject2, true)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should modify the object with the reloaded data" do
|
68
|
+
@backend.reload_object(@robject)
|
69
|
+
end
|
70
|
+
|
71
|
+
[1,2,3,:one,:quorum,:all,:default].each do |q|
|
72
|
+
it "should accept a valid R value of #{q.inspect} for the request" do
|
73
|
+
@backend.reload_object(@robject, q)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
after do
|
78
|
+
@robject.vclock.should == @robject2.vclock
|
79
|
+
@robject.data['test'].should == "second"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# store_object
|
84
|
+
context "storing an object" do
|
85
|
+
before do
|
86
|
+
@robject = Riak::RObject.new(@client.bucket('test'), 'store')
|
87
|
+
@robject.content_type = "application/json"
|
88
|
+
@robject.data = {"test" => "pass"}
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should save the object" do
|
92
|
+
@backend.store_object(@robject)
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should modify the object with the returned data if returnbody" do
|
96
|
+
@backend.store_object(@robject, true)
|
97
|
+
@robject.vclock.should be_present
|
98
|
+
end
|
99
|
+
|
100
|
+
[1,2,3,:one,:quorum,:all,:default].each do |q|
|
101
|
+
it "should accept a W value of #{q.inspect} for the request" do
|
102
|
+
@backend.store_object(@robject, false, q)
|
103
|
+
@client.bucket("test").exists?("store").should be_true
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should accept a DW value of #{q.inspect} for the request" do
|
107
|
+
@backend.store_object(@robject, false, nil, q)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
after do
|
112
|
+
@client.bucket("test").exists?("store").should be_true
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# delete_object
|
117
|
+
context "deleting an object" do
|
118
|
+
before do
|
119
|
+
@obj = Riak::RObject.new(@client.bucket("test"), "delete")
|
120
|
+
@obj.content_type = "application/json"
|
121
|
+
@obj.data = [1]
|
122
|
+
@backend.store_object(@obj)
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should remove the object" do
|
126
|
+
@backend.delete_object("test", "delete")
|
127
|
+
@obj.bucket.exists?("delete").should be_false
|
128
|
+
end
|
129
|
+
|
130
|
+
[1,2,3,:one,:quorum,:all,:default].each do |q|
|
131
|
+
it "should accept an RW value of #{q.inspect} for the request" do
|
132
|
+
@backend.delete_object("test", "delete", q)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
after do
|
137
|
+
@obj.bucket.exists?("delete").should be_false
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# get_bucket_props
|
142
|
+
context "fetching bucket properties" do
|
143
|
+
it "should fetch a hash of bucket properties" do
|
144
|
+
props = @backend.get_bucket_props("test")
|
145
|
+
props.should be_kind_of(Hash)
|
146
|
+
props.should include("n_val")
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# set_bucket_props
|
151
|
+
context "setting bucket properties" do
|
152
|
+
it "should store properties for the bucket" do
|
153
|
+
@backend.set_bucket_props("test", {"n_val" => 3})
|
154
|
+
@backend.get_bucket_props("test")["n_val"].should == 3
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# list_keys
|
159
|
+
context "listing keys in a bucket" do
|
160
|
+
before do
|
161
|
+
obj = Riak::RObject.new(@client.bucket("test"), "keys")
|
162
|
+
obj.content_type = "application/json"
|
163
|
+
obj.data = [1]
|
164
|
+
@backend.store_object(obj)
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should fetch an array of string keys" do
|
168
|
+
@backend.list_keys("test").should == ["keys"]
|
169
|
+
end
|
170
|
+
|
171
|
+
context "streaming through a block" do
|
172
|
+
it "should pass an array of keys to the block" do
|
173
|
+
@backend.list_keys("test") do |keys|
|
174
|
+
keys.should == ["keys"] unless keys.empty?
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should allow requests issued inside the block to execute" do
|
179
|
+
errors = []
|
180
|
+
@backend.list_keys("test") do |keys|
|
181
|
+
keys.each do |key|
|
182
|
+
begin
|
183
|
+
@backend.fetch_object("test", key)
|
184
|
+
rescue => e
|
185
|
+
errors << e
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
errors.should be_empty
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
# list_buckets
|
195
|
+
context "listing buckets" do
|
196
|
+
before do
|
197
|
+
obj = Riak::RObject.new(@client.bucket("test"), "buckets")
|
198
|
+
obj.content_type = "application/json"
|
199
|
+
obj.data = [1]
|
200
|
+
@backend.store_object(obj)
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should fetch a list of string bucket names" do
|
204
|
+
list = @backend.list_buckets
|
205
|
+
list.should be_kind_of(Array)
|
206
|
+
list.should include("test")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# mapred
|
211
|
+
context "performing MapReduce" do
|
212
|
+
before do
|
213
|
+
obj = Riak::RObject.new(@client.bucket("test"), "1")
|
214
|
+
obj.content_type = "application/json"
|
215
|
+
obj.data = {"value" => "1" }
|
216
|
+
@backend.store_object(obj)
|
217
|
+
@mapred = Riak::MapReduce.new(@client).add("test").map("Riak.mapValuesJson", :keep => true)
|
218
|
+
end
|
219
|
+
|
220
|
+
it "should perform a simple MapReduce request" do
|
221
|
+
@backend.mapred(@mapred).should == [{"value" => "1"}]
|
222
|
+
end
|
223
|
+
|
224
|
+
context "streaming results through a block" do
|
225
|
+
it "should pass phase number and result to the block" do
|
226
|
+
@backend.mapred(@mapred) do |phase, result|
|
227
|
+
unless result.empty?
|
228
|
+
phase.should == 0
|
229
|
+
result.should == [{"value" => "1"}]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
it "should allow requests issued inside the block to execute" do
|
235
|
+
errors = []
|
236
|
+
@backend.mapred(@mapred) do |phase, result|
|
237
|
+
unless result.empty?
|
238
|
+
result.each do |v|
|
239
|
+
begin
|
240
|
+
@backend.fetch_object("test", v['value'])
|
241
|
+
rescue => e
|
242
|
+
errors << e
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
errors.should be_empty
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
after do
|
253
|
+
$test_server.recycle if $test_server.started?
|
254
|
+
end
|
255
|
+
end
|