riak-client 0.7.1 → 0.8.0.beta

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # A sample Gemfile
2
+ source :gemcutter
3
+
4
+ gem 'activesupport', '~>2.3.5' # '3.0.0.rc'
5
+ gem 'i18n'
6
+
7
+ gem 'rspec', "~>2.0.0.beta.18"
8
+ gem 'fakeweb', ">=1.2"
9
+ gem 'rack', '>=1.0'
10
+ gem 'curb', '>=0.6'
11
+ gem 'yajl-ruby'
12
+ gem 'rake'
13
+ gem 'bundler'
@@ -0,0 +1,33 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ activesupport (2.3.8)
5
+ curb (0.7.8)
6
+ diff-lcs (1.1.2)
7
+ fakeweb (1.3.0)
8
+ i18n (0.4.1)
9
+ rack (1.2.1)
10
+ rake (0.8.7)
11
+ rspec (2.0.0.beta.20)
12
+ rspec-core (= 2.0.0.beta.20)
13
+ rspec-expectations (= 2.0.0.beta.20)
14
+ rspec-mocks (= 2.0.0.beta.20)
15
+ rspec-core (2.0.0.beta.20)
16
+ rspec-expectations (2.0.0.beta.20)
17
+ diff-lcs (>= 1.1.2)
18
+ rspec-mocks (2.0.0.beta.20)
19
+ yajl-ruby (0.7.7)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ activesupport (~> 2.3.5)
26
+ bundler
27
+ curb (>= 0.6)
28
+ fakeweb (>= 1.2)
29
+ i18n
30
+ rack (>= 1.0)
31
+ rake
32
+ rspec (~> 2.0.0.beta.18)
33
+ yajl-ruby
data/Rakefile CHANGED
@@ -9,11 +9,12 @@ gemspec = Gem::Specification.new do |gem|
9
9
  gem.email = "seancribbs@gmail.com"
10
10
  gem.homepage = "http://seancribbs.github.com/ripple"
11
11
  gem.authors = ["Sean Cribbs"]
12
- gem.add_development_dependency "rspec", "~>2.0.0.beta.6"
12
+ gem.add_development_dependency "rspec", "~>2.0.0.beta.11"
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
16
  gem.add_dependency "activesupport", ">= 2.3.5"
17
+ gem.add_dependency "i18n", "~>0.4.0"
17
18
  gem.requirements << "`gem install curb` for better HTTP performance"
18
19
 
19
20
  files = FileList["**/*"]
@@ -48,7 +49,7 @@ end
48
49
 
49
50
  desc %{Release the gem to RubyGems.org}
50
51
  task :release => :gem do
51
- "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
52
+ system "gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
52
53
  end
53
54
 
54
55
  require 'rspec/core'
@@ -15,8 +15,10 @@ $KCODE = "UTF8" if RUBY_VERSION < "1.9"
15
15
 
16
16
  require 'active_support/all'
17
17
  require 'active_support/json'
18
+ require 'active_support/version'
18
19
  require 'base64'
19
20
  require 'uri'
21
+ require 'cgi'
20
22
  require 'net/http'
21
23
  require 'yaml'
22
24
  require 'riak/i18n'
@@ -32,8 +34,11 @@ module Riak
32
34
  autoload :RObject, "riak/robject"
33
35
  autoload :MapReduce, "riak/map_reduce"
34
36
 
35
- # Cache store
36
- autoload :CacheStore, "riak/cache_store"
37
+ # Cache store - only supports Rails 3 style
38
+ if ActiveSupport::VERSION::STRING >= "3.0.0"
39
+ autoload :CacheStore, "riak/cache_store"
40
+ ::ActiveSupport::Cache.autoload :RiakStore, "riak/cache_store"
41
+ end
37
42
 
38
43
  # Exceptions
39
44
  autoload :FailedRequest, "riak/failed_request"
@@ -26,10 +26,6 @@ module Riak
26
26
  # @return [String] the bucket name
27
27
  attr_reader :name
28
28
 
29
- # @return [Hash] Internal Riak bucket properties.
30
- attr_reader :props
31
- alias_attribute :properties, :props
32
-
33
29
  # Create a Riak bucket manually.
34
30
  # @param [Client] client the {Riak::Client} for this bucket
35
31
  # @param [String] name the name of the bucket
@@ -49,7 +45,7 @@ module Riak
49
45
  raise Riak::InvalidResponse.new({"content-type" => ["application/json"]}, response[:headers], t("loading_bucket", :name => name))
50
46
  end
51
47
  payload = ActiveSupport::JSON.decode(response[:body])
52
- @keys = payload['keys'].map {|k| URI.unescape(k) } if payload['keys']
48
+ @keys = payload['keys'].map {|k| CGI.unescape(k) } if payload['keys']
53
49
  @props = payload['props'] if payload['props']
54
50
  self
55
51
  end
@@ -66,26 +62,48 @@ module Riak
66
62
  if block_given?
67
63
  @client.http.get(200, @client.prefix, escape(name), {:props => false, :keys => 'stream'}, {}) do |chunk|
68
64
  obj = ActiveSupport::JSON.decode(chunk) rescue {}
69
- yield obj['keys'].map {|k| URI.unescape(k) } if obj['keys']
65
+ yield obj['keys'].map {|k| CGI.unescape(k) } if obj['keys']
70
66
  end
71
67
  elsif @keys.nil? || options[:reload]
72
- response = @client.http.get(200, @client.prefix, escape(name), {:props => false}, {})
68
+ response = @client.http.get(200, @client.prefix, escape(name), {:props => false, :keys => true}, {})
73
69
  load(response)
74
70
  end
75
71
  @keys
76
72
  end
77
73
 
74
+
78
75
  # Sets internal properties on the bucket
79
76
  # Note: this results in a request to the Riak server!
80
77
  # @param [Hash] properties new properties for the bucket
81
- # @return [Hash] the properties that were accepted
82
- # @raise [FailedRequest] if the new properties were not accepted by the Riak server
78
+ # @option properties [Fixnum] :n_val (3) The N value (replication factor)
79
+ # @option properties [true,false] :allow_mult (false) Whether to permit object siblings
80
+ # @option properties [true,false] :last_write_wins (false) Whether to ignore vclocks
81
+ # @option properties [Array<Hash>] :precommit ([]) precommit hooks
82
+ # @option properties [Array<Hash>] :postcommit ([])postcommit hooks
83
+ # @option properties [Fixnum,String] :r ("quorum") read quorum (numeric or
84
+ # symbolic)
85
+ # @option properties [Fixnum,String] :w ("quorum") write quorum (numeric or
86
+ # symbolic)
87
+ # @option properties [Fixnum,String] :dw ("quorum") durable write quorum
88
+ # (numeric or symbolic)
89
+ # @option properties [Fixnum,String] :rw ("quorum") delete quorum (numeric or
90
+ # symbolic)
91
+ # @return [Hash] the merged bucket properties
92
+ # @raise [FailedRequest] if the new properties were not accepted by the Riakserver
93
+ # @see #n_value, #allow_mult, #r, #w, #dw, #rw
83
94
  def props=(properties)
84
95
  raise ArgumentError, t("hash_type", :hash => properties.inspect) unless Hash === properties
85
96
  body = {'props' => properties}.to_json
86
97
  @client.http.put(204, @client.prefix, escape(name), body, {"Content-Type" => "application/json"})
87
- @props = properties
98
+ @props.merge!(properties)
99
+ end
100
+
101
+ # @return [Hash] Internal Riak bucket properties.
102
+ # @see #props=
103
+ def props
104
+ @props
88
105
  end
106
+ alias_attribute :properties, :props
89
107
 
90
108
  # Retrieve an object from within the bucket.
91
109
  # @param [String] key the key of the object to retrieve
@@ -168,9 +186,22 @@ module Riak
168
186
  value
169
187
  end
170
188
 
171
- # @return [String] a representation suitable for IRB and debugging output
172
- def inspect
173
- "#<Riak::Bucket #{client.http.path(client.prefix, escape(name)).to_s}#{" keys=[#{keys.join(',')}]" if defined?(@keys)}>"
189
+ [:r,:w,:dw,:rw].each do |q|
190
+ class_eval <<-CODE
191
+ def #{q}
192
+ props["#{q}"]
193
+ end
194
+
195
+ def #{q}=(value)
196
+ self.props = {"#{q}" => value}
197
+ value
198
+ end
199
+ CODE
200
+ end
201
+
202
+ # @return [String] a representation suitable for IRB and debugging output
203
+ def inspect
204
+ "#<Riak::Bucket #{client.http.path(client.prefix, escape(name)).to_s}#{" keys=[#{keys.join(',')}]" if defined?(@keys)}>"
205
+ end
174
206
  end
175
207
  end
176
- end
@@ -19,63 +19,61 @@ module Riak
19
19
  def initialize(options = {})
20
20
  @bucket_name = options.delete(:bucket) || '_cache'
21
21
  @n_value = options.delete(:n_value) || 2
22
- @r = [options.delete(:r) || 1, @n_value].min
23
- @w = [options.delete(:w) || 1, @n_value].min
24
- @dw = [options.delete(:dw) || 0, @n_value].min
25
- @rw = [options.delete(:rw) || 1, @n_value].min
22
+ @r = options.delete(:r) || 1
23
+ @w = options.delete(:w) || 1
24
+ @dw = options.delete(:dw) || 0
25
+ @rw = options.delete(:rw) || "quorum"
26
26
  @client = Riak::Client.new(options)
27
+ set_bucket_defaults
27
28
  end
28
29
 
29
30
  def bucket
30
- @bucket ||= @client.bucket(@bucket_name, :keys => false).tap do |b|
31
- begin
32
- b.n_value = @n_value unless b.n_value == @n_value
33
- rescue
34
- end
35
- end
36
- end
37
-
38
- def write(key, value, options={})
39
- super do
40
- object = bucket.get_or_new(key, :r => @r)
41
- object.content_type = 'application/yaml'
42
- object.data = value
43
- object.store(:r => @r, :w => @w, :dw => @dw)
44
- end
31
+ @bucket ||= @client.bucket(@bucket_name, :keys => false)
45
32
  end
46
33
 
47
- def read(key, options={})
48
- super do
49
- begin
50
- bucket.get(key, :r => @r).data
51
- rescue Riak::FailedRequest => fr
52
- raise fr unless fr.code == 404
53
- nil
34
+ def delete_matched(matcher, options={})
35
+ instrument(:delete_matched, matcher) do
36
+ bucket.keys do |keys|
37
+ keys.grep(matcher).each do |k|
38
+ bucket.delete(k)
39
+ end
54
40
  end
55
41
  end
56
42
  end
57
43
 
58
- def exist?(key)
59
- super do
60
- bucket.exists?(key, :r => @r)
44
+ protected
45
+ def set_bucket_defaults
46
+ begin
47
+ new_values = {}
48
+ new_values['n_val'] = @n_value unless bucket.n_value == @n_value
49
+ new_values['r'] = @r unless bucket.r == @r
50
+ new_values['w'] = @w unless bucket.w == @w
51
+ new_values['dw'] = @dw unless bucket.dw == @dw
52
+ new_values['rw'] = @rw unless bucket.rw == @rw
53
+ bucket.props = new_values unless new_values.empty?
54
+ rescue
61
55
  end
62
56
  end
63
57
 
64
- def delete_matched(matcher, options={})
65
- super do
66
- bucket.keys do |keys|
67
- keys.grep(matcher).each do |k|
68
- bucket.delete(k, :rw => @rw)
69
- end
70
- end
71
- end
58
+ def write_entry(key, value, options={})
59
+ object = bucket.get_or_new(key)
60
+ object.content_type = 'application/yaml'
61
+ object.data = value
62
+ object.store
72
63
  end
73
64
 
74
- def delete(key, options={})
75
- super do
76
- bucket.delete(key, :rw => @rw)
65
+ def read_entry(key, options={})
66
+ begin
67
+ bucket.get(key).data
68
+ rescue Riak::FailedRequest => fr
69
+ raise fr unless fr.code == 404
70
+ nil
77
71
  end
78
72
  end
73
+
74
+ def delete_entry(key, options={})
75
+ bucket.delete(key)
76
+ end
79
77
  end
80
78
  end
81
79
 
@@ -117,7 +117,7 @@ module Riak
117
117
  # @return [Bucket] the requested bucket
118
118
  def bucket(name, options={})
119
119
  options.assert_valid_keys(:keys, :props)
120
- response = http.get(200, prefix, escape(name), options, {})
120
+ response = http.get(200, prefix, escape(name), {:keys => false}.merge(options), {})
121
121
  Bucket.new(self, name).load(response)
122
122
  end
123
123
  alias :[] :bucket
@@ -11,9 +11,9 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- begin
14
+ if ActiveSupport::VERSION::STRING >= "3.0"
15
15
  require 'active_support/i18n'
16
- rescue LoadError
16
+ else
17
17
  require 'i18n' # support ActiveSupport < 3
18
18
  end
19
19
 
@@ -39,12 +39,12 @@ module Riak
39
39
 
40
40
  # @return [String] bucket_name, if the Link url is a known Riak link ("/riak/<bucket>/<key>")
41
41
  def bucket
42
- URI.unescape($1) if url =~ %r{^/[^/]+/([^/]+)/?}
42
+ CGI.unescape($1) if url =~ %r{^/[^/]+/([^/]+)/?}
43
43
  end
44
44
 
45
45
  # @return [String] key, if the Link url is a known Riak link ("/riak/<bucket>/<key>")
46
46
  def key
47
- URI.unescape($1) if url =~ %r{^/[^/]+/[^/]+/([^/]+)/?}
47
+ CGI.unescape($1) if url =~ %r{^/[^/]+/[^/]+/([^/]+)/?}
48
48
  end
49
49
 
50
50
  def inspect; to_s; end
@@ -13,25 +13,25 @@
13
13
  # limitations under the License.
14
14
  en:
15
15
  riak:
16
- client_type: "invalid argument {{client}} is not a Riak::Client"
17
- string_type: "invalid_argument {{string}} is not a String"
18
- loading_bucket: "while loading bucket '{{name}}'"
19
- failed_request: "Expected {{expected}} from Riak but received {{code}}. {{body}}"
20
- hash_type: "invalid argument {{hash}} is not a Hash"
21
- path_and_body_required: "You must supply both a resource path and a body."
22
- request_body_type: "Request body must be a string or IO."
23
- resource_path_short: "Resource path too short"
24
- missing_host_and_port: "You must specify a host and port, or use the defaults of 127.0.0.1:8098"
25
- invalid_client_id: "Invalid client ID, must be a string or between 0 and {{max_id}}"
16
+ bucket_link_conversion: "Can't convert a bucket link to a walk spec"
17
+ client_type: "invalid argument %{client} is not a Riak::Client"
18
+ content_type_undefined: "content_type is not defined!"
19
+ failed_request: "Expected %{expected} from Riak but received %{code}. %{body}"
20
+ hash_type: "invalid argument %{hash} is not a Hash"
26
21
  hostname_invalid: "host must be a valid hostname"
27
- port_invalid: "port must be an integer between 0 and 65535"
28
22
  install_curb: "curb library not found! Please `gem install curb` for better performance."
29
- bucket_link_conversion: "Can't convert a bucket link to a walk spec"
23
+ invalid_client_id: "Invalid client ID, must be a string or between 0 and %{max_id}"
24
+ invalid_function_value: "invalid value for function: %{value}"
30
25
  invalid_phase_type: "type must be :map, :reduce, or :link"
26
+ loading_bucket: "while loading bucket '%{name}'"
27
+ missing_host_and_port: "You must specify a host and port, or use the defaults of 127.0.0.1:8098"
31
28
  module_function_pair_required: "function must have two elements when an array"
29
+ path_and_body_required: "You must supply both a resource path and a body."
30
+ port_invalid: "port must be an integer between 0 and 65535"
31
+ request_body_type: "Request body must be a string or IO."
32
+ resource_path_short: "Resource path too short"
32
33
  stored_function_invalid: "function must have :bucket and :key when a hash"
34
+ string_type: "invalid_argument %{string} is not a String"
35
+ too_few_arguments: "too few arguments: %{params}"
33
36
  walk_spec_invalid_unless_link: "WalkSpec is only valid for a function when the type is :link"
34
- invalid_function_value: "invalid value for function: {{value}}"
35
- content_type_undefined: "content_type is not defined!"
36
- too_few_arguments: "too few arguments: {{params}}"
37
- wrong_argument_count_walk_spec: "wrong number of arguments (one Hash or bucket,tag,keep required)"
37
+ wrong_argument_count_walk_spec: "wrong number of arguments (one Hash or bucket,tag,keep required)"
@@ -17,6 +17,8 @@ module Riak
17
17
  # Class for invoking map-reduce jobs using the HTTP interface.
18
18
  class MapReduce
19
19
  include Util::Translation
20
+ include Util::Escape
21
+
20
22
  # @return [Array<[bucket,key]>,String] The bucket/keys for input to the job, or the bucket (all keys).
21
23
  # @see #add
22
24
  attr_accessor :inputs
@@ -58,16 +60,17 @@ module Riak
58
60
  p = params.first
59
61
  case p
60
62
  when Bucket
61
- @inputs = p.name
63
+ @inputs = escape(p.name)
62
64
  when RObject
63
- @inputs << [p.bucket.name, p.key]
65
+ @inputs << [escape(p.bucket.name), escape(p.key)]
64
66
  when String
65
- @inputs = p
67
+ @inputs = escape(p)
66
68
  end
67
69
  when 2..3
68
70
  bucket = params.shift
69
71
  bucket = bucket.name if Bucket === bucket
70
- @inputs << params.unshift(bucket)
72
+ key = params.shift
73
+ @inputs << params.unshift(escape(key)).unshift(escape(bucket))
71
74
  end
72
75
  self
73
76
  end
@@ -38,7 +38,7 @@ module Riak
38
38
  # @return [Object] the data stored in Riak at this object's key. Varies in format by content-type, defaulting to String from the response body.
39
39
  attr_accessor :data
40
40
 
41
- # @return [Set<Link>] an Set of {Riak::Link} objects for relationships between this object and other resources
41
+ # @return [Set<Link>] a Set of {Riak::Link} objects for relationships between this object and other resources
42
42
  attr_accessor :links
43
43
 
44
44
  # @return [String] the ETag header from the most recent HTTP response, useful for caching and reloading
@@ -50,6 +50,19 @@ module Riak
50
50
  # @return [Hash] a hash of any X-Riak-Meta-* headers that were in the HTTP response, keyed on the trailing portion
51
51
  attr_accessor :meta
52
52
 
53
+ # Loads a list of RObjects that were emitted from a MapReduce
54
+ # query.
55
+ # @param [Client] client A Riak::Client with which the results will be
56
+ # associated
57
+ # @param [Array<Hash>] response A list of results a MapReduce job. Each
58
+ # entry should contain these keys: bucket, key, vclock, values
59
+ # @return [Array<RObject>] An array of RObject instances
60
+ def self.load_from_mapreduce(client, response)
61
+ response.map do |item|
62
+ RObject.new(client[CGI.unescape(item['bucket'])], CGI.unescape(item['key'])).load_from_mapreduce(item)
63
+ end
64
+ end
65
+
53
66
  # Create a new object manually
54
67
  # @param [Bucket] bucket the bucket in which the object exists
55
68
  # @param [String] key the key at which the object resides. If nil, a key will be assigned when the object is saved.
@@ -81,6 +94,28 @@ module Riak
81
94
  self
82
95
  end
83
96
 
97
+ # Load object data from a map/reduce response item.
98
+ # This method is used by RObject::load_from_mapreduce to instantiate the necessary
99
+ # objects.
100
+ # @param [Hash] response a response from {Riak::MapReduce}
101
+ # @return [RObject] self
102
+ def load_from_mapreduce(response)
103
+ self.vclock = response['vclock']
104
+ if response['values'].size == 1
105
+ value = response['values'].first
106
+ load_map_reduce_value(value)
107
+ else
108
+ @conflict = true
109
+ @siblings = response['values'].map do |v|
110
+ RObject.new(self.bucket, self.key) do |robj|
111
+ robj.vclock = self.vclock
112
+ robj.load_map_reduce_value(v)
113
+ end
114
+ end
115
+ end
116
+ self
117
+ end
118
+
84
119
  # HTTP header hash that will be sent along when storing the object
85
120
  # @return [Hash] hash of HTTP Headers
86
121
  def store_headers
@@ -141,9 +176,9 @@ module Riak
141
176
 
142
177
  # Delete the object from Riak and freeze this instance. Will work whether or not the object actually
143
178
  # exists in the Riak database.
144
- def delete
179
+ def delete(options={})
145
180
  return if key.blank?
146
- @bucket.delete(key)
181
+ @bucket.delete(key, options)
147
182
  freeze
148
183
  end
149
184
 
@@ -179,12 +214,8 @@ module Riak
179
214
  ActiveSupport::JSON.encode(payload)
180
215
  when /yaml/
181
216
  YAML.dump(payload)
182
- when "application/octet-stream"
183
- if @meta['ruby-serialization'] == "Marshal"
184
- Marshal.dump(payload)
185
- else
186
- payload.to_s
187
- end
217
+ when "application/x-ruby-marshal"
218
+ Marshal.dump(payload)
188
219
  else
189
220
  payload.to_s
190
221
  end
@@ -203,12 +234,8 @@ module Riak
203
234
  ActiveSupport::JSON.decode(body)
204
235
  when /yaml/
205
236
  YAML.load(body)
206
- when "application/octet-stream"
207
- if @meta['ruby-serialization'] == "Marshal"
208
- Marshal.load(body)
209
- else
210
- body
211
- end
237
+ when "application/x-ruby-marshal"
238
+ Marshal.load(body)
212
239
  else
213
240
  body
214
241
  end
@@ -246,12 +273,37 @@ module Riak
246
273
  @bucket.client.http.path(*segments).to_s
247
274
  end
248
275
 
276
+ protected
277
+ def load_map_reduce_value(hash)
278
+ metadata = hash['metadata']
279
+ extract_if_present(metadata, 'X-Riak-VTag', :etag)
280
+ extract_if_present(metadata, 'content-type', :content_type)
281
+ extract_if_present(metadata, 'X-Riak-Last-Modified', :last_modified) { |v| Time.httpdate( v ) }
282
+ extract_if_present(metadata, 'Links', :links) do |links|
283
+ Set.new( links.map { |l| Link.new("#{@bucket.client.prefix}#{l[0]}/#{l[1]}", l[2]) } )
284
+ end
285
+ extract_if_present(metadata, 'X-Riak-Meta', :meta) do |meta|
286
+ Hash[
287
+ meta.map do |k,v|
288
+ [k.sub(%r{^x-riak-meta-}i, ''), [v]]
289
+ end
290
+ ]
291
+ end
292
+ extract_if_present(hash, 'data', :data) { |v| deserialize(v) }
293
+ end
294
+
249
295
  private
250
- def extract_header(response, name, attribute=nil)
251
- if response[:headers][name].present?
252
- value = response[:headers][name].try(:first)
253
- value = yield value if block_given?
254
- send("#{attribute}=", value) if attribute
296
+ def extract_if_present(hash, key, attribute=nil)
297
+ if hash[key].present?
298
+ attribute ||= key
299
+ value = block_given? ? yield(hash[key]) : hash[key]
300
+ send("#{attribute}=", value)
301
+ end
302
+ end
303
+
304
+ def extract_header(response, name, attribute=nil, &block)
305
+ extract_if_present(response[:headers], name, attribute) do |value|
306
+ block ? block.call(value[0]) : value[0]
255
307
  end
256
308
  end
257
309
 
@@ -1,11 +1,11 @@
1
1
  module Riak
2
2
  module Util
3
3
  module Escape
4
- # URI-escapes bucket or key names that may contain slashes for use in URLs.
4
+ # CGI-escapes bucket or key names that may contain slashes for use in URLs.
5
5
  # @param [String] bucket_or_key the bucket or key name
6
6
  # @return [String] the escaped path segment
7
7
  def escape(bucket_or_key)
8
- URI.escape(bucket_or_key.to_s).gsub("/", "%2F")
8
+ CGI.escape(bucket_or_key.to_s).gsub("+", "%20")
9
9
  end
10
10
  end
11
11
  end
@@ -57,6 +57,42 @@ describe Riak::CacheStore do
57
57
  @cache = ActiveSupport::Cache.lookup_store(:riak_store, :n_value => 1)
58
58
  @cache.bucket.n_value.should == 1
59
59
  end
60
+
61
+ it "should set the bucket R value to 1 by default" do
62
+ @cache.bucket.r.should == 1
63
+ end
64
+
65
+ it "should set the bucket R default to the specified value" do
66
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :r => "quorum")
67
+ @cache.bucket.r.should == "quorum"
68
+ end
69
+
70
+ it "should set the bucket W value to 1 by default" do
71
+ @cache.bucket.w.should == 1
72
+ end
73
+
74
+ it "should set the bucket W default to the specified value" do
75
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :w => "all")
76
+ @cache.bucket.w.should == "all"
77
+ end
78
+
79
+ it "should set the bucket DW value to 0 by default" do
80
+ @cache.bucket.dw.should == 0
81
+ end
82
+
83
+ it "should set the bucket DW default to the specified value" do
84
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :dw => "quorum")
85
+ @cache.bucket.dw.should == "quorum"
86
+ end
87
+
88
+ it "should set the bucket RW value to quorum by default" do
89
+ @cache.bucket.rw.should == "quorum"
90
+ end
91
+
92
+ it "should set the bucket RW default to the specified value" do
93
+ @cache = ActiveSupport::Cache.lookup_store(:riak_store, :rw => "all")
94
+ @cache.bucket.rw.should == "all"
95
+ end
60
96
  end
61
97
 
62
98
 
@@ -93,20 +129,6 @@ describe Riak::CacheStore do
93
129
  @cache.fetch('foo', :force => true){'bar'}.should == 'bar'
94
130
  end
95
131
 
96
- it "should increment an integer value in the cache" do
97
- @cache.write('foo', 1, :raw => true)
98
- @cache.read('foo', :raw => true).to_i.should == 1
99
- @cache.increment('foo')
100
- @cache.read('foo', :raw => true).to_i.should == 2
101
- end
102
-
103
- it "should decrement an integer value in the cache" do
104
- @cache.write('foo', 1, :raw => true)
105
- @cache.read('foo', :raw => true).to_i.should == 1
106
- @cache.decrement('foo')
107
- @cache.read('foo', :raw => true).to_i.should == 0
108
- end
109
-
110
132
  it "should detect if a value exists in the cache" do
111
133
  @cache.write('foo', 'bar')
112
134
  @cache.exist?('foo').should be_true
@@ -21,7 +21,7 @@ describe Riak::Bucket do
21
21
 
22
22
  def do_load(overrides={})
23
23
  @bucket.load({
24
- :body => '{"props":{"name":"foo","allow_mult":false,"big_vclock":50,"chash_keyfun":{"mod":"riak_util","fun":"chash_std_keyfun"},"linkfun":{"mod":"jiak_object","fun":"mapreduce_linkfun"},"n_val":3,"old_vclock":86400,"small_vclock":10,"young_vclock":20},"keys":["bar"]}',
24
+ :body => '{"props":{"name":"foo","n_val":3,"allow_mult":false,"last_write_wins":false,"precommit":[],"postcommit":[],"chash_keyfun":{"mod":"riak_core_util","fun":"chash_std_keyfun"},"linkfun":{"mod":"riak_kv_wm_link_walker","fun":"mapreduce_linkfun"},"old_vclock":86400,"young_vclock":20,"big_vclock":50,"small_vclock":10,"r":"quorum","w":"quorum","dw":"quorum","rw":"quorum"},"keys":["bar"]}',
25
25
  :headers => {
26
26
  "vary" => ["Accept-Encoding"],
27
27
  "server" => ["MochiWeb/1.1 WebMachine/1.5.1 (hack the charles gibson)"],
@@ -53,7 +53,7 @@ describe Riak::Bucket do
53
53
  describe "when loading data from an HTTP response" do
54
54
  it "should load the bucket properties from the response body" do
55
55
  do_load
56
- @bucket.props.should == {"name"=>"foo","allow_mult" => false,"big_vclock" => 50,"chash_keyfun" => {"mod" =>"riak_util","fun"=>"chash_std_keyfun"},"linkfun"=>{"mod"=>"jiak_object","fun"=>"mapreduce_linkfun"},"n_val"=>3,"old_vclock"=>86400,"small_vclock"=>10,"young_vclock"=>20}
56
+ @bucket.props.should == {"name"=>"foo", "n_val"=>3, "allow_mult"=>false, "last_write_wins"=>false, "precommit"=>[], "postcommit"=>[], "chash_keyfun"=>{"mod"=>"riak_core_util", "fun"=>"chash_std_keyfun"}, "linkfun"=>{"mod"=>"riak_kv_wm_link_walker", "fun"=>"mapreduce_linkfun"}, "old_vclock"=>86400, "young_vclock"=>20, "big_vclock"=>50, "small_vclock"=>10, "r"=>"quorum", "w"=>"quorum", "dw"=>"quorum", "rw"=>"quorum"}
57
57
  end
58
58
 
59
59
  it "should load the keys from the response body" do
@@ -80,12 +80,12 @@ describe Riak::Bucket do
80
80
  end
81
81
 
82
82
  it "should load the keys if not present" do
83
- @http.should_receive(:get).with(200, "/riak/", "foo", {:props => false}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
83
+ @http.should_receive(:get).with(200, "/riak/", "foo", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
84
84
  @bucket.keys.should == ["bar"]
85
85
  end
86
86
 
87
87
  it "should allow reloading of the keys" do
88
- @http.should_receive(:get).with(200, "/riak/","foo", {:props => false}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
88
+ @http.should_receive(:get).with(200, "/riak/","foo", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
89
89
  do_load # Ensures they're already loaded
90
90
  @bucket.keys(:reload => true).should == ["bar"]
91
91
  end
@@ -101,13 +101,13 @@ describe Riak::Bucket do
101
101
  end
102
102
 
103
103
  it "should unescape key names" do
104
- @http.should_receive(:get).with(200, "/riak/","foo", {:props => false}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar%20baz"]}'})
104
+ @http.should_receive(:get).with(200, "/riak/","foo", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar%20baz"]}'})
105
105
  @bucket.keys.should == ["bar baz"]
106
106
  end
107
107
 
108
108
  it "should escape the bucket name" do
109
109
  @bucket.instance_variable_set :@name, "unescaped "
110
- @http.should_receive(:get).with(200, "/riak/","unescaped%20", {:props => false}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
110
+ @http.should_receive(:get).with(200, "/riak/","unescaped%20", {:props => false, :keys => true}, {}).and_return({:headers => {"content-type" => ["application/json"]}, :body => '{"keys":["bar"]}'})
111
111
  @bucket.keys.should == ["bar"]
112
112
  end
113
113
  end
@@ -228,6 +228,24 @@ describe Riak::Bucket do
228
228
  end
229
229
  end
230
230
 
231
+ [:r, :w, :dw, :rw].each do |q|
232
+ describe "get/set the default #{q} quorum" do
233
+ before :each do
234
+ do_load
235
+ end
236
+
237
+ it "should extract the default #{q} quorum" do
238
+ @bucket.send(q).should == "quorum"
239
+ end
240
+
241
+ it "should set the #{q} quorum" do
242
+ @bucket.should_receive(:props=).with(hash_including("#{q}" => 1))
243
+ @bucket.send("#{q}=",1)
244
+ end
245
+ end
246
+ end
247
+
248
+
231
249
  describe "checking whether a key exists" do
232
250
  it "should return true if the object does exist" do
233
251
  @client.http.should_receive(:head).and_return(:code => 200)
@@ -1,4 +1,4 @@
1
- # Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
1
+ 60# Copyright 2010 Sean Cribbs, Sonian Inc., and Basho Technologies, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -157,13 +157,13 @@ describe Riak::Client do
157
157
  end
158
158
 
159
159
  it "should send a GET request to the bucket name and return a Riak::Bucket" do
160
- @http.should_receive(:get).with(200, "/riak/", "foo", {}, {}).and_return(@payload)
160
+ @http.should_receive(:get).with(200, "/riak/", "foo", {:keys => false}, {}).and_return(@payload)
161
161
  @client.bucket("foo").should be_kind_of(Riak::Bucket)
162
162
  end
163
163
 
164
- it "should allow requesting bucket properties without the keys" do
165
- @http.should_receive(:get).with(200, "/riak/", "foo", {:keys => false}, {}).and_return(@payload)
166
- @client.bucket("foo", :keys => false)
164
+ it "should allow requesting bucket properties with the keys" do
165
+ @http.should_receive(:get).with(200, "/riak/", "foo", {:keys => true}, {}).and_return(@payload)
166
+ @client.bucket("foo", :keys => true)
167
167
  end
168
168
 
169
169
  it "should escape bucket names with invalid characters" do
@@ -9,6 +9,7 @@ describe Riak::Util::Escape do
9
9
  it "should escape standard non-safe characters" do
10
10
  @object.escape("some string").should == "some%20string"
11
11
  @object.escape("another^one").should == "another%5Eone"
12
+ @object.escape("bracket[one").should == "bracket%5Bone"
12
13
  end
13
14
 
14
15
  it "should escape slashes" do
@@ -49,11 +49,21 @@ describe Riak::MapReduce do
49
49
  @mr.inputs.should == [["foo","bar"]]
50
50
  end
51
51
 
52
+ it "should add bucket/key pairs to the inputs" do
53
+ @mr.add("[foo]","(bar)")
54
+ @mr.inputs.should == [["%5Bfoo%5D","%28bar%29"]]
55
+ end
56
+
52
57
  it "should add an array containing a bucket/key pair to the inputs" do
53
58
  @mr.add(["foo","bar"])
54
59
  @mr.inputs.should == [["foo","bar"]]
55
60
  end
56
61
 
62
+ it "should add an escaped array containing a bucket/key pair to the inputs" do
63
+ @mr.add(["[foo]","(bar)"])
64
+ @mr.inputs.should == [["%5Bfoo%5D","%28bar%29"]]
65
+ end
66
+
57
67
  it "should add an object to the inputs by its bucket and key" do
58
68
  bucket = Riak::Bucket.new(@client, "foo")
59
69
  obj = Riak::RObject.new(bucket, "bar")
@@ -61,17 +71,36 @@ describe Riak::MapReduce do
61
71
  @mr.inputs.should == [["foo", "bar"]]
62
72
  end
63
73
 
74
+ it "should add an object to the inputs by its escaped bucket and key" do
75
+ bucket = Riak::Bucket.new(@client, "[foo]")
76
+ obj = Riak::RObject.new(bucket, "(bar)")
77
+ @mr.add(obj)
78
+ @mr.inputs.should == [["%5Bfoo%5D", "%28bar%29"]]
79
+ end
80
+
64
81
  it "should add an array containing a bucket/key/key-data triple to the inputs" do
65
82
  @mr.add(["foo","bar",1000])
66
83
  @mr.inputs.should == [["foo","bar",1000]]
67
84
  end
68
85
 
86
+ it "should add an escaped array containing a bucket/key/key-data triple to the inputs" do
87
+ @mr.add(["[foo]","(bar)","[]()"])
88
+ @mr.inputs.should == [["%5Bfoo%5D", "%28bar%29","[]()"]]
89
+ end
90
+
69
91
  it "should use a bucket name as the single input" do
70
92
  @mr.add(Riak::Bucket.new(@client, "foo"))
71
93
  @mr.inputs.should == "foo"
72
94
  @mr.add("docs")
73
95
  @mr.inputs.should == "docs"
74
96
  end
97
+
98
+ it "should use an escaped bucket name as the single input" do
99
+ @mr.add(Riak::Bucket.new(@client, "[foo]"))
100
+ @mr.inputs.should == "%5Bfoo%5D"
101
+ @mr.add("docs")
102
+ @mr.inputs.should == "docs"
103
+ end
75
104
  end
76
105
 
77
106
  [:map, :reduce].each do |type|
@@ -101,39 +101,19 @@ describe Riak::RObject do
101
101
  end
102
102
  end
103
103
 
104
- describe "when the content type is an octet-stream" do
104
+ describe "when the content type is application/x-ruby-marshal" do
105
105
  before :each do
106
- @object.content_type = "application/octet-stream"
106
+ @object.content_type = "application/x-ruby-marshal"
107
+ @payload = Marshal.dump({"foo" => "bar"})
107
108
  end
108
109
 
109
- describe "if the ruby-serialization meta field is set to Marshal" do
110
- before :each do
111
- @object.meta['ruby-serialization'] = "Marshal"
112
- @payload = Marshal.dump({"foo" => "bar"})
113
- end
114
110
 
115
- it "should dump via Marshal" do
116
- @object.serialize({"foo" => "bar"}).should == @payload
117
- end
118
-
119
- it "should load from Marshal" do
120
- @object.deserialize(@payload).should == {"foo" => "bar"}
121
- end
111
+ it "should dump via Marshal" do
112
+ @object.serialize({"foo" => "bar"}).should == @payload
122
113
  end
123
114
 
124
- describe "if the ruby-serialization meta field is not set to Marshal" do
125
- before :each do
126
- @object.meta.delete("ruby-serialization")
127
- end
128
-
129
- it "should dump to a string" do
130
- @object.serialize(2).should == "2"
131
- @object.serialize("foo").should == "foo"
132
- end
133
-
134
- it "should load the body unmodified" do
135
- @object.deserialize("foo").should == "foo"
136
- end
115
+ it "should load from Marshal" do
116
+ @object.deserialize(@payload).should == {"foo" => "bar"}
137
117
  end
138
118
  end
139
119
  end
@@ -198,6 +178,11 @@ describe Riak::RObject do
198
178
  @object.key.should == "baz"
199
179
  end
200
180
 
181
+ it "should parse and escape the location header into the key when present" do
182
+ @object.load({:headers => {"content-type" => ["application/json"], "location" => ["/riak/foo/%5Bbaz%5D"]}})
183
+ @object.key.should == "[baz]"
184
+ end
185
+
201
186
  it "should be in conflict when the response code is 300 and the content-type is multipart/mixed" do
202
187
  @object.load({:headers => {"content-type" => ["multipart/mixed; boundary=foo"]}, :code => 300 })
203
188
  @object.should be_conflict
@@ -209,6 +194,106 @@ describe Riak::RObject do
209
194
  end
210
195
  end
211
196
 
197
+ describe "instantiating new object from a map reduce operation" do
198
+ before :each do
199
+ @client.stub!(:[]).and_return(@bucket)
200
+
201
+ @sample_response = [
202
+ {"bucket"=>"users",
203
+ "key"=>"A2IbUQ2KEMbe4WGtdL97LoTi1DN%5B%28%5C%2F%29%5D",
204
+ "vclock"=> "a85hYGBgzmDKBVIsCfs+fc9gSN9wlA8q/hKosDpIOAsA",
205
+ "values"=> [
206
+ {"metadata"=>
207
+ {"Links"=>[["addresses", "A2cbUQ2KEMbeyWGtdz97LoTi1DN", "home_address"]],
208
+ "X-Riak-VTag"=>"5bnavU3rrubcxLI8EvFXhB",
209
+ "content-type"=>"application/json",
210
+ "X-Riak-Last-Modified"=>"Mon, 12 Jul 2010 21:37:43 GMT",
211
+ "X-Riak-Meta"=>{"X-Riak-Meta-King-Of-Robots"=>"I"}},
212
+ "data"=>
213
+ "{\"email\":\"mail@test.com\",\"_type\":\"User\"}"
214
+ }
215
+ ]
216
+ }
217
+ ]
218
+ @object = Riak::RObject.load_from_mapreduce(@client,@sample_response).first
219
+ @object.should be_kind_of(Riak::RObject)
220
+ end
221
+
222
+ it "should load the content type" do
223
+ @object.content_type.should == "application/json"
224
+ end
225
+
226
+ it "should load the body data" do
227
+ @object.data.should be_present
228
+ end
229
+
230
+ it "should deserialize the body data" do
231
+ @object.data.should == {"email" => "mail@test.com", "_type" => "User"}
232
+ end
233
+
234
+ it "should set the vclock" do
235
+ @object.vclock.should == "a85hYGBgzmDKBVIsCfs+fc9gSN9wlA8q/hKosDpIOAsA"
236
+ end
237
+
238
+ it "should load and parse links" do
239
+ @object.links.should have(1).item
240
+ @object.links.first.url.should == "/riak/addresses/A2cbUQ2KEMbeyWGtdz97LoTi1DN"
241
+ @object.links.first.rel.should == "home_address"
242
+ end
243
+
244
+ it "should set the ETag" do
245
+ @object.etag.should == "5bnavU3rrubcxLI8EvFXhB"
246
+ end
247
+
248
+ it "should set modified date" do
249
+ @object.last_modified.to_i.should == Time.httpdate("Mon, 12 Jul 2010 21:37:43 GMT").to_i
250
+ end
251
+
252
+ it "should load meta information" do
253
+ @object.meta["King-Of-Robots"].should == ["I"]
254
+ end
255
+
256
+ it "should set the key" do
257
+ @object.key.should == "A2IbUQ2KEMbe4WGtdL97LoTi1DN[(\\/)]"
258
+ end
259
+
260
+ it "should not set conflict when there is none" do
261
+ @object.conflict?.should be_false
262
+ end
263
+
264
+ describe "when there are multiple values in an object" do
265
+ before :each do
266
+ response = @sample_response.dup
267
+ response[0]['values'] << {
268
+ "metadata"=> {
269
+ "Links"=>[],
270
+ "X-Riak-VTag"=>"7jDZLdu0fIj2iRsjGD8qq8",
271
+ "content-type"=>"application/json",
272
+ "X-Riak-Last-Modified"=>"Mon, 14 Jul 2010 19:28:27 GMT",
273
+ "X-Riak-Meta"=>[]
274
+ },
275
+ "data"=> "{\"email\":\"mail@domain.com\",\"_type\":\"User\"}"
276
+ }
277
+ @object = Riak::RObject.load_from_mapreduce( @client, response ).first
278
+ end
279
+
280
+ it "should expose siblings" do
281
+ @object.should have(2).siblings
282
+ @object.siblings[0].etag.should == "5bnavU3rrubcxLI8EvFXhB"
283
+ @object.siblings[1].etag.should == "7jDZLdu0fIj2iRsjGD8qq8"
284
+ end
285
+
286
+ it "should be in conflict" do
287
+ @object.data.should_not be_present
288
+ @object.should be_conflict
289
+ end
290
+
291
+ it "should assign the same vclock to all the siblings" do
292
+ @object.siblings.should be_all {|s| s.vclock == @object.vclock }
293
+ end
294
+ end
295
+ end
296
+
212
297
  describe "extracting siblings" do
213
298
  before :each do
214
299
  @object = Riak::RObject.new(@bucket, "bar").load({:headers => {"x-riak-vclock" => ["merged"], "content-type" => ["multipart/mixed; boundary=foo"]}, :code => 300, :body => "\n--foo\nContent-Type: text/plain\n\nbar\n--foo\nContent-Type: text/plain\n\nbaz\n--foo--\n"})
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riak-client
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ prerelease: true
5
5
  segments:
6
6
  - 0
7
- - 7
8
- - 1
9
- version: 0.7.1
7
+ - 8
8
+ - 0
9
+ - beta
10
+ version: 0.8.0.beta
10
11
  platform: ruby
11
12
  authors:
12
13
  - Sean Cribbs
@@ -14,13 +15,14 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-06-08 00:00:00 -04:00
18
+ date: 2010-08-24 00:00:00 -04:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: rspec
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ~>
26
28
  - !ruby/object:Gem::Version
@@ -29,14 +31,15 @@ dependencies:
29
31
  - 0
30
32
  - 0
31
33
  - beta
32
- - 6
33
- version: 2.0.0.beta.6
34
+ - 11
35
+ version: 2.0.0.beta.11
34
36
  type: :development
35
37
  version_requirements: *id001
36
38
  - !ruby/object:Gem::Dependency
37
39
  name: fakeweb
38
40
  prerelease: false
39
41
  requirement: &id002 !ruby/object:Gem::Requirement
42
+ none: false
40
43
  requirements:
41
44
  - - ">="
42
45
  - !ruby/object:Gem::Version
@@ -50,6 +53,7 @@ dependencies:
50
53
  name: rack
51
54
  prerelease: false
52
55
  requirement: &id003 !ruby/object:Gem::Requirement
56
+ none: false
53
57
  requirements:
54
58
  - - ">="
55
59
  - !ruby/object:Gem::Version
@@ -63,6 +67,7 @@ dependencies:
63
67
  name: curb
64
68
  prerelease: false
65
69
  requirement: &id004 !ruby/object:Gem::Requirement
70
+ none: false
66
71
  requirements:
67
72
  - - ">="
68
73
  - !ruby/object:Gem::Version
@@ -76,6 +81,7 @@ dependencies:
76
81
  name: activesupport
77
82
  prerelease: false
78
83
  requirement: &id005 !ruby/object:Gem::Requirement
84
+ none: false
79
85
  requirements:
80
86
  - - ">="
81
87
  - !ruby/object:Gem::Version
@@ -86,6 +92,21 @@ dependencies:
86
92
  version: 2.3.5
87
93
  type: :runtime
88
94
  version_requirements: *id005
95
+ - !ruby/object:Gem::Dependency
96
+ name: i18n
97
+ prerelease: false
98
+ requirement: &id006 !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ segments:
104
+ - 0
105
+ - 4
106
+ - 0
107
+ version: 0.4.0
108
+ type: :runtime
109
+ version_requirements: *id006
89
110
  description: 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.
90
111
  email: seancribbs@gmail.com
91
112
  executables: []
@@ -95,6 +116,8 @@ extensions: []
95
116
  extra_rdoc_files: []
96
117
 
97
118
  files:
119
+ - Gemfile
120
+ - Gemfile.lock
98
121
  - lib/riak/bucket.rb
99
122
  - lib/riak/cache_store.rb
100
123
  - lib/riak/client/curb_backend.rb
@@ -147,6 +170,7 @@ rdoc_options: []
147
170
  require_paths:
148
171
  - lib
149
172
  required_ruby_version: !ruby/object:Gem::Requirement
173
+ none: false
150
174
  requirements:
151
175
  - - ">="
152
176
  - !ruby/object:Gem::Version
@@ -154,16 +178,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
154
178
  - 0
155
179
  version: "0"
156
180
  required_rubygems_version: !ruby/object:Gem::Requirement
181
+ none: false
157
182
  requirements:
158
- - - ">="
183
+ - - ">"
159
184
  - !ruby/object:Gem::Version
160
185
  segments:
161
- - 0
162
- version: "0"
186
+ - 1
187
+ - 3
188
+ - 1
189
+ version: 1.3.1
163
190
  requirements:
164
191
  - `gem install curb` for better HTTP performance
165
192
  rubyforge_project:
166
- rubygems_version: 1.3.6
193
+ rubygems_version: 1.3.7
167
194
  signing_key:
168
195
  specification_version: 3
169
196
  summary: riak-client is a rich client for Riak, the distributed database by Basho.