cassandra_mocks 0.0.13 → 1.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cassandra_mocks.rb +3 -0
- data/lib/cassandra_mocks/cluster.rb +6 -3
- data/lib/cassandra_mocks/keyspace.rb +7 -3
- data/lib/cassandra_mocks/record.rb +6 -0
- data/lib/cassandra_mocks/row.rb +90 -0
- data/lib/cassandra_mocks/session.rb +22 -7
- data/lib/cassandra_mocks/statement.rb +12 -2
- data/lib/cassandra_mocks/statement/arithmetic.rb +2 -2
- data/lib/cassandra_mocks/table.rb +171 -57
- metadata +26 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 952c46e902eb98a2c45ff133302d2f867a9e3f06
|
4
|
+
data.tar.gz: 80aae79947b0bf4c7c7c6d3d7ecbf6724dc1549f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4b5bc8c274cbd897c234e41c4ac4ad5bc77d6db745733146b63866ecb77af307e34b490d50e5c45cf39dc500875dc81443444bd4da331ef369207fc3cf9b323b
|
7
|
+
data.tar.gz: 5c9411783f168d2228d8c3717166e5179f9ff28188240ee8d347fdbdf7284d8b94a820f410eb386b4fbe710e6904e3396a9e52058ecb957c55b77f03e558636c
|
data/lib/cassandra_mocks.rb
CHANGED
@@ -14,11 +14,14 @@
|
|
14
14
|
# limitations under the License.
|
15
15
|
#++
|
16
16
|
|
17
|
+
require 'concurrent'
|
17
18
|
require 'cassandra'
|
18
19
|
require 'active_support/all'
|
19
20
|
require 'active_support/core_ext/class/attribute_accessors'
|
20
21
|
|
21
22
|
require 'cassandra_mocks/result_page'
|
23
|
+
require 'cassandra_mocks/record'
|
24
|
+
require 'cassandra_mocks/row'
|
22
25
|
require 'cassandra_mocks/table'
|
23
26
|
require 'cassandra_mocks/statement/batch'
|
24
27
|
require 'cassandra_mocks/statement/arithmetic'
|
@@ -6,7 +6,7 @@ module Cassandra
|
|
6
6
|
def_delegator :@keyspaces, :[], :keyspace
|
7
7
|
|
8
8
|
def initialize
|
9
|
-
@keyspaces =
|
9
|
+
@keyspaces = Concurrent::Map.new
|
10
10
|
end
|
11
11
|
|
12
12
|
def connect_async(keyspace = nil)
|
@@ -26,8 +26,11 @@ module Cassandra
|
|
26
26
|
connect_async(keyspace).get
|
27
27
|
end
|
28
28
|
|
29
|
-
def add_keyspace(name)
|
30
|
-
|
29
|
+
def add_keyspace(name, ignore_existing)
|
30
|
+
if @keyspaces[name]
|
31
|
+
return if ignore_existing
|
32
|
+
raise Errors::AlreadyExistsError.new('Cannot create already existing keyspace', 'MockStatement', name, nil)
|
33
|
+
end
|
31
34
|
@keyspaces[name] = Keyspace.new(name)
|
32
35
|
end
|
33
36
|
|
@@ -5,11 +5,15 @@ module Cassandra
|
|
5
5
|
def initialize(name)
|
6
6
|
replication = Replication.new('mock', {})
|
7
7
|
super(name, false, replication, {})
|
8
|
+
@tables = Concurrent::Map.new
|
8
9
|
end
|
9
10
|
|
10
|
-
def add_table(table_name, primary_key, columns)
|
11
|
+
def add_table(table_name, primary_key, columns, ignore_existing)
|
11
12
|
raise Errors::InvalidError.new("Table name '#{table_name}' cannot be greater than 48 characters", 'MockStatement') if table_name.length > 48
|
12
|
-
|
13
|
+
if @tables[table_name]
|
14
|
+
return if ignore_existing
|
15
|
+
raise Errors::AlreadyExistsError.new('Cannot create already existing table', 'MockStatement', nil, table_name)
|
16
|
+
end
|
13
17
|
|
14
18
|
partition_key = primary_key.shift
|
15
19
|
partition_key_columns = partition_key_part(columns, partition_key)
|
@@ -29,7 +33,7 @@ module Cassandra
|
|
29
33
|
end
|
30
34
|
|
31
35
|
def fields(columns, partition_key, primary_key)
|
32
|
-
columns.except(partition_key + primary_key).map { |name, type| Cassandra::Column.new(name, type, :asc) }
|
36
|
+
columns.except(*(partition_key + primary_key)).map { |name, type| Cassandra::Column.new(name, type, :asc) }
|
33
37
|
end
|
34
38
|
|
35
39
|
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module Cassandra
|
2
|
+
module Mocks
|
3
|
+
class Row
|
4
|
+
include MonitorMixin
|
5
|
+
|
6
|
+
attr_reader :clusters, :partition_key
|
7
|
+
|
8
|
+
def initialize(partition_key)
|
9
|
+
@clusters = {}
|
10
|
+
@partition_key = partition_key
|
11
|
+
super()
|
12
|
+
end
|
13
|
+
|
14
|
+
def insert_record(clustering_columns, record_values, check_exists)
|
15
|
+
synchronize do
|
16
|
+
record_cluster = record_cluster(clustering_columns[0..-2])
|
17
|
+
update_record(check_exists, clustering_columns, record_cluster, record_values)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_records(clustering_columns)
|
22
|
+
cluster = find_cluster(clustering_columns)
|
23
|
+
if cluster
|
24
|
+
[cluster_values(cluster, [*clustering_columns])].flatten.map(&:values)
|
25
|
+
else
|
26
|
+
[]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete_records(clustering_columns)
|
31
|
+
synchronize do
|
32
|
+
if clustering_columns.any?
|
33
|
+
cluster = find_cluster(clustering_columns[0..-2])
|
34
|
+
cluster.delete(clustering_columns.last) if cluster
|
35
|
+
else
|
36
|
+
@clusters.clear
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def cluster_values(cluster, values)
|
44
|
+
if cluster.is_a?(Record)
|
45
|
+
Record.new([*partition_key, *values, *cluster.values])
|
46
|
+
else
|
47
|
+
cluster.map do |clustering_key, child_cluster|
|
48
|
+
cluster_values(child_cluster, values + [clustering_key])
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_cluster(clustering_columns)
|
54
|
+
clustering_columns.inject(@clusters) do |cluster, cluster_key|
|
55
|
+
cluster[cluster_key] if cluster
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def record_cluster(partial_clustering_columns)
|
60
|
+
partial_clustering_columns.inject(clusters) do |memo, cluster_key|
|
61
|
+
memo[cluster_key] ||= {}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_record(check_exists, clustering_columns, record_cluster, record_values)
|
66
|
+
previous_record = record_cluster[clustering_columns.last]
|
67
|
+
if previous_record
|
68
|
+
update_if_not_exists(check_exists, previous_record, record_values)
|
69
|
+
else
|
70
|
+
create_record(clustering_columns, record_cluster, record_values)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def create_record(clustering_columns, record_cluster, record_values)
|
75
|
+
record_cluster[clustering_columns.last] = Record.new(record_values)
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
def update_if_not_exists(check_exists, previous_record, record_values)
|
80
|
+
if check_exists
|
81
|
+
false
|
82
|
+
else
|
83
|
+
previous_record.values = record_values
|
84
|
+
true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -25,6 +25,9 @@ module Cassandra
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def execute_async(cql, *args)
|
28
|
+
options = fetch_options(args)
|
29
|
+
args = options.fetch(:arguments) { args }
|
30
|
+
|
28
31
|
if cql.is_a?(Cassandra::Statements::Batch)
|
29
32
|
futures = cql.statements.map do |batched_statement|
|
30
33
|
execute_async(batched_statement.statement, *batched_statement.args)
|
@@ -35,17 +38,18 @@ module Cassandra
|
|
35
38
|
future = cql.is_a?(Statement) ? Cassandra::Future.value(cql.fill_params(args)) : prepare_async(cql)
|
36
39
|
future.then do |statement|
|
37
40
|
result = ResultPage.new
|
41
|
+
# noinspection RubyCaseWithoutElseBlockInspection
|
38
42
|
case statement.action
|
39
43
|
when :create_keyspace
|
40
|
-
cluster.add_keyspace(statement.args[:keyspace])
|
44
|
+
cluster.add_keyspace(statement.args[:keyspace], statement.args[:check_exists])
|
41
45
|
when :create_table
|
42
|
-
cluster.keyspace(keyspace).add_table(statement.args[:table], statement.args[:primary_key], statement.args[:columns])
|
46
|
+
cluster.keyspace(keyspace).add_table(statement.args[:table], statement.args[:primary_key], statement.args[:columns], statement.args[:check_exists])
|
43
47
|
when :insert
|
44
48
|
insert_query(result, statement)
|
45
49
|
when :update
|
46
50
|
update_query(statement)
|
47
51
|
when :truncate
|
48
|
-
cluster.keyspace(keyspace_for_statement(statement)).table(statement.args[:table]).
|
52
|
+
cluster.keyspace(keyspace_for_statement(statement)).table(statement.args[:table]).clear
|
49
53
|
when :drop_keyspace
|
50
54
|
cluster.drop_keyspace(statement.args[:keyspace])
|
51
55
|
when :drop_table
|
@@ -71,9 +75,19 @@ module Cassandra
|
|
71
75
|
|
72
76
|
private
|
73
77
|
|
78
|
+
def fetch_options(args)
|
79
|
+
if args.last.is_a?(Hash)
|
80
|
+
args.pop
|
81
|
+
else
|
82
|
+
{arguments: args}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
74
86
|
def insert_query(result, statement)
|
75
87
|
check_exists = !!statement.args[:check_exists]
|
76
|
-
|
88
|
+
table = cluster.keyspace(keyspace_for_statement(statement)).table(statement.args[:table])
|
89
|
+
raise Cassandra::Errors::InvalidError.new('INSERT statement are not allowed on counter tables, use UPDATE instead', 'MockStatement') if table.counter_table?
|
90
|
+
inserted = table.insert(statement.args[:values], check_exists: check_exists)
|
77
91
|
result << {'[applied]' => inserted} if check_exists
|
78
92
|
end
|
79
93
|
|
@@ -93,15 +107,16 @@ module Cassandra
|
|
93
107
|
rows_to_update = table.select('*', restriction: statement.args[:filter])
|
94
108
|
rows_to_update = [statement.args[:filter].dup] if rows_to_update.empty?
|
95
109
|
rows_to_update.each do |row|
|
96
|
-
updated_row = updated_row(row, statement)
|
110
|
+
updated_row = updated_row(table, row, statement)
|
97
111
|
cluster.keyspace(keyspace_for_statement(statement)).table(statement.args[:table]).insert(updated_row)
|
98
112
|
end
|
99
113
|
end
|
100
114
|
|
101
|
-
def updated_row(row, statement)
|
115
|
+
def updated_row(table, row, statement)
|
102
116
|
statement.args[:values].inject(row.dup) do |memo, (column, value)|
|
103
117
|
if value.is_a?(Statement::Arithmetic)
|
104
|
-
|
118
|
+
raise Cassandra::Errors::InvalidError.new("Invalid operation (#{column} = #{column} + ?) for non counter column #{column}", 'MockStatement') unless table.counter_table?
|
119
|
+
value.apply!(memo)
|
105
120
|
else
|
106
121
|
memo.merge!(column => value)
|
107
122
|
end
|
@@ -16,7 +16,7 @@ module Cassandra
|
|
16
16
|
parse_create_table
|
17
17
|
else
|
18
18
|
@action = :create_keyspace
|
19
|
-
|
19
|
+
parse_create_keyspace
|
20
20
|
end
|
21
21
|
elsif type_token.truncate?
|
22
22
|
parse_truncate_query
|
@@ -124,6 +124,16 @@ module Cassandra
|
|
124
124
|
end
|
125
125
|
end
|
126
126
|
|
127
|
+
def parse_create_keyspace
|
128
|
+
keyspace_name_token = next_token
|
129
|
+
check_exists = if keyspace_name_token.if?
|
130
|
+
next_token
|
131
|
+
next_token
|
132
|
+
keyspace_name_token = next_token
|
133
|
+
end
|
134
|
+
@args = {keyspace: keyspace_name_token.value, check_exists: !!check_exists}
|
135
|
+
end
|
136
|
+
|
127
137
|
def parse_create_table
|
128
138
|
table_name_token = next_token
|
129
139
|
check_exists = if table_name_token.if?
|
@@ -159,7 +169,7 @@ module Cassandra
|
|
159
169
|
primary_key = [partition_key, *primary_key_parts]
|
160
170
|
end
|
161
171
|
|
162
|
-
@args = {table: table_name, check_exists: !!check_exists, columns: additional_columns.merge({column_name => column_type}), primary_key: primary_key}
|
172
|
+
@args = {table: table_name, check_exists: !!check_exists, columns: additional_columns.merge!({column_name => column_type}), primary_key: primary_key}
|
163
173
|
end
|
164
174
|
|
165
175
|
def parse_truncate_query
|
@@ -3,8 +3,8 @@ module Cassandra
|
|
3
3
|
class Statement
|
4
4
|
class Arithmetic < Struct.new(:operation, :column, :amount)
|
5
5
|
|
6
|
-
def apply(row)
|
7
|
-
row.merge(column => (row[column] || 0).public_send(operator, amount))
|
6
|
+
def apply!(row)
|
7
|
+
row.merge!(column => (row[column] || 0).public_send(operator, amount))
|
8
8
|
end
|
9
9
|
|
10
10
|
private
|
@@ -1,42 +1,96 @@
|
|
1
1
|
module Cassandra
|
2
2
|
module Mocks
|
3
3
|
class Table < Cassandra::Table
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegator :@rows, :clear
|
7
|
+
|
4
8
|
def initialize(keyspace, name, partition_key, clustering_key, fields)
|
9
|
+
@rows = Concurrent::Map.new { |hash, key| hash[key] = Row.new(key) }
|
10
|
+
|
5
11
|
compaction = Cassandra::Table::Compaction.new('mock', {})
|
6
12
|
options = Cassandra::Table::Options.new({}, compaction, {}, false, 'mock')
|
7
13
|
column_map = column_map(partition_key, clustering_key, fields)
|
14
|
+
|
15
|
+
@counter_column = fields.find { |field| field.type.to_sym == :counter }
|
16
|
+
has_non_counters = !!fields.find { |field| field.type.to_sym != :counter }
|
17
|
+
|
18
|
+
if counter_column && has_non_counters
|
19
|
+
raise Cassandra::Errors::ConfigurationError.new("Cannot add counter column '#{counter_column.name}' to non-counter column family", 'MockStatement')
|
20
|
+
end
|
21
|
+
|
8
22
|
super(keyspace, name, partition_key, clustering_key, column_map, options, [])
|
23
|
+
@column_map = columns.inject({}) do |memo, column|
|
24
|
+
memo.merge!(column.name => column)
|
25
|
+
end
|
26
|
+
@partition_key_set = Set.new(partition_key_names)
|
27
|
+
end
|
28
|
+
|
29
|
+
def rows
|
30
|
+
results = []
|
31
|
+
@rows.each_pair do |_, row|
|
32
|
+
row_results = record_attributes(row.find_records([]))
|
33
|
+
results << row_results
|
34
|
+
end
|
35
|
+
results.flatten
|
36
|
+
end
|
37
|
+
|
38
|
+
def counter_table?
|
39
|
+
!!counter_column
|
9
40
|
end
|
10
41
|
|
11
42
|
def insert(attributes, options = {})
|
12
43
|
validate_columns!(attributes)
|
13
44
|
validate_primary_key_presence!(attributes)
|
14
45
|
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
if prev_row_index
|
20
|
-
return false if options[:check_exists]
|
21
|
-
rows[prev_row_index] = attributes
|
22
|
-
else
|
23
|
-
rows << attributes
|
24
|
-
end
|
25
|
-
true
|
46
|
+
row = @rows[attribute_partition_key(attributes)]
|
47
|
+
row.insert_record(attribute_clustering_key(attributes), attribute_fields(attributes), options[:check_exists])
|
26
48
|
end
|
27
49
|
|
28
50
|
def select(*columns)
|
29
51
|
filter = select_filter(columns)
|
30
52
|
limit = filter.delete(:limit)
|
31
53
|
order = select_order(filter)
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
54
|
+
|
55
|
+
order_keys_in_partition = order.keys.select { |column| partition_key_names.include?(column) }
|
56
|
+
if order_keys_in_partition.any?
|
57
|
+
raise Cassandra::Errors::InvalidError.new("Order by is currently only supported on the clustered columns of the PRIMARY KEY, got #{order_keys_in_partition * ', '}", 'MockStatement')
|
58
|
+
end
|
59
|
+
|
60
|
+
missing_ordering_keys = order.keys.select { |column| !column_names.include?(column) }
|
61
|
+
if missing_ordering_keys.any?
|
62
|
+
raise Cassandra::Errors::InvalidError.new("Order by on unknown column(s) #{missing_ordering_keys * ', '}", 'MockStatement')
|
63
|
+
end
|
64
|
+
|
65
|
+
out_of_order = order.keys.each.with_index.any? { |column, index| clustering_key_names[index] != column }
|
66
|
+
if out_of_order
|
67
|
+
raise Cassandra::Errors::InvalidError.new("Order by currently only support the ordering of columns following their declared order in the PRIMARY KEY (expected #{clustering_key_names * ', '} got #{order.keys * ', '})", 'MockStatement')
|
68
|
+
end
|
69
|
+
|
70
|
+
inconsistent_order = (order.values.uniq.count > 1)
|
71
|
+
if inconsistent_order
|
72
|
+
raise Cassandra::Errors::InvalidError.new('Ordering direction must be consistent across all clustering columns', 'MockStatement')
|
37
73
|
end
|
38
74
|
|
39
|
-
|
75
|
+
filter = filter.fetch(:restriction) { {} }
|
76
|
+
validate_filter!(filter)
|
77
|
+
|
78
|
+
filtered_rows = if filter.any?
|
79
|
+
partition_key = attribute_partition_key(filter)
|
80
|
+
partition_range = (partition_key.pop if partition_key.last.is_a?(Array))
|
81
|
+
cluster_filter = filter.except(*partition_key_names)
|
82
|
+
cluster_key = attribute_partial_clustering_key(cluster_filter)
|
83
|
+
_, cluster_slice = cluster_filter.find do |_, value|
|
84
|
+
restriction_is_slice?(value)
|
85
|
+
end
|
86
|
+
|
87
|
+
records = partially_filtered_rows(partition_range, partition_key, cluster_key)
|
88
|
+
rows = record_attributes(records)
|
89
|
+
filtered_rows(cluster_slice, rows)
|
90
|
+
else
|
91
|
+
self.rows
|
92
|
+
end
|
93
|
+
|
40
94
|
sorted_rows = filtered_rows.sort do |lhs, rhs|
|
41
95
|
compare_rows(0, lhs, rhs, order)
|
42
96
|
end
|
@@ -50,12 +104,41 @@ module Cassandra
|
|
50
104
|
end
|
51
105
|
|
52
106
|
def delete(filter)
|
53
|
-
|
54
|
-
@rows
|
107
|
+
validate_filter!(filter)
|
108
|
+
row = @rows[attribute_partition_key(filter)]
|
109
|
+
row.delete_records(attribute_partial_clustering_key(filter))
|
55
110
|
end
|
56
111
|
|
57
|
-
def
|
58
|
-
|
112
|
+
def attribute_partition_key(attributes)
|
113
|
+
partition_key_names.map { |column| attributes[column] }
|
114
|
+
end
|
115
|
+
|
116
|
+
def attribute_clustering_key(attributes)
|
117
|
+
clustering_key_names.map do |column|
|
118
|
+
value = attributes[column]
|
119
|
+
value unless restriction_is_slice?(value)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def attribute_partial_clustering_key(attributes)
|
124
|
+
attribute_clustering_key(attributes).compact
|
125
|
+
end
|
126
|
+
|
127
|
+
def attribute_fields(attributes)
|
128
|
+
field_names.map { |column| attributes[column] }
|
129
|
+
end
|
130
|
+
|
131
|
+
def restriction_is_slice?(value)
|
132
|
+
value.is_a?(Statement::Comparitor) || value.is_a?(Array)
|
133
|
+
end
|
134
|
+
|
135
|
+
def validate_filter!(filter)
|
136
|
+
unless filter.empty?
|
137
|
+
validate_range_query!(filter)
|
138
|
+
validate_partition_key_filter!(filter)
|
139
|
+
validate_clustering_column_filter!(filter)
|
140
|
+
raise_if_fields_restricted!(filter)
|
141
|
+
end
|
59
142
|
end
|
60
143
|
|
61
144
|
# make #partition_key public
|
@@ -70,8 +153,11 @@ module Cassandra
|
|
70
153
|
|
71
154
|
private
|
72
155
|
|
156
|
+
attr_reader :counter_column
|
157
|
+
def_delegator :@column_map, :[], :find_column
|
158
|
+
|
73
159
|
def select_filter(columns)
|
74
|
-
columns.last.is_a?(Hash) ? columns.pop : {}
|
160
|
+
columns.last.is_a?(Hash) ? columns.pop.dup : {}
|
75
161
|
end
|
76
162
|
|
77
163
|
def select_order(filter)
|
@@ -145,42 +231,15 @@ module Cassandra
|
|
145
231
|
filter[column]
|
146
232
|
end
|
147
233
|
|
148
|
-
def
|
149
|
-
filter
|
150
|
-
|
151
|
-
|
152
|
-
def apply_filter(filter)
|
153
|
-
rows.select do |row|
|
154
|
-
partial_row = filter_slices_row(filter, row)
|
155
|
-
filter.all? do |column, value|
|
156
|
-
if value.is_a?(Statement::Comparitor)
|
157
|
-
value.check_against(partial_row)
|
158
|
-
elsif value.is_a?(Array)
|
159
|
-
if value.first.is_a?(Statement::Comparitor)
|
160
|
-
value.all? { |value| value.check_against(partial_row) }
|
161
|
-
else
|
162
|
-
value.include?(partial_row[column])
|
163
|
-
end
|
164
|
-
else
|
165
|
-
partial_row[column] == value
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
def filter_slices_row(filter, row)
|
172
|
-
filter.keys.inject({}) do |memo, key, _|
|
173
|
-
value = if key.is_a?(Array)
|
174
|
-
row.values_at(*key)
|
175
|
-
else
|
176
|
-
row[key]
|
177
|
-
end
|
178
|
-
memo.merge!(key => value)
|
234
|
+
def validate_range_query!(filter)
|
235
|
+
invalid_partition_range_column = partition_key_names[0..-2].find { |column| filter[column].is_a?(Array) }
|
236
|
+
if invalid_partition_range_column
|
237
|
+
raise Cassandra::Errors::InvalidError.new("Partition KEY part #{invalid_partition_range_column} cannot be restricted by IN relation (only the last part of the partition key can)", 'MockStatement')
|
179
238
|
end
|
180
239
|
end
|
181
240
|
|
182
|
-
def
|
183
|
-
missing_partition_keys =
|
241
|
+
def validate_partition_key_filter!(filter)
|
242
|
+
missing_partition_keys = @partition_key_set - filter.keys
|
184
243
|
raise Cassandra::Errors::InvalidError.new("Missing partition key part(s) #{missing_partition_keys.map(&:inspect) * ', '}", 'MockStatement') unless missing_partition_keys.empty?
|
185
244
|
end
|
186
245
|
|
@@ -203,6 +262,61 @@ module Cassandra
|
|
203
262
|
row[primary_key_names[primary_key_index]]
|
204
263
|
end
|
205
264
|
|
265
|
+
def record_attributes(records)
|
266
|
+
records.map do |record|
|
267
|
+
attributes = {}
|
268
|
+
column_names.each.with_index do |column, index|
|
269
|
+
attributes[column] = record[index]
|
270
|
+
end
|
271
|
+
attributes
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def partially_filtered_rows(partition_range, partition_key, cluster_key)
|
276
|
+
if partition_range
|
277
|
+
partition_range_records(partition_range, partition_key, cluster_key)
|
278
|
+
else
|
279
|
+
records_for_row(partition_key, cluster_key)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def partition_range_records(partition_range, partition_key, cluster_key)
|
284
|
+
partition_range.map do |part|
|
285
|
+
records_for_row(partition_key + [part], cluster_key)
|
286
|
+
end.flatten(1)
|
287
|
+
end
|
288
|
+
|
289
|
+
def records_for_row(partition_key, cluster_key)
|
290
|
+
row = @rows[partition_key]
|
291
|
+
row.find_records(cluster_key)
|
292
|
+
end
|
293
|
+
|
294
|
+
def filtered_rows(cluster_slice, rows)
|
295
|
+
cluster_slice ? apply_filter(cluster_slice, rows) : rows
|
296
|
+
end
|
297
|
+
|
298
|
+
def apply_filter(cluster_slice, rows)
|
299
|
+
rows.select do |row|
|
300
|
+
partial_row = partial_row(cluster_slice, row)
|
301
|
+
|
302
|
+
if cluster_slice.is_a?(Statement::Comparitor)
|
303
|
+
cluster_slice.check_against(partial_row)
|
304
|
+
elsif cluster_slice.is_a?(Array)
|
305
|
+
cluster_slice.all? { |value| value.check_against(partial_row) }
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def partial_row(cluster_slice, row)
|
311
|
+
comparitor = cluster_slice.is_a?(Statement::Comparitor) ? cluster_slice : cluster_slice.first
|
312
|
+
value = if comparitor.column.is_a?(Array)
|
313
|
+
row.values_at(*comparitor.column)
|
314
|
+
else
|
315
|
+
row[comparitor.column]
|
316
|
+
end
|
317
|
+
{comparitor.column => value}
|
318
|
+
end
|
319
|
+
|
206
320
|
def primary_key_names
|
207
321
|
partition_key_names + clustering_key_names
|
208
322
|
end
|
@@ -219,8 +333,8 @@ module Cassandra
|
|
219
333
|
columns.map(&:name)
|
220
334
|
end
|
221
335
|
|
222
|
-
def
|
223
|
-
|
336
|
+
def field_names
|
337
|
+
column_names - (partition_key_names + clustering_key_names)
|
224
338
|
end
|
225
339
|
|
226
340
|
def column_map(partition_key, clustering_key, fields)
|
metadata
CHANGED
@@ -1,29 +1,49 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cassandra_mocks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas RM Rogers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cassandra-driver
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.1'
|
20
|
+
- - <=
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.0.1
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
|
-
- -
|
27
|
+
- - '>='
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: '1.1'
|
30
|
+
- - <=
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.0.1
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: concurrent-ruby
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ~>
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 1.0.0
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.0.0
|
27
47
|
- !ruby/object:Gem::Dependency
|
28
48
|
name: activesupport
|
29
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,7 +72,9 @@ files:
|
|
52
72
|
- lib/cassandra_mocks.rb
|
53
73
|
- lib/cassandra_mocks/cluster.rb
|
54
74
|
- lib/cassandra_mocks/keyspace.rb
|
75
|
+
- lib/cassandra_mocks/record.rb
|
55
76
|
- lib/cassandra_mocks/result_page.rb
|
77
|
+
- lib/cassandra_mocks/row.rb
|
56
78
|
- lib/cassandra_mocks/session.rb
|
57
79
|
- lib/cassandra_mocks/statement.rb
|
58
80
|
- lib/cassandra_mocks/statement/arithmetic.rb
|