seomoz-riak-client 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/Gemfile +27 -0
  2. data/Guardfile +14 -0
  3. data/Rakefile +76 -0
  4. data/erl_src/riak_kv_test_backend.beam +0 -0
  5. data/erl_src/riak_kv_test_backend.erl +174 -0
  6. data/erl_src/riak_search_test_backend.beam +0 -0
  7. data/erl_src/riak_search_test_backend.erl +175 -0
  8. data/lib/active_support/cache/riak_store.rb +2 -0
  9. data/lib/riak.rb +21 -0
  10. data/lib/riak/bucket.rb +215 -0
  11. data/lib/riak/cache_store.rb +84 -0
  12. data/lib/riak/client.rb +415 -0
  13. data/lib/riak/client/beefcake/messages.rb +147 -0
  14. data/lib/riak/client/beefcake/object_methods.rb +92 -0
  15. data/lib/riak/client/beefcake_protobuffs_backend.rb +176 -0
  16. data/lib/riak/client/excon_backend.rb +65 -0
  17. data/lib/riak/client/http_backend.rb +203 -0
  18. data/lib/riak/client/http_backend/configuration.rb +46 -0
  19. data/lib/riak/client/http_backend/key_streamer.rb +43 -0
  20. data/lib/riak/client/http_backend/object_methods.rb +94 -0
  21. data/lib/riak/client/http_backend/request_headers.rb +34 -0
  22. data/lib/riak/client/http_backend/transport_methods.rb +218 -0
  23. data/lib/riak/client/net_http_backend.rb +79 -0
  24. data/lib/riak/client/protobuffs_backend.rb +97 -0
  25. data/lib/riak/client/pump.rb +30 -0
  26. data/lib/riak/client/search.rb +94 -0
  27. data/lib/riak/core_ext.rb +6 -0
  28. data/lib/riak/core_ext/blank.rb +53 -0
  29. data/lib/riak/core_ext/extract_options.rb +7 -0
  30. data/lib/riak/core_ext/json.rb +15 -0
  31. data/lib/riak/core_ext/slice.rb +18 -0
  32. data/lib/riak/core_ext/stringify_keys.rb +10 -0
  33. data/lib/riak/core_ext/symbolize_keys.rb +10 -0
  34. data/lib/riak/core_ext/to_param.rb +31 -0
  35. data/lib/riak/encoding.rb +6 -0
  36. data/lib/riak/failed_request.rb +81 -0
  37. data/lib/riak/i18n.rb +3 -0
  38. data/lib/riak/json.rb +28 -0
  39. data/lib/riak/link.rb +85 -0
  40. data/lib/riak/locale/en.yml +48 -0
  41. data/lib/riak/map_reduce.rb +206 -0
  42. data/lib/riak/map_reduce/filter_builder.rb +94 -0
  43. data/lib/riak/map_reduce/phase.rb +98 -0
  44. data/lib/riak/map_reduce_error.rb +7 -0
  45. data/lib/riak/robject.rb +290 -0
  46. data/lib/riak/search.rb +3 -0
  47. data/lib/riak/serializers.rb +74 -0
  48. data/lib/riak/stamp.rb +77 -0
  49. data/lib/riak/test_server.rb +252 -0
  50. data/lib/riak/util/escape.rb +45 -0
  51. data/lib/riak/util/fiber1.8.rb +48 -0
  52. data/lib/riak/util/headers.rb +53 -0
  53. data/lib/riak/util/multipart.rb +52 -0
  54. data/lib/riak/util/multipart/stream_parser.rb +62 -0
  55. data/lib/riak/util/tcp_socket_extensions.rb +58 -0
  56. data/lib/riak/util/translation.rb +19 -0
  57. data/lib/riak/walk_spec.rb +105 -0
  58. data/riak-client.gemspec +55 -0
  59. data/seomoz-riak-client.gemspec +55 -0
  60. data/spec/fixtures/cat.jpg +0 -0
  61. data/spec/fixtures/multipart-blank.txt +7 -0
  62. data/spec/fixtures/multipart-mapreduce.txt +10 -0
  63. data/spec/fixtures/multipart-with-body.txt +16 -0
  64. data/spec/fixtures/server.cert.crt +15 -0
  65. data/spec/fixtures/server.cert.key +15 -0
  66. data/spec/fixtures/test.pem +1 -0
  67. data/spec/integration/riak/cache_store_spec.rb +154 -0
  68. data/spec/integration/riak/http_backends_spec.rb +58 -0
  69. data/spec/integration/riak/protobuffs_backends_spec.rb +32 -0
  70. data/spec/integration/riak/test_server_spec.rb +161 -0
  71. data/spec/riak/beefcake_protobuffs_backend_spec.rb +59 -0
  72. data/spec/riak/bucket_spec.rb +205 -0
  73. data/spec/riak/client_spec.rb +517 -0
  74. data/spec/riak/core_ext/to_param_spec.rb +15 -0
  75. data/spec/riak/escape_spec.rb +69 -0
  76. data/spec/riak/excon_backend_spec.rb +64 -0
  77. data/spec/riak/headers_spec.rb +38 -0
  78. data/spec/riak/http_backend/configuration_spec.rb +51 -0
  79. data/spec/riak/http_backend/object_methods_spec.rb +217 -0
  80. data/spec/riak/http_backend/transport_methods_spec.rb +117 -0
  81. data/spec/riak/http_backend_spec.rb +269 -0
  82. data/spec/riak/link_spec.rb +71 -0
  83. data/spec/riak/map_reduce/filter_builder_spec.rb +32 -0
  84. data/spec/riak/map_reduce/phase_spec.rb +136 -0
  85. data/spec/riak/map_reduce_spec.rb +310 -0
  86. data/spec/riak/multipart_spec.rb +23 -0
  87. data/spec/riak/net_http_backend_spec.rb +16 -0
  88. data/spec/riak/robject_spec.rb +427 -0
  89. data/spec/riak/search_spec.rb +178 -0
  90. data/spec/riak/serializers_spec.rb +93 -0
  91. data/spec/riak/stamp_spec.rb +54 -0
  92. data/spec/riak/stream_parser_spec.rb +53 -0
  93. data/spec/riak/walk_spec_spec.rb +195 -0
  94. data/spec/spec_helper.rb +39 -0
  95. data/spec/support/drb_mock_server.rb +39 -0
  96. data/spec/support/http_backend_implementation_examples.rb +266 -0
  97. data/spec/support/integration_setup.rb +10 -0
  98. data/spec/support/mock_server.rb +81 -0
  99. data/spec/support/mocks.rb +4 -0
  100. data/spec/support/test_server.yml.example +2 -0
  101. data/spec/support/unified_backend_examples.rb +255 -0
  102. metadata +271 -0
@@ -0,0 +1,147 @@
1
+ require 'beefcake'
2
+
3
+ module Riak
4
+ class Client
5
+ # @private
6
+ class BeefcakeProtobuffsBackend
7
+ # Embedded messages
8
+ class RpbPair
9
+ include Beefcake::Message
10
+ required :key, :bytes, 1
11
+ optional :value, :bytes, 2
12
+ end
13
+
14
+ class RpbBucketProps
15
+ include Beefcake::Message
16
+ optional :n_val, :uint32, 1
17
+ optional :allow_mult, :bool, 2
18
+ end
19
+
20
+ class RpbLink
21
+ include Beefcake::Message
22
+ optional :bucket, :bytes, 1
23
+ optional :key, :bytes, 2
24
+ optional :tag, :bytes, 3
25
+ end
26
+
27
+ class RpbContent
28
+ include Beefcake::Message
29
+ required :value, :bytes, 1
30
+ optional :content_type, :bytes, 2
31
+ optional :charset, :bytes, 3
32
+ optional :content_encoding, :bytes, 4
33
+ optional :vtag, :bytes, 5
34
+ repeated :links, RpbLink, 6
35
+ optional :last_mod, :uint32, 7
36
+ optional :last_mod_usecs, :uint32, 8
37
+ repeated :usermeta, RpbPair, 9
38
+ end
39
+
40
+ # Primary messages
41
+ class RpbErrorResp
42
+ include Beefcake::Message
43
+ required :errmsg, :bytes, 1
44
+ required :errcode, :uint32, 2
45
+ end
46
+
47
+ class RpbGetClientIdResp
48
+ include Beefcake::Message
49
+ required :client_id, :bytes, 1
50
+ end
51
+
52
+ class RpbSetClientIdReq
53
+ include Beefcake::Message
54
+ required :client_id, :bytes, 1
55
+ end
56
+
57
+ class RpbGetServerInfoResp
58
+ include Beefcake::Message
59
+ optional :node, :bytes, 1
60
+ optional :server_version, :bytes, 2
61
+ end
62
+
63
+ class RpbGetReq
64
+ include Beefcake::Message
65
+ required :bucket, :bytes, 1
66
+ required :key, :bytes, 2
67
+ optional :r, :uint32, 3
68
+ end
69
+
70
+ class RpbGetResp
71
+ include Beefcake::Message
72
+ repeated :content, RpbContent, 1
73
+ optional :vclock, :bytes, 2
74
+ end
75
+
76
+ class RpbPutReq
77
+ include Beefcake::Message
78
+ required :bucket, :bytes, 1
79
+ required :key, :bytes, 2
80
+ optional :vclock, :bytes, 3
81
+ required :content, RpbContent, 4
82
+ optional :w, :uint32, 5
83
+ optional :dw, :uint32, 6
84
+ optional :return_body, :bool, 7
85
+ end
86
+
87
+ # Optional since it has the same structure as GetResp
88
+ # class RpbPutResp
89
+ # include Beefcake::Message
90
+ # repeated :content, RpbContent, 1
91
+ # optional :vclock, :bytes, 2
92
+ # end
93
+
94
+ class RpbDelReq
95
+ include Beefcake::Message
96
+ required :bucket, :bytes, 1
97
+ required :key, :bytes, 2
98
+ optional :rw, :uint32, 3
99
+ end
100
+
101
+ class RpbListBucketsResp
102
+ include Beefcake::Message
103
+ repeated :buckets, :bytes, 1
104
+ end
105
+
106
+ class RpbListKeysReq
107
+ include Beefcake::Message
108
+ required :bucket, :bytes, 1
109
+ end
110
+
111
+ class RpbListKeysResp
112
+ include Beefcake::Message
113
+ repeated :keys, :bytes, 1
114
+ optional :done, :bool, 2
115
+ end
116
+
117
+ class RpbGetBucketReq
118
+ include Beefcake::Message
119
+ required :bucket, :bytes, 1
120
+ end
121
+
122
+ class RpbGetBucketResp
123
+ include Beefcake::Message
124
+ required :props, RpbBucketProps, 1
125
+ end
126
+
127
+ class RpbSetBucketReq
128
+ include Beefcake::Message
129
+ required :bucket, :bytes, 1
130
+ required :props, RpbBucketProps, 2
131
+ end
132
+
133
+ class RpbMapRedReq
134
+ include Beefcake::Message
135
+ required :request, :bytes, 1
136
+ required :content_type, :bytes, 2
137
+ end
138
+
139
+ class RpbMapRedResp
140
+ include Beefcake::Message
141
+ optional :phase, :uint32, 1
142
+ optional :response, :bytes, 2
143
+ optional :done, :bool, 3
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,92 @@
1
+ require 'riak/robject'
2
+ require 'riak/link'
3
+ require 'riak/client/beefcake/messages'
4
+
5
+ module Riak
6
+ class Client
7
+ class BeefcakeProtobuffsBackend
8
+ module ObjectMethods
9
+ ENCODING = "Riak".respond_to?(:encoding)
10
+
11
+ # Returns RpbPutReq
12
+ def dump_object(robject)
13
+ pbuf = RpbPutReq.new(:bucket => maybe_encode(robject.bucket.name))
14
+ pbuf.key = maybe_encode(robject.key ||= generate_key)
15
+ pbuf.vclock = maybe_encode Base64.decode64(robject.vclock) if robject.vclock
16
+ pbuf.content = RpbContent.new(:value => maybe_encode(robject.raw_data),
17
+ :content_type => maybe_encode(robject.content_type),
18
+ :links => robject.links.map {|l| encode_link(l) }.compact)
19
+
20
+ pbuf.content.usermeta = robject.meta.map {|k,v| encode_meta(k,v)} if robject.meta.any?
21
+ pbuf.content.vtag = maybe_encode(robject.etag) if robject.etag.present?
22
+ if ENCODING # 1.9 support
23
+ pbuf.content.charset = maybe_encode(robject.raw_data.encoding.name)
24
+ end
25
+ pbuf
26
+ end
27
+
28
+ # Returns RObject
29
+ def load_object(pbuf, robject)
30
+ robject.vclock = Base64.encode64(pbuf.vclock).chomp if pbuf.vclock
31
+ if pbuf.content.size > 1
32
+ robject.conflict = true
33
+ robject.siblings = pbuf.content.map do |c|
34
+ sibling = RObject.new(robject.bucket, robject.key)
35
+ sibling.vclock = robject.vclock
36
+ load_content(c, sibling)
37
+ end
38
+
39
+ return robject.attempt_conflict_resolution
40
+ else
41
+ load_content(pbuf.content.first, robject)
42
+ end
43
+ robject
44
+ end
45
+
46
+ private
47
+ def load_content(pbuf, robject)
48
+ if ENCODING && pbuf.charset.present?
49
+ pbuf.value.force_encoding(pbuf.charset) if Encoding.find(pbuf.charset)
50
+ end
51
+ robject.raw_data = pbuf.value
52
+ robject.etag = pbuf.vtag if pbuf.vtag.present?
53
+ robject.content_type = pbuf.content_type if pbuf.content_type.present?
54
+ robject.links = pbuf.links.map(&method(:decode_link)) if pbuf.links.present?
55
+ pbuf.usermeta.each {|pair| decode_meta(pair, robject.meta) } if pbuf.usermeta.present?
56
+ if pbuf.last_mod.present?
57
+ robject.last_modified = Time.at(pbuf.last_mod)
58
+ robject.last_modified += pbuf.last_mod_usecs / 1000000 if pbuf.last_mod_usecs.present?
59
+ end
60
+ robject
61
+ end
62
+
63
+ def decode_link(pbuf)
64
+ Riak::Link.new(pbuf.bucket, pbuf.key, pbuf.tag)
65
+ end
66
+
67
+ def encode_link(link)
68
+ return nil unless link.key.present?
69
+ RpbLink.new(:bucket => maybe_encode(link.bucket.to_s),
70
+ :key => maybe_encode(link.key.to_s),
71
+ :tag => maybe_encode(link.tag.to_s))
72
+ end
73
+
74
+ def decode_meta(pbuf, hash)
75
+ hash[pbuf.key] = pbuf.value
76
+ end
77
+
78
+ def encode_meta(key,value)
79
+ return nil unless value.present?
80
+ RpbPair.new(:key => maybe_encode(key.to_s),
81
+ :value => maybe_encode(value.to_s))
82
+ end
83
+
84
+ def maybe_encode(string)
85
+ ENCODING ? string.encode('BINARY') : string
86
+ end
87
+ end
88
+
89
+ include ObjectMethods
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,176 @@
1
+
2
+ require 'riak/json'
3
+ require 'riak/client'
4
+ require 'riak/failed_request'
5
+ require 'riak/client/protobuffs_backend'
6
+ require 'riak/client/pump'
7
+
8
+ module Riak
9
+ class Client
10
+ class BeefcakeProtobuffsBackend < ProtobuffsBackend
11
+ def self.configured?
12
+ begin
13
+ require 'beefcake'
14
+ require 'riak/client/beefcake/messages'
15
+ require 'riak/client/beefcake/object_methods'
16
+ true
17
+ rescue LoadError, NameError
18
+ false
19
+ end
20
+ end
21
+
22
+ def set_client_id(id)
23
+ value = case id
24
+ when Integer
25
+ [id].pack("N")
26
+ else
27
+ id.to_s
28
+ end
29
+ req = RpbSetClientIdReq.new(:client_id => value)
30
+ write_protobuff(:SetClientIdReq, req)
31
+ decode_response
32
+ end
33
+
34
+ def fetch_object(bucket, key, r=nil)
35
+ bucket = Bucket === bucket ? bucket.name : bucket
36
+ req = RpbGetReq.new(:bucket => bucket, :key => key)
37
+ req.r = normalize_quorum_value(r) if r
38
+ write_protobuff(:GetReq, req)
39
+ decode_response(RObject.new(client.bucket(bucket), key))
40
+ end
41
+
42
+ def reload_object(robject, r=nil)
43
+ req = RpbGetReq.new(:bucket => robject.bucket.name, :key => robject.key)
44
+ req.r = normalize_quorum_value(r) if r
45
+ write_protobuff(:GetReq, req)
46
+ decode_response(robject)
47
+ end
48
+
49
+ def store_object(robject, returnbody=false, w=nil, dw=nil)
50
+ if robject.prevent_stale_writes
51
+ other = fetch_object(robject.bucket, robject.key)
52
+ raise Riak::ProtobuffsFailedRequest(:stale_object, t("stale_write_prevented")) unless other.vclock == robject.vclock
53
+ end
54
+ req = dump_object(robject)
55
+ req.w = normalize_quorum_value(w) if w
56
+ req.dw = normalize_quorum_value(dw) if dw
57
+ req.return_body = returnbody
58
+ write_protobuff(:PutReq, req)
59
+ decode_response(robject)
60
+ end
61
+
62
+ def delete_object(bucket, key, rw=nil)
63
+ bucket = Bucket === bucket ? bucket.name : bucket
64
+ req = RpbDelReq.new(:bucket => bucket, :key => key)
65
+ req.rw = normalize_quorum_value(rw) if rw
66
+ write_protobuff(:DelReq, req)
67
+ decode_response
68
+ end
69
+
70
+ def get_bucket_props(bucket)
71
+ bucket = bucket.name if Bucket === bucket
72
+ req = RpbGetBucketReq.new(:bucket => bucket)
73
+ write_protobuff(:GetBucketReq, req)
74
+ decode_response
75
+ end
76
+
77
+ def set_bucket_props(bucket, props)
78
+ bucket = bucket.name if Bucket === bucket
79
+ props = props.slice('n_val', 'allow_mult')
80
+ req = RpbSetBucketReq.new(:bucket => bucket, :props => RpbBucketProps.new(props))
81
+ write_protobuff(:SetBucketReq, req)
82
+ decode_response
83
+ end
84
+
85
+ def list_keys(bucket, &block)
86
+ bucket = bucket.name if Bucket === bucket
87
+ req = RpbListKeysReq.new(:bucket => bucket)
88
+ write_protobuff(:ListKeysReq, req)
89
+ keys = []
90
+ pump = Pump.new(block) if block_given?
91
+ while msg = decode_response
92
+ break if msg.done
93
+ if pump
94
+ pump.pump msg.keys
95
+ else
96
+ keys += msg.keys
97
+ end
98
+ end
99
+ block_given? || keys
100
+ end
101
+
102
+ def mapred(mr, &block)
103
+ req = RpbMapRedReq.new(:request => mr.to_json, :content_type => "application/json")
104
+ write_protobuff(:MapRedReq, req)
105
+ results = []
106
+ pump = Pump.new(lambda do |msg|
107
+ block.call msg.phase, JSON.parse(msg.response)
108
+ end) if block_given?
109
+ while msg = decode_response
110
+ break if msg.done
111
+ if pump
112
+ pump.pump msg
113
+ else
114
+ results[msg.phase] ||= []
115
+ results[msg.phase] += JSON.parse(msg.response)
116
+ end
117
+ end
118
+ block_given? || results.compact.size == 1 ? results.last : results
119
+ end
120
+
121
+ private
122
+ def write_protobuff(code, message)
123
+ encoded = message.encode
124
+ header = [encoded.length+1, MESSAGE_CODES.index(code)].pack("NC")
125
+ socket.write(header + encoded)
126
+ end
127
+
128
+ def decode_response(*args)
129
+ header = socket.read(5)
130
+ raise SocketError, "Unexpected EOF on PBC socket" if header.nil?
131
+ msglen, msgcode = header.unpack("NC")
132
+ if msglen == 1
133
+ case MESSAGE_CODES[msgcode]
134
+ when :PingResp, :SetClientIdResp, :PutResp, :DelResp, :SetBucketResp
135
+ true
136
+ when :ListBucketsResp, :ListKeysResp
137
+ []
138
+ when :GetResp
139
+ raise Riak::ProtobuffsFailedRequest.new(:not_found, t('not_found'))
140
+ else
141
+ false
142
+ end
143
+ else
144
+ message = socket.read(msglen-1)
145
+ case MESSAGE_CODES[msgcode]
146
+ when :ErrorResp
147
+ res = RpbErrorResp.decode(message)
148
+ raise Riak::ProtobuffsFailedRequest.new(res.errcode, res.errmsg)
149
+ when :GetClientIdResp
150
+ res = RpbGetClientIdResp.decode(message)
151
+ res.client_id
152
+ when :GetServerInfoResp
153
+ res = RpbGetServerInfoResp.decode(message)
154
+ {:node => res.node, :server_version => res.server_version}
155
+ when :GetResp, :PutResp
156
+ res = RpbGetResp.decode(message)
157
+ load_object(res, args.first)
158
+ when :ListBucketsResp
159
+ res = RpbListBucketsResp.decode(message)
160
+ res.buckets
161
+ when :ListKeysResp
162
+ RpbListKeysResp.decode(message)
163
+ when :GetBucketResp
164
+ res = RpbGetBucketResp.decode(message)
165
+ {'n_val' => res.props.n_val, 'allow_mult' => res.props.allow_mult}
166
+ when :MapRedResp
167
+ RpbMapRedResp.decode(message)
168
+ end
169
+ end
170
+ rescue SocketError => e
171
+ reset_socket
172
+ raise Riak::ProtobuffsFailedRequest.new(:server_error, e.message)
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,65 @@
1
+
2
+ require 'riak/failed_request'
3
+ require 'riak/client/http_backend'
4
+ require 'riak/client/http_backend/request_headers'
5
+ require 'riak/client/pump'
6
+
7
+ module Riak
8
+ class Client
9
+ # An HTTP backend for Riak::Client that uses Wesley Beary's Excon
10
+ # HTTP library. Conforms to the Riak::Client::HTTPBackend
11
+ # interface.
12
+ class ExconBackend < HTTPBackend
13
+ def self.configured?
14
+ begin
15
+ require 'excon'
16
+ Excon::VERSION >= "0.5.7"
17
+ rescue LoadError
18
+ false
19
+ end
20
+ end
21
+
22
+ private
23
+ def perform(method, uri, headers, expect, data=nil, &block)
24
+ configure_ssl if @client.ssl_enabled?
25
+
26
+ params = {
27
+ :method => method.to_s.upcase,
28
+ :headers => RequestHeaders.new(headers).to_hash,
29
+ :path => uri.path
30
+ }
31
+ params[:query] = uri.query if uri.query
32
+ params[:body] = data if [:put,:post].include?(method)
33
+ params[:idempotent] = (method != :post)
34
+
35
+ if block_given?
36
+ pump = Pump.new(block)
37
+ # Later versions of Excon pass multiple arguments to the block
38
+ block = lambda {|*args| pump.pump(args.first) }
39
+ end
40
+
41
+ response = connection.request(params, &block)
42
+ response_headers.initialize_http_header(response.headers)
43
+
44
+ if valid_response?(expect, response.status)
45
+ result = {:headers => response_headers.to_hash, :code => response.status}
46
+ if return_body?(method, response.status, block_given?)
47
+ result[:body] = response.body
48
+ end
49
+ result
50
+ else
51
+ raise HTTPFailedRequest.new(method, expect, response.status, response_headers.to_hash, response.body)
52
+ end
53
+ end
54
+
55
+ def connection
56
+ @connection ||= Excon::Connection.new(root_uri.to_s)
57
+ end
58
+
59
+ def configure_ssl
60
+ Excon.ssl_verify_peer = @client.ssl_options[:verify_mode].to_s === "peer"
61
+ Excon.ssl_ca_path = @client.ssl_options[:ca_path] if @client.ssl_options[:ca_path]
62
+ end
63
+ end
64
+ end
65
+ end