riak-client 2.2.2 → 2.3.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.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/RELEASE_NOTES.md +16 -0
- data/Rakefile +3 -2
- data/lib/riak/client/beefcake/message_codes.rb +12 -0
- data/lib/riak/client/beefcake/messages.rb +205 -5
- data/lib/riak/client/beefcake/object_methods.rb +1 -2
- data/lib/riak/client/beefcake/operator.rb +9 -0
- data/lib/riak/client/beefcake/time_series_delete_operator.rb +24 -0
- data/lib/riak/client/beefcake/time_series_get_operator.rb +35 -0
- data/lib/riak/client/beefcake/time_series_list_operator.rb +42 -0
- data/lib/riak/client/beefcake/time_series_put_operator.rb +32 -0
- data/lib/riak/client/beefcake/time_series_query_operator.rb +42 -0
- data/lib/riak/client/beefcake/ts_cell_codec.rb +63 -0
- data/lib/riak/client/beefcake_protobuffs_backend.rb +5 -0
- data/lib/riak/client.rb +3 -0
- data/lib/riak/errors/time_series.rb +23 -0
- data/lib/riak/locale/en.yml +5 -0
- data/lib/riak/time_series/collection.rb +5 -0
- data/lib/riak/time_series/deletion.rb +26 -0
- data/lib/riak/time_series/list.rb +66 -0
- data/lib/riak/time_series/query.rb +43 -0
- data/lib/riak/time_series/read.rb +18 -0
- data/lib/riak/time_series/row.rb +4 -0
- data/lib/riak/time_series/submission.rb +32 -0
- data/lib/riak/time_series.rb +17 -0
- data/lib/riak/version.rb +1 -1
- data/lib/riak.rb +16 -0
- data/spec/integration/riak/bucket_types_spec.rb +6 -6
- data/spec/integration/riak/time_series_spec.rb +168 -0
- data/spec/riak/beefcake_protobuffs_backend/object_methods_spec.rb +31 -2
- data/spec/riak/beefcake_protobuffs_backend/ts_cell_codec_spec.rb +116 -0
- data/spec/riak/client_spec.rb +11 -0
- data/spec/riak/crdt/inner_flag_spec.rb +1 -1
- data/spec/riak/time_series/deletion_spec.rb +33 -0
- data/spec/riak/time_series/listing_spec.rb +50 -0
- data/spec/riak/time_series/submission_spec.rb +35 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/test_client.yml +0 -1
- metadata +29 -3
@@ -0,0 +1,26 @@
|
|
1
|
+
module Riak::TimeSeries
|
2
|
+
|
3
|
+
# Delete entries from Riak Time Series.
|
4
|
+
class Deletion
|
5
|
+
attr_accessor :key
|
6
|
+
attr_accessor :options
|
7
|
+
|
8
|
+
attr_reader :client
|
9
|
+
attr_reader :table_name
|
10
|
+
|
11
|
+
def initialize(client, table_name)
|
12
|
+
@client = client
|
13
|
+
@table_name = table_name
|
14
|
+
@options = Hash.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete!
|
18
|
+
client.backend do |be|
|
19
|
+
be.time_series_delete_operator.delete(table_name,
|
20
|
+
key,
|
21
|
+
options)
|
22
|
+
end
|
23
|
+
true
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Riak::TimeSeries
|
2
|
+
|
3
|
+
# A request to list keys in a Riak Time Series collection. Very expensive,
|
4
|
+
# not recommended for use in production.
|
5
|
+
class List
|
6
|
+
include Riak::Util::Translation
|
7
|
+
|
8
|
+
# @!attribute [r] table_name
|
9
|
+
# @return [String] the table name to list keys in
|
10
|
+
attr_reader :table_name
|
11
|
+
|
12
|
+
# @!attribute [r] client
|
13
|
+
# @return [Riak::Client] the Riak client to use for the list keys operation
|
14
|
+
attr_reader :client
|
15
|
+
|
16
|
+
# @!attribute [rw] timeout
|
17
|
+
# @return [Integer] how many milliseconds Riak should wait for listing
|
18
|
+
attr_accessor :timeout
|
19
|
+
|
20
|
+
# @!attribute [r] results
|
21
|
+
# @return [Riak::TimeSeries::Collection<Riak::TimeSeries::Row>] each key
|
22
|
+
# as a row in a collection; nil if keys were streamed to a block
|
23
|
+
attr_reader :results
|
24
|
+
|
25
|
+
# Initializes but does not issue the list keys operation
|
26
|
+
#
|
27
|
+
# @param [Riak::Client] client the Riak Client to list keys with
|
28
|
+
# @param [String] table_name the table name to list keys in
|
29
|
+
def initialize(client, table_name)
|
30
|
+
@client = client
|
31
|
+
@table_name = table_name
|
32
|
+
@timeout = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# Issue the list keys request. Takes a block for streaming results, or
|
36
|
+
# sets the #results read-only attribute iff no block is given.
|
37
|
+
#
|
38
|
+
# @yieldparam key [Riak::TimeSeries::Row] a listed key
|
39
|
+
def issue!(&block)
|
40
|
+
list_keys_warning(caller)
|
41
|
+
|
42
|
+
options = { timeout: self.timeout }
|
43
|
+
|
44
|
+
potential_results = nil
|
45
|
+
|
46
|
+
client.backend do |be|
|
47
|
+
potential_results = be.time_series_list_operator.list(table_name,
|
48
|
+
block,
|
49
|
+
options)
|
50
|
+
end
|
51
|
+
|
52
|
+
return @results = potential_results unless block_given?
|
53
|
+
|
54
|
+
true
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def list_keys_warning(bound_caller)
|
59
|
+
return if Riak.disable_list_keys_warnings
|
60
|
+
|
61
|
+
backtrace = bound_caller.join("\n ")
|
62
|
+
|
63
|
+
warn(t('time_series.list_keys'), backtrace: backtrace)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Riak::TimeSeries
|
2
|
+
|
3
|
+
# A query for Riak Time Series. Supports SQL for querying (data
|
4
|
+
# manipulation language, or DML).
|
5
|
+
class Query
|
6
|
+
|
7
|
+
# @!attribute [rw] query_text
|
8
|
+
# @return [String] the SQL query to run
|
9
|
+
attr_accessor :query_text
|
10
|
+
|
11
|
+
# Values to be interpolated into the query, support planned in Riak TS
|
12
|
+
# 1.2
|
13
|
+
attr_accessor :interpolations
|
14
|
+
|
15
|
+
# @!attribute [r] client
|
16
|
+
# @return [Riak::Client] the Riak client to use for the TS query
|
17
|
+
attr_reader :client
|
18
|
+
|
19
|
+
# #!attribute [r] results
|
20
|
+
# @return [Riak::Client::BeefcakeProtobuffsBackend::TsQueryResp]
|
21
|
+
# backend-dependent results object
|
22
|
+
attr_reader :results
|
23
|
+
|
24
|
+
# Initialize a query object
|
25
|
+
#
|
26
|
+
# @param [Riak::Client] client the client connected to the riak cluster
|
27
|
+
# @param [String] query_text the SQL query to run
|
28
|
+
# @param interpolations planned for Riak TS 1.1
|
29
|
+
def initialize(client, query_text, interpolations = {})
|
30
|
+
@client = client
|
31
|
+
@query_text = query_text
|
32
|
+
@interpolations = interpolations
|
33
|
+
end
|
34
|
+
|
35
|
+
# Run the query against Riak TS, and store the results in the `results`
|
36
|
+
# attribute
|
37
|
+
def issue!
|
38
|
+
@results = client.backend do |be|
|
39
|
+
be.time_series_query_operator.query(query_text, interpolations)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Riak::TimeSeries
|
2
|
+
class Read
|
3
|
+
attr_accessor :key
|
4
|
+
attr_reader :client
|
5
|
+
attr_reader :table_name
|
6
|
+
|
7
|
+
def initialize(client, table_name)
|
8
|
+
@client = client
|
9
|
+
@table_name = table_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def read!
|
13
|
+
client.backend do |be|
|
14
|
+
be.time_series_get_operator.get(table_name, key)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Riak::TimeSeries
|
2
|
+
class Submission
|
3
|
+
|
4
|
+
# @!attributes [rw] measurements
|
5
|
+
# @return [Array<Array<Object>>] measurements to write to Riak TS
|
6
|
+
attr_accessor :measurements
|
7
|
+
|
8
|
+
# @!attribute [r] client
|
9
|
+
# @return [Riak::Client] the client to write submissions to
|
10
|
+
attr_reader :client
|
11
|
+
|
12
|
+
# @!attribute [r] table_name
|
13
|
+
# @return [String] the table name to write submissions to
|
14
|
+
attr_reader :table_name
|
15
|
+
|
16
|
+
# Initializes the submission object with a client and table name
|
17
|
+
#
|
18
|
+
# @param [Riak::Client] client the client connected to the Riak TS cluster
|
19
|
+
# @param [String] table_name the table name in the cluster
|
20
|
+
def initialize(client, table_name)
|
21
|
+
@client = client
|
22
|
+
@table_name = table_name
|
23
|
+
end
|
24
|
+
|
25
|
+
# Write the submitted data to Riak.
|
26
|
+
def write!
|
27
|
+
client.backend do |be|
|
28
|
+
be.time_series_put_operator.put(table_name, measurements)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Riak
|
2
|
+
|
3
|
+
# Container module for Riak Time Series features.
|
4
|
+
module TimeSeries
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'riak/errors/time_series'
|
9
|
+
|
10
|
+
require 'riak/time_series/collection'
|
11
|
+
require 'riak/time_series/row'
|
12
|
+
|
13
|
+
require 'riak/time_series/deletion'
|
14
|
+
require 'riak/time_series/list'
|
15
|
+
require 'riak/time_series/query'
|
16
|
+
require 'riak/time_series/submission'
|
17
|
+
require 'riak/time_series/read'
|
data/lib/riak/version.rb
CHANGED
data/lib/riak.rb
CHANGED
@@ -13,11 +13,27 @@ module Riak
|
|
13
13
|
module Util; end
|
14
14
|
extend Util::Translation
|
15
15
|
|
16
|
+
class NullLogger
|
17
|
+
def fatal(msg) end
|
18
|
+
|
19
|
+
def error(msg) end
|
20
|
+
|
21
|
+
def warn(msg) end
|
22
|
+
|
23
|
+
def info(msg) end
|
24
|
+
|
25
|
+
def debug(msg) end
|
26
|
+
end
|
27
|
+
|
16
28
|
class << self
|
17
29
|
# Only change this if you really know what you're doing. Better to
|
18
30
|
# err on the side of caution and assume you don't.
|
19
31
|
# @private
|
20
32
|
attr_accessor :disable_list_keys_warnings
|
33
|
+
|
34
|
+
# Set a custom logger object (e.g. Riak.logger = Rails.logger)
|
35
|
+
attr_accessor :logger
|
21
36
|
end
|
22
37
|
self.disable_list_keys_warnings = false
|
38
|
+
self.logger = NullLogger.new
|
23
39
|
end
|
@@ -162,15 +162,15 @@ describe 'Bucket Types', test_client: true, integration: true do
|
|
162
162
|
let(:untyped_bucket){ test_client.bucket bucket.name }
|
163
163
|
|
164
164
|
it 'allows reading and writing bucket properties' do
|
165
|
-
expect(test_client.get_bucket_props(bucket, type: 'yokozuna')['
|
166
|
-
expect(test_client.get_bucket_props(untyped_bucket)['
|
165
|
+
expect(test_client.get_bucket_props(bucket, type: 'yokozuna')['notfound_ok']).to be
|
166
|
+
expect(test_client.get_bucket_props(untyped_bucket)['notfound_ok']).to be
|
167
167
|
|
168
168
|
# test setting
|
169
|
-
expect{ bucket.props = {'
|
169
|
+
expect{ bucket.props = {'notfound_ok' => false} }.to_not raise_error
|
170
170
|
|
171
171
|
# make sure setting doesn't leak to untyped bucket
|
172
|
-
expect(test_client.get_bucket_props(bucket, type: 'yokozuna')['
|
173
|
-
expect(test_client.get_bucket_props(untyped_bucket)['
|
172
|
+
expect(test_client.get_bucket_props(bucket, type: 'yokozuna')['notfound_ok']).to_not be
|
173
|
+
expect(test_client.get_bucket_props(untyped_bucket)['notfound_ok']).to be
|
174
174
|
|
175
175
|
# add canary setting on untyped bucket
|
176
176
|
expect{ untyped_bucket.props = { 'n_val' => 1} }.to_not raise_error
|
@@ -183,7 +183,7 @@ describe 'Bucket Types', test_client: true, integration: true do
|
|
183
183
|
expect{ bucket.clear_props }.to_not raise_error
|
184
184
|
|
185
185
|
# make sure clearing doesn't leak to canary setting on untyped bucket
|
186
|
-
expect(test_client.get_bucket_props(bucket, type: 'yokozuna')['
|
186
|
+
expect(test_client.get_bucket_props(bucket, type: 'yokozuna')['notfound_ok']).to be
|
187
187
|
expect(test_client.get_bucket_props(untyped_bucket)['n_val']).to eq 1
|
188
188
|
end
|
189
189
|
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'riak'
|
3
|
+
|
4
|
+
describe 'Time Series',
|
5
|
+
test_client: true, integration: true, time_series: true do
|
6
|
+
let(:table_name){ 'GeoCheckin' }
|
7
|
+
|
8
|
+
let(:now){ Time.at(Time.now.to_i) }
|
9
|
+
let(:five_minutes_ago){ now - 300 }
|
10
|
+
let(:now_range_str) do
|
11
|
+
past = (now.to_i - 100) * 1000
|
12
|
+
future = (now.to_i + 100) * 1000
|
13
|
+
"time > #{ past } AND time < #{ future }"
|
14
|
+
end
|
15
|
+
let(:never_range_str) do
|
16
|
+
range_start = '1'
|
17
|
+
range_end = '2'
|
18
|
+
"time > #{range_start} AND time < #{range_end}"
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:family){ 'family-' + random_key }
|
22
|
+
let(:series){ 'series-' + random_key }
|
23
|
+
|
24
|
+
let(:key){ [family, series, now] }
|
25
|
+
let(:key2){ [family, series, five_minutes_ago] }
|
26
|
+
let(:datum){ [*key, 'cloudy', 27.1] }
|
27
|
+
let(:datum_null){ [*key2, 'cloudy', nil] }
|
28
|
+
|
29
|
+
let(:family_series_str) do
|
30
|
+
"myfamily = '#{family}' AND myseries = '#{series}'"
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:query) do
|
34
|
+
<<-SQL
|
35
|
+
SELECT * FROM #{table_name}
|
36
|
+
WHERE
|
37
|
+
#{family_series_str} AND
|
38
|
+
#{now_range_str}
|
39
|
+
SQL
|
40
|
+
end
|
41
|
+
let(:no_data_query) do
|
42
|
+
<<-SQL
|
43
|
+
SELECT * FROM #{table_name}
|
44
|
+
WHERE
|
45
|
+
#{family_series_str} AND
|
46
|
+
#{never_range_str}
|
47
|
+
SQL
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:stored_datum_expectation) do
|
51
|
+
submission = Riak::TimeSeries::Submission.new test_client, table_name
|
52
|
+
submission.measurements = [datum]
|
53
|
+
expect{ submission.write! }.to_not raise_error
|
54
|
+
end
|
55
|
+
|
56
|
+
let(:stored_datum_null_expectation) do
|
57
|
+
submission = Riak::TimeSeries::Submission.new test_client, table_name
|
58
|
+
submission.measurements = [datum_null]
|
59
|
+
expect{ submission.write! }.to_not raise_error
|
60
|
+
end
|
61
|
+
|
62
|
+
describe 'query interface' do
|
63
|
+
subject{ Riak::TimeSeries::Query.new test_client, query }
|
64
|
+
let(:subject_without_data) do
|
65
|
+
Riak::TimeSeries::Query.new test_client, no_data_query
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'queries data without error' do
|
69
|
+
stored_datum_expectation
|
70
|
+
|
71
|
+
expect{ subject.issue! }.to_not raise_error
|
72
|
+
expect(subject.results).to be
|
73
|
+
expect(subject.results).to_not be_empty
|
74
|
+
expect(subject.results.columns).to_not be_empty
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'returns an empty collection when not finding data' do
|
78
|
+
expect{ subject_without_data.issue! }.to_not raise_error
|
79
|
+
expect(subject.results).to_not be
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'single-key get interface' do
|
84
|
+
subject{ Riak::TimeSeries::Read.new test_client, table_name }
|
85
|
+
it 'retrieves data without error' do
|
86
|
+
stored_datum_expectation
|
87
|
+
|
88
|
+
subject.key = key
|
89
|
+
result = nil
|
90
|
+
expect{ result = subject.read! }.to_not raise_error
|
91
|
+
expect(result).to be
|
92
|
+
expect(result).to_not be_empty
|
93
|
+
expect(result.first).to_not be_empty
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'retrieves data with a null value without error' do
|
97
|
+
stored_datum_null_expectation
|
98
|
+
|
99
|
+
subject.key = key2
|
100
|
+
result = nil
|
101
|
+
expect{ result = subject.read! }.to_not raise_error
|
102
|
+
expect(result).to be
|
103
|
+
expect(result).to_not be_empty
|
104
|
+
|
105
|
+
row = result.first
|
106
|
+
expect(row).to_not be_empty
|
107
|
+
expect(row[4]).to_not be
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'attempts retrieval of non-existent data without error' do
|
111
|
+
subject.key = [ 'foo', 'bar', now ]
|
112
|
+
result = nil
|
113
|
+
expect{ result = subject.read! }.to_not raise_error
|
114
|
+
expect(result).to_not be
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'single-key delete interface' do
|
119
|
+
subject{ Riak::TimeSeries::Deletion.new test_client, table_name }
|
120
|
+
let(:test_read){ Riak::TimeSeries::Read.new test_client, table_name }
|
121
|
+
|
122
|
+
it 'deletes data without error' do
|
123
|
+
stored_datum_expectation
|
124
|
+
|
125
|
+
test_read.key = key
|
126
|
+
expect(test_read.read!).to_not be_empty
|
127
|
+
|
128
|
+
subject.key = key
|
129
|
+
expect{ subject.delete! }.to_not raise_error
|
130
|
+
|
131
|
+
expect(test_read.read!).to_not be
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe 'submission interface' do
|
136
|
+
it 'writes data without error' do
|
137
|
+
stored_datum_expectation
|
138
|
+
end
|
139
|
+
it 'writes data with a null value without error' do
|
140
|
+
stored_datum_null_expectation
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe 'list interface' do
|
145
|
+
it 'passes listed keys to a block' do
|
146
|
+
stored_datum_expectation
|
147
|
+
found_expectation = double 'expectation'
|
148
|
+
expect(found_expectation).to receive(:found!).once
|
149
|
+
|
150
|
+
lister = Riak::TimeSeries::List.new test_client, table_name
|
151
|
+
|
152
|
+
lister.issue! do |row|
|
153
|
+
found_expectation.found! if row.to_a == key
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'returns a list of keys without a block' do
|
158
|
+
stored_datum_expectation
|
159
|
+
found_expectation = double 'expectation'
|
160
|
+
|
161
|
+
lister = Riak::TimeSeries::List.new test_client, table_name
|
162
|
+
|
163
|
+
results = lister.issue!
|
164
|
+
|
165
|
+
expect(results).to include key
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -8,16 +8,45 @@ describe Riak::Client::BeefcakeProtobuffsBackend::ObjectMethods do
|
|
8
8
|
@backend = Riak::Client::BeefcakeProtobuffsBackend.new(@client, @client.node)
|
9
9
|
@bucket = Riak::Bucket.new(@client, "bucket")
|
10
10
|
@object = Riak::RObject.new(@bucket, "bar")
|
11
|
+
@content = double(
|
12
|
+
:value => '',
|
13
|
+
:vtag => nil,
|
14
|
+
:content_type => nil,
|
15
|
+
:links => nil,
|
16
|
+
:usermeta => nil,
|
17
|
+
:last_mod => nil,
|
18
|
+
:last_mod_usecs => nil,
|
19
|
+
:indexes => nil,
|
20
|
+
:charset => nil
|
21
|
+
)
|
11
22
|
end
|
12
23
|
|
13
24
|
describe "loading object data from the response" do
|
14
25
|
it "loads the key" do
|
15
|
-
|
16
|
-
pbuf = double(:vclock => nil, :content => [content], :value => nil, :key => 'akey')
|
26
|
+
pbuf = double(:vclock => nil, :content => [@content], :value => nil, :key => 'akey')
|
17
27
|
o = @backend.load_object(pbuf, @object)
|
18
28
|
expect(o).to eq(@object)
|
19
29
|
expect(o.key).to eq(pbuf.key)
|
20
30
|
end
|
31
|
+
|
32
|
+
describe "last_modified" do
|
33
|
+
before :each do
|
34
|
+
allow(@content).to receive(:last_mod) { 1271442363 }
|
35
|
+
end
|
36
|
+
|
37
|
+
it "is set to time of last_mod with microseconds from last_mod_usecs" do
|
38
|
+
allow(@content).to receive(:last_mod_usecs) { 105696 }
|
39
|
+
pbuf = double(:vclock => nil, :content => [@content], :value => nil, :key => 'akey')
|
40
|
+
o = @backend.load_object(pbuf, @object)
|
41
|
+
expect(o.last_modified).to eq(Time.at(1271442363, 105696))
|
42
|
+
end
|
43
|
+
|
44
|
+
it "is set to time of last_mod without microseconds if last_mod_usecs is missing" do
|
45
|
+
pbuf = double(:vclock => nil, :content => [@content], :value => nil, :key => 'akey')
|
46
|
+
o = @backend.load_object(pbuf, @object)
|
47
|
+
expect(o.last_modified).to eq(Time.at(1271442363, 0))
|
48
|
+
end
|
49
|
+
end
|
21
50
|
end
|
22
51
|
|
23
52
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'bigdecimal'
|
3
|
+
require 'time'
|
4
|
+
|
5
|
+
Riak::Client::BeefcakeProtobuffsBackend.configured?
|
6
|
+
|
7
|
+
describe Riak::Client::BeefcakeProtobuffsBackend::TsCellCodec do
|
8
|
+
describe 'symmetric serialziation' do
|
9
|
+
it { is_expected.to symmetric_serialize("hello", varchar_value: "hello")}
|
10
|
+
it { is_expected.to symmetric_serialize(5, sint64_value: 5)}
|
11
|
+
it { is_expected.to symmetric_serialize(123.45, double_value: 123.45) }
|
12
|
+
it do
|
13
|
+
is_expected.to symmetric_serialize(Time.parse("June 23, 2015 at 9:46:28 EDT"),
|
14
|
+
timestamp_value: 1_435_067_188_000)
|
15
|
+
end
|
16
|
+
it { is_expected.to symmetric_serialize(true, boolean_value: true) }
|
17
|
+
it { is_expected.to symmetric_serialize(false, boolean_value: false) }
|
18
|
+
it { is_expected.to symmetric_serialize(nil, {}) }
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'serializing values' do
|
22
|
+
it do
|
23
|
+
is_expected.to serialize(BigDecimal.new("0.1"), double_value: 0.1)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'refuses to serialize big numbers' do
|
27
|
+
expect{ subject.cell_for 2**64 }.
|
28
|
+
to raise_error Riak::TimeSeriesError::SerializeBigIntegerError
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'refuses to serialize complex numbers' do
|
32
|
+
expect{ subject.cell_for(Complex(1, 1)) }.
|
33
|
+
to raise_error Riak::TimeSeriesError::SerializeComplexNumberError
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'refuses to serialize rational numbers' do
|
37
|
+
expect{ subject.cell_for(Rational(1, 1)) }.
|
38
|
+
to raise_error Riak::TimeSeriesError::SerializeRationalNumberError
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# deserialization is handled by the symmetric cases above
|
43
|
+
# describe 'deserializing values'
|
44
|
+
|
45
|
+
describe 'with a collection' do
|
46
|
+
let(:not_serialized){ ['hi', 5, 12.34] }
|
47
|
+
let(:serialized) do
|
48
|
+
[
|
49
|
+
Riak::Client::BeefcakeProtobuffsBackend::TsCell.new(varchar_value: 'hi'),
|
50
|
+
Riak::Client::BeefcakeProtobuffsBackend::TsCell.new(sint64_value: 5),
|
51
|
+
Riak::Client::BeefcakeProtobuffsBackend::TsCell.new(double_value: 12.34)
|
52
|
+
]
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'serializes' do
|
56
|
+
expect(subject.cells_for(not_serialized)).to eq serialized
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'deserializes' do
|
60
|
+
expect(subject.scalars_for(serialized)).to eq not_serialized
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
RSpec::Matchers.define :symmetric_serialize do |scalar, cell_options|
|
65
|
+
match do |codec|
|
66
|
+
expect(codec).to(
|
67
|
+
serialize(scalar, cell_options)
|
68
|
+
.and(deserialize(scalar, cell_options)))
|
69
|
+
end
|
70
|
+
|
71
|
+
failure_message do |codec|
|
72
|
+
cell = Riak::Client::BeefcakeProtobuffsBackend::TsCell.new cell_options
|
73
|
+
deserialized = codec.scalar_for cell
|
74
|
+
"expected #{scalar} => #{cell_options} => #{scalar}, got #{scalar} => #{cell.to_hash} => #{deserialized}"
|
75
|
+
end
|
76
|
+
|
77
|
+
description do
|
78
|
+
"serialize #{scalar.class} #{scalar.inspect} to and from TsCell #{cell_options}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
RSpec::Matchers.define :serialize do |measure, options|
|
83
|
+
match do |actual|
|
84
|
+
serialized = actual.cell_for(measure)
|
85
|
+
serialized.to_hash == options
|
86
|
+
end
|
87
|
+
|
88
|
+
failure_message do |actual|
|
89
|
+
serialized = actual.cell_for(measure)
|
90
|
+
"expected #{options}, got #{serialized.to_hash}"
|
91
|
+
end
|
92
|
+
|
93
|
+
description do
|
94
|
+
"serialize #{measure.class} #{measure.inspect} to TsCell #{options}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
RSpec::Matchers.define :deserialize do |expected, options|
|
99
|
+
|
100
|
+
cell = Riak::Client::BeefcakeProtobuffsBackend::TsCell.new options
|
101
|
+
|
102
|
+
match do |codec|
|
103
|
+
deserialized = codec.scalar_for cell
|
104
|
+
deserialized == expected
|
105
|
+
end
|
106
|
+
|
107
|
+
failure_message do |codec|
|
108
|
+
deserialized = codec.scalar_for cell
|
109
|
+
"expected TsCell #{options.inspect} to deserialize to #{expected.class} #{expected.inspect}"
|
110
|
+
end
|
111
|
+
|
112
|
+
description do
|
113
|
+
"deserialize TsCell #{options} to #{expected.class} #{expected.inspect}"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
data/spec/riak/client_spec.rb
CHANGED
@@ -242,5 +242,16 @@ describe Riak::Client, test_client: true do
|
|
242
242
|
expect(error).not_to be_nil
|
243
243
|
expect(error).to be_instance_of(RuntimeError)
|
244
244
|
end
|
245
|
+
|
246
|
+
it "logs the error" do
|
247
|
+
expect(Riak.logger).to receive(:warn).with(/Riak::ProtobuffsFailedHeader/).at_least(:once)
|
248
|
+
|
249
|
+
begin
|
250
|
+
@client.backend do |b|
|
251
|
+
raise Riak::ProtobuffsFailedHeader
|
252
|
+
end
|
253
|
+
rescue RuntimeError
|
254
|
+
end
|
255
|
+
end
|
245
256
|
end
|
246
257
|
end
|
@@ -21,7 +21,7 @@ describe Riak::Crdt::InnerFlag do
|
|
21
21
|
describe 'updating' do
|
22
22
|
let(:new_value){ false }
|
23
23
|
|
24
|
-
it '
|
24
|
+
it 'asks the class for an update operation' do
|
25
25
|
operation = described_class.update(new_value)
|
26
26
|
|
27
27
|
expect(operation.value).to eq new_value
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Riak::TimeSeries::Deletion do
|
4
|
+
subject{ described_class.new client, table_name }
|
5
|
+
let(:table_name){ 'GeoCheckin' }
|
6
|
+
let(:client){ instance_double('Riak::Client') }
|
7
|
+
let(:key){ double 'key' }
|
8
|
+
let(:backend) do
|
9
|
+
instance_double('Riak::Client::BeefcakeProtobuffsBackend').tap do |be|
|
10
|
+
allow(client).to receive(:backend).and_yield be
|
11
|
+
end
|
12
|
+
end
|
13
|
+
let(:operator) do
|
14
|
+
Riak::Client::BeefcakeProtobuffsBackend.configured?
|
15
|
+
instance_double(
|
16
|
+
'Riak::Client::BeefcakeProtobuffsBackend::TimeSeriesDeleteOperator'
|
17
|
+
).tap do |op|
|
18
|
+
allow(backend).to receive(:time_series_delete_operator).
|
19
|
+
and_return(op)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'initializes with client and table name' do
|
24
|
+
expect{ described_class.new client, table_name }.to_not raise_error
|
25
|
+
expect{ described_class.new client }.to raise_error ArgumentError
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'passes keys to delete to a delete operator' do
|
29
|
+
expect{ subject.key = key }.to_not raise_error
|
30
|
+
expect(operator).to receive(:delete).with(table_name, key, Hash.new)
|
31
|
+
expect{ subject.delete! }.to_not raise_error
|
32
|
+
end
|
33
|
+
end
|