riakpb 0.1.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/History.txt +4 -0
- data/Manifest.txt +30 -0
- data/README.rdoc +156 -0
- data/Rakefile +43 -0
- data/lib/riak/bucket.rb +177 -0
- data/lib/riak/client/rpc.rb +127 -0
- data/lib/riak/client.rb +227 -0
- data/lib/riak/client_pb.rb +372 -0
- data/lib/riak/failed_exchange.rb +21 -0
- data/lib/riak/failed_request.rb +22 -0
- data/lib/riak/i18n.rb +7 -0
- data/lib/riak/key.rb +284 -0
- data/lib/riak/locale/en.yml +37 -0
- data/lib/riak/riak_content.rb +219 -0
- data/lib/riak/sibling_error.rb +16 -0
- data/lib/riak/util/decode.rb +37 -0
- data/lib/riak/util/encode.rb +31 -0
- data/lib/riak/util/message_code.rb +73 -0
- data/lib/riak/util/translation.rb +16 -0
- data/lib/riak.rb +34 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/riak/bucket_spec.rb +118 -0
- data/spec/riak/client_spec.rb +212 -0
- data/spec/riak/key_spec.rb +54 -0
- data/spec/riak/map_reduce_spec.rb +4 -0
- data/spec/riak/riak_content_spec.rb +88 -0
- data/spec/riak/rpc_spec.rb +17 -0
- data/spec/spec_helper.rb +25 -0
- metadata +121 -0
data/lib/riak/key.rb
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
require 'riak'
|
2
|
+
|
3
|
+
module Riak
|
4
|
+
# Represents and encapsulates operations on a Riak bucket. You may retrieve a bucket
|
5
|
+
# using {Client#bucket}, or create it manually and retrieve its meta-information later.
|
6
|
+
class Key
|
7
|
+
include Util::Translation
|
8
|
+
include Util::MessageCode
|
9
|
+
|
10
|
+
# @return [Riak::Client] the associated client
|
11
|
+
attr_reader :bucket
|
12
|
+
|
13
|
+
# @return [String] the bucket name
|
14
|
+
attr_reader :name
|
15
|
+
|
16
|
+
# @return [String] the bucket name
|
17
|
+
attr_reader :vclock
|
18
|
+
|
19
|
+
# Create a Riak bucket manually.
|
20
|
+
# @param [Bucket] bucket the Bucket object within which this Key exists
|
21
|
+
# @option options [String] name the name assigned to this Key entity
|
22
|
+
# @option options [Fixnum] vclock Careful! Do not set this unless you have a reason to
|
23
|
+
# @option options [RiakContent] content a content object, that's to be inserted in this Key
|
24
|
+
def initialize(bucket, key, get_response=nil)
|
25
|
+
# options.assert_valid_keys(:name, :vclock, :content)
|
26
|
+
|
27
|
+
self.bucket = bucket
|
28
|
+
self.name = key
|
29
|
+
|
30
|
+
@contents = Hash.new{|k,v| k[v] = Riak::RiakContent.new(self)}
|
31
|
+
|
32
|
+
# @contents[:new]
|
33
|
+
|
34
|
+
load(get_response) unless get_response.nil?
|
35
|
+
end
|
36
|
+
|
37
|
+
# Load information for the key from the response object, Riak::RpbGetResp.
|
38
|
+
#
|
39
|
+
# @param [RpbGetResp/Hash] response an RpbGetResp/RpbPutResp object or a Hash.
|
40
|
+
# @return [Key] self
|
41
|
+
def load(response)
|
42
|
+
@blargh = response
|
43
|
+
raise ArgumentError, t("response_type") unless response.is_a?(Protobuf::Message)
|
44
|
+
|
45
|
+
self.vclock = response.vclock if response.has_field?(:vclock)
|
46
|
+
|
47
|
+
if response.has_field?(:content)
|
48
|
+
self.content = response.content
|
49
|
+
elsif @contents.empty?
|
50
|
+
@contents[:new]
|
51
|
+
end
|
52
|
+
|
53
|
+
return(self)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Load information for the key from Riak::RpbGetResp object.
|
57
|
+
#
|
58
|
+
# @param [RpbGetResp/Hash] response an RpbGetResp/RpbPutResp object or a Hash.
|
59
|
+
# @return [Key] self
|
60
|
+
def load!(response)
|
61
|
+
raise ArgumentError, t("response_type") unless response.is_a?(Riak::RpbGetResp)
|
62
|
+
|
63
|
+
if response.has_field?(:vclock) and response.has_field?(:content)
|
64
|
+
|
65
|
+
self.vclock = response.vclock
|
66
|
+
self.content = response.content
|
67
|
+
|
68
|
+
elsif response.has_field?(:vclock) or response.has_field?(:content)
|
69
|
+
raise MalformedKeyError # This should never happen
|
70
|
+
|
71
|
+
else
|
72
|
+
raise KeyNotFoundError
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
return(self)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Indicates whether or not the Key is empty
|
80
|
+
# @return [Boolean] true or false, whether or not the vclock/content is empty
|
81
|
+
def empty?
|
82
|
+
return(true) if @vclock.empty? && (@contents.nil? || @contents.empty?)
|
83
|
+
return(false)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Refreshes the Key and its content with fresh data, if there's concern that separate updates may have taken place.
|
87
|
+
# @return
|
88
|
+
def reload!
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
# Retrieves any Keys that are linked to, inside RiakContent elements.
|
93
|
+
# @return [Key] the Key to which the RiakContent is linked (may be empty if it does not exist)
|
94
|
+
def get_linked(bucket, key, options={})
|
95
|
+
@bucket.get_linked(bucket, key, options)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Sets the name attribute for this Key object
|
99
|
+
# @param [String] key_name sets the name of the Key
|
100
|
+
# @return [Hash] the properties that were accepted
|
101
|
+
# @raise [FailedRequest] if the new properties were not accepted by the Riak server
|
102
|
+
def name=(key_name)
|
103
|
+
raise ArgumentError, t("key_name_type") unless key_name.is_a?(String)
|
104
|
+
|
105
|
+
@name = key_name
|
106
|
+
end
|
107
|
+
|
108
|
+
# Sets the content object for this Key. I do not yet support siblings in this method and, therefore,
|
109
|
+
# you may or may not destroy them if you use this and are not careful.
|
110
|
+
# @param [Riak::RiakContent] content a RiakContent instance that should be contained within this Key
|
111
|
+
# @return [Riak::RiakContent] the RiakContent instance that was just set
|
112
|
+
# @raise [ArgumentError] will yell at you if the supplied riak_content is not of the RiakContent class
|
113
|
+
def content=(riak_contents)
|
114
|
+
|
115
|
+
if riak_contents.is_a?(Protobuf::Field::FieldArray)
|
116
|
+
@contents.clear
|
117
|
+
|
118
|
+
return(false) if riak_contents.empty?
|
119
|
+
|
120
|
+
riak_contents.each do |rc|
|
121
|
+
@contents[rc.vtag].load(rc)
|
122
|
+
end
|
123
|
+
|
124
|
+
return(true)
|
125
|
+
elsif riak_contents.is_a?(Riak::RiakContent)
|
126
|
+
|
127
|
+
@contents.clear
|
128
|
+
|
129
|
+
@contents[riak_contents.vtag].load(riak_contents)
|
130
|
+
|
131
|
+
elsif riak_contents.nil?
|
132
|
+
@contents.clear
|
133
|
+
|
134
|
+
else
|
135
|
+
raise ArgumentError, t("riak_content_type")
|
136
|
+
end # if riak_contents
|
137
|
+
|
138
|
+
end # def content=
|
139
|
+
|
140
|
+
# "@contents" is an array of RiakContent objects, though only contains more than one in the event that
|
141
|
+
# there are siblings.
|
142
|
+
# @return [Riak::RiakContent] the content of this Key instance's value (ie, key/value)
|
143
|
+
def content
|
144
|
+
case @contents.size
|
145
|
+
when 0 then @contents[:new]
|
146
|
+
when 1 then contents[0]
|
147
|
+
else contents
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# "@contents" is an array of RiakContent objects. This gives you that entire array.
|
152
|
+
# @return [Array<Riak::RiakContent>] the contents of this Key instance's value and its siblings, if any
|
153
|
+
def contents
|
154
|
+
retr_c = []
|
155
|
+
|
156
|
+
@contents.each{|k,v| retr_c << v}
|
157
|
+
|
158
|
+
return(retr_c)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Save the Key+RiakContent instance in riak.
|
162
|
+
# @option options [RiakContent] content RiakContent instance to be saved in this Key. Must be specified if there are siblings.
|
163
|
+
# @option options [Fixnum] w (write quorum) how many replicas to write to before returning a successful response
|
164
|
+
# @option options [Fixnum] dw how many replicas to commit to durable storage before returning a successful response
|
165
|
+
# @option options [Boolean] return_body whether or not to have riak return the key, once saved. default = true
|
166
|
+
# TODO: Add in content checking, perhaps?
|
167
|
+
def save(options={})
|
168
|
+
rcontent = options[:content]
|
169
|
+
|
170
|
+
if rcontent.nil?
|
171
|
+
case contents.size
|
172
|
+
when 0 then raise ArgumentError, t('empty_content')
|
173
|
+
when 1 then rcontent = contents[0]
|
174
|
+
else raise SiblingError.new(self.name)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
options[:content] = rcontent.to_pb if rcontent.is_a?(Riak::RiakContent)
|
179
|
+
options[:content] = rcontent if rcontent.is_a?(Riak::RpbContent)
|
180
|
+
options[:key] = @name
|
181
|
+
options[:vclock] = @vclock unless @vclock.nil?
|
182
|
+
|
183
|
+
begin
|
184
|
+
response = @bucket.store(options)
|
185
|
+
load(response)
|
186
|
+
return(true) if @contents.count == 1
|
187
|
+
return(false)
|
188
|
+
rescue FailedRequest
|
189
|
+
return(false)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
# Save the RiakContent instance in riak. Raise/do not rescue on failure.
|
194
|
+
# @option options [Fixnum] w (write quorum) how many replicas to write to before returning a successful response
|
195
|
+
# @option options [Fixnum] dw how many replicas to commit to durable storage before returning a successful response
|
196
|
+
# @option options [Boolean] return_body whether or not to have riak return the key, once saved. default = true
|
197
|
+
def save!(options={})
|
198
|
+
begin
|
199
|
+
save(options)
|
200
|
+
return(true) if @contents.count == 1
|
201
|
+
raise FailedRequest.new("save_resp_siblings", 1, @contents.count, @contents) if @contents.count > 1
|
202
|
+
rescue FailedRequest
|
203
|
+
raise FailedRequest.new("save_resp_err")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Creates an RpbPutReq instance, to be shipped off to riak and saved
|
208
|
+
# @option options [Fixnum] w (write quorum) how many replicas to write to before returning a successful response
|
209
|
+
# @option options [Fixnum] dw how many replicas to commit to durable storage before returning a successful response
|
210
|
+
# @option options [Boolean] return_body whether or not to have riak return the key, once saved. default = true
|
211
|
+
# @return [Riak::RpbPutReq]
|
212
|
+
def to_pb_put(options={})
|
213
|
+
rcontent = options[:content]
|
214
|
+
|
215
|
+
if rcontent.nil?
|
216
|
+
case contents.size
|
217
|
+
when 0 then raise ArgumentError, t('empty_content')
|
218
|
+
when 1 then rcontent = contents[0]
|
219
|
+
else raise SiblingError.new(self.name)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
pb_put_req = Riak::RpbPutReq.new
|
224
|
+
pb_put_req.key = @name
|
225
|
+
pb_put_req.content = rcontent.to_pb if rcontent.is_a?(Riak::RiakContent)
|
226
|
+
pb_put_req.content = rcontent if rcontent.is_a?(Riak::RpbContent)
|
227
|
+
pb_put_req.vclock = @vclock unless @vclock.nil?
|
228
|
+
|
229
|
+
return(pb_put_req)
|
230
|
+
end
|
231
|
+
|
232
|
+
# "@contents" is an array of RiakContent objects. This gives you that entire array.
|
233
|
+
# @return [Riak::RpbLink]
|
234
|
+
def to_pb_link
|
235
|
+
pb_link = Riak::RpbLink.new
|
236
|
+
pb_link[:bucket] = @bucket.name
|
237
|
+
pb_link[:key] = @name
|
238
|
+
|
239
|
+
return(pb_link)
|
240
|
+
end
|
241
|
+
|
242
|
+
# Converts this Key into an array, that can be used by a RiakContent, if desired.
|
243
|
+
# @return [Array] contains the name of the bucket and the name of the key
|
244
|
+
def to_link
|
245
|
+
[@bucket.name, @name]
|
246
|
+
end
|
247
|
+
alias :to_input :to_link
|
248
|
+
|
249
|
+
# Deletes this key from its Bucket container
|
250
|
+
# @param [Hash] options quorum options
|
251
|
+
# @option options [Fixnum] :rw - the read/write quorum for the delete
|
252
|
+
def delete(options={})
|
253
|
+
bucket.delete(@name, options)
|
254
|
+
end
|
255
|
+
|
256
|
+
# @return [String] a representation suitable for IRB and debugging output
|
257
|
+
def inspect
|
258
|
+
"#<Riak::Key name=#{@name.inspect}, vclock=#{@vclock.inspect}, contents=#{contents.inspect}>"
|
259
|
+
end
|
260
|
+
|
261
|
+
private
|
262
|
+
|
263
|
+
# Sets the name attribute for this Key object
|
264
|
+
# @param [String] key_name sets the name of the Key
|
265
|
+
# @return [Hash] the properties that were accepted
|
266
|
+
# @raise [FailedRequest] if the new properties were not accepted by the Riak server
|
267
|
+
def bucket=(bucket)
|
268
|
+
raise ArgumentError, t("invalid_bucket") unless bucket.is_a?(Riak::Bucket)
|
269
|
+
|
270
|
+
@bucket ||= bucket
|
271
|
+
end
|
272
|
+
|
273
|
+
# Sets the vclock attribute for this Key, which was supplied by the Riak node (if you're doing it right)
|
274
|
+
# @param [Fixnum] vclock the vector clock
|
275
|
+
# @return [Fixnum] the vector clock
|
276
|
+
# @raise [ArgumentError] if you failed at this task, you'll be instructed to place your head on the keyboard
|
277
|
+
def vclock=(vclock)
|
278
|
+
raise ArgumentError, t("vclock_type") unless vclock.is_a?(String)
|
279
|
+
|
280
|
+
@vclock = vclock
|
281
|
+
end
|
282
|
+
|
283
|
+
end # class Key
|
284
|
+
end # module Riak
|
@@ -0,0 +1,37 @@
|
|
1
|
+
en:
|
2
|
+
riak:
|
3
|
+
client_type: "invalid argument {{client}} is not a Riak::Client"
|
4
|
+
string_type: "invalid_argument {{string}} is not a String"
|
5
|
+
loading_bucket: "while loading bucket '{{name}}'"
|
6
|
+
invalid_bucket: "the specified bucket is invalid"
|
7
|
+
invalid_key: "the specified key does not exist"
|
8
|
+
value_empty: "a value must be set in order to serialize into a protocol buffer"
|
9
|
+
failed_request: "Expected message code {{expected}} from Riak but received {{actual}}. {{output}}"
|
10
|
+
decode_error: "Riak reported a message length of {{expected}} but length was {{actual}}. {{output}}"
|
11
|
+
save_resp_siblings: "The save operation has resulted in unresolved siblings. {{output}}"
|
12
|
+
save_resp_err: "The attempted save operation resulted in an error response from riak."
|
13
|
+
failed_rx: "You might have discovered a bug. {{failure}}"
|
14
|
+
hash_type: "invalid argument {{hash}} is not a Hash"
|
15
|
+
path_and_body_required: "You must supply both a resource path and a body."
|
16
|
+
request_body_type: "Request body must be a string or IO."
|
17
|
+
resource_path_short: "Resource path too short"
|
18
|
+
missing_host_and_port: "You must specify a host and port, or use the defaults of 127.0.0.1:8098"
|
19
|
+
invalid_client_id: "Invalid client ID, must be a string or between 0 and {{max_id}}"
|
20
|
+
hostname_invalid: "host must be a valid hostname"
|
21
|
+
port_invalid: "port must be an integer between 0 and 65535"
|
22
|
+
install_curb: "curb library not found! Please `gem install curb` for better performance."
|
23
|
+
bucket_link_conversion: "Can't convert a bucket link to a walk spec"
|
24
|
+
invalid_phase_type: "type must be :map, :reduce, or :link"
|
25
|
+
module_function_pair_required: "function must have two elements when an array"
|
26
|
+
stored_function_invalid: "function must have :bucket and :key when a hash"
|
27
|
+
walk_spec_invalid_unless_link: "WalkSpec is only valid for a function when the type is :link"
|
28
|
+
invalid_function_value: "invalid value for function: {{value}}"
|
29
|
+
content_type_undefined: "content_type is not defined!"
|
30
|
+
too_few_arguments: "too few arguments: {{params}}"
|
31
|
+
wrong_argument_count_walk_spec: "wrong number of arguments (one Hash or bucket,tag,keep required)"
|
32
|
+
boolean_type: "invalid_argument {{value}} is not true or false"
|
33
|
+
fixnum_type: "invalid_argument {{value}} must be an integer"
|
34
|
+
error_response: "the riak node responded with an error message code"
|
35
|
+
unresolved_siblings: "this Key contains unresolved siblings, which must first be resolved"
|
36
|
+
empty_content: "key instance cannot be serialized into a protobuf without a valid key name and content"
|
37
|
+
|
@@ -0,0 +1,219 @@
|
|
1
|
+
require 'riak'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Riak
|
5
|
+
# Parent class of all object types supported by ripple. {Riak::RObject} represents
|
6
|
+
# the data and metadata stored in a bucket/key pair in the Riak database.
|
7
|
+
class RiakContent
|
8
|
+
include Util::Translation
|
9
|
+
include Util::MessageCode
|
10
|
+
|
11
|
+
# @return [Key] the key in which this RiakContent is stored.
|
12
|
+
attr_accessor :key
|
13
|
+
|
14
|
+
# @return [String] the data stored in Riak at this object's key. Varies in format by content-type.
|
15
|
+
attr_accessor :value
|
16
|
+
alias_attribute :data, :value
|
17
|
+
|
18
|
+
# @return [String] the MIME content type of the object
|
19
|
+
attr_accessor :content_type
|
20
|
+
|
21
|
+
# @return [String] the charset of the object
|
22
|
+
attr_accessor :charset
|
23
|
+
|
24
|
+
# @return [String] the content encoding of the object
|
25
|
+
attr_accessor :content_encoding
|
26
|
+
|
27
|
+
# @return [String] the vtag of the object
|
28
|
+
attr_accessor :vtag
|
29
|
+
|
30
|
+
# @return [Set<Link>] an Set of {Riak::Link} objects for relationships between this object and other resources
|
31
|
+
attr_accessor :links
|
32
|
+
|
33
|
+
# @return [Time] the Last-Modified header from the most recent HTTP response, useful for caching and reloading
|
34
|
+
attr_accessor :last_mod
|
35
|
+
alias_attribute :last_modified, :last_mod
|
36
|
+
|
37
|
+
# @return [Time] the Last-Modified header from the most recent HTTP response, useful for caching and reloading
|
38
|
+
attr_accessor :last_mod_usecs
|
39
|
+
alias_attribute :last_modified_usecs, :last_mod_usecs
|
40
|
+
|
41
|
+
# @return [Hash] a hash of any user-supplied metadata, consisting of a key/value pair
|
42
|
+
attr_accessor :usermeta
|
43
|
+
alias_attribute :meta, :usermeta
|
44
|
+
|
45
|
+
# Create a new riak_content object manually
|
46
|
+
# @param [Riak::Key] key Key instance that owns this RiakContent (really, you should use the Key to get this)
|
47
|
+
# @param [Hash] contents Any contents to initialize this instance with
|
48
|
+
# @see Key#content
|
49
|
+
# @see RiakContent#load
|
50
|
+
def initialize(key, contents={})
|
51
|
+
@key = key unless key.nil?
|
52
|
+
@links = Hash.new{|k,v| k[v] = []}
|
53
|
+
@_links = []
|
54
|
+
@usermeta = {}
|
55
|
+
|
56
|
+
load(contents) unless contents.empty?
|
57
|
+
|
58
|
+
yield self if block_given?
|
59
|
+
end
|
60
|
+
|
61
|
+
# Load information for the content from the response object, Riak::RpbContent.
|
62
|
+
#
|
63
|
+
# @param [RpbContent/Hash] contents an RpbContent object or a Hash.
|
64
|
+
# @return [RiakContent] self
|
65
|
+
def load(contents)
|
66
|
+
if contents.is_a?(Riak::RpbContent) or contents.is_a?(Hash)
|
67
|
+
@content_type = contents[:content_type] unless contents[:content_type].blank?
|
68
|
+
@charset = contents[:charset] unless contents[:charset].blank?
|
69
|
+
@content_encoding = contents[:content_encoding] unless contents[:content_encoding].blank?
|
70
|
+
@vtag = contents[:vtag] unless contents[:vtag].blank?
|
71
|
+
self.links = contents[:links] unless contents[:links].blank?
|
72
|
+
@last_mod = contents[:last_mod]
|
73
|
+
@last_mod_usecs = contents[:last_mod_usecs]
|
74
|
+
self.usermeta = contents[:usermeta] unless contents[:usermeta].blank?
|
75
|
+
|
76
|
+
unless contents[:value].blank?
|
77
|
+
case @content_type
|
78
|
+
when /json/
|
79
|
+
@value = ActiveSupport::JSON.decode(contents[:value])
|
80
|
+
when /octet/
|
81
|
+
@value = Marshal.load(contents[:value])
|
82
|
+
else
|
83
|
+
@value = contents[:value]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
return(self)
|
88
|
+
end
|
89
|
+
|
90
|
+
raise ArgumentError, t("riak_content_type")
|
91
|
+
end
|
92
|
+
|
93
|
+
# Save the RiakContent instance in riak.
|
94
|
+
# @option options [Fixnum] w (write quorum) how many replicas to write to before returning a successful response
|
95
|
+
# @option options [Fixnum] dw how many replicas to commit to durable storage before returning a successful response
|
96
|
+
# @option options [true/false] return_body whether or not to have riak return the key, once saved. default = true
|
97
|
+
def save(options={})
|
98
|
+
begin
|
99
|
+
save!(options)
|
100
|
+
rescue FailedRequest
|
101
|
+
return(false)
|
102
|
+
end
|
103
|
+
return(true)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Save the RiakContent instance in riak. Raise/do not rescue on failure.
|
107
|
+
# @option options [Fixnum] w (write quorum) how many replicas to write to before returning a successful response
|
108
|
+
# @option options [Fixnum] dw how many replicas to commit to durable storage before returning a successful response
|
109
|
+
# @option options [true/false] return_body whether or not to have riak return the key, once saved. default = true
|
110
|
+
def save!(options={})
|
111
|
+
options[:content] = self
|
112
|
+
return(true) if @key.save(options)
|
113
|
+
return(false) # Create and raise Error message for this? Extend "Failed Request"?
|
114
|
+
end
|
115
|
+
|
116
|
+
# Internalizes a link to a Key, which will be saved on next call to... uh, save
|
117
|
+
# @param [Hash] tags name of the tag, pointing to a Key instance, or an array ["bucket", "key"]
|
118
|
+
# @return [Hash] links that this RiakContent points to
|
119
|
+
def link_key(tags)
|
120
|
+
raise TypeError.new t('invalid_tag') unless tag.is_a?(Hash)
|
121
|
+
tags.each do |tag, link|
|
122
|
+
case link
|
123
|
+
when Array
|
124
|
+
bucket ||= link[0]
|
125
|
+
key ||= link[1]
|
126
|
+
raise TypeError.new t('invalid_tag') if bucket.nil? or key.nil?
|
127
|
+
|
128
|
+
get_link ||= @key.get_linked(bucket, key, {:safely => true})
|
129
|
+
raise RuntimeError.new t('invalid_key') if get_link.nil?
|
130
|
+
|
131
|
+
@links[tag.to_s] << get_link
|
132
|
+
|
133
|
+
when Riak::Key
|
134
|
+
@links[tag.to_s] << link
|
135
|
+
|
136
|
+
else
|
137
|
+
raise TypeError.new t('invalid_tag')
|
138
|
+
end
|
139
|
+
end # tags.each do |tag, link|
|
140
|
+
end
|
141
|
+
|
142
|
+
# Set the links to other Key in riak.
|
143
|
+
# @param [RpbGetResp, RpbPutResp] contains the tag/bucket/key of a given link
|
144
|
+
# @return [Set] links that this RiakContent points to
|
145
|
+
def links=(pb_links)
|
146
|
+
@links.clear
|
147
|
+
|
148
|
+
pb_links.each do |pb_link|
|
149
|
+
@links[pb_link.tag] << @key.get_linked(pb_link.bucket, pb_link.key, {:safely => true})
|
150
|
+
end
|
151
|
+
|
152
|
+
return(@links)
|
153
|
+
end
|
154
|
+
|
155
|
+
# @return [Riak::RpbContent] An instance of a RpbContent, suitable for protobuf exchange
|
156
|
+
def to_pb
|
157
|
+
raise TypeError.new t('value_empty') if @value.nil?
|
158
|
+
|
159
|
+
rpb_content = Riak::RpbContent.new
|
160
|
+
|
161
|
+
links = []
|
162
|
+
@links.each do |link|
|
163
|
+
pb_link = link[1].to_pb_link
|
164
|
+
pb_link[:tag] = link[0]
|
165
|
+
links << pb_link
|
166
|
+
end
|
167
|
+
|
168
|
+
usermeta = []
|
169
|
+
@usermeta.each do |key,value|
|
170
|
+
pb_pair = Riak::RpbPair.new
|
171
|
+
pb_pair[:key] = key
|
172
|
+
pb_pair[:value] = value
|
173
|
+
usermeta << pb_pair
|
174
|
+
end
|
175
|
+
|
176
|
+
catch(:redo) do
|
177
|
+
case @content_type
|
178
|
+
when /octet/
|
179
|
+
rpb_content.value = Marshal.dump(@value)
|
180
|
+
when /json/
|
181
|
+
rpb_content.value = ActiveSupport::JSON.encode(@value)
|
182
|
+
when "", nil
|
183
|
+
@content_type = "application/json"
|
184
|
+
redo
|
185
|
+
else
|
186
|
+
rpb_content.value = @value.to_s
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
rpb_content.content_type = @content_type unless @content_type.nil?
|
191
|
+
rpb_content.charset = @charset unless @charset.nil? # || @charset.empty?
|
192
|
+
rpb_content.content_encoding = @content_encoding unless @content_encoding.nil? # || @content_encoding.empty?
|
193
|
+
rpb_content.vtag = @vtag unless @vtag.nil?
|
194
|
+
rpb_content.links = links unless links.empty?
|
195
|
+
rpb_content.usermeta = usermeta unless usermeta.empty?
|
196
|
+
|
197
|
+
return(rpb_content)
|
198
|
+
end
|
199
|
+
|
200
|
+
# @return [String] A representation suitable for IRB and debugging output
|
201
|
+
def inspect
|
202
|
+
"#<#Riak::RiakContent " + [
|
203
|
+
(@value.nil?) ? nil : "value=#{@value.inspect}",
|
204
|
+
(@content_type.nil?) ? nil : "content_type=#{@content_type.inspect}",
|
205
|
+
(@charset.nil?) ? nil : "charset=#{@charset.inspect}",
|
206
|
+
(@content_encoding.nil?) ? nil : "content_encoding=#{@content_encoding.inspect}",
|
207
|
+
(@vtag.nil?) ? nil : "vtag=#{@vtag.inspect}",
|
208
|
+
(@links.nil?) ? nil : "links=#{@_links.inspect}",
|
209
|
+
(@last_mod.nil?) ? nil : "last_mod=#{last_mod.inspect}",
|
210
|
+
(@last_mod_usecs.nil?) ? nil : "last_mod_usecs=#{last_mod_usecs.inspect}",
|
211
|
+
(@usermeta.nil?) ? nil : "usermeta=#{@usermeta.inspect}"
|
212
|
+
|
213
|
+
].compact.join(", ") + ">"
|
214
|
+
end
|
215
|
+
|
216
|
+
private
|
217
|
+
|
218
|
+
end # class RiakContent
|
219
|
+
end # module Riak
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'riak'
|
2
|
+
|
3
|
+
module Riak
|
4
|
+
# Exception raised when the expected response code from Riak
|
5
|
+
# fails to match the actual response code.
|
6
|
+
class SiblingError < StandardError
|
7
|
+
include Riak::Util::Translation
|
8
|
+
|
9
|
+
attr_reader :key
|
10
|
+
|
11
|
+
def initialize(key)
|
12
|
+
@key = key
|
13
|
+
super t("unresolved_siblings", :key => @key)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'riak'
|
2
|
+
|
3
|
+
module Riak
|
4
|
+
module Util
|
5
|
+
|
6
|
+
module Decode
|
7
|
+
PLEN = (0..3)
|
8
|
+
PBMC = PLEN.count
|
9
|
+
POFF = (PBMC+1)
|
10
|
+
|
11
|
+
def decode_message(message)
|
12
|
+
pb_len = 0
|
13
|
+
pb_mc = ''
|
14
|
+
pb_msg = ''
|
15
|
+
|
16
|
+
until message.empty?
|
17
|
+
pb_len = message[PLEN].unpack('N')[0] # message[0..3]unpack('N')[0]
|
18
|
+
pb_mc = pb_mc + message[PBMC] # prior message codes + message[4]
|
19
|
+
|
20
|
+
prange = POFF..(pb_len+3) # range for the start->finish of the pb message
|
21
|
+
mrange = (pb_len+4)..(message.size-1) # range for any remaining portions of message
|
22
|
+
|
23
|
+
if(prange.count != message[prange].size)
|
24
|
+
raise FailedExchange.new(prange.count, message[prange].size, message[prange], "decode_error")
|
25
|
+
end
|
26
|
+
|
27
|
+
pb_msg = pb_msg + message[prange]
|
28
|
+
message = message[mrange] # message[(5+pb_len)..(message.size)]
|
29
|
+
end
|
30
|
+
|
31
|
+
[pb_msg, pb_mc.unpack("c" * pb_mc.size)]
|
32
|
+
end
|
33
|
+
|
34
|
+
end # module Decode
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'riak'
|
2
|
+
|
3
|
+
module Riak
|
4
|
+
module Util
|
5
|
+
|
6
|
+
module Encode
|
7
|
+
|
8
|
+
# Construct a Request Message for Riak, which adheres to the following structure:
|
9
|
+
#
|
10
|
+
# 00 00 00 07 09 0A 01 62 12 01 6B
|
11
|
+
# |----Len---|MC|----Message-----|
|
12
|
+
#
|
13
|
+
# @raise [TypeError] if an invalid hostname is given
|
14
|
+
# @return [String] the assigned hostname
|
15
|
+
def assemble_request(mc, msg='')
|
16
|
+
raise TypeError, t("message_code_invalid") unless mc.is_a?(Fixnum)
|
17
|
+
raise TypeError, t("pb_message_invalid") unless msg.is_a?(String)
|
18
|
+
|
19
|
+
encode_message mc, msg
|
20
|
+
end
|
21
|
+
|
22
|
+
def encode_message(mc, msg='')
|
23
|
+
message = [mc].pack('c') + msg
|
24
|
+
|
25
|
+
message = [message.size].pack('N') + message
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|