riak-client 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|