riak-client 1.0.5 → 1.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/.document +5 -0
- data/.gitignore +4 -3
- data/.rspec +1 -0
- data/Gemfile +1 -0
- data/RELEASE_NOTES.md +47 -0
- data/Rakefile +0 -1
- data/erl_src/riak_kv_test_backend.erl +34 -0
- data/lib/riak/client.rb +3 -1
- data/lib/riak/client/beefcake/messages.rb +49 -1
- data/lib/riak/client/beefcake/object_methods.rb +14 -21
- data/lib/riak/client/beefcake_protobuffs_backend.rb +58 -12
- data/lib/riak/client/decaying.rb +31 -23
- data/lib/riak/client/feature_detection.rb +88 -0
- data/lib/riak/client/http_backend.rb +27 -6
- data/lib/riak/client/http_backend/configuration.rb +13 -0
- data/lib/riak/client/http_backend/object_methods.rb +33 -25
- data/lib/riak/client/node.rb +7 -2
- data/lib/riak/client/protobuffs_backend.rb +54 -3
- data/lib/riak/client/search.rb +2 -2
- data/lib/riak/conflict.rb +13 -0
- data/lib/riak/locale/en.yml +2 -0
- data/lib/riak/map_reduce.rb +1 -1
- data/lib/riak/map_reduce/filter_builder.rb +2 -2
- data/lib/riak/map_reduce/results.rb +49 -0
- data/lib/riak/node/console.rb +17 -16
- data/lib/riak/node/generation.rb +9 -0
- data/lib/riak/rcontent.rb +168 -0
- data/lib/riak/robject.rb +37 -157
- data/lib/riak/util/escape.rb +5 -1
- data/lib/riak/version.rb +1 -1
- data/riak-client.gemspec +37 -5
- data/spec/fixtures/multipart-basic-conflict.txt +15 -0
- data/spec/fixtures/munchausen.txt +1033 -0
- data/spec/integration/riak/cluster_spec.rb +1 -1
- data/spec/integration/riak/http_backends_spec.rb +23 -2
- data/spec/integration/riak/node_spec.rb +2 -2
- data/spec/integration/riak/protobuffs_backends_spec.rb +17 -2
- data/spec/integration/riak/test_server_spec.rb +1 -1
- data/spec/integration/riak/threading_spec.rb +3 -3
- data/spec/riak/beefcake_protobuffs_backend_spec.rb +58 -25
- data/spec/riak/escape_spec.rb +3 -0
- data/spec/riak/feature_detection_spec.rb +61 -0
- data/spec/riak/http_backend/object_methods_spec.rb +4 -13
- data/spec/riak/http_backend_spec.rb +6 -5
- data/spec/riak/map_reduce_spec.rb +0 -5
- data/spec/riak/robject_spec.rb +12 -11
- data/spec/spec_helper.rb +3 -1
- data/spec/support/riak_test.rb +77 -0
- data/spec/support/search_corpus_setup.rb +18 -0
- data/spec/support/sometimes.rb +1 -1
- data/spec/support/test_server.rb +1 -1
- data/spec/support/unified_backend_examples.rb +53 -7
- data/spec/support/version_filter.rb +4 -11
- metadata +56 -22
- data/lib/riak/client/pool.rb +0 -180
- data/spec/riak/pool_spec.rb +0 -306
@@ -0,0 +1,49 @@
|
|
1
|
+
module Riak
|
2
|
+
class MapReduce
|
3
|
+
# @api private
|
4
|
+
# Collects and normalizes results from MapReduce requests
|
5
|
+
class Results
|
6
|
+
# Creates a new result collector
|
7
|
+
# @param [MapReduce] mr the MapReduce query
|
8
|
+
def initialize(mr)
|
9
|
+
@keep_count = mr.query.select {|p| p.keep }.size
|
10
|
+
@hash = create_results_hash(mr.query)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Adds a new result to the collector
|
14
|
+
# @param [Fixnum] phase the phase index
|
15
|
+
# @param [Array] data the phase result
|
16
|
+
def add(phase, result)
|
17
|
+
@hash[phase] += result
|
18
|
+
end
|
19
|
+
|
20
|
+
# Coalesces the query results
|
21
|
+
# @return [Array] the query results, coalesced according to the
|
22
|
+
# phase configuration
|
23
|
+
def report
|
24
|
+
if @keep_count > 1
|
25
|
+
@hash.to_a.sort.map {|(num, results)| results }
|
26
|
+
else
|
27
|
+
@hash[@hash.keys.first]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def create_results_hash(query)
|
33
|
+
# When the query is empty, only bucket/key pairs are returned,
|
34
|
+
# but implicitly in phase 0.
|
35
|
+
return { 0 => [] } if query.empty?
|
36
|
+
|
37
|
+
# Pre-populate the hash with empty results for kept phases.
|
38
|
+
# Additionally, the last phase is always implictly kept, even
|
39
|
+
# when keep is false.
|
40
|
+
query.inject({}) do |hash, phase|
|
41
|
+
if phase.keep || query[-1] == phase
|
42
|
+
hash[query.index(phase)] = []
|
43
|
+
end
|
44
|
+
hash
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/riak/node/console.rb
CHANGED
@@ -46,15 +46,25 @@ module Riak
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
raise ArgumentError, t('no_pipes', :path => pipedir.to_s) if [@rfile, @wfile].any? {|p| p.nil? }
|
49
|
-
|
50
|
-
# to the write pipe, otherwise JRuby hangs.
|
49
|
+
|
51
50
|
begin
|
52
51
|
debug "Opening write pipe."
|
53
52
|
@w = open_write_pipe
|
54
53
|
@w.sync = true
|
55
|
-
|
56
|
-
|
57
|
-
|
54
|
+
read_ready = false
|
55
|
+
# Using threads, we get around JRuby's blocking-open
|
56
|
+
# behavior. The main thread pumps the write pipe full of
|
57
|
+
# data so that run_erl will start echoing it into the read
|
58
|
+
# pipe once we have started the open. On non-JVM rubies,
|
59
|
+
# O_NONBLOCK works and we proceed as expected.
|
60
|
+
Thread.new do
|
61
|
+
debug "Opening read pipe."
|
62
|
+
@r = open_read_pipe
|
63
|
+
read_ready = true
|
64
|
+
end
|
65
|
+
Thread.pass
|
66
|
+
@w.print "\n" until read_ready
|
67
|
+
command "ok."
|
58
68
|
debug "Initialized console: #{@r.inspect} #{@w.inspect}"
|
59
69
|
rescue => e
|
60
70
|
debug e.message
|
@@ -102,7 +112,6 @@ module Riak
|
|
102
112
|
def close
|
103
113
|
@r.close if @r && !@r.closed?
|
104
114
|
@w.close if @w && !@w.closed?
|
105
|
-
Signal.trap("WINCH", @winch)
|
106
115
|
freeze
|
107
116
|
end
|
108
117
|
|
@@ -113,19 +122,11 @@ module Riak
|
|
113
122
|
end
|
114
123
|
|
115
124
|
def open_write_pipe
|
116
|
-
|
117
|
-
java.io.FileOutputStream.new(@wfile.to_s).to_io
|
118
|
-
else
|
119
|
-
@wfile.open(File::WRONLY|File::NONBLOCK)
|
120
|
-
end
|
125
|
+
@wfile.open(File::WRONLY|File::NONBLOCK)
|
121
126
|
end
|
122
127
|
|
123
128
|
def open_read_pipe
|
124
|
-
|
125
|
-
IO.popen("cat #{@rfile}", "rb")
|
126
|
-
else
|
127
|
-
@rfile.open(File::RDONLY|File::NONBLOCK)
|
128
|
-
end
|
129
|
+
@rfile.open(File::RDONLY|File::NONBLOCK)
|
129
130
|
end
|
130
131
|
end
|
131
132
|
end
|
data/lib/riak/node/generation.rb
CHANGED
@@ -16,6 +16,7 @@ module Riak
|
|
16
16
|
# Generates the node.
|
17
17
|
def create
|
18
18
|
unless exist?
|
19
|
+
touch_ssl_distribution_args
|
19
20
|
create_directories
|
20
21
|
write_scripts
|
21
22
|
write_vm_args
|
@@ -102,5 +103,13 @@ module Riak
|
|
102
103
|
@configuration[:vm] = @vm
|
103
104
|
manifest.open('w') {|f| YAML.dump(@configuration, f) }
|
104
105
|
end
|
106
|
+
|
107
|
+
def touch_ssl_distribution_args
|
108
|
+
# To make sure that the ssl_distribution.args file is present,
|
109
|
+
# the control script in the source node has to have been run at
|
110
|
+
# least once. Running the `chkconfig` command is innocuous
|
111
|
+
# enough to accomplish this without other side-effects.
|
112
|
+
`#{(source + control_script.basename).to_s} chkconfig`
|
113
|
+
end
|
105
114
|
end
|
106
115
|
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'time'
|
3
|
+
require 'yaml'
|
4
|
+
require 'forwardable'
|
5
|
+
require 'riak/util/translation'
|
6
|
+
require 'riak/serializers'
|
7
|
+
|
8
|
+
module Riak
|
9
|
+
# Represents single (potentially-conflicted) value stored against a
|
10
|
+
# key in the Riak database. This includes the raw value as well as
|
11
|
+
# metadata.
|
12
|
+
# @since 1.1.0
|
13
|
+
class RContent
|
14
|
+
extend Forwardable
|
15
|
+
include Util::Translation
|
16
|
+
|
17
|
+
# @return [String] the MIME content type of the value
|
18
|
+
attr_accessor :content_type
|
19
|
+
|
20
|
+
# @return [Set<Link>] a Set of {Riak::Link} objects for relationships between this object and other resources
|
21
|
+
attr_accessor :links
|
22
|
+
|
23
|
+
# @return [String] the ETag header from the most recent HTTP response, useful for caching and reloading
|
24
|
+
attr_accessor :etag
|
25
|
+
|
26
|
+
# @return [Time] the Last-Modified header from the most recent HTTP response, useful for caching and reloading
|
27
|
+
attr_accessor :last_modified
|
28
|
+
|
29
|
+
# @return [Hash] a hash of any X-Riak-Meta-* headers that were in the HTTP response, keyed on the trailing portion
|
30
|
+
attr_accessor :meta
|
31
|
+
|
32
|
+
# @return [Hash<Set>] a hash of secondary indexes, where the
|
33
|
+
# key is the index name and the value is a Set of index
|
34
|
+
# entries for that index
|
35
|
+
attr_accessor :indexes
|
36
|
+
|
37
|
+
# @return [Riak::RObject] the RObject to which this sibling belongs
|
38
|
+
attr_accessor :robject
|
39
|
+
|
40
|
+
def_delegators :robject, :bucket, :key, :vclock
|
41
|
+
|
42
|
+
# Creates a new object value. This should not normally need to be
|
43
|
+
# called by users of the client. Normal, single-value use can rely
|
44
|
+
# on the delegating accessors on {Riak::RObject}.
|
45
|
+
# @param [RObject] robject the object that this value belongs to
|
46
|
+
# @yield self the new RContent
|
47
|
+
def initialize(robject)
|
48
|
+
@robject = robject
|
49
|
+
@links, @meta = Set.new, {}
|
50
|
+
@indexes = new_index_hash
|
51
|
+
yield self if block_given?
|
52
|
+
end
|
53
|
+
|
54
|
+
def indexes=(hash)
|
55
|
+
@indexes = hash.inject(new_index_hash) do |h, (k,v)|
|
56
|
+
h[k].merge([*v])
|
57
|
+
h
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return [Object] the unmarshaled form of {#raw_data} stored in riak at this object's key
|
62
|
+
def data
|
63
|
+
if @raw_data && !@data
|
64
|
+
raw = @raw_data.respond_to?(:read) ? @raw_data.read : @raw_data
|
65
|
+
@data = deserialize(raw)
|
66
|
+
@raw_data = nil
|
67
|
+
end
|
68
|
+
@data
|
69
|
+
end
|
70
|
+
|
71
|
+
# @param [Object] unmarshaled form of the data to be stored in riak. Object will be serialized using {#serialize} if a known content_type is used. Setting this overrides values stored with {#raw_data=}
|
72
|
+
# @return [Object] the object stored
|
73
|
+
def data=(new_data)
|
74
|
+
if new_data.respond_to?(:read)
|
75
|
+
raise ArgumentError.new(t("invalid_io_object"))
|
76
|
+
end
|
77
|
+
|
78
|
+
@raw_data = nil
|
79
|
+
@data = new_data
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
# @return [String] raw data stored in riak for this object's key
|
84
|
+
def raw_data
|
85
|
+
if @data && !@raw_data
|
86
|
+
@raw_data = serialize(@data)
|
87
|
+
@data = nil
|
88
|
+
end
|
89
|
+
@raw_data
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param [String, IO-like] the raw data to be stored in riak at this key, will not be marshaled or manipulated prior to storage. Overrides any data stored by {#data=}
|
93
|
+
# @return [String] the data stored
|
94
|
+
def raw_data=(new_raw_data)
|
95
|
+
@data = nil
|
96
|
+
@raw_data = new_raw_data
|
97
|
+
end
|
98
|
+
|
99
|
+
# Serializes the internal object data for sending to Riak. Differs based on the content-type.
|
100
|
+
# This method is called internally when storing the object.
|
101
|
+
# Automatically serialized formats:
|
102
|
+
# * JSON (application/json)
|
103
|
+
# * YAML (text/yaml)
|
104
|
+
# * Marshal (application/x-ruby-marshal)
|
105
|
+
# When given an IO-like object (e.g. File), no serialization will
|
106
|
+
# be done.
|
107
|
+
# @param [Object] payload the data to serialize
|
108
|
+
def serialize(payload)
|
109
|
+
Serializers.serialize(@content_type, payload)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Deserializes the internal object data from a Riak response. Differs based on the content-type.
|
113
|
+
# This method is called internally when loading the object.
|
114
|
+
# Automatically deserialized formats:
|
115
|
+
# * JSON (application/json)
|
116
|
+
# * YAML (text/yaml)
|
117
|
+
# * Marshal (application/x-ruby-marshal)
|
118
|
+
# @param [String] body the serialized response body
|
119
|
+
def deserialize(body)
|
120
|
+
Serializers.deserialize(@content_type, body)
|
121
|
+
end
|
122
|
+
|
123
|
+
# @return [String] A representation suitable for IRB and debugging output
|
124
|
+
def inspect
|
125
|
+
body = if @data || Serializers[content_type]
|
126
|
+
data.inspect
|
127
|
+
else
|
128
|
+
@raw_data && "(#{@raw_data.size} bytes)"
|
129
|
+
end
|
130
|
+
"#<#{self.class.name} [#{@content_type}]:#{body}>"
|
131
|
+
end
|
132
|
+
|
133
|
+
# @hidden
|
134
|
+
def load_map_reduce_value(hash)
|
135
|
+
metadata = hash['metadata']
|
136
|
+
extract_if_present(metadata, 'X-Riak-VTag', :etag)
|
137
|
+
extract_if_present(metadata, 'content-type', :content_type)
|
138
|
+
extract_if_present(metadata, 'X-Riak-Last-Modified', :last_modified) { |v| Time.httpdate( v ) }
|
139
|
+
extract_if_present(metadata, 'index', :indexes) do |entries|
|
140
|
+
Hash[ entries.map {|k,v| [k, Set.new(Array(v))] } ]
|
141
|
+
end
|
142
|
+
extract_if_present(metadata, 'Links', :links) do |links|
|
143
|
+
Set.new( links.map { |l| Link.new(*l) } )
|
144
|
+
end
|
145
|
+
extract_if_present(metadata, 'X-Riak-Meta', :meta) do |meta|
|
146
|
+
Hash[
|
147
|
+
meta.map do |k,v|
|
148
|
+
[k.sub(%r{^x-riak-meta-}i, ''), [v]]
|
149
|
+
end
|
150
|
+
]
|
151
|
+
end
|
152
|
+
extract_if_present(hash, 'data', :data) { |v| deserialize(v) }
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
def extract_if_present(hash, key, attribute=nil)
|
157
|
+
if hash[key].present?
|
158
|
+
attribute ||= key
|
159
|
+
value = block_given? ? yield(hash[key]) : hash[key]
|
160
|
+
send("#{attribute}=", value)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def new_index_hash
|
165
|
+
Hash.new {|h,k| h[k] = Set.new }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
data/lib/riak/robject.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require '
|
1
|
+
require 'forwardable'
|
2
|
+
require 'riak/rcontent'
|
3
|
+
require 'riak/conflict'
|
4
4
|
require 'riak/util/translation'
|
5
5
|
require 'riak/util/escape'
|
6
6
|
require 'riak/bucket'
|
7
7
|
require 'riak/link'
|
8
8
|
require 'riak/walk_spec'
|
9
|
-
require 'riak/serializers'
|
10
9
|
|
11
10
|
module Riak
|
12
11
|
# Represents the data and metadata stored in a bucket/key pair in
|
@@ -16,6 +15,7 @@ module Riak
|
|
16
15
|
extend Util::Translation
|
17
16
|
include Util::Escape
|
18
17
|
extend Util::Escape
|
18
|
+
extend Forwardable
|
19
19
|
|
20
20
|
# @return [Bucket] the bucket in which this object is contained
|
21
21
|
attr_accessor :bucket
|
@@ -23,28 +23,11 @@ module Riak
|
|
23
23
|
# @return [String] the key of this object within its bucket
|
24
24
|
attr_accessor :key
|
25
25
|
|
26
|
-
# @return [String] the MIME content type of the object
|
27
|
-
attr_accessor :content_type
|
28
|
-
|
29
26
|
# @return [String] the Riak vector clock for the object
|
30
27
|
attr_accessor :vclock
|
31
28
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# @return [String] the ETag header from the most recent HTTP response, useful for caching and reloading
|
36
|
-
attr_accessor :etag
|
37
|
-
|
38
|
-
# @return [Time] the Last-Modified header from the most recent HTTP response, useful for caching and reloading
|
39
|
-
attr_accessor :last_modified
|
40
|
-
|
41
|
-
# @return [Hash] a hash of any X-Riak-Meta-* headers that were in the HTTP response, keyed on the trailing portion
|
42
|
-
attr_accessor :meta
|
43
|
-
|
44
|
-
# @return [Hash<Set>] a hash of secondary indexes, where the
|
45
|
-
# key is the index name and the value is a Set of index
|
46
|
-
# entries for that index
|
47
|
-
attr_accessor :indexes
|
29
|
+
alias :vector_clock :vclock
|
30
|
+
alias :vector_clock= :vclock=
|
48
31
|
|
49
32
|
# @return [Boolean] whether to attempt to prevent stale writes using conditional PUT semantics, If-None-Match: * or If-Match: {#etag}
|
50
33
|
# @see http://wiki.basho.com/display/RIAK/REST+API#RESTAPI-Storeaneworexistingobjectwithakey Riak Rest API Docs
|
@@ -69,6 +52,16 @@ module Riak
|
|
69
52
|
@on_conflict_hooks ||= []
|
70
53
|
end
|
71
54
|
|
55
|
+
def_delegators :content, :content_type, :content_type=,
|
56
|
+
:links, :links=,
|
57
|
+
:etag, :etag=,
|
58
|
+
:last_modified, :last_modified=,
|
59
|
+
:meta, :meta=,
|
60
|
+
:indexes, :indexes=,
|
61
|
+
:data, :data=,
|
62
|
+
:raw_data, :raw_data=,
|
63
|
+
:deserialize, :serialize
|
64
|
+
|
72
65
|
# Attempts to resolve conflict using the registered conflict callbacks.
|
73
66
|
#
|
74
67
|
# @return [RObject] the RObject
|
@@ -102,18 +95,10 @@ module Riak
|
|
102
95
|
# @see Bucket#get
|
103
96
|
def initialize(bucket, key=nil)
|
104
97
|
@bucket, @key = bucket, key
|
105
|
-
@
|
106
|
-
@indexes = new_index_hash
|
98
|
+
@siblings = [ RContent.new(self) ]
|
107
99
|
yield self if block_given?
|
108
100
|
end
|
109
101
|
|
110
|
-
def indexes=(hash)
|
111
|
-
@indexes = hash.inject(new_index_hash) do |h, (k,v)|
|
112
|
-
h[k].merge([*v])
|
113
|
-
h
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
102
|
# Load object data from a map/reduce response item.
|
118
103
|
# This method is used by RObject::load_from_mapreduce to instantiate the necessary
|
119
104
|
# objects.
|
@@ -121,58 +106,14 @@ module Riak
|
|
121
106
|
# @return [RObject] self
|
122
107
|
def load_from_mapreduce(response)
|
123
108
|
self.vclock = response['vclock']
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
else
|
128
|
-
@conflict = true
|
129
|
-
@siblings = response['values'].map do |v|
|
130
|
-
RObject.new(self.bucket, self.key) do |robj|
|
131
|
-
robj.vclock = self.vclock
|
132
|
-
robj.load_map_reduce_value(v)
|
133
|
-
end
|
109
|
+
@siblings = response['values'].map do |v|
|
110
|
+
RContent.new(self) do |rcontent|
|
111
|
+
rcontent.load_map_reduce_value(v)
|
134
112
|
end
|
135
113
|
end
|
136
114
|
self
|
137
115
|
end
|
138
116
|
|
139
|
-
# @return [Object] the unmarshaled form of {#raw_data} stored in riak at this object's key
|
140
|
-
def data
|
141
|
-
if @raw_data && !@data
|
142
|
-
raw = @raw_data.respond_to?(:read) ? @raw_data.read : @raw_data
|
143
|
-
@data = deserialize(raw)
|
144
|
-
@raw_data = nil
|
145
|
-
end
|
146
|
-
@data
|
147
|
-
end
|
148
|
-
|
149
|
-
# @param [Object] unmarshaled form of the data to be stored in riak. Object will be serialized using {#serialize} if a known content_type is used. Setting this overrides values stored with {#raw_data=}
|
150
|
-
# @return [Object] the object stored
|
151
|
-
def data=(new_data)
|
152
|
-
if new_data.respond_to?(:read)
|
153
|
-
raise ArgumentError.new(t("invalid_io_object"))
|
154
|
-
end
|
155
|
-
|
156
|
-
@raw_data = nil
|
157
|
-
@data = new_data
|
158
|
-
end
|
159
|
-
|
160
|
-
# @return [String] raw data stored in riak for this object's key
|
161
|
-
def raw_data
|
162
|
-
if @data && !@raw_data
|
163
|
-
@raw_data = serialize(@data)
|
164
|
-
@data = nil
|
165
|
-
end
|
166
|
-
@raw_data
|
167
|
-
end
|
168
|
-
|
169
|
-
# @param [String, IO-like] the raw data to be stored in riak at this key, will not be marshaled or manipulated prior to storage. Overrides any data stored by {#data=}
|
170
|
-
# @return [String] the data stored
|
171
|
-
def raw_data=(new_raw_data)
|
172
|
-
@data = nil
|
173
|
-
@raw_data = new_raw_data
|
174
|
-
end
|
175
|
-
|
176
117
|
# Store the object in Riak
|
177
118
|
# @param [Hash] options query parameters
|
178
119
|
# @option options [Fixnum] :r the "r" parameter (Read quorum for the implicit read performed when validating the store operation)
|
@@ -181,8 +122,10 @@ module Riak
|
|
181
122
|
# @option options [Boolean] :returnbody (true) whether to return the result of a successful write in the body of the response. Set to false for fire-and-forget updates, set to true to immediately have access to the object's stored representation.
|
182
123
|
# @return [Riak::RObject] self
|
183
124
|
# @raise [ArgumentError] if the content_type is not defined
|
125
|
+
# @raise [Conflict] if the object has siblings
|
184
126
|
def store(options={})
|
185
|
-
raise
|
127
|
+
raise Conflict, self if conflict?
|
128
|
+
raise ArgumentError, t("content_type_undefined") unless content_type.present?
|
186
129
|
@bucket.client.store_object(self, options)
|
187
130
|
self
|
188
131
|
end
|
@@ -213,54 +156,29 @@ module Riak
|
|
213
156
|
freeze
|
214
157
|
end
|
215
158
|
|
216
|
-
|
159
|
+
# Returns sibling values. If the object is not in conflict, then
|
160
|
+
# only one value will be present in the array.
|
161
|
+
# @return [Array<RContent>] an array of conflicting sibling values
|
162
|
+
# for this key, possibly containing only one
|
163
|
+
attr_accessor :siblings
|
217
164
|
|
218
|
-
# Returns sibling
|
219
|
-
# @return [
|
220
|
-
# @
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
@siblings
|
165
|
+
# Returns the solitary sibling when not in conflict.
|
166
|
+
# @return [RContent] the sole value/sibling on this object
|
167
|
+
# @raise [Conflict] when multiple siblings are present
|
168
|
+
def content
|
169
|
+
raise Conflict, self if conflict?
|
170
|
+
@siblings.first
|
225
171
|
end
|
226
172
|
|
227
173
|
# @return [true,false] Whether this object has conflicting sibling objects (divergent vclocks)
|
228
174
|
def conflict?
|
229
|
-
@
|
230
|
-
end
|
231
|
-
|
232
|
-
# Serializes the internal object data for sending to Riak. Differs based on the content-type.
|
233
|
-
# This method is called internally when storing the object.
|
234
|
-
# Automatically serialized formats:
|
235
|
-
# * JSON (application/json)
|
236
|
-
# * YAML (text/yaml)
|
237
|
-
# * Marshal (application/x-ruby-marshal)
|
238
|
-
# When given an IO-like object (e.g. File), no serialization will
|
239
|
-
# be done.
|
240
|
-
# @param [Object] payload the data to serialize
|
241
|
-
def serialize(payload)
|
242
|
-
Serializers.serialize(@content_type, payload)
|
243
|
-
end
|
244
|
-
|
245
|
-
# Deserializes the internal object data from a Riak response. Differs based on the content-type.
|
246
|
-
# This method is called internally when loading the object.
|
247
|
-
# Automatically deserialized formats:
|
248
|
-
# * JSON (application/json)
|
249
|
-
# * YAML (text/yaml)
|
250
|
-
# * Marshal (application/x-ruby-marshal)
|
251
|
-
# @param [String] body the serialized response body
|
252
|
-
def deserialize(body)
|
253
|
-
Serializers.deserialize(@content_type, body)
|
175
|
+
@siblings.size != 1
|
254
176
|
end
|
255
177
|
|
256
178
|
# @return [String] A representation suitable for IRB and debugging output
|
257
179
|
def inspect
|
258
|
-
body =
|
259
|
-
|
260
|
-
else
|
261
|
-
@raw_data && "(#{@raw_data.size} bytes)"
|
262
|
-
end
|
263
|
-
"#<#{self.class.name} {#{bucket.name}#{"," + @key if @key}} [#{@content_type}]:#{body}>"
|
180
|
+
body = @siblings.map {|s| s.inspect }.join(", ")
|
181
|
+
"#<#{self.class.name} {#{bucket.name}#{"," + @key if @key}} [#{body}]>"
|
264
182
|
end
|
265
183
|
|
266
184
|
# Walks links from this object to other objects in Riak.
|
@@ -276,43 +194,5 @@ module Riak
|
|
276
194
|
def to_link(tag)
|
277
195
|
Link.new(@bucket.name, @key, tag)
|
278
196
|
end
|
279
|
-
|
280
|
-
alias :vector_clock :vclock
|
281
|
-
alias :vector_clock= :vclock=
|
282
|
-
|
283
|
-
protected
|
284
|
-
def load_map_reduce_value(hash)
|
285
|
-
metadata = hash['metadata']
|
286
|
-
extract_if_present(metadata, 'X-Riak-VTag', :etag)
|
287
|
-
extract_if_present(metadata, 'content-type', :content_type)
|
288
|
-
extract_if_present(metadata, 'X-Riak-Last-Modified', :last_modified) { |v| Time.httpdate( v ) }
|
289
|
-
extract_if_present(metadata, 'index', :indexes) do |entries|
|
290
|
-
Hash[ entries.map {|k,v| [k, Set.new(Array(v))] } ]
|
291
|
-
end
|
292
|
-
extract_if_present(metadata, 'Links', :links) do |links|
|
293
|
-
Set.new( links.map { |l| Link.new(*l) } )
|
294
|
-
end
|
295
|
-
extract_if_present(metadata, 'X-Riak-Meta', :meta) do |meta|
|
296
|
-
Hash[
|
297
|
-
meta.map do |k,v|
|
298
|
-
[k.sub(%r{^x-riak-meta-}i, ''), [v]]
|
299
|
-
end
|
300
|
-
]
|
301
|
-
end
|
302
|
-
extract_if_present(hash, 'data', :data) { |v| deserialize(v) }
|
303
|
-
end
|
304
|
-
|
305
|
-
private
|
306
|
-
def extract_if_present(hash, key, attribute=nil)
|
307
|
-
if hash[key].present?
|
308
|
-
attribute ||= key
|
309
|
-
value = block_given? ? yield(hash[key]) : hash[key]
|
310
|
-
send("#{attribute}=", value)
|
311
|
-
end
|
312
|
-
end
|
313
|
-
|
314
|
-
def new_index_hash
|
315
|
-
Hash.new {|h,k| h[k] = Set.new }
|
316
|
-
end
|
317
197
|
end
|
318
198
|
end
|