riak-client 2.2.2 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|