seomoz-riak-client 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/Gemfile +27 -0
  2. data/Guardfile +14 -0
  3. data/Rakefile +76 -0
  4. data/erl_src/riak_kv_test_backend.beam +0 -0
  5. data/erl_src/riak_kv_test_backend.erl +174 -0
  6. data/erl_src/riak_search_test_backend.beam +0 -0
  7. data/erl_src/riak_search_test_backend.erl +175 -0
  8. data/lib/active_support/cache/riak_store.rb +2 -0
  9. data/lib/riak.rb +21 -0
  10. data/lib/riak/bucket.rb +215 -0
  11. data/lib/riak/cache_store.rb +84 -0
  12. data/lib/riak/client.rb +415 -0
  13. data/lib/riak/client/beefcake/messages.rb +147 -0
  14. data/lib/riak/client/beefcake/object_methods.rb +92 -0
  15. data/lib/riak/client/beefcake_protobuffs_backend.rb +176 -0
  16. data/lib/riak/client/excon_backend.rb +65 -0
  17. data/lib/riak/client/http_backend.rb +203 -0
  18. data/lib/riak/client/http_backend/configuration.rb +46 -0
  19. data/lib/riak/client/http_backend/key_streamer.rb +43 -0
  20. data/lib/riak/client/http_backend/object_methods.rb +94 -0
  21. data/lib/riak/client/http_backend/request_headers.rb +34 -0
  22. data/lib/riak/client/http_backend/transport_methods.rb +218 -0
  23. data/lib/riak/client/net_http_backend.rb +79 -0
  24. data/lib/riak/client/protobuffs_backend.rb +97 -0
  25. data/lib/riak/client/pump.rb +30 -0
  26. data/lib/riak/client/search.rb +94 -0
  27. data/lib/riak/core_ext.rb +6 -0
  28. data/lib/riak/core_ext/blank.rb +53 -0
  29. data/lib/riak/core_ext/extract_options.rb +7 -0
  30. data/lib/riak/core_ext/json.rb +15 -0
  31. data/lib/riak/core_ext/slice.rb +18 -0
  32. data/lib/riak/core_ext/stringify_keys.rb +10 -0
  33. data/lib/riak/core_ext/symbolize_keys.rb +10 -0
  34. data/lib/riak/core_ext/to_param.rb +31 -0
  35. data/lib/riak/encoding.rb +6 -0
  36. data/lib/riak/failed_request.rb +81 -0
  37. data/lib/riak/i18n.rb +3 -0
  38. data/lib/riak/json.rb +28 -0
  39. data/lib/riak/link.rb +85 -0
  40. data/lib/riak/locale/en.yml +48 -0
  41. data/lib/riak/map_reduce.rb +206 -0
  42. data/lib/riak/map_reduce/filter_builder.rb +94 -0
  43. data/lib/riak/map_reduce/phase.rb +98 -0
  44. data/lib/riak/map_reduce_error.rb +7 -0
  45. data/lib/riak/robject.rb +290 -0
  46. data/lib/riak/search.rb +3 -0
  47. data/lib/riak/serializers.rb +74 -0
  48. data/lib/riak/stamp.rb +77 -0
  49. data/lib/riak/test_server.rb +252 -0
  50. data/lib/riak/util/escape.rb +45 -0
  51. data/lib/riak/util/fiber1.8.rb +48 -0
  52. data/lib/riak/util/headers.rb +53 -0
  53. data/lib/riak/util/multipart.rb +52 -0
  54. data/lib/riak/util/multipart/stream_parser.rb +62 -0
  55. data/lib/riak/util/tcp_socket_extensions.rb +58 -0
  56. data/lib/riak/util/translation.rb +19 -0
  57. data/lib/riak/walk_spec.rb +105 -0
  58. data/riak-client.gemspec +55 -0
  59. data/seomoz-riak-client.gemspec +55 -0
  60. data/spec/fixtures/cat.jpg +0 -0
  61. data/spec/fixtures/multipart-blank.txt +7 -0
  62. data/spec/fixtures/multipart-mapreduce.txt +10 -0
  63. data/spec/fixtures/multipart-with-body.txt +16 -0
  64. data/spec/fixtures/server.cert.crt +15 -0
  65. data/spec/fixtures/server.cert.key +15 -0
  66. data/spec/fixtures/test.pem +1 -0
  67. data/spec/integration/riak/cache_store_spec.rb +154 -0
  68. data/spec/integration/riak/http_backends_spec.rb +58 -0
  69. data/spec/integration/riak/protobuffs_backends_spec.rb +32 -0
  70. data/spec/integration/riak/test_server_spec.rb +161 -0
  71. data/spec/riak/beefcake_protobuffs_backend_spec.rb +59 -0
  72. data/spec/riak/bucket_spec.rb +205 -0
  73. data/spec/riak/client_spec.rb +517 -0
  74. data/spec/riak/core_ext/to_param_spec.rb +15 -0
  75. data/spec/riak/escape_spec.rb +69 -0
  76. data/spec/riak/excon_backend_spec.rb +64 -0
  77. data/spec/riak/headers_spec.rb +38 -0
  78. data/spec/riak/http_backend/configuration_spec.rb +51 -0
  79. data/spec/riak/http_backend/object_methods_spec.rb +217 -0
  80. data/spec/riak/http_backend/transport_methods_spec.rb +117 -0
  81. data/spec/riak/http_backend_spec.rb +269 -0
  82. data/spec/riak/link_spec.rb +71 -0
  83. data/spec/riak/map_reduce/filter_builder_spec.rb +32 -0
  84. data/spec/riak/map_reduce/phase_spec.rb +136 -0
  85. data/spec/riak/map_reduce_spec.rb +310 -0
  86. data/spec/riak/multipart_spec.rb +23 -0
  87. data/spec/riak/net_http_backend_spec.rb +16 -0
  88. data/spec/riak/robject_spec.rb +427 -0
  89. data/spec/riak/search_spec.rb +178 -0
  90. data/spec/riak/serializers_spec.rb +93 -0
  91. data/spec/riak/stamp_spec.rb +54 -0
  92. data/spec/riak/stream_parser_spec.rb +53 -0
  93. data/spec/riak/walk_spec_spec.rb +195 -0
  94. data/spec/spec_helper.rb +39 -0
  95. data/spec/support/drb_mock_server.rb +39 -0
  96. data/spec/support/http_backend_implementation_examples.rb +266 -0
  97. data/spec/support/integration_setup.rb +10 -0
  98. data/spec/support/mock_server.rb +81 -0
  99. data/spec/support/mocks.rb +4 -0
  100. data/spec/support/test_server.yml.example +2 -0
  101. data/spec/support/unified_backend_examples.rb +255 -0
  102. metadata +271 -0
@@ -0,0 +1,39 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+
4
+ require 'rubygems' # Use the gems path only for the spec suite
5
+ require 'riak'
6
+ require 'rspec'
7
+ require 'fakeweb'
8
+
9
+ # Only the tests should really get away with this.
10
+ Riak.disable_list_keys_warnings = true
11
+
12
+ begin
13
+ require 'yaml'
14
+ require 'riak/test_server'
15
+ config = YAML.load_file("spec/support/test_server.yml")
16
+ $test_server = Riak::TestServer.new(config.symbolize_keys)
17
+ $test_server.prepare!
18
+ $test_server.start
19
+ at_exit { $test_server.cleanup }
20
+ rescue => e
21
+ warn "Can't run Riak::TestServer specs. Specify the location of your Riak installation in spec/support/test_server.yml. See Riak::TestServer docs for more info."
22
+ warn e.inspect
23
+ end
24
+
25
+ Dir[File.join(File.dirname(__FILE__), "support", "*.rb")].sort.each {|f| require f }
26
+
27
+
28
+ RSpec.configure do |config|
29
+ config.debug = true
30
+ config.mock_with :rspec
31
+
32
+ config.before(:each) do
33
+ Riak::RObject.on_conflict_hooks.clear
34
+ FakeWeb.clean_registry
35
+ end
36
+
37
+ config.filter_run :focus => true
38
+ config.run_all_when_everything_filtered = true
39
+ end
@@ -0,0 +1,39 @@
1
+ require 'drb/drb'
2
+ DRBURI="druby://localhost:8787"
3
+
4
+ module DrbMockServer
5
+ extend self
6
+
7
+ def start_client
8
+ # JRuby doesn't support fork
9
+ if defined? JRUBY_VERSION
10
+ @server = MockServer.new(2)
11
+ at_exit { @server.stop }
12
+ else
13
+ child_pid = Process.fork do
14
+ start_server
15
+ end
16
+ sleep 1
17
+ at_exit { Process.kill("HUP", child_pid); Process.wait2 }
18
+ DRb.start_service
19
+ @server = DRbObject.new_with_uri(DRBURI)
20
+ sleep 1
21
+ end
22
+ true
23
+ end
24
+
25
+ def maybe_start
26
+ start_client unless @server
27
+ end
28
+
29
+ def method_missing(meth, *args, &block)
30
+ @server.send(meth, *args, &block)
31
+ end
32
+
33
+ def start_server
34
+ server = MockServer.new
35
+ DRb.start_service(DRBURI, server)
36
+ Signal.trap("HUP") { server.stop; exit }
37
+ DRb.thread.join
38
+ end
39
+ end
@@ -0,0 +1,266 @@
1
+ shared_examples_for "HTTP backend" do
2
+ describe "HEAD requests" do
3
+ before :each do
4
+ setup_http_mock(:head, @backend.path("/riak/","foo").to_s, :body => "")
5
+ end
6
+
7
+ it "should return only the headers when the request succeeds" do
8
+ response = @backend.head(200, "/riak/","foo")
9
+ response.should_not have_key(:body)
10
+ response[:headers].should be_kind_of(Hash)
11
+ response[:code].should == 200
12
+ end
13
+
14
+ it "should raise a FailedRequest exception when the request fails" do
15
+ lambda { @backend.head(301, "/riak/", "foo") }.should raise_error(Riak::FailedRequest)
16
+ end
17
+
18
+ it "should raise an error if an invalid resource path is given" do
19
+ lambda { @backend.head(200, "/riak/") }.should raise_error(ArgumentError)
20
+ end
21
+
22
+ it "should not raise a FailedRequest if one of the expected response codes matches" do
23
+ lambda { @backend.head([200, 301], "/riak/", "foo") }.should_not raise_error(Riak::FailedRequest)
24
+ end
25
+ end
26
+
27
+ describe "GET requests" do
28
+ before :each do
29
+ setup_http_mock(:get, @backend.path("/riak/","foo").to_s, :body => "Success!")
30
+ end
31
+
32
+ it "should return the response body and headers when the request succeeds" do
33
+ response = @backend.get(200, "/riak/","foo")
34
+ response[:body].should == "Success!"
35
+ response[:headers].should be_kind_of(Hash)
36
+ response[:code].should == 200
37
+ end
38
+
39
+ it "should raise a FailedRequest exception when the request fails" do
40
+ lambda { @backend.get(304, "/riak/","foo") }.should raise_error(Riak::FailedRequest)
41
+ end
42
+
43
+ it "should not raise a FailedRequest if one of the expected response codes matches" do
44
+ lambda { @backend.get([200, 301], "/riak/","foo") }.should_not raise_error(Riak::FailedRequest)
45
+ end
46
+
47
+ it "should yield successive chunks of the response to the given block but not return the entire body" do
48
+ chunks = ""
49
+ response = @backend.get(200, "/riak/","foo") do |chunk|
50
+ chunks << chunk
51
+ end
52
+ chunks.should == "Success!"
53
+ response.should_not have_key(:body)
54
+ response[:headers].should be_kind_of(Hash)
55
+ response[:code].should == 200
56
+ end
57
+
58
+ it "should raise an error if an invalid resource path is given" do
59
+ lambda { @backend.get(200, "/riak/") }.should raise_error(ArgumentError)
60
+ end
61
+ end
62
+
63
+ describe "DELETE requests" do
64
+ before :each do
65
+ setup_http_mock(:delete, @backend.path("/riak/","foo").to_s, :body => "Success!")
66
+ end
67
+
68
+ it "should return the response body and headers when the request succeeds" do
69
+ response = @backend.delete(200, "/riak/","foo")
70
+ response[:body].should == "Success!"
71
+ response[:headers].should be_kind_of(Hash)
72
+ end
73
+
74
+ it "should raise a FailedRequest exception when the request fails" do
75
+ lambda { @backend.delete(304, "/riak/","foo") }.should raise_error(Riak::FailedRequest)
76
+ end
77
+
78
+ it "should not raise a FailedRequest if one of the expected response codes matches" do
79
+ lambda { @backend.delete([200, 301], "/riak/","foo") }.should_not raise_error(Riak::FailedRequest)
80
+ end
81
+
82
+ it "should yield successive chunks of the response to the given block but not return the entire body" do
83
+ chunks = ""
84
+ response = @backend.delete(200, "/riak/","foo") do |chunk|
85
+ chunks << chunk
86
+ end
87
+ chunks.should == "Success!"
88
+ response.should_not have_key(:body)
89
+ response[:headers].should be_kind_of(Hash)
90
+ response[:code].should == 200
91
+ end
92
+
93
+ it "should raise an error if an invalid resource path is given" do
94
+ lambda { @backend.delete(200, "/riak/") }.should raise_error(ArgumentError)
95
+ end
96
+ end
97
+
98
+ describe "PUT requests" do
99
+ before :each do
100
+ setup_http_mock(:put, @backend.path("/riak/","foo").to_s, :body => "Success!")
101
+ end
102
+
103
+ it "should return the response body, headers, and code when the request succeeds" do
104
+ response = @backend.put(200, "/riak/","foo", "This is the body.")
105
+ response[:body].should == "Success!"
106
+ response[:headers].should be_kind_of(Hash)
107
+ response[:code].should == 200
108
+ end
109
+
110
+ it "should raise a FailedRequest exception when the request fails" do
111
+ lambda { @backend.put(204, "/riak/","foo", "This is the body.") }.should raise_error(Riak::FailedRequest)
112
+ end
113
+
114
+ it "should not raise a FailedRequest if one of the expected response codes matches" do
115
+ lambda { @backend.put([200, 204], "/riak/","foo", "This is the body.") }.should_not raise_error(Riak::FailedRequest)
116
+ end
117
+
118
+
119
+ it "should yield successive chunks of the response to the given block but not return the entire body" do
120
+ chunks = ""
121
+ response = @backend.put(200, "/riak/","foo", "This is the body.") do |chunk|
122
+ chunks << chunk
123
+ end
124
+ chunks.should == "Success!"
125
+ response.should_not have_key(:body)
126
+ response[:headers].should be_kind_of(Hash)
127
+ response[:code].should == 200
128
+ end
129
+
130
+ it "should raise an error if an invalid resource path is given" do
131
+ lambda { @backend.put(200, "/riak/") }.should raise_error(ArgumentError)
132
+ end
133
+
134
+ it "should raise an error if no body data is given" do
135
+ lambda { @backend.put(200, "/riak/","foo") }.should raise_error(ArgumentError)
136
+ end
137
+
138
+ it "should raise an error if the body is not a string" do
139
+ lambda { @backend.put(200, "/riak/","foo", 123) }.should raise_error(ArgumentError)
140
+ end
141
+ end
142
+
143
+ describe "POST requests" do
144
+ before :each do
145
+ setup_http_mock(:post, @backend.path("/riak/","foo").to_s, :body => "Success!")
146
+ end
147
+
148
+ it "should return the response body, headers, and code when the request succeeds" do
149
+ response = @backend.post(200, "/riak/","foo", "This is the body.")
150
+ response[:body].should == "Success!"
151
+ response[:headers].should be_kind_of(Hash)
152
+ response[:code].should == 200
153
+ end
154
+
155
+ it "should raise a FailedRequest exception when the request fails" do
156
+ lambda { @backend.post(204, "/riak/", "foo", "This is the body.") }.should raise_error(Riak::FailedRequest)
157
+ end
158
+
159
+ it "should not raise a FailedRequest if one of the expected response codes matches" do
160
+ lambda { @backend.post([200, 204], "/riak/", "foo", "This is the body.") }.should_not raise_error(Riak::FailedRequest)
161
+ end
162
+
163
+ it "should yield successive chunks of the response to the given block but not return the entire body" do
164
+ chunks = ""
165
+ response = @backend.post(200, "/riak/", "foo", "This is the body.") do |chunk|
166
+ chunks << chunk
167
+ end
168
+ chunks.should == "Success!"
169
+ response.should_not have_key(:body)
170
+ response[:headers].should be_kind_of(Hash)
171
+ response[:code].should == 200
172
+ end
173
+
174
+ it "should raise an error if an invalid resource path is given" do
175
+ lambda { @backend.post(200, "/riak/") }.should raise_error(ArgumentError)
176
+ end
177
+
178
+ it "should raise an error if no body data is given" do
179
+ lambda { @backend.post(200, "/riak/", "foo") }.should raise_error(ArgumentError)
180
+ end
181
+
182
+ it "should raise an error if the body is not a string" do
183
+ lambda { @backend.post(200, "/riak/", "foo", 123) }.should raise_error(ArgumentError)
184
+ end
185
+ end
186
+
187
+ describe "Responses with no body" do
188
+ [204, 205, 304].each do |code|
189
+ [:get, :post, :put, :delete].each do |method|
190
+ it "should not return a body on #{method.to_s.upcase} for #{code}" do
191
+ setup_http_mock(method, @backend.path("/riak/","foo").to_s, :status => code)
192
+ response = if method == :post || method == :put
193
+ @backend.send(method, code,"/riak/","foo", "This is the body")
194
+ else
195
+ @backend.send(method, code, "/riak/", "foo")
196
+ end
197
+ response.should_not have_key(:body)
198
+ end
199
+ end
200
+ end
201
+ end
202
+
203
+ describe "SSL" do
204
+ it "should be supported" do
205
+ @client.http_port = $mock_server.port + 1 unless @client.http_backend == :NetHTTP
206
+ @client.ssl = true
207
+ setup_http_mock(:get, @backend.path("/riak/","ssl").to_s, :body => "Success!")
208
+ response = @backend.get(200, "/riak/","ssl")
209
+ response[:code].should == 200
210
+ end
211
+ end
212
+
213
+ describe "HTTP Basic Authentication", :basic_auth => true do
214
+ it "should add the http basic auth header" do
215
+ @client.basic_auth = "ripple:rocks"
216
+ if @client.http_backend == :NetHTTP
217
+ setup_http_mock(:get, "http://ripple:rocks@127.0.0.1:8098/riak/auth", :body => 'Success!')
218
+ else
219
+ @_mock_set = "Basic #{Base64::encode64("ripple:rocks").strip}"
220
+ $mock_server.attach do |env|
221
+ $mock_server.satisfied = env['HTTP_AUTHORIZATION'] == @_mock_set
222
+ [200, {}, Array('Success!')]
223
+ end
224
+ end
225
+ response = @backend.get(200, "/riak/", "auth")
226
+ response[:code].should == 200
227
+ end
228
+ end
229
+
230
+ describe "Invalid responses" do
231
+
232
+ def bad_request(method)
233
+ if method == :post || method == :put
234
+ @backend.send(method, 200, "/riak/","foo", "body")
235
+ else
236
+ @backend.send(method, 200, "/riak/","foo")
237
+ end
238
+ end
239
+
240
+ [:get, :post, :put, :delete].each do |method|
241
+ context method.to_s do
242
+
243
+ before(:each) do
244
+ setup_http_mock(method, @backend.path("/riak/","foo").to_s, :body => "Failure!", :status => 400, 'Content-Type' => 'text/plain' )
245
+ end
246
+
247
+ it "raises an HTTPFailedRequest exeption" do
248
+ lambda { bad_request(method) }.should raise_error(Riak::HTTPFailedRequest)
249
+ end
250
+
251
+ it "should normalize the response header keys to lower case" do
252
+ begin
253
+ bad_request(method)
254
+ rescue Riak::HTTPFailedRequest => fr
255
+ fr.headers.keys.should =~ fr.headers.keys.collect(&:downcase)
256
+ else
257
+ fail "No exception raised!"
258
+ end
259
+ end
260
+
261
+ end
262
+ end
263
+
264
+ end
265
+
266
+ end
@@ -0,0 +1,10 @@
1
+ # auto-tag all integration specs with :integration => true
2
+ module IntegrationSpecs
3
+ def self.included(klass)
4
+ klass.metadata[:integration] = true
5
+ end
6
+ end
7
+
8
+ RSpec.configure do |config|
9
+ config.include IntegrationSpecs, :example_group => { :file_path => %r{spec/integration} }
10
+ end
@@ -0,0 +1,81 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Based on code from Rob Styles and Chris Tierney found at:
3
+ # http://dynamicorange.com/2009/02/18/ruby-mock-web-server/
4
+ require 'rack'
5
+ require 'openssl'
6
+ require 'webrick/https'
7
+ require 'rack/handler/webrick'
8
+
9
+ class MockServer
10
+ attr_accessor :port
11
+ attr_accessor :satisfied
12
+
13
+ def initialize(pause = 1)
14
+ self.port = 4000 + rand(61535)
15
+ @block = nil
16
+ @parent_thread = Thread.current
17
+ options = {:AccessLog => [], :Logger => NullLogger.new, :Host => '127.0.0.1'}
18
+ @thread = Thread.new do
19
+ Rack::Handler::WEBrick.run(self, options.merge(:Port => port))
20
+ end
21
+ @ssl_thread = Thread.new do
22
+ Rack::Handler::WEBrick.run(self, options.merge(:Port => port+1,
23
+ :SSLEnable => true,
24
+ :SSLVerifyClient => OpenSSL::SSL::VERIFY_NONE,
25
+ :SSLCertificate => read_cert,
26
+ :SSLPrivateKey => read_pkey,
27
+ :SSLCertName => [ [ "CN",'127.0.0.1' ] ]))
28
+ end
29
+ sleep pause # give the server time to fire up… YUK!
30
+ end
31
+
32
+ def stop
33
+ Thread.kill(@thread)
34
+ Thread.kill(@ssl_thread)
35
+ end
36
+
37
+ def expect(status, headers, method, path, query, body)
38
+ attach do |env|
39
+ @satisfied = (env["REQUEST_METHOD"] == method &&
40
+ env["PATH_INFO"] == path &&
41
+ env["QUERY_STRING"] == query)
42
+ [status, headers, Array(body)]
43
+ end
44
+ end
45
+
46
+ def attach(&block)
47
+ @block = block
48
+ end
49
+
50
+ def detach()
51
+ @block = nil
52
+ end
53
+
54
+ def call(env)
55
+ begin
56
+ raise "Specify a handler for the request using attach(block), the block should return a valid rack response and can test expectations" unless @block
57
+ @block.call(env)
58
+ rescue Exception => e
59
+ @satisfied = false
60
+ # @parent_thread.raise e
61
+ body = "Bad test code\n#{e.inspect}\n#{e.backtrace}"
62
+ [ 500, { 'Content-Type' => 'text/plain', 'Content-Length' => body.length.to_s }, [ body ]]
63
+ end
64
+ end
65
+
66
+ def read_pkey
67
+ OpenSSL::PKey::RSA.new(File.read(File.expand_path(File.dirname(__FILE__) + '/../fixtures/server.cert.key')), 'ripple')
68
+ end
69
+
70
+ def read_cert
71
+ OpenSSL::X509::Certificate.new(File.read((File.expand_path(File.dirname(__FILE__) + '/../fixtures/server.cert.crt'))))
72
+ end
73
+
74
+ class NullLogger
75
+ def fatal(msg) end
76
+ def error(msg) end
77
+ def warn(msg) end
78
+ def info(msg) end
79
+ def debug(msg) end
80
+ end
81
+ end
@@ -0,0 +1,4 @@
1
+
2
+ def mock_response(overrides={})
3
+ {:headers => {"content-type" => ["application/json"]}, :body => '{}'}.merge(overrides)
4
+ end
@@ -0,0 +1,2 @@
1
+ bin_dir: /Users/sean/Development/riak/rel/riak/bin
2
+ temp_dir: /Users/sean/Development/ripple/.riaktest