riakpb 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/riak.rb +2 -2
  2. data/spec/riak/map_reduce_spec.rb +324 -1
  3. metadata +2 -2
data/lib/riak.rb CHANGED
@@ -9,7 +9,7 @@ require 'base64'
9
9
  require 'riak/client_pb'
10
10
 
11
11
  module Riak
12
- VERSION = '0.1.2'
12
+ VERSION = '0.1.3'
13
13
 
14
14
  # Domain objects
15
15
  autoload :I18n, 'riak/i18n'
@@ -31,4 +31,4 @@ module Riak
31
31
  autoload :Encode, 'riak/util/encode'
32
32
  autoload :Decode, 'riak/util/decode'
33
33
  end
34
- end
34
+ end
@@ -1,4 +1,327 @@
1
1
  require File.expand_path("../spec_helper", File.dirname(__FILE__))
2
2
 
3
3
  describe Riak::MapReduce do
4
- end
4
+ before :each do
5
+ @client = Riak::Client.new
6
+ @client.rpc.stub!(:request).and_return(nil)
7
+ @mr = Riak::MapReduce.new(@client)
8
+ end
9
+
10
+ it "should require a client" do
11
+ lambda { Riak::MapReduce.new }.should raise_error
12
+ lambda { Riak::MapReduce.new(@client) }.should_not raise_error
13
+ end
14
+
15
+ it "should initialize the inputs and query to empty arrays" do
16
+ @mr.inputs.should == []
17
+ @mr.query.should == []
18
+ end
19
+
20
+ it "should yield itself when given a block on initializing" do
21
+ @mr2 = nil
22
+ @mr = Riak::MapReduce.new(@client) do |mr|
23
+ @mr2 = mr
24
+ end
25
+ @mr2.should == @mr
26
+ end
27
+
28
+ describe "adding inputs" do
29
+ it "should return self for chaining" do
30
+ @mr.add("foo", "bar").should == @mr
31
+ end
32
+
33
+ it "should add bucket/key pairs to the inputs" do
34
+ @mr.add("foo","bar")
35
+ @mr.inputs.should == [["foo","bar"]]
36
+ end
37
+
38
+ it "should add an array containing a bucket/key pair to the inputs" do
39
+ @mr.add(["foo","bar"])
40
+ @mr.inputs.should == [["foo","bar"]]
41
+ end
42
+
43
+ it "should add an object to the inputs by its bucket and key" do
44
+ bucket = Riak::Bucket.new(@client, "foo")
45
+ key = Riak::Key.new(bucket, "bar")
46
+ @mr.add(key)
47
+ @mr.inputs.should == [["foo", "bar"]]
48
+ end
49
+
50
+ it "should add an array containing a bucket/key/key-data triple to the inputs" do
51
+ @mr.add(["foo","bar",1000])
52
+ @mr.inputs.should == [["foo","bar",1000]]
53
+ end
54
+
55
+ it "should use a bucket name as the single input" do
56
+ @mr.add(Riak::Bucket.new(@client, "foo"))
57
+ @mr.inputs.should == "foo"
58
+ @mr.add("docs")
59
+ @mr.inputs.should == "docs"
60
+ end
61
+ end
62
+
63
+ [:map, :reduce].each do |type|
64
+ describe "adding #{type} phases" do
65
+ it "should return self for chaining" do
66
+ @mr.send(type, "function(){}").should == @mr
67
+ end
68
+
69
+ it "should accept a function string" do
70
+ @mr.send(type, "function(){}")
71
+ @mr.query.should have(1).items
72
+ phase = @mr.query.first
73
+ phase.function.should == "function(){}"
74
+ phase.type.should == type
75
+ end
76
+
77
+ it "should accept a function and options" do
78
+ @mr.send(type, "function(){}", :keep => true)
79
+ @mr.query.should have(1).items
80
+ phase = @mr.query.first
81
+ phase.function.should == "function(){}"
82
+ phase.type.should == type
83
+ phase.keep.should be_true
84
+ end
85
+
86
+ it "should accept a module/function pair" do
87
+ @mr.send(type, ["riak","mapsomething"])
88
+ @mr.query.should have(1).items
89
+ phase = @mr.query.first
90
+ phase.function.should == ["riak", "mapsomething"]
91
+ phase.type.should == type
92
+ phase.language.should == "erlang"
93
+ end
94
+
95
+ it "should accept a module/function pair with extra options" do
96
+ @mr.send(type, ["riak", "mapsomething"], :arg => [1000])
97
+ @mr.query.should have(1).items
98
+ phase = @mr.query.first
99
+ phase.function.should == ["riak", "mapsomething"]
100
+ phase.type.should == type
101
+ phase.language.should == "erlang"
102
+ phase.arg.should == [1000]
103
+ end
104
+ end
105
+ end
106
+
107
+ describe "adding link phases" do
108
+ it "should return self for chaining" do
109
+ @mr.link({}).should == @mr
110
+ end
111
+
112
+ it "should accept a Link" do
113
+ @mr.link({:tag => "next"})
114
+ @mr.query.should have(1).items
115
+ phase = @mr.query.first
116
+ phase.type.should == :link
117
+ # phase.function.should be_kind_of(Riak::WalkSpec)
118
+ phase.function[:tag].should == "next"
119
+ end
120
+
121
+ it "should accept a Link and a statement to keep" do
122
+ @mr.link({:bucket => "foo", :keep => true})
123
+ @mr.query.should have(1).items
124
+ phase = @mr.query.first
125
+ phase.type.should == :link
126
+ # phase.function.should be_kind_of(Riak::WalkSpec)
127
+ phase.function[:bucket].should == "foo"
128
+ phase.keep.should be_true
129
+ end
130
+ end
131
+
132
+ describe "converting to JSON for the job" do
133
+ it "should include the inputs and query keys" do
134
+ @mr.to_json.should =~ /"inputs":/
135
+ end
136
+
137
+ it "should map phases to their JSON equivalents" do
138
+ phase = Riak::MapReduce::Phase.new(:type => :map, :function => "function(){}")
139
+ @mr.query << phase
140
+ @mr.to_json.should include('"source":"function(){}"')
141
+ @mr.to_json.should include('"query":[{"map":{')
142
+ end
143
+
144
+ it "should emit only the bucket name when the input is the whole bucket" do
145
+ @mr.add("foo")
146
+ @mr.to_json.should include('"inputs":"foo"')
147
+ end
148
+
149
+ it "should emit an array of inputs when there are multiple inputs" do
150
+ @mr.add("foo","bar",1000).add("foo","baz")
151
+ @mr.to_json.should include('"inputs":[["foo","bar",1000],["foo","baz"]]')
152
+ end
153
+
154
+ it "should add the timeout value when set" do
155
+ @mr.timeout(50000)
156
+ @mr.to_json.should include('"timeout":50000')
157
+ end
158
+ end
159
+ =begin
160
+ describe "executing the map reduce job" do
161
+ it "should issue POST request to the mapred endpoint" do
162
+ @http.should_receive(:post).with(200, "/mapred", @mr.to_json, hash_including("Content-Type" => "application/json")).and_return({:headers => {'content-type' => ["application/json"]}, :body => "{}"})
163
+ @mr.run
164
+ end
165
+
166
+ it "should vivify JSON responses" do
167
+ @http.stub!(:post).and_return(:headers => {'content-type' => ["application/json"]}, :body => '{"key":"value"}')
168
+ @mr.run.should == {"key" => "value"}
169
+ end
170
+
171
+ it "should return the full response hash for non-JSON responses" do
172
+ response = {:code => 200, :headers => {'content-type' => ["text/plain"]}, :body => 'This is some text.'}
173
+ @http.stub!(:post).and_return(response)
174
+ @mr.run.should == response
175
+ end
176
+
177
+ it "should interpret failed requests with JSON content-types as map reduce errors" do
178
+ @http.stub!(:post).and_raise(Riak::FailedRequest.new(:post, 200, 500, {"content-type" => ["application/json"]}, '{"error":"syntax error"}'))
179
+ lambda { @mr.run }.should raise_error(Riak::MapReduceError)
180
+ begin
181
+ @mr.run
182
+ rescue Riak::MapReduceError => mre
183
+ mre.message.should == '{"error":"syntax error"}'
184
+ else
185
+ fail "No exception raised!"
186
+ end
187
+ end
188
+
189
+ it "should re-raise non-JSON error responses" do
190
+ @http.stub!(:post).and_raise(Riak::FailedRequest.new(:post, 200, 500, {"content-type" => ["text/plain"]}, 'Oops, you bwoke it.'))
191
+ lambda { @mr.run }.should raise_error(Riak::FailedRequest)
192
+ end
193
+ end
194
+ =end
195
+ end
196
+
197
+ describe Riak::MapReduce::Phase do
198
+ before :each do
199
+ @fun = "function(v,_,_){ return v['values'][0]['data']; }"
200
+ end
201
+
202
+ it "should initialize with a type and a function" do
203
+ phase = Riak::MapReduce::Phase.new(:type => :map, :function => @fun, :language => "javascript")
204
+ phase.type.should == :map
205
+ phase.function.should == @fun
206
+ phase.language.should == "javascript"
207
+ end
208
+
209
+ it "should initialize with a type and an MF" do
210
+ phase = Riak::MapReduce::Phase.new(:type => :map, :function => ["module", "function"], :language => "erlang")
211
+ phase.type.should == :map
212
+ phase.function.should == ["module", "function"]
213
+ phase.language.should == "erlang"
214
+ end
215
+
216
+ it "should initialize with a type and a bucket/key" do
217
+ phase = Riak::MapReduce::Phase.new(:type => :map, :function => {:bucket => "funs", :key => "awesome_map"}, :language => "javascript")
218
+ phase.type.should == :map
219
+ phase.function.should == {:bucket => "funs", :key => "awesome_map"}
220
+ phase.language.should == "javascript"
221
+ end
222
+
223
+ it "should assume the language is erlang when the function is an array" do
224
+ phase = Riak::MapReduce::Phase.new(:type => :map, :function => ["module", "function"])
225
+ phase.language.should == "erlang"
226
+ end
227
+
228
+ it "should assume the language is javascript when the function is a string" do
229
+ phase = Riak::MapReduce::Phase.new(:type => :map, :function => @fun)
230
+ phase.language.should == "javascript"
231
+ end
232
+
233
+ it "should assume the language is javascript when the function is a hash" do
234
+ phase = Riak::MapReduce::Phase.new(:type => :map, :function => {:bucket => "jobs", :key => "awesome_map"})
235
+ phase.language.should == "javascript"
236
+ end
237
+
238
+ it "should accept a WalkSpec for the function when a link phase" do
239
+ # phase = Riak::MapReduce::Phase.new(:type => :link, :function => Riak::WalkSpec.new({}))
240
+ # phase.function.should be_kind_of(Riak::WalkSpec)
241
+ end
242
+
243
+ it "should raise an error if a WalkSpec is given for a phase type other than :link" do
244
+ # lambda { Riak::MapReduce::Phase.new(:type => :map, :function => Riak::WalkSpec.new({})) }.should raise_error(ArgumentError)
245
+ end
246
+
247
+ describe "converting to JSON for the job" do
248
+ before :each do
249
+ @phase = Riak::MapReduce::Phase.new(:type => :map, :function => "")
250
+ end
251
+
252
+ [:map, :reduce].each do |type|
253
+ describe "when a #{type} phase" do
254
+ before :each do
255
+ @phase.type = type
256
+ end
257
+
258
+ it "should be an object with a single key of '#{type}'" do
259
+ @phase.to_json.should =~ /^\{"#{type}":/
260
+ end
261
+
262
+ it "should include the language" do
263
+ @phase.to_json.should =~ /"language":/
264
+ end
265
+
266
+ it "should include the keep value" do
267
+ @phase.to_json.should =~ /"keep":false/
268
+ @phase.keep = true
269
+ @phase.to_json.should =~ /"keep":true/
270
+ end
271
+
272
+ it "should include the function source when the function is a source string" do
273
+ @phase.function = "function(v,_,_){ return v; }"
274
+ @phase.to_json.should include(@phase.function)
275
+ @phase.to_json.should =~ /"source":/
276
+ end
277
+
278
+ it "should include the function name when the function is not a lambda" do
279
+ @phase.function = "Riak.mapValues"
280
+ @phase.to_json.should include('"name":"Riak.mapValues"')
281
+ @phase.to_json.should_not include('"source"')
282
+ end
283
+
284
+ it "should include the bucket and key when referring to a stored function" do
285
+ @phase.function = {:bucket => "design", :key => "wordcount_map"}
286
+ @phase.to_json.should include('"bucket":"design"')
287
+ @phase.to_json.should include('"key":"wordcount_map"')
288
+ end
289
+
290
+ it "should include the module and function when invoking an Erlang function" do
291
+ @phase.function = ["riak_mapreduce", "mapreduce_fun"]
292
+ @phase.to_json.should include('"module":"riak_mapreduce"')
293
+ @phase.to_json.should include('"function":"mapreduce_fun"')
294
+ end
295
+ end
296
+ end
297
+
298
+ describe "when a link phase" do
299
+ before :each do
300
+ @phase.type = :link
301
+ @phase.function = {}
302
+ end
303
+
304
+ it "should be an object of a single key 'link'" do
305
+ @phase.to_json.should == "{\"link\":{\"keep\":false}}"
306
+ end
307
+
308
+ it "should include the bucket" do
309
+ @phase.function[:bucket] = "foo"
310
+ @phase.to_json.should == "{\"link\":{\"bucket\":\"foo\",\"keep\":false}}"
311
+ end
312
+
313
+ it "should include the tag" do
314
+ @phase.function[:tag] = "parent"
315
+ @phase.to_json.should == "{\"link\":{\"tag\":\"parent\",\"keep\":false}}"
316
+ end
317
+
318
+ it "should include the keep value" do
319
+ @phase.keep = true
320
+ @phase.to_json.should == "{\"link\":{\"keep\":true}}"
321
+ @phase.keep = false
322
+ @phase.function[:keep] = true
323
+ @phase.to_json.should == "{\"link\":{\"keep\":false}}"
324
+ end
325
+ end
326
+ end
327
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 1
8
- - 2
9
- version: 0.1.2
8
+ - 3
9
+ version: 0.1.3
10
10
  platform: ruby
11
11
  authors:
12
12
  - Scott Gonyea