better-riak-client 1.0.5
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/LICENSE +16 -0
- data/README.markdown +198 -0
- data/RELEASE_NOTES.md +211 -0
- data/better-riak-client.gemspec +61 -0
- data/erl_src/riak_kv_test014_backend.beam +0 -0
- data/erl_src/riak_kv_test014_backend.erl +189 -0
- data/erl_src/riak_kv_test_backend.beam +0 -0
- data/erl_src/riak_kv_test_backend.erl +697 -0
- data/erl_src/riak_search_test_backend.beam +0 -0
- data/erl_src/riak_search_test_backend.erl +175 -0
- data/lib/riak/bucket.rb +221 -0
- data/lib/riak/client/beefcake/messages.rb +213 -0
- data/lib/riak/client/beefcake/object_methods.rb +111 -0
- data/lib/riak/client/beefcake_protobuffs_backend.rb +226 -0
- data/lib/riak/client/decaying.rb +36 -0
- data/lib/riak/client/excon_backend.rb +162 -0
- data/lib/riak/client/feature_detection.rb +88 -0
- data/lib/riak/client/http_backend/configuration.rb +211 -0
- data/lib/riak/client/http_backend/key_streamer.rb +43 -0
- data/lib/riak/client/http_backend/object_methods.rb +106 -0
- data/lib/riak/client/http_backend/request_headers.rb +34 -0
- data/lib/riak/client/http_backend/transport_methods.rb +201 -0
- data/lib/riak/client/http_backend.rb +340 -0
- data/lib/riak/client/net_http_backend.rb +82 -0
- data/lib/riak/client/node.rb +115 -0
- data/lib/riak/client/protobuffs_backend.rb +173 -0
- data/lib/riak/client/search.rb +91 -0
- data/lib/riak/client.rb +540 -0
- data/lib/riak/cluster.rb +151 -0
- data/lib/riak/core_ext/blank.rb +53 -0
- data/lib/riak/core_ext/deep_dup.rb +13 -0
- data/lib/riak/core_ext/extract_options.rb +7 -0
- data/lib/riak/core_ext/json.rb +15 -0
- data/lib/riak/core_ext/slice.rb +18 -0
- data/lib/riak/core_ext/stringify_keys.rb +10 -0
- data/lib/riak/core_ext/symbolize_keys.rb +10 -0
- data/lib/riak/core_ext/to_param.rb +31 -0
- data/lib/riak/core_ext.rb +7 -0
- data/lib/riak/encoding.rb +6 -0
- data/lib/riak/failed_request.rb +81 -0
- data/lib/riak/i18n.rb +5 -0
- data/lib/riak/json.rb +52 -0
- data/lib/riak/link.rb +94 -0
- data/lib/riak/locale/en.yml +53 -0
- data/lib/riak/locale/fr.yml +52 -0
- data/lib/riak/map_reduce/filter_builder.rb +103 -0
- data/lib/riak/map_reduce/phase.rb +98 -0
- data/lib/riak/map_reduce.rb +225 -0
- data/lib/riak/map_reduce_error.rb +7 -0
- data/lib/riak/node/configuration.rb +293 -0
- data/lib/riak/node/console.rb +133 -0
- data/lib/riak/node/control.rb +207 -0
- data/lib/riak/node/defaults.rb +83 -0
- data/lib/riak/node/generation.rb +106 -0
- data/lib/riak/node/log.rb +34 -0
- data/lib/riak/node/version.rb +43 -0
- data/lib/riak/node.rb +38 -0
- data/lib/riak/robject.rb +318 -0
- data/lib/riak/search.rb +3 -0
- data/lib/riak/serializers.rb +74 -0
- data/lib/riak/stamp.rb +77 -0
- data/lib/riak/test_server.rb +89 -0
- data/lib/riak/util/escape.rb +76 -0
- data/lib/riak/util/headers.rb +53 -0
- data/lib/riak/util/multipart/stream_parser.rb +62 -0
- data/lib/riak/util/multipart.rb +52 -0
- data/lib/riak/util/tcp_socket_extensions.rb +58 -0
- data/lib/riak/util/translation.rb +19 -0
- data/lib/riak/version.rb +3 -0
- data/lib/riak/walk_spec.rb +105 -0
- data/lib/riak.rb +21 -0
- metadata +348 -0
data/lib/riak/cluster.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'riak/node'
|
3
|
+
require 'riak/util/translation'
|
4
|
+
|
5
|
+
module Riak
|
6
|
+
# Generates and controls a cluster of {Riak::Node} instances for use
|
7
|
+
# in development or testing on a single machine.
|
8
|
+
class Cluster
|
9
|
+
include Util::Translation
|
10
|
+
# @return [Array<Node>] the member Nodes of this cluster
|
11
|
+
attr_reader :nodes
|
12
|
+
|
13
|
+
# @return [Hash] the cluster configuration
|
14
|
+
attr_reader :configuration
|
15
|
+
|
16
|
+
# @return [Pathname] the root directory of the cluster
|
17
|
+
attr_reader :root
|
18
|
+
|
19
|
+
# Creates a {Cluster} of {Node}s.
|
20
|
+
# @param [Hash] config the configuration for the cluster
|
21
|
+
# @option config [Fixnum] :count the number of nodes to create
|
22
|
+
# @option config [String] :source path to the Riak bin/ directory.
|
23
|
+
# See {Node#source}.
|
24
|
+
# @option config [String] :root path to where the nodes will be
|
25
|
+
# generated.
|
26
|
+
# @option config [Fixnum] :min_port the base port number from
|
27
|
+
# which nodes will claim IP ports for HTTP, PB, handoff.
|
28
|
+
def initialize(config={})
|
29
|
+
raise ArgumentError, t('source_and_root_required') unless config[:source] && config[:root]
|
30
|
+
@configuration = config
|
31
|
+
@count = config.delete(:count) || 4
|
32
|
+
@min_port = config.delete(:min_port) || 9000
|
33
|
+
@root = Pathname.new(config.delete(:root))
|
34
|
+
@nodes = []
|
35
|
+
cookie = "#{rand(100000).to_s}_#{rand(1000000).to_s}"
|
36
|
+
@count.times do |i|
|
37
|
+
nodes << Riak::Node.new(config.merge(:min_port => @min_port + (i * 3),
|
38
|
+
:root => @root + (i+1).to_s,
|
39
|
+
:cookie => cookie))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [true,false] whether the cluster has been created
|
44
|
+
def exist?
|
45
|
+
root.directory? && nodes.all? {|n| n.exist? }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Generates all nodes in the cluster.
|
49
|
+
def create
|
50
|
+
unless exist?
|
51
|
+
root.mkpath unless root.exist?
|
52
|
+
nodes.each {|n| n.create }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Removes all nodes in the cluster and the root, and freezes the
|
57
|
+
# object.
|
58
|
+
def destroy
|
59
|
+
nodes.each {|n| n.destroy }
|
60
|
+
root.rmtree if root.exist?
|
61
|
+
freeze
|
62
|
+
end
|
63
|
+
|
64
|
+
# Removes and recreates the cluster.
|
65
|
+
def recreate
|
66
|
+
stop unless stopped?
|
67
|
+
root.rmtree if root.exist?
|
68
|
+
create
|
69
|
+
end
|
70
|
+
|
71
|
+
# Drops all data from the cluster without destroying the nodes.
|
72
|
+
def drop
|
73
|
+
nodes.each {|n| n.drop }
|
74
|
+
end
|
75
|
+
|
76
|
+
# Starts all nodes in the cluster.
|
77
|
+
def start
|
78
|
+
nodes.each {|n| n.start }
|
79
|
+
end
|
80
|
+
|
81
|
+
# Stops all nodes in the cluster.
|
82
|
+
def stop
|
83
|
+
nodes.each {|n| n.stop }
|
84
|
+
end
|
85
|
+
|
86
|
+
# Restarts all nodes in the cluster (without exiting the Erlang
|
87
|
+
# runtime)
|
88
|
+
def restart
|
89
|
+
nodes.each {|n| n.restart }
|
90
|
+
end
|
91
|
+
|
92
|
+
# Reboots all nodes in the cluster
|
93
|
+
def reboot
|
94
|
+
nodes.each {|n| n.reboot }
|
95
|
+
end
|
96
|
+
|
97
|
+
# Forces the cluster nodes to restart/reload their JavaScript VMs,
|
98
|
+
# effectively reloading any user-provided code.
|
99
|
+
def js_reload
|
100
|
+
nodes.each {|n| n.js_reload }
|
101
|
+
end
|
102
|
+
|
103
|
+
# Attaches to the console on all nodes, returning a list of
|
104
|
+
# {Riak::Node::Console} objects.
|
105
|
+
# @return [Array<Riak::Node::Console>] consoles for all running
|
106
|
+
# nodes, with nil for nodes that aren't running or otherwise
|
107
|
+
# fail to connect
|
108
|
+
def attach
|
109
|
+
nodes.map do |n|
|
110
|
+
begin
|
111
|
+
n.attach
|
112
|
+
rescue ArgumentError, SystemCallError
|
113
|
+
nil
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Executes the given block on each node against the node's
|
119
|
+
# console. You could use this to send Erlang commands to all nodes
|
120
|
+
# in the cluster.
|
121
|
+
# @yield [console] A block of commands to be run against the
|
122
|
+
# console
|
123
|
+
# @yieldparam [Riak::Node::Console] console A console manager for
|
124
|
+
# sending commands to the current node in the iteration
|
125
|
+
def with_console(&block)
|
126
|
+
nodes.each do |n|
|
127
|
+
n.with_console(&block)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Is the cluster started?
|
132
|
+
def started?
|
133
|
+
nodes.all? {|n| n.started? }
|
134
|
+
end
|
135
|
+
|
136
|
+
# Is the cluster stopped?
|
137
|
+
def stopped?
|
138
|
+
nodes.all? {|n| n.stopped? }
|
139
|
+
end
|
140
|
+
|
141
|
+
# Joins the nodes together into a cluster.
|
142
|
+
# @note This method relies on cluster membership changes present
|
143
|
+
# in the 1.0 series of Riak, and is NOT safe on 0.14 and
|
144
|
+
# earlier.
|
145
|
+
def join
|
146
|
+
claimant = nodes.first.name # Not really the claimant, just a
|
147
|
+
# node to join to
|
148
|
+
nodes[1..-1].each {|n| n.join(claimant) unless n.peers.include?(claimant) }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
unless Object.new.respond_to? :blank?
|
4
|
+
class Object
|
5
|
+
def blank?
|
6
|
+
false
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class NilClass
|
11
|
+
def blank?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class FalseClass
|
17
|
+
def blank?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class TrueClass
|
23
|
+
def blank?
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Set
|
29
|
+
alias :blank? :empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
class String
|
33
|
+
def blank?
|
34
|
+
self !~ /[^\s]/
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Array
|
39
|
+
alias :blank? :empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
class Hash
|
43
|
+
alias :blank? :empty?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
unless Object.new.respond_to? :present?
|
48
|
+
class Object
|
49
|
+
def present?
|
50
|
+
!blank?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
unless {}.respond_to?(:deep_dup)
|
2
|
+
class Hash
|
3
|
+
# Returns a deep copy of hash.
|
4
|
+
def deep_dup
|
5
|
+
duplicate = self.dup
|
6
|
+
duplicate.each_pair do |k,v|
|
7
|
+
tv = duplicate[k]
|
8
|
+
duplicate[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_dup : v
|
9
|
+
end
|
10
|
+
duplicate
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
unless {}.respond_to? :slice
|
2
|
+
class Hash
|
3
|
+
def slice(*keys)
|
4
|
+
allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
|
5
|
+
hash = {}
|
6
|
+
allowed.each { |k| hash[k] = self[k] if has_key?(k) }
|
7
|
+
hash
|
8
|
+
end
|
9
|
+
|
10
|
+
def slice!(*keys)
|
11
|
+
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
|
12
|
+
omit = slice(*self.keys - keys)
|
13
|
+
hash = slice(*keys)
|
14
|
+
replace(hash)
|
15
|
+
omit
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
unless Object.new.respond_to? :to_query and Object.new.respond_to? :to_param
|
2
|
+
class Object
|
3
|
+
def to_param
|
4
|
+
to_s
|
5
|
+
end
|
6
|
+
|
7
|
+
def to_query(key)
|
8
|
+
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
|
9
|
+
"#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Array
|
14
|
+
def to_param
|
15
|
+
map(&:to_param).join('/')
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_query(key)
|
19
|
+
prefix = "#{key}[]"
|
20
|
+
collect { |value| value.to_query(prefix) }.join '&'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Hash
|
25
|
+
def to_param(namespace = nil)
|
26
|
+
collect do |key, value|
|
27
|
+
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
|
28
|
+
end.sort * '&'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
|
2
|
+
require 'riak/util/translation'
|
3
|
+
require 'riak/json'
|
4
|
+
|
5
|
+
module Riak
|
6
|
+
# Exception raised when receiving an unexpected client response from
|
7
|
+
# Riak.
|
8
|
+
class FailedRequest < StandardError
|
9
|
+
include Util::Translation
|
10
|
+
|
11
|
+
def initialize(message)
|
12
|
+
super(message || t('failed_request'))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Exception raised when the expected HTTP response code from Riak
|
17
|
+
# fails to match the actual response code.
|
18
|
+
class HTTPFailedRequest < FailedRequest
|
19
|
+
# @return [Symbol] the HTTP method, one of :head, :get, :post, :put, :delete
|
20
|
+
attr_reader :method
|
21
|
+
# @return [Fixnum] the expected response code
|
22
|
+
attr_reader :expected
|
23
|
+
# @return [Fixnum] the received response code
|
24
|
+
attr_reader :code
|
25
|
+
# @return [Hash] the response headers
|
26
|
+
attr_reader :headers
|
27
|
+
# @return [String] the response body, if present
|
28
|
+
attr_reader :body
|
29
|
+
|
30
|
+
def initialize(method, expected_code, received_code, headers, body)
|
31
|
+
@method, @expected, @code, @headers, @body = method, expected_code, received_code, headers, body
|
32
|
+
super t("http_failed_request", :expected => @expected.inspect, :code => @code, :body => @body)
|
33
|
+
end
|
34
|
+
|
35
|
+
def is_json?
|
36
|
+
headers['content-type'].include?('application/json')
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [true,false] whether the error represents a "not found" response
|
40
|
+
def not_found?
|
41
|
+
@code.to_i == 404
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [true,false] whether the error represents an internal
|
45
|
+
# server error
|
46
|
+
def server_error?
|
47
|
+
@code.to_i == 500
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Exception raised when receiving an unexpected Protocol Buffers response from Riak
|
52
|
+
class ProtobuffsFailedRequest < FailedRequest
|
53
|
+
def initialize(code, message)
|
54
|
+
super t('protobuffs_failed_request', :code => code, :body => message)
|
55
|
+
@original_message = message
|
56
|
+
@not_found = code == :not_found
|
57
|
+
@server_error = code == :server_error
|
58
|
+
end
|
59
|
+
|
60
|
+
# @return [true, false] whether the error response is in JSON
|
61
|
+
def is_json?
|
62
|
+
begin
|
63
|
+
JSON.parse(original_message)
|
64
|
+
true
|
65
|
+
rescue
|
66
|
+
false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [true,false] whether the error represents a "not found" response
|
71
|
+
def not_found?
|
72
|
+
@not_found
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [true,false] whether the error represents an internal
|
76
|
+
# server error
|
77
|
+
def server_error?
|
78
|
+
@server_error
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/riak/i18n.rb
ADDED
data/lib/riak/json.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
if MultiJson.respond_to?(:adapter)
|
3
|
+
MultiJson.adapter
|
4
|
+
else
|
5
|
+
MultiJson.engine # Force loading of an engine
|
6
|
+
end
|
7
|
+
require 'riak/core_ext/json'
|
8
|
+
|
9
|
+
module Riak
|
10
|
+
class << self
|
11
|
+
# Options that will be passed to the JSON parser and encoder.
|
12
|
+
# Defaults to {:max_nesting => 20}
|
13
|
+
attr_accessor :json_options
|
14
|
+
end
|
15
|
+
self.json_options = {:max_nesting => 20}
|
16
|
+
|
17
|
+
# JSON module for internal use inside riak-client
|
18
|
+
module JSON
|
19
|
+
class << self
|
20
|
+
if MultiJson.respond_to?(:dump) # MultiJson 1.2 or later
|
21
|
+
# Parse a JSON string
|
22
|
+
# @param [String] str a JSON payload
|
23
|
+
# @return [Array,Hash] a Ruby object decoded from the JSON payload
|
24
|
+
def parse(str)
|
25
|
+
MultiJson.load(str, Riak.json_options)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Generate a JSON string
|
29
|
+
# @param [Array, Hash] obj an object to JSON-encode
|
30
|
+
# @return [String] a JSON payload
|
31
|
+
def encode(obj)
|
32
|
+
MultiJson.dump(obj)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
# Parse a JSON string
|
36
|
+
# @param [String] str a JSON payload
|
37
|
+
# @return [Array,Hash] a Ruby object decoded from the JSON payload
|
38
|
+
def parse(str)
|
39
|
+
MultiJson.decode(str, Riak.json_options)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Generate a JSON string
|
43
|
+
# @param [Array, Hash] obj an object to JSON-encode
|
44
|
+
# @return [String] a JSON payload
|
45
|
+
def encode(obj)
|
46
|
+
MultiJson.encode(obj)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
alias :dump :encode
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|