riak-client 0.8.2 → 0.8.3

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/Gemfile CHANGED
@@ -1,13 +1,22 @@
1
- # A sample Gemfile
2
1
  source :gemcutter
3
2
 
4
- gem 'activesupport', '~>2.3.5' #'~>3.0.0'
5
3
  gem 'i18n'
6
-
4
+ gem 'builder'
7
5
  gem 'rspec', "~>2.0.0"
8
6
  gem 'fakeweb', ">=1.2"
9
7
  gem 'rack', '>=1.0'
10
- gem 'curb', '>=0.6'
11
- gem 'yajl-ruby'
12
8
  gem 'rake'
13
9
  gem 'bundler'
10
+ gem 'excon', "~>0.3.4"
11
+
12
+ if defined? JRUBY_VERSION
13
+ gem 'json'
14
+ gem 'jruby-openssl'
15
+ else
16
+ gem 'curb', '>=0.6'
17
+ gem 'yajl-ruby'
18
+ end
19
+
20
+ group :integration do
21
+ gem 'activesupport', '~>3.0'
22
+ end
data/Rakefile CHANGED
@@ -6,32 +6,85 @@ gemspec = Gem::Specification.new do |gem|
6
6
  gem.summary = %Q{riak-client is a rich client for Riak, the distributed database by Basho.}
7
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
8
  gem.version = File.read('../VERSION').strip
9
- gem.email = "seancribbs@gmail.com"
9
+ gem.email = "sean@basho.com"
10
10
  gem.homepage = "http://seancribbs.github.com/ripple"
11
11
  gem.authors = ["Sean Cribbs"]
12
12
  gem.add_development_dependency "rspec", "~>2.0.0"
13
13
  gem.add_development_dependency "fakeweb", ">=1.2"
14
14
  gem.add_development_dependency "rack", ">=1.0"
15
15
  gem.add_development_dependency "curb", ">=0.6"
16
- gem.add_dependency "activesupport", ">= 2.3.5"
17
- gem.add_dependency "i18n", "~>0.4.0"
18
- gem.requirements << "`gem install curb` for better HTTP performance"
16
+ gem.add_development_dependency "excon", "~>0.3.4"
17
+ gem.add_dependency "i18n", ">=0.4.0"
18
+ gem.add_dependency "builder", "~>2.1.2"
19
19
 
20
- files = FileList["**/*"]
21
- files.exclude /\.DS_Store/
22
- files.exclude /\#/
23
- files.exclude /~/
24
- files.exclude /\.swp/
25
- files.exclude '**/._*'
26
- files.exclude '**/*.orig'
27
- files.exclude '**/*.rej'
28
- files.exclude /^pkg/
29
- files.exclude 'riak-client.gemspec'
30
- files.exclude 'spec/support/test_server.yml'
20
+ gem.files = %W{
21
+ erl_src/riak_kv_test_backend.beam
22
+ erl_src/riak_kv_test_backend.erl
23
+ Gemfile
24
+ lib/active_support/cache/riak_store.rb
25
+ lib/riak/bucket.rb
26
+ lib/riak/cache_store.rb
27
+ lib/riak/client/curb_backend.rb
28
+ lib/riak/client/excon_backend.rb
29
+ lib/riak/client/http_backend.rb
30
+ lib/riak/client/net_http_backend.rb
31
+ lib/riak/client.rb
32
+ lib/riak/core_ext/blank.rb
33
+ lib/riak/core_ext/extract_options.rb
34
+ lib/riak/core_ext/slice.rb
35
+ lib/riak/core_ext/stringify_keys.rb
36
+ lib/riak/core_ext/symbolize_keys.rb
37
+ lib/riak/core_ext/to_param.rb
38
+ lib/riak/core_ext.rb
39
+ lib/riak/failed_request.rb
40
+ lib/riak/i18n.rb
41
+ lib/riak/invalid_response.rb
42
+ lib/riak/link.rb
43
+ lib/riak/locale
44
+ lib/riak/locale/en.yml
45
+ lib/riak/map_reduce.rb
46
+ lib/riak/map_reduce_error.rb
47
+ lib/riak/robject.rb
48
+ lib/riak/search.rb
49
+ lib/riak/test_server.rb
50
+ lib/riak/util/escape.rb
51
+ lib/riak/util/fiber1.8.rb
52
+ lib/riak/util/headers.rb
53
+ lib/riak/util/multipart.rb
54
+ lib/riak/util/tcp_socket_extensions.rb
55
+ lib/riak/util/translation.rb
56
+ lib/riak/walk_spec.rb
57
+ lib/riak.rb
58
+ Rakefile
59
+ riak-client.gemspec
60
+ spec/fixtures/cat.jpg
61
+ spec/fixtures/multipart-blank.txt
62
+ spec/fixtures/multipart-with-body.txt
63
+ spec/integration/riak/cache_store_spec.rb
64
+ spec/integration/riak/test_server_spec.rb
65
+ spec/riak/bucket_spec.rb
66
+ spec/riak/client_spec.rb
67
+ spec/riak/curb_backend_spec.rb
68
+ spec/riak/escape_spec.rb
69
+ spec/riak/excon_backend_spec.rb
70
+ spec/riak/headers_spec.rb
71
+ spec/riak/http_backend_spec.rb
72
+ spec/riak/link_spec.rb
73
+ spec/riak/map_reduce_spec.rb
74
+ spec/riak/multipart_spec.rb
75
+ spec/riak/net_http_backend_spec.rb
76
+ spec/riak/object_spec.rb
77
+ spec/riak/search_spec.rb
78
+ spec/riak/walk_spec_spec.rb
79
+ spec/spec_helper.rb
80
+ spec/support/drb_mock_server.rb
81
+ spec/support/http_backend_implementation_examples.rb
82
+ spec/support/mock_server.rb
83
+ spec/support/mocks.rb
84
+ spec/support/test_server.yml.example
85
+ }
31
86
 
32
- gem.files = files.to_a
33
-
34
- gem.test_files = FileList["spec/**/*.rb"].to_a
87
+ gem.test_files = gem.files.grep(/_spec\.rb$/)
35
88
  end
36
89
 
37
90
  # Gem packaging tasks
data/lib/riak.rb CHANGED
@@ -13,16 +13,25 @@
13
13
  # limitations under the License.
14
14
  $KCODE = "UTF8" if RUBY_VERSION < "1.9"
15
15
 
16
- require 'active_support/all'
17
- require 'active_support/json'
18
- require 'active_support/version'
19
16
  require 'base64'
20
17
  require 'uri'
21
18
  require 'cgi'
19
+ require 'set'
22
20
  require 'net/http'
23
21
  require 'yaml'
24
22
  require 'riak/i18n'
25
23
 
24
+ # Load JSON
25
+ unless defined? JSON
26
+ begin
27
+ require 'yajl/json_gem'
28
+ rescue LoadError
29
+ require 'json'
30
+ end
31
+ end
32
+
33
+ require 'riak/core_ext'
34
+
26
35
  # The Riak module contains all aspects of the HTTP client interface
27
36
  # to Riak.
28
37
  module Riak
@@ -35,9 +44,7 @@ module Riak
35
44
  autoload :MapReduce, "riak/map_reduce"
36
45
 
37
46
  # Cache store - only supports Rails 3 style
38
- if ActiveSupport::VERSION::STRING >= "3.0.0"
39
- autoload :CacheStore, "riak/cache_store"
40
- end
47
+ autoload :CacheStore, "riak/cache_store"
41
48
 
42
49
  # Exceptions
43
50
  autoload :FailedRequest, "riak/failed_request"
data/lib/riak/bucket.rb CHANGED
@@ -41,10 +41,11 @@ module Riak
41
41
  # @return [Bucket] self
42
42
  # @see Client#bucket
43
43
  def load(response={})
44
- unless response.try(:[], :headers).try(:[],'content-type').try(:first) =~ /json$/
44
+ content_type = response[:headers]['content-type'].first rescue ""
45
+ unless content_type =~ /json$/
45
46
  raise Riak::InvalidResponse.new({"content-type" => ["application/json"]}, response[:headers], t("loading_bucket", :name => name))
46
47
  end
47
- payload = ActiveSupport::JSON.decode(response[:body])
48
+ payload = JSON.parse(response[:body])
48
49
  @keys = payload['keys'].map {|k| CGI.unescape(k) } if payload['keys']
49
50
  @props = payload['props'] if payload['props']
50
51
  self
@@ -61,7 +62,8 @@ module Riak
61
62
  def keys(options={})
62
63
  if block_given?
63
64
  @client.http.get(200, @client.prefix, escape(name), {:props => false, :keys => 'stream'}, {}) do |chunk|
64
- obj = ActiveSupport::JSON.decode(chunk) rescue {}
65
+ obj = JSON.parse(chunk) rescue nil
66
+ next unless obj and obj['keys']
65
67
  yield obj['keys'].map {|k| CGI.unescape(k) } if obj['keys']
66
68
  end
67
69
  elsif @keys.nil? || options[:reload]
@@ -97,13 +99,14 @@ module Riak
97
99
  @client.http.put(204, @client.prefix, escape(name), body, {"Content-Type" => "application/json"})
98
100
  @props.merge!(properties)
99
101
  end
102
+ alias properties= props=
100
103
 
101
104
  # @return [Hash] Internal Riak bucket properties.
102
105
  # @see #props=
103
106
  def props
104
107
  @props
105
108
  end
106
- alias_attribute :properties, :props
109
+ alias properties props
107
110
 
108
111
  # Retrieve an object from within the bucket.
109
112
  # @param [String] key the key of the object to retrieve
@@ -12,6 +12,12 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  require 'riak'
15
+ require 'active_support/version'
16
+ if ActiveSupport::VERSION::STRING < "3.0.0"
17
+ raise LoadError, "ActiveSupport 3.0.0 or greater is required to use Riak::CacheStore."
18
+ else
19
+ require 'active_support/cache'
20
+ end
15
21
 
16
22
  module Riak
17
23
  # An ActiveSupport::Cache::Store implementation that uses Riak.
@@ -71,7 +77,7 @@ module Riak
71
77
  begin
72
78
  bucket.get(key).data
73
79
  rescue Riak::FailedRequest => fr
74
- raise fr unless fr.code == 404
80
+ raise fr unless fr.code.to_i == 404
75
81
  nil
76
82
  end
77
83
  end
data/lib/riak/client.rb CHANGED
@@ -24,6 +24,7 @@ module Riak
24
24
  autoload :HTTPBackend, "riak/client/http_backend"
25
25
  autoload :NetHTTPBackend, "riak/client/net_http_backend"
26
26
  autoload :CurbBackend, "riak/client/curb_backend"
27
+ autoload :ExconBackend, "riak/client/excon_backend"
27
28
 
28
29
  # When using integer client IDs, the exclusive upper-bound of valid values.
29
30
  MAX_CLIENT_ID = 4294967296
@@ -49,6 +50,9 @@ module Riak
49
50
  # @return [String] The URL path to the luwak HTTP endpoint
50
51
  attr_accessor :luwak
51
52
 
53
+ # @return [Symbol] The HTTP backend/client to use
54
+ attr_accessor :http_backend
55
+
52
56
  # Creates a client connection to Riak
53
57
  # @param [Hash] options configuration options for the client
54
58
  # @option options [String] :host ('127.0.0.1') The host or IP address for the Riak endpoint
@@ -56,15 +60,19 @@ module Riak
56
60
  # @option options [String] :prefix ('/riak/') The URL path prefix to the main HTTP endpoint
57
61
  # @option options [String] :mapred ('/mapred') The path to the map-reduce HTTP endpoint
58
62
  # @option options [Fixnum, String] :client_id (rand(MAX_CLIENT_ID)) The internal client ID used by Riak to route responses
63
+ # @option options [String, Symbol] :http_backend (:NetHTTP) which HTTP backend to use
59
64
  # @raise [ArgumentError] raised if any options are invalid
60
65
  def initialize(options={})
61
- options.assert_valid_keys(:host, :port, :prefix, :client_id, :mapred, :luwak)
62
- self.host = options[:host] || "127.0.0.1"
63
- self.port = options[:port] || 8098
64
- self.client_id = options[:client_id] || make_client_id
65
- self.prefix = options[:prefix] || "/riak/"
66
- self.mapred = options[:mapred] || "/mapred"
67
- self.luwak = options[:luwak] || "/luwak"
66
+ unless (options.keys - [:host, :port, :prefix, :client_id, :mapred, :luwak, :http_backend]).empty?
67
+ raise ArgumentError, "invalid options"
68
+ end
69
+ self.host = options[:host] || "127.0.0.1"
70
+ self.port = options[:port] || 8098
71
+ self.client_id = options[:client_id] || make_client_id
72
+ self.prefix = options[:prefix] || "/riak/"
73
+ self.mapred = options[:mapred] || "/mapred"
74
+ self.luwak = options[:luwak] || "/luwak"
75
+ self.http_backend = options[:http_backend] || :NetHTTP
68
76
  raise ArgumentError, t("missing_host_and_port") unless @host && @port
69
77
  end
70
78
 
@@ -101,17 +109,24 @@ module Riak
101
109
  @port = value
102
110
  end
103
111
 
112
+ # Sets the desired HTTP backend
113
+ def http_backend=(value)
114
+ @http = nil
115
+ @http_backend = value
116
+ end
117
+
104
118
  # Automatically detects and returns an appropriate HTTP backend.
105
119
  # The HTTP backend is used internally by the Riak client, but can also
106
120
  # be used to access the server directly.
107
121
  # @return [HTTPBackend] the HTTP backend for this client
108
122
  def http
109
123
  @http ||= begin
110
- require 'curb'
111
- CurbBackend.new(self)
112
- rescue LoadError, NameError
113
- warn t("install_curb")
114
- NetHTTPBackend.new(self)
124
+ klass = self.class.const_get("#{@http_backend}Backend")
125
+ if klass.configured?
126
+ klass.new(self)
127
+ else
128
+ raise t('http_configuration', :backend => @http_backend)
129
+ end
115
130
  end
116
131
  end
117
132
 
@@ -122,7 +137,9 @@ module Riak
122
137
  # @option options [Boolean] :props (true) whether to retreive the bucket properties
123
138
  # @return [Bucket] the requested bucket
124
139
  def bucket(name, options={})
125
- options.assert_valid_keys(:keys, :props)
140
+ unless (options.keys - [:keys, :props]).empty?
141
+ raise ArgumentError, "invalid options"
142
+ end
126
143
  response = http.get(200, prefix, escape(name), {:keys => false}.merge(options), {})
127
144
  Bucket.new(self, name).load(response)
128
145
  end
@@ -185,7 +202,7 @@ module Riak
185
202
  http.delete([204,404], luwak, escape(filename))
186
203
  true
187
204
  end
188
-
205
+
189
206
  # Checks whether a file exists in "Luwak".
190
207
  # @param [String] key the key to check
191
208
  # @return [true, false] whether the key exists in "Luwak"
@@ -22,14 +22,21 @@ end
22
22
  module Riak
23
23
  class Client
24
24
  # An HTTP backend for Riak::Client that uses the 'curb' library/gem.
25
- # If the 'curb' library is present, this backend will be preferred to
26
- # the backend based on Net::HTTP.
27
25
  # Conforms to the Riak::Client::HTTPBackend interface.
28
26
  class CurbBackend < HTTPBackend
27
+ def self.configured?
28
+ begin
29
+ require 'curb'
30
+ true
31
+ rescue LoadError
32
+ false
33
+ end
34
+ end
35
+
29
36
  private
30
37
  def perform(method, uri, headers, expect, data=nil)
31
38
  # Setup
32
- curl.headers = create_request_headers(headers)
39
+ curl.headers = RequestHeaders.new(headers).to_a
33
40
  curl.url = uri.to_s
34
41
  response_headers.initialize_http_header(nil)
35
42
  if block_given?
@@ -53,7 +60,7 @@ module Riak
53
60
  # Hacks around limitations in curb's PUT semantics
54
61
  _headers, curl.headers = curl.headers, {}
55
62
  curl.put_data = data
56
- curl.headers = create_request_headers(curl.headers) + _headers
63
+ curl.headers = RequestHeaders.new(curl.headers).to_a + _headers
57
64
  curl.http("PUT")
58
65
  else
59
66
  curl.send("http_#{method}")
@@ -80,20 +87,6 @@ module Riak
80
87
  end
81
88
  end
82
89
  end
83
-
84
- def response_headers
85
- Thread.current[:response_headers] ||= Riak::Util::Headers.new
86
- end
87
-
88
- def create_request_headers(hash)
89
- h = Riak::Util::Headers.new
90
- hash.each {|k,v| h.add_field(k,v) }
91
- [].tap do |arr|
92
- h.each_capitalized do |k,v|
93
- arr << "#{k}: #{v}"
94
- end
95
- end
96
- end
97
90
  end
98
91
  end
99
92
  end
@@ -0,0 +1,60 @@
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
+ class Client
18
+ # An HTTP backend for Riak::Client that uses Wesley Beary's Excon
19
+ # HTTP library. Comforms to the Riak::Client::HTTPBackend
20
+ # interface.
21
+ class ExconBackend < HTTPBackend
22
+ def self.configured?
23
+ begin
24
+ require 'excon'
25
+ Excon::VERSION >= "0.3.4"
26
+ rescue LoadError
27
+ false
28
+ end
29
+ end
30
+
31
+ private
32
+ def perform(method, uri, headers, expect, data=nil, &block)
33
+ params = {
34
+ :method => method.to_s.upcase,
35
+ :headers => RequestHeaders.new(headers).to_hash,
36
+ :path => uri.path
37
+ }
38
+ params[:query] = uri.query if uri.query
39
+ params[:body] = data if [:put,:post].include?(method)
40
+ params[:idempotent] = (method != :post)
41
+
42
+ response = connection.request(params, &block)
43
+ if valid_response?(expect, response.status)
44
+ response_headers.initialize_http_header(response.headers)
45
+ result = {:headers => response_headers.to_hash, :code => response.status}
46
+ if return_body?(method, response.status, block_given?)
47
+ result[:body] = response.body
48
+ end
49
+ result
50
+ else
51
+ raise FailedRequest.new(method, expect, response.status, response.headers, response.body)
52
+ end
53
+ end
54
+
55
+ def connection
56
+ @connection ||= Excon::Connection.new(root_uri.to_s)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -171,7 +171,7 @@ module Riak
171
171
  resource = Array(resource).flatten
172
172
  raise ArgumentError, t("resource_path_short") unless resource.length > 1 || resource.include?(@client.mapred)
173
173
  end
174
-
174
+
175
175
  # Checks the expected response codes against the actual response code. Use internally when
176
176
  # implementing {#perform}.
177
177
  # @param [String, Fixnum, Array<String,Fixnum>] expected the expected response code(s)
@@ -190,7 +190,7 @@ module Riak
190
190
  def return_body?(method, code, has_block)
191
191
  method != :head && !valid_response?([204,205,304], code) && !has_block
192
192
  end
193
-
193
+
194
194
  # Executes requests according to the underlying HTTP client library semantics.
195
195
  # @abstract Subclasses must implement this internal method to perform HTTP requests
196
196
  # according to the API of their HTTP libraries.
@@ -205,6 +205,37 @@ module Riak
205
205
  def perform(method, uri, headers, expect, body=nil)
206
206
  raise NotImplementedError
207
207
  end
208
+
209
+ private
210
+ def response_headers
211
+ Thread.current[:response_headers] ||= Riak::Util::Headers.new
212
+ end
213
+
214
+ # @private
215
+ class RequestHeaders < Riak::Util::Headers
216
+ alias each each_capitalized
217
+
218
+ def initialize(hash)
219
+ initialize_http_header(hash)
220
+ end
221
+
222
+ def to_a
223
+ [].tap do |arr|
224
+ each_capitalized do |k,v|
225
+ arr << "#{k}: #{v}"
226
+ end
227
+ end
228
+ end
229
+
230
+ def to_hash
231
+ {}.tap do |hash|
232
+ each_capitalized do |k,v|
233
+ hash[k] ||= []
234
+ hash[k] << v
235
+ end
236
+ end
237
+ end
238
+ end
208
239
  end
209
240
  end
210
241
  end