riak-client 0.7.0
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/Rakefile +74 -0
- data/lib/riak.rb +49 -0
- data/lib/riak/bucket.rb +176 -0
- data/lib/riak/cache_store.rb +82 -0
- data/lib/riak/client.rb +139 -0
- data/lib/riak/client/curb_backend.rb +82 -0
- data/lib/riak/client/http_backend.rb +209 -0
- data/lib/riak/client/net_http_backend.rb +49 -0
- data/lib/riak/failed_request.rb +37 -0
- data/lib/riak/i18n.rb +20 -0
- data/lib/riak/invalid_response.rb +25 -0
- data/lib/riak/link.rb +73 -0
- data/lib/riak/locale/en.yml +37 -0
- data/lib/riak/map_reduce.rb +248 -0
- data/lib/riak/map_reduce_error.rb +20 -0
- data/lib/riak/robject.rb +267 -0
- data/lib/riak/util/escape.rb +12 -0
- data/lib/riak/util/fiber1.8.rb +48 -0
- data/lib/riak/util/headers.rb +44 -0
- data/lib/riak/util/multipart.rb +52 -0
- data/lib/riak/util/translation.rb +29 -0
- data/lib/riak/walk_spec.rb +117 -0
- data/spec/fixtures/cat.jpg +0 -0
- data/spec/fixtures/multipart-blank.txt +7 -0
- data/spec/fixtures/multipart-with-body.txt +16 -0
- data/spec/integration/riak/cache_store_spec.rb +129 -0
- data/spec/riak/bucket_spec.rb +247 -0
- data/spec/riak/client_spec.rb +174 -0
- data/spec/riak/curb_backend_spec.rb +53 -0
- data/spec/riak/escape_spec.rb +21 -0
- data/spec/riak/headers_spec.rb +34 -0
- data/spec/riak/http_backend_spec.rb +131 -0
- data/spec/riak/link_spec.rb +82 -0
- data/spec/riak/map_reduce_spec.rb +352 -0
- data/spec/riak/multipart_spec.rb +36 -0
- data/spec/riak/net_http_backend_spec.rb +28 -0
- data/spec/riak/object_spec.rb +538 -0
- data/spec/riak/walk_spec_spec.rb +208 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/http_backend_implementation_examples.rb +215 -0
- data/spec/support/mock_server.rb +61 -0
- data/spec/support/mocks.rb +3 -0
- metadata +187 -0
data/Rakefile
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
|
4
|
+
gemspec = Gem::Specification.new do |gem|
|
5
|
+
gem.name = "riak-client"
|
6
|
+
gem.summary = %Q{riak-client is a rich client for Riak, the distributed database by Basho.}
|
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
|
+
gem.version = File.read('../VERSION').strip
|
9
|
+
gem.email = "seancribbs@gmail.com"
|
10
|
+
gem.homepage = "http://seancribbs.github.com/ripple"
|
11
|
+
gem.authors = ["Sean Cribbs"]
|
12
|
+
gem.add_development_dependency "rspec", "~>2.0.0.beta.6"
|
13
|
+
gem.add_development_dependency "fakeweb", ">=1.2"
|
14
|
+
gem.add_development_dependency "rack", ">=1.0"
|
15
|
+
gem.add_development_dependency "curb", ">=0.6"
|
16
|
+
gem.add_dependency "activesupport", ">= 2.3.5"
|
17
|
+
gem.requirements << "`gem install curb` for better HTTP performance"
|
18
|
+
|
19
|
+
files = FileList["**/*"]
|
20
|
+
files.exclude /\.DS_Store/
|
21
|
+
files.exclude /\#/
|
22
|
+
files.exclude /~/
|
23
|
+
files.exclude /\.swp/
|
24
|
+
files.exclude '**/._*'
|
25
|
+
files.exclude '**/*.orig'
|
26
|
+
files.exclude '**/*.rej'
|
27
|
+
files.exclude /^pkg/
|
28
|
+
files.exclude 'riak-client.gemspec'
|
29
|
+
|
30
|
+
gem.files = files.to_a
|
31
|
+
|
32
|
+
gem.test_files = FileList["spec/**/*.rb"].to_a
|
33
|
+
end
|
34
|
+
|
35
|
+
# Gem packaging tasks
|
36
|
+
Rake::GemPackageTask.new(gemspec) do |pkg|
|
37
|
+
pkg.need_zip = false
|
38
|
+
pkg.need_tar = false
|
39
|
+
end
|
40
|
+
|
41
|
+
task :gem => :gemspec
|
42
|
+
|
43
|
+
desc %{Build the gemspec file.}
|
44
|
+
task :gemspec do
|
45
|
+
gemspec.validate
|
46
|
+
File.open("#{gemspec.name}.gemspec", 'w'){|f| f.write gemspec.to_ruby }
|
47
|
+
end
|
48
|
+
|
49
|
+
desc %{Release the gem to RubyGems.org}
|
50
|
+
task :release => :gem do
|
51
|
+
"gem push pkg/#{gemspec.name}-#{gemspec.version}.gem"
|
52
|
+
end
|
53
|
+
|
54
|
+
require 'rspec/core'
|
55
|
+
require 'rspec/core/rake_task'
|
56
|
+
|
57
|
+
desc "Run Unit Specs Only"
|
58
|
+
Rspec::Core::RakeTask.new(:spec) do |spec|
|
59
|
+
spec.pattern = "spec/riak/**/*_spec.rb"
|
60
|
+
end
|
61
|
+
|
62
|
+
namespace :spec do
|
63
|
+
desc "Run Integration Specs Only"
|
64
|
+
Rspec::Core::RakeTask.new(:integration) do |spec|
|
65
|
+
spec.pattern = "spec/integration/**/*_spec.rb"
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "Run All Specs"
|
69
|
+
Rspec::Core::RakeTask.new(:all) do |spec|
|
70
|
+
spec.pattern = "spec/**/*_spec.rb"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
task :default => :spec
|
data/lib/riak.rb
ADDED
@@ -0,0 +1,49 @@
|
|
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
|
+
$KCODE = "UTF8" if RUBY_VERSION < "1.9"
|
15
|
+
|
16
|
+
require 'active_support/all'
|
17
|
+
require 'active_support/json'
|
18
|
+
require 'base64'
|
19
|
+
require 'uri'
|
20
|
+
require 'net/http'
|
21
|
+
require 'yaml'
|
22
|
+
require 'riak/i18n'
|
23
|
+
|
24
|
+
# The Riak module contains all aspects of the HTTP client interface
|
25
|
+
# to Riak.
|
26
|
+
module Riak
|
27
|
+
# Domain objects
|
28
|
+
autoload :Bucket, "riak/bucket"
|
29
|
+
autoload :Client, "riak/client"
|
30
|
+
autoload :Link, "riak/link"
|
31
|
+
autoload :WalkSpec, "riak/walk_spec"
|
32
|
+
autoload :RObject, "riak/robject"
|
33
|
+
autoload :MapReduce, "riak/map_reduce"
|
34
|
+
|
35
|
+
# Cache store
|
36
|
+
autoload :CacheStore, "riak/cache_store"
|
37
|
+
|
38
|
+
# Exceptions
|
39
|
+
autoload :FailedRequest, "riak/failed_request"
|
40
|
+
autoload :InvalidResponse, "riak/invalid_response"
|
41
|
+
autoload :MapReduceError, "riak/map_reduce_error"
|
42
|
+
|
43
|
+
module Util
|
44
|
+
autoload :Escape, "riak/util/escape"
|
45
|
+
autoload :Headers, "riak/util/headers"
|
46
|
+
autoload :Multipart, "riak/util/multipart"
|
47
|
+
autoload :Translation, "riak/util/translation"
|
48
|
+
end
|
49
|
+
end
|
data/lib/riak/bucket.rb
ADDED
@@ -0,0 +1,176 @@
|
|
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
|
+
# Represents and encapsulates operations on a Riak bucket. You may retrieve a bucket
|
18
|
+
# using {Client#bucket}, or create it manually and retrieve its meta-information later.
|
19
|
+
class Bucket
|
20
|
+
include Util::Translation
|
21
|
+
include Util::Escape
|
22
|
+
|
23
|
+
# @return [Riak::Client] the associated client
|
24
|
+
attr_reader :client
|
25
|
+
|
26
|
+
# @return [String] the bucket name
|
27
|
+
attr_reader :name
|
28
|
+
|
29
|
+
# @return [Hash] Internal Riak bucket properties.
|
30
|
+
attr_reader :props
|
31
|
+
alias_attribute :properties, :props
|
32
|
+
|
33
|
+
# Create a Riak bucket manually.
|
34
|
+
# @param [Client] client the {Riak::Client} for this bucket
|
35
|
+
# @param [String] name the name of the bucket
|
36
|
+
def initialize(client, name)
|
37
|
+
raise ArgumentError, t("client_type", :client => client.inspect) unless Client === client
|
38
|
+
raise ArgumentError, t("string_type", :string => name.inspect) unless String === name
|
39
|
+
@client, @name, @props = client, name, {}
|
40
|
+
end
|
41
|
+
|
42
|
+
# Load information for the bucket from a response given by the {Riak::Client::HTTPBackend}.
|
43
|
+
# Used mostly internally - use {Riak::Client#bucket} to get a {Bucket} instance.
|
44
|
+
# @param [Hash] response a response from {Riak::Client::HTTPBackend}
|
45
|
+
# @return [Bucket] self
|
46
|
+
# @see Client#bucket
|
47
|
+
def load(response={})
|
48
|
+
unless response.try(:[], :headers).try(:[],'content-type').try(:first) =~ /json$/
|
49
|
+
raise Riak::InvalidResponse.new({"content-type" => ["application/json"]}, response[:headers], t("loading_bucket", :name => name))
|
50
|
+
end
|
51
|
+
payload = ActiveSupport::JSON.decode(response[:body])
|
52
|
+
@keys = payload['keys'].map {|k| URI.unescape(k) } if payload['keys']
|
53
|
+
@props = payload['props'] if payload['props']
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Accesses or retrieves a list of keys in this bucket.
|
58
|
+
# If a block is given, keys will be streamed through
|
59
|
+
# the block (useful for large buckets). When streaming,
|
60
|
+
# results of the operation will not be retained in the local Bucket object.
|
61
|
+
# @param [Hash] options extra options
|
62
|
+
# @yield [Array<String>] a list of keys from the current chunk
|
63
|
+
# @option options [Boolean] :reload (false) If present, will force reloading of the bucket's keys from Riak
|
64
|
+
# @return [Array<String>] Keys in this bucket
|
65
|
+
def keys(options={})
|
66
|
+
if block_given?
|
67
|
+
@client.http.get(200, @client.prefix, escape(name), {:props => false, :keys => 'stream'}, {}) do |chunk|
|
68
|
+
obj = ActiveSupport::JSON.decode(chunk) rescue {}
|
69
|
+
yield obj['keys'].map {|k| URI.unescape(k) } if obj['keys']
|
70
|
+
end
|
71
|
+
elsif @keys.nil? || options[:reload]
|
72
|
+
response = @client.http.get(200, @client.prefix, escape(name), {:props => false}, {})
|
73
|
+
load(response)
|
74
|
+
end
|
75
|
+
@keys
|
76
|
+
end
|
77
|
+
|
78
|
+
# Sets internal properties on the bucket
|
79
|
+
# Note: this results in a request to the Riak server!
|
80
|
+
# @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
|
83
|
+
def props=(properties)
|
84
|
+
raise ArgumentError, t("hash_type", :hash => properties.inspect) unless Hash === properties
|
85
|
+
body = {'props' => properties}.to_json
|
86
|
+
@client.http.put(204, @client.prefix, escape(name), body, {"Content-Type" => "application/json"})
|
87
|
+
@props = properties
|
88
|
+
end
|
89
|
+
|
90
|
+
# Retrieve an object from within the bucket.
|
91
|
+
# @param [String] key the key of the object to retrieve
|
92
|
+
# @param [Hash] options query parameters for the request
|
93
|
+
# @option options [Fixnum] :r - the read quorum for the request - how many nodes should concur on the read
|
94
|
+
# @return [Riak::RObject] the object
|
95
|
+
# @raise [FailedRequest] if the object is not found or some other error occurs
|
96
|
+
def get(key, options={})
|
97
|
+
code = allow_mult ? [200,300] : 200
|
98
|
+
response = @client.http.get(code, @client.prefix, escape(name), escape(key), options, {})
|
99
|
+
RObject.new(self, key).load(response)
|
100
|
+
end
|
101
|
+
alias :[] :get
|
102
|
+
|
103
|
+
# Create a new blank object
|
104
|
+
# @param [String] key the key of the new object
|
105
|
+
# @return [RObject] the new, unsaved object
|
106
|
+
def new(key=nil)
|
107
|
+
RObject.new(self, key).tap do |obj|
|
108
|
+
obj.content_type = "application/json"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Fetches an object if it exists, otherwise creates a new one with the given key
|
113
|
+
# @param [String] key the key to fetch or create
|
114
|
+
# @return [RObject] the new or existing object
|
115
|
+
def get_or_new(key, options={})
|
116
|
+
begin
|
117
|
+
get(key, options)
|
118
|
+
rescue Riak::FailedRequest => fr
|
119
|
+
if fr.code.to_i == 404
|
120
|
+
new(key)
|
121
|
+
else
|
122
|
+
raise fr
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Checks whether an object exists in Riak.
|
128
|
+
# @param [String] key the key to check
|
129
|
+
# @param [Hash] options quorum options
|
130
|
+
# @option options [Fixnum] :r - the read quorum value for the request (R)
|
131
|
+
# @return [true, false] whether the key exists in this bucket
|
132
|
+
def exists?(key, options={})
|
133
|
+
result = client.http.head([200,404], client.prefix, escape(name), escape(key), options, {})
|
134
|
+
result[:code] == 200
|
135
|
+
end
|
136
|
+
alias :exist? :exists?
|
137
|
+
|
138
|
+
# Deletes a key from the bucket
|
139
|
+
# @param [String] key the key to delete
|
140
|
+
# @param [Hash] options quorum options
|
141
|
+
# @option options [Fixnum] :rw - the read/write quorum for the delete
|
142
|
+
def delete(key, options={})
|
143
|
+
client.http.delete([204,404], client.prefix, escape(name), escape(key), options, {})
|
144
|
+
end
|
145
|
+
|
146
|
+
# @return [true, false] whether the bucket allows divergent siblings
|
147
|
+
def allow_mult
|
148
|
+
props['allow_mult']
|
149
|
+
end
|
150
|
+
|
151
|
+
# Set the allow_mult property. *NOTE* This will result in a PUT request to Riak.
|
152
|
+
# @param [true, false] value whether the bucket should allow siblings
|
153
|
+
def allow_mult=(value)
|
154
|
+
self.props = {'allow_mult' => value}
|
155
|
+
value
|
156
|
+
end
|
157
|
+
|
158
|
+
# @return [Fixnum] the N value, or number of replicas for this bucket
|
159
|
+
def n_value
|
160
|
+
props['n_val']
|
161
|
+
end
|
162
|
+
|
163
|
+
# Set the N value (number of replicas). *NOTE* This will result in a PUT request to Riak.
|
164
|
+
# Setting this value after the bucket has objects stored in it may have unpredictable results.
|
165
|
+
# @param [Fixnum] value the number of replicas the bucket should keep of each object
|
166
|
+
def n_value=(value)
|
167
|
+
self.props = {'n_val' => value}
|
168
|
+
value
|
169
|
+
end
|
170
|
+
|
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)}>"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,82 @@
|
|
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
|
+
module Riak
|
16
|
+
class CacheStore < ActiveSupport::Cache::Store
|
17
|
+
attr_accessor :client
|
18
|
+
|
19
|
+
def initialize(options = {})
|
20
|
+
@bucket_name = options.delete(:bucket) || '_cache'
|
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
|
26
|
+
@client = Riak::Client.new(options)
|
27
|
+
end
|
28
|
+
|
29
|
+
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
|
45
|
+
end
|
46
|
+
|
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
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def exist?(key)
|
59
|
+
super do
|
60
|
+
bucket.exists?(key, :r => @r)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
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
|
72
|
+
end
|
73
|
+
|
74
|
+
def delete(key, options={})
|
75
|
+
super do
|
76
|
+
bucket.delete(key, :rw => @rw)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
ActiveSupport::Cache::RiakStore = Riak::CacheStore unless defined?(ActiveSupport::Cache::RiakStore)
|
data/lib/riak/client.rb
ADDED
@@ -0,0 +1,139 @@
|
|
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
|
+
# A client connection to Riak.
|
18
|
+
class Client
|
19
|
+
include Util::Translation
|
20
|
+
include Util::Escape
|
21
|
+
|
22
|
+
autoload :HTTPBackend, "riak/client/http_backend"
|
23
|
+
autoload :NetHTTPBackend, "riak/client/net_http_backend"
|
24
|
+
autoload :CurbBackend, "riak/client/curb_backend"
|
25
|
+
|
26
|
+
# When using integer client IDs, the exclusive upper-bound of valid values.
|
27
|
+
MAX_CLIENT_ID = 4294967296
|
28
|
+
|
29
|
+
# Regexp for validating hostnames, lifted from uri.rb in Ruby 1.8.6
|
30
|
+
HOST_REGEX = /^(?:(?:(?:[a-zA-Z\d](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-fA-F\d]{1,4}:)*[a-fA-F\d]{1,4})?::(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)\])$/n
|
31
|
+
|
32
|
+
# @return [String] The host or IP address for the Riak endpoint
|
33
|
+
attr_reader :host
|
34
|
+
|
35
|
+
# @return [Fixnum] The port of the Riak HTTP endpoint
|
36
|
+
attr_reader :port
|
37
|
+
|
38
|
+
# @return [String] The internal client ID used by Riak to route responses
|
39
|
+
attr_reader :client_id
|
40
|
+
|
41
|
+
# @return [String] The URL path prefix to the "raw" HTTP endpoint
|
42
|
+
attr_accessor :prefix
|
43
|
+
|
44
|
+
# @return [String] The URL path to the map-reduce HTTP endpoint
|
45
|
+
attr_accessor :mapred
|
46
|
+
|
47
|
+
# Creates a client connection to Riak
|
48
|
+
# @param [Hash] options configuration options for the client
|
49
|
+
# @option options [String] :host ('127.0.0.1') The host or IP address for the Riak endpoint
|
50
|
+
# @option options [Fixnum] :port (8098) The port of the Riak HTTP endpoint
|
51
|
+
# @option options [String] :prefix ('/riak/') The URL path prefix to the main HTTP endpoint
|
52
|
+
# @option options [String] :mapred ('/mapred') The path to the map-reduce HTTP endpoint
|
53
|
+
# @option options [Fixnum, String] :client_id (rand(MAX_CLIENT_ID)) The internal client ID used by Riak to route responses
|
54
|
+
# @raise [ArgumentError] raised if any options are invalid
|
55
|
+
def initialize(options={})
|
56
|
+
options.assert_valid_keys(:host, :port, :prefix, :client_id, :mapred)
|
57
|
+
self.host = options[:host] || "127.0.0.1"
|
58
|
+
self.port = options[:port] || 8098
|
59
|
+
self.client_id = options[:client_id] || make_client_id
|
60
|
+
self.prefix = options[:prefix] || "/riak/"
|
61
|
+
self.mapred = options[:mapred] || "/mapred"
|
62
|
+
raise ArgumentError, t("missing_host_and_port") unless @host && @port
|
63
|
+
end
|
64
|
+
|
65
|
+
# Set the client ID for this client. Must be a string or Fixnum value 0 =< value < MAX_CLIENT_ID.
|
66
|
+
# @param [String, Fixnum] value The internal client ID used by Riak to route responses
|
67
|
+
# @raise [ArgumentError] when an invalid client ID is given
|
68
|
+
# @return [String] the assigned client ID
|
69
|
+
def client_id=(value)
|
70
|
+
@client_id = case value
|
71
|
+
when 0...MAX_CLIENT_ID
|
72
|
+
b64encode(value)
|
73
|
+
when String
|
74
|
+
value
|
75
|
+
else
|
76
|
+
raise ArgumentError, t("invalid_client_id", :max_id => MAX_CLIENT_ID)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Set the hostname of the Riak endpoint. Must be an IPv4, IPv6, or valid hostname
|
81
|
+
# @param [String] value The host or IP address for the Riak endpoint
|
82
|
+
# @raise [ArgumentError] if an invalid hostname is given
|
83
|
+
# @return [String] the assigned hostname
|
84
|
+
def host=(value)
|
85
|
+
raise ArgumentError, t("hostname_invalid") unless String === value && value.present? && value =~ HOST_REGEX
|
86
|
+
@host = value
|
87
|
+
end
|
88
|
+
|
89
|
+
# Set the port number of the Riak endpoint. This must be an integer between 0 and 65535.
|
90
|
+
# @param [Fixnum] value The port number of the Riak endpoint
|
91
|
+
# @raise [ArgumentError] if an invalid port number is given
|
92
|
+
# @return [Fixnum] the assigned port number
|
93
|
+
def port=(value)
|
94
|
+
raise ArgumentError, t("port_invalid") unless (0..65535).include?(value)
|
95
|
+
@port = value
|
96
|
+
end
|
97
|
+
|
98
|
+
# Automatically detects and returns an appropriate HTTP backend.
|
99
|
+
# The HTTP backend is used internally by the Riak client, but can also
|
100
|
+
# be used to access the server directly.
|
101
|
+
# @return [HTTPBackend] the HTTP backend for this client
|
102
|
+
def http
|
103
|
+
@http ||= begin
|
104
|
+
require 'curb'
|
105
|
+
CurbBackend.new(self)
|
106
|
+
rescue LoadError, NameError
|
107
|
+
warn t("install_curb")
|
108
|
+
NetHTTPBackend.new(self)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Retrieves a bucket from Riak.
|
113
|
+
# @param [String] bucket the bucket to retrieve
|
114
|
+
# @param [Hash] options options for retrieving the bucket
|
115
|
+
# @option options [Boolean] :keys (true) whether to retrieve the bucket keys
|
116
|
+
# @option options [Boolean] :props (true) whether to retreive the bucket properties
|
117
|
+
# @return [Bucket] the requested bucket
|
118
|
+
def bucket(name, options={})
|
119
|
+
options.assert_valid_keys(:keys, :props)
|
120
|
+
response = http.get(200, prefix, escape(name), options, {})
|
121
|
+
Bucket.new(self, name).load(response)
|
122
|
+
end
|
123
|
+
alias :[] :bucket
|
124
|
+
|
125
|
+
# @return [String] A representation suitable for IRB and debugging output.
|
126
|
+
def inspect
|
127
|
+
"#<Riak::Client #{http.root_uri.to_s}>"
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
def make_client_id
|
132
|
+
b64encode(rand(MAX_CLIENT_ID))
|
133
|
+
end
|
134
|
+
|
135
|
+
def b64encode(n)
|
136
|
+
Base64.encode64([n].pack("N")).chomp
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|