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.
Files changed (72) hide show
  1. data/LICENSE +16 -0
  2. data/README.markdown +198 -0
  3. data/RELEASE_NOTES.md +211 -0
  4. data/better-riak-client.gemspec +61 -0
  5. data/erl_src/riak_kv_test014_backend.beam +0 -0
  6. data/erl_src/riak_kv_test014_backend.erl +189 -0
  7. data/erl_src/riak_kv_test_backend.beam +0 -0
  8. data/erl_src/riak_kv_test_backend.erl +697 -0
  9. data/erl_src/riak_search_test_backend.beam +0 -0
  10. data/erl_src/riak_search_test_backend.erl +175 -0
  11. data/lib/riak/bucket.rb +221 -0
  12. data/lib/riak/client/beefcake/messages.rb +213 -0
  13. data/lib/riak/client/beefcake/object_methods.rb +111 -0
  14. data/lib/riak/client/beefcake_protobuffs_backend.rb +226 -0
  15. data/lib/riak/client/decaying.rb +36 -0
  16. data/lib/riak/client/excon_backend.rb +162 -0
  17. data/lib/riak/client/feature_detection.rb +88 -0
  18. data/lib/riak/client/http_backend/configuration.rb +211 -0
  19. data/lib/riak/client/http_backend/key_streamer.rb +43 -0
  20. data/lib/riak/client/http_backend/object_methods.rb +106 -0
  21. data/lib/riak/client/http_backend/request_headers.rb +34 -0
  22. data/lib/riak/client/http_backend/transport_methods.rb +201 -0
  23. data/lib/riak/client/http_backend.rb +340 -0
  24. data/lib/riak/client/net_http_backend.rb +82 -0
  25. data/lib/riak/client/node.rb +115 -0
  26. data/lib/riak/client/protobuffs_backend.rb +173 -0
  27. data/lib/riak/client/search.rb +91 -0
  28. data/lib/riak/client.rb +540 -0
  29. data/lib/riak/cluster.rb +151 -0
  30. data/lib/riak/core_ext/blank.rb +53 -0
  31. data/lib/riak/core_ext/deep_dup.rb +13 -0
  32. data/lib/riak/core_ext/extract_options.rb +7 -0
  33. data/lib/riak/core_ext/json.rb +15 -0
  34. data/lib/riak/core_ext/slice.rb +18 -0
  35. data/lib/riak/core_ext/stringify_keys.rb +10 -0
  36. data/lib/riak/core_ext/symbolize_keys.rb +10 -0
  37. data/lib/riak/core_ext/to_param.rb +31 -0
  38. data/lib/riak/core_ext.rb +7 -0
  39. data/lib/riak/encoding.rb +6 -0
  40. data/lib/riak/failed_request.rb +81 -0
  41. data/lib/riak/i18n.rb +5 -0
  42. data/lib/riak/json.rb +52 -0
  43. data/lib/riak/link.rb +94 -0
  44. data/lib/riak/locale/en.yml +53 -0
  45. data/lib/riak/locale/fr.yml +52 -0
  46. data/lib/riak/map_reduce/filter_builder.rb +103 -0
  47. data/lib/riak/map_reduce/phase.rb +98 -0
  48. data/lib/riak/map_reduce.rb +225 -0
  49. data/lib/riak/map_reduce_error.rb +7 -0
  50. data/lib/riak/node/configuration.rb +293 -0
  51. data/lib/riak/node/console.rb +133 -0
  52. data/lib/riak/node/control.rb +207 -0
  53. data/lib/riak/node/defaults.rb +83 -0
  54. data/lib/riak/node/generation.rb +106 -0
  55. data/lib/riak/node/log.rb +34 -0
  56. data/lib/riak/node/version.rb +43 -0
  57. data/lib/riak/node.rb +38 -0
  58. data/lib/riak/robject.rb +318 -0
  59. data/lib/riak/search.rb +3 -0
  60. data/lib/riak/serializers.rb +74 -0
  61. data/lib/riak/stamp.rb +77 -0
  62. data/lib/riak/test_server.rb +89 -0
  63. data/lib/riak/util/escape.rb +76 -0
  64. data/lib/riak/util/headers.rb +53 -0
  65. data/lib/riak/util/multipart/stream_parser.rb +62 -0
  66. data/lib/riak/util/multipart.rb +52 -0
  67. data/lib/riak/util/tcp_socket_extensions.rb +58 -0
  68. data/lib/riak/util/translation.rb +19 -0
  69. data/lib/riak/version.rb +3 -0
  70. data/lib/riak/walk_spec.rb +105 -0
  71. data/lib/riak.rb +21 -0
  72. metadata +348 -0
@@ -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,7 @@
1
+ unless Array.new.respond_to? :extract_options!
2
+ class Array
3
+ def extract_options!
4
+ last.is_a?(::Hash) ? pop : {}
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ unless Object.new.respond_to?(:to_json)
2
+ # @private
3
+ class Object
4
+ def to_json(*args)
5
+ Riak::JSON.encode(self)
6
+ end
7
+ end
8
+
9
+ # @private
10
+ class Symbol
11
+ def to_json(*args)
12
+ to_s.to_json(*args)
13
+ end
14
+ end
15
+ 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,10 @@
1
+ unless {}.respond_to? :stringify_keys
2
+ class Hash
3
+ def stringify_keys
4
+ inject({}) do |hash, pair|
5
+ hash[pair[0].to_s] = pair[1]
6
+ hash
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ unless {}.respond_to? :symbolize_keys
2
+ class Hash
3
+ def symbolize_keys
4
+ inject({}) do |hash, pair|
5
+ hash[pair[0].to_sym] = pair[1]
6
+ hash
7
+ end
8
+ end
9
+ end
10
+ 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,7 @@
1
+ require 'riak/core_ext/blank'
2
+ require 'riak/core_ext/extract_options'
3
+ require 'riak/core_ext/slice'
4
+ require 'riak/core_ext/stringify_keys'
5
+ require 'riak/core_ext/symbolize_keys'
6
+ require 'riak/core_ext/to_param'
7
+ require 'riak/core_ext/deep_dup'
@@ -0,0 +1,6 @@
1
+ if defined? Encoding
2
+ Encoding.default_internal = "UTF-8" if Encoding.default_internal.nil? ||
3
+ !Encoding.default_internal.ascii_compatible?
4
+ else
5
+ $KCODE = "U"
6
+ 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
@@ -0,0 +1,5 @@
1
+ require 'i18n'
2
+
3
+ Dir.glob(File.expand_path("../locale/*.yml", __FILE__)).each do |locale_file|
4
+ I18n.load_path << locale_file
5
+ end
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