cassandra_mocks 0.0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +13 -0
- data/README.md +17 -0
- data/lib/cassandra_mocks/cluster.rb +52 -0
- data/lib/cassandra_mocks/keyspace.rb +37 -0
- data/lib/cassandra_mocks/result_page.rb +24 -0
- data/lib/cassandra_mocks/session.rb +117 -0
- data/lib/cassandra_mocks/statement/arithmetic.rb +19 -0
- data/lib/cassandra_mocks/statement/batch.rb +12 -0
- data/lib/cassandra_mocks/statement/comparitor.rb +41 -0
- data/lib/cassandra_mocks/statement/token.rb +37 -0
- data/lib/cassandra_mocks/statement/tokenizer.rb +123 -0
- data/lib/cassandra_mocks/statement.rb +364 -0
- data/lib/cassandra_mocks/table.rb +223 -0
- data/lib/cassandra_mocks/uuid.rb +7 -0
- data/lib/cassandra_mocks.rb +32 -0
- data/lib/cassandra_model/mock_connection.rb +15 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e116917fa04c58c22eba8ccc9c08e9787a9ba54d
|
4
|
+
data.tar.gz: a860d89cdece466b8a74af23abb2fd646a6846b5
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e98abfe9fe97cb40bcf5f97829d86673902a5e7d0e97257bf84cbbecc5a3f7a67eb20270c6a10ddfc3affb23e88a6ff3f2ab7e0645dd751163c4e6f2f72b7ed1
|
7
|
+
data.tar.gz: 2898cbb5fd7759b4ddc80d922ee57daf67827887fd7cf24abf1ab638e69bccd28893a79dc88eaecb8dbe55bade37bf8824c2faf3eec4de549e38057fedf41aba
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2014-2015 Thomas RM Rogers
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# cassandra_mocks
|
2
|
+
|
3
|
+
## Copyright
|
4
|
+
|
5
|
+
Copyright 2015 Thomas Rogers.
|
6
|
+
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
|
8
|
+
|
9
|
+
[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
|
10
|
+
|
11
|
+
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Cassandra
|
2
|
+
module Mocks
|
3
|
+
class Cluster
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegator :@keyspaces, :[], :keyspace
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@keyspaces = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def connect_async(keyspace = nil)
|
13
|
+
session = Session.new(keyspace, self)
|
14
|
+
Cassandra::Future.value(session)
|
15
|
+
end
|
16
|
+
|
17
|
+
def close_async
|
18
|
+
Cassandra::Future.value(nil)
|
19
|
+
end
|
20
|
+
|
21
|
+
def close
|
22
|
+
close_async.get
|
23
|
+
end
|
24
|
+
|
25
|
+
def connect(keyspace = nil)
|
26
|
+
connect_async(keyspace).get
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_keyspace(name)
|
30
|
+
raise Errors::AlreadyExistsError.new('Cannot create already existing keyspace', 'MockStatement', name, nil) if @keyspaces[name]
|
31
|
+
@keyspaces[name] = Keyspace.new(name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def drop_keyspace(name)
|
35
|
+
@keyspaces.delete(name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def each_keyspace(&block)
|
39
|
+
@keyspaces.values.each(&block)
|
40
|
+
end
|
41
|
+
|
42
|
+
def hosts
|
43
|
+
%w(DummyHost)
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_replicas(*_)
|
47
|
+
[]
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Cassandra
|
2
|
+
module Mocks
|
3
|
+
class Keyspace < ::Cassandra::Keyspace
|
4
|
+
|
5
|
+
def initialize(name)
|
6
|
+
replication = Replication.new('mock', {})
|
7
|
+
super(name, false, replication, {})
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_table(table_name, primary_key, columns)
|
11
|
+
raise Errors::InvalidError.new("Table name '#{table_name}' cannot be greater than 48 characters", 'MockStatement') if table_name.length > 48
|
12
|
+
raise Errors::AlreadyExistsError.new('Cannot create already existing table', 'MockStatement', nil, table_name) if @tables[table_name]
|
13
|
+
|
14
|
+
partition_key = primary_key.shift
|
15
|
+
partition_key_columns = partition_key_part(columns, partition_key)
|
16
|
+
clustering_columns = partition_key_part(columns, primary_key)
|
17
|
+
fields = fields(columns, partition_key, primary_key)
|
18
|
+
@tables[table_name] = Table.new(name, table_name, partition_key_columns, clustering_columns, fields)
|
19
|
+
end
|
20
|
+
|
21
|
+
def drop_table(table_name)
|
22
|
+
@tables.delete(table_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def partition_key_part(columns, primary_key)
|
28
|
+
primary_key.map { |name| Cassandra::Column.new(name, columns[name], :asc) }
|
29
|
+
end
|
30
|
+
|
31
|
+
def fields(columns, partition_key, primary_key)
|
32
|
+
columns.except(partition_key + primary_key).map { |name, type| Cassandra::Column.new(name, type, :asc) }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Cassandra
|
2
|
+
module Mocks
|
3
|
+
class ResultPage < Array
|
4
|
+
|
5
|
+
def last_page?
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
9
|
+
def next_page
|
10
|
+
end
|
11
|
+
|
12
|
+
def next_page_async
|
13
|
+
Cassandra::Future.value(next_page)
|
14
|
+
end
|
15
|
+
|
16
|
+
def paging_state
|
17
|
+
end
|
18
|
+
|
19
|
+
def execution_info
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Cassandra
|
2
|
+
module Mocks
|
3
|
+
class Session
|
4
|
+
attr_reader :keyspace, :cluster
|
5
|
+
|
6
|
+
def initialize(keyspace, cluster)
|
7
|
+
@keyspace = keyspace
|
8
|
+
@cluster = cluster
|
9
|
+
end
|
10
|
+
|
11
|
+
def close_async
|
12
|
+
Cassandra::Future.value(nil)
|
13
|
+
end
|
14
|
+
|
15
|
+
def close
|
16
|
+
close_async.get
|
17
|
+
end
|
18
|
+
|
19
|
+
def prepare_async(cql)
|
20
|
+
Cassandra::Future.value(Statement.new(cql, []))
|
21
|
+
end
|
22
|
+
|
23
|
+
def prepare(cql)
|
24
|
+
prepare_async(cql).get
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute_async(cql, *args)
|
28
|
+
if cql.is_a?(Cassandra::Statements::Batch)
|
29
|
+
futures = cql.statements.map do |batched_statement|
|
30
|
+
execute_async(batched_statement.statement, *batched_statement.args)
|
31
|
+
end
|
32
|
+
return Cassandra::Future.all(futures).then { ResultPage.new }
|
33
|
+
end
|
34
|
+
|
35
|
+
future = cql.is_a?(Statement) ? Cassandra::Future.value(cql.fill_params(args)) : prepare_async(cql)
|
36
|
+
future.then do |statement|
|
37
|
+
result = ResultPage.new
|
38
|
+
case statement.action
|
39
|
+
when :create_keyspace
|
40
|
+
cluster.add_keyspace(statement.args[:keyspace])
|
41
|
+
when :create_table
|
42
|
+
cluster.keyspace(keyspace).add_table(statement.args[:table], statement.args[:primary_key], statement.args[:columns])
|
43
|
+
when :insert
|
44
|
+
insert_query(result, statement)
|
45
|
+
when :update
|
46
|
+
update_query(statement)
|
47
|
+
when :truncate
|
48
|
+
cluster.keyspace(keyspace_for_statement(statement)).table(statement.args[:table]).rows.clear
|
49
|
+
when :drop_keyspace
|
50
|
+
cluster.drop_keyspace(statement.args[:keyspace])
|
51
|
+
when :drop_table
|
52
|
+
cluster.keyspace(keyspace_for_statement(statement)).drop_table(statement.args[:table])
|
53
|
+
when :select
|
54
|
+
result = select_query(statement)
|
55
|
+
when :delete
|
56
|
+
delete_query(statement)
|
57
|
+
end
|
58
|
+
result
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def execute(cql)
|
63
|
+
execute_async(cql).get
|
64
|
+
end
|
65
|
+
|
66
|
+
def ==(rhs)
|
67
|
+
rhs.is_a?(Session) &&
|
68
|
+
rhs.keyspace == keyspace &&
|
69
|
+
rhs.cluster == cluster
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def insert_query(result, statement)
|
75
|
+
check_exists = !!statement.args[:check_exists]
|
76
|
+
inserted = cluster.keyspace(keyspace_for_statement(statement)).table(statement.args[:table]).insert(statement.args[:values], check_exists: check_exists)
|
77
|
+
result << {'[applied]' => inserted} if check_exists
|
78
|
+
end
|
79
|
+
|
80
|
+
def select_query(statement)
|
81
|
+
table = cluster.keyspace(keyspace_for_statement(statement)).table(statement.args[:table])
|
82
|
+
options = statement.args[:filter].merge(limit: statement.args[:limit], order: statement.args[:order])
|
83
|
+
table.select(*statement.args[:columns], options)
|
84
|
+
end
|
85
|
+
|
86
|
+
def delete_query(statement)
|
87
|
+
table = cluster.keyspace(keyspace_for_statement(statement)).table(statement.args[:table])
|
88
|
+
table.delete(statement.args[:filter])
|
89
|
+
end
|
90
|
+
|
91
|
+
def update_query(statement)
|
92
|
+
table = cluster.keyspace(keyspace_for_statement(statement)).table(statement.args[:table])
|
93
|
+
rows_to_update = table.select('*', statement.args[:filter])
|
94
|
+
rows_to_update = [statement.args[:filter].dup] if rows_to_update.empty?
|
95
|
+
rows_to_update.each do |row|
|
96
|
+
updated_row = updated_row(row, statement)
|
97
|
+
cluster.keyspace(keyspace_for_statement(statement)).table(statement.args[:table]).insert(updated_row)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def updated_row(row, statement)
|
102
|
+
statement.args[:values].inject(row.dup) do |memo, (column, value)|
|
103
|
+
if value.is_a?(Statement::Arithmetic)
|
104
|
+
value.apply(memo)
|
105
|
+
else
|
106
|
+
memo.merge!(column => value)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def keyspace_for_statement(statement)
|
112
|
+
statement.args[:keyspace] || keyspace
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Cassandra
|
2
|
+
module Mocks
|
3
|
+
class Statement
|
4
|
+
class Arithmetic < Struct.new(:operation, :column, :amount)
|
5
|
+
|
6
|
+
def apply(row)
|
7
|
+
row.merge(column => (row[column] || 0).public_send(operator, amount))
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def operator
|
13
|
+
operation == :plus ? :+ : :-
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Cassandra
|
2
|
+
module Mocks
|
3
|
+
class Statement
|
4
|
+
class Comparitor < Struct.new(:operation, :column, :value)
|
5
|
+
|
6
|
+
COMPARISON_MAP = {
|
7
|
+
lt: [-1],
|
8
|
+
le: [-1, 0],
|
9
|
+
eq: [0],
|
10
|
+
ge: [0, 1],
|
11
|
+
gt: [1]
|
12
|
+
}
|
13
|
+
|
14
|
+
def initialize(*args)
|
15
|
+
super
|
16
|
+
@comparitor = COMPARISON_MAP[operation]
|
17
|
+
end
|
18
|
+
|
19
|
+
def check_against(row)
|
20
|
+
if column.is_a?(Array)
|
21
|
+
check_against_array(row[column])
|
22
|
+
else
|
23
|
+
@comparitor.include?(row[column] <=> value)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def check_against_array(row_values, index = 0, prev_result = false)
|
28
|
+
row_value = row_values[index]
|
29
|
+
comparison_value = value[index]
|
30
|
+
return prev_result unless row_value
|
31
|
+
|
32
|
+
comparison = @comparitor.include?(row_value <=> comparison_value)
|
33
|
+
return comparison if row_value != comparison_value
|
34
|
+
|
35
|
+
check_against_array(row_values, index+1, comparison)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Cassandra
|
2
|
+
module Mocks
|
3
|
+
class Statement
|
4
|
+
class Token < Struct.new(:type, :value)
|
5
|
+
def normalized_value
|
6
|
+
case type
|
7
|
+
when :int
|
8
|
+
value.to_i
|
9
|
+
when :float
|
10
|
+
value.to_f
|
11
|
+
else
|
12
|
+
value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def respond_to?(method)
|
17
|
+
method_inquiry?(method) || super
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(method, *args)
|
21
|
+
if method_inquiry?(method)
|
22
|
+
method[/[^\?]+/].to_sym == type
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def method_inquiry?(method)
|
31
|
+
method =~ /\?$/
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module Cassandra
|
2
|
+
module Mocks
|
3
|
+
class Statement
|
4
|
+
class Tokenizer
|
5
|
+
#noinspection RubyStringKeysInHashInspection
|
6
|
+
KEYWORD_MAP = {
|
7
|
+
'CREATE' => :create,
|
8
|
+
'DROP' => :drop,
|
9
|
+
'TRUNCATE' => :truncate,
|
10
|
+
'PRIMARY' => :primary,
|
11
|
+
'KEY' => :key,
|
12
|
+
'TABLE' => :table,
|
13
|
+
'KEYSPACE' => :keyspace,
|
14
|
+
'INSERT' => :insert,
|
15
|
+
'UPDATE' => :update,
|
16
|
+
'SET' => :set,
|
17
|
+
'VALUES' => :values,
|
18
|
+
'SELECT' => :select,
|
19
|
+
'DELETE' => :delete,
|
20
|
+
'FROM' => :from,
|
21
|
+
'WHERE' => :where,
|
22
|
+
'ORDER' => :order,
|
23
|
+
'BY' => :by,
|
24
|
+
'ASC' => :asc,
|
25
|
+
'DESC' => :desc,
|
26
|
+
'LIMIT' => :limit,
|
27
|
+
'AND' => :and,
|
28
|
+
'IN' => :in,
|
29
|
+
'IF' => :if,
|
30
|
+
'NOT' => :not,
|
31
|
+
'EXISTS' => :exists,
|
32
|
+
'(' => :lparen,
|
33
|
+
')' => :rparen,
|
34
|
+
'<' => :ltri,
|
35
|
+
'>' => :rtri,
|
36
|
+
',' => :comma,
|
37
|
+
'.' => :dot,
|
38
|
+
'[' => :lbracket,
|
39
|
+
']' => :rbracket,
|
40
|
+
'=' => :eql,
|
41
|
+
'+' => :plus,
|
42
|
+
'-' => :minus,
|
43
|
+
'*' => :star,
|
44
|
+
'?' => :parameter,
|
45
|
+
}
|
46
|
+
|
47
|
+
attr_reader :tokens
|
48
|
+
|
49
|
+
def initialize(cql)
|
50
|
+
@tokens = []
|
51
|
+
current_token = ''
|
52
|
+
|
53
|
+
in_string = false
|
54
|
+
in_name = false
|
55
|
+
prev_char = nil
|
56
|
+
tokenize(cql, current_token, in_name, in_string, prev_char)
|
57
|
+
end
|
58
|
+
|
59
|
+
def token_queue
|
60
|
+
Queue.new.tap do |queue|
|
61
|
+
tokens.each { |token| queue << token }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def tokenize(cql, current_token, in_name, in_string, prev_char)
|
68
|
+
cql.chars.each do |char|
|
69
|
+
if char == '"' && prev_char != '\\'
|
70
|
+
in_name = tokenize_string(:name, in_name, current_token)
|
71
|
+
current_token = '' unless in_name
|
72
|
+
elsif char == "'" && prev_char != '\\'
|
73
|
+
in_string = tokenize_string(:string, in_string, current_token)
|
74
|
+
current_token = '' unless in_string
|
75
|
+
elsif !in_name && !in_string && char == '.' && prev_char !~ /\d/
|
76
|
+
translate_multiple_tokens(char, current_token)
|
77
|
+
current_token = ''
|
78
|
+
elsif !in_name && !in_string && %w(, ( ) < > = ? + -).include?(char)
|
79
|
+
translate_multiple_tokens(char, current_token)
|
80
|
+
current_token = ''
|
81
|
+
elsif !in_name && !in_string && char == ' '
|
82
|
+
translate_token(current_token)
|
83
|
+
current_token = ''
|
84
|
+
elsif char == '\\'
|
85
|
+
# do nothing...
|
86
|
+
else
|
87
|
+
current_token << char
|
88
|
+
end
|
89
|
+
prev_char = char
|
90
|
+
end
|
91
|
+
translate_token(current_token) if current_token.present?
|
92
|
+
end
|
93
|
+
|
94
|
+
def translate_multiple_tokens(char, current_token)
|
95
|
+
translate_token(current_token)
|
96
|
+
translate_token(char)
|
97
|
+
end
|
98
|
+
|
99
|
+
def tokenize_string(type, in_string, current_token)
|
100
|
+
if in_string
|
101
|
+
@tokens << Token.new(type, current_token)
|
102
|
+
false
|
103
|
+
else
|
104
|
+
true
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def translate_token(current_token)
|
109
|
+
if current_token.present?
|
110
|
+
@tokens << if current_token =~ /^\d+\.\d+$/
|
111
|
+
Token.new(:float, current_token)
|
112
|
+
elsif current_token =~ /^\d+$/
|
113
|
+
Token.new(:int, current_token)
|
114
|
+
else
|
115
|
+
Token.new(KEYWORD_MAP[current_token.upcase] || :id, current_token)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,364 @@
|
|
1
|
+
module Cassandra
|
2
|
+
module Mocks
|
3
|
+
class Statement
|
4
|
+
attr_reader :cql, :action, :args
|
5
|
+
|
6
|
+
def initialize(cql, args)
|
7
|
+
@cql = cql
|
8
|
+
@input_args = param_queue(args)
|
9
|
+
|
10
|
+
type_token = next_token
|
11
|
+
@action = type_token.type
|
12
|
+
if type_token.create?
|
13
|
+
create_type_token = next_token
|
14
|
+
if create_type_token.table?
|
15
|
+
@action = :create_table
|
16
|
+
parse_create_table
|
17
|
+
else
|
18
|
+
@action = :create_keyspace
|
19
|
+
@args = {keyspace: next_token.value}
|
20
|
+
end
|
21
|
+
elsif type_token.truncate?
|
22
|
+
parse_truncate_query
|
23
|
+
elsif type_token.drop?
|
24
|
+
if next_token.keyspace?
|
25
|
+
@action = :drop_keyspace
|
26
|
+
@args = {keyspace: next_token.value}
|
27
|
+
else
|
28
|
+
@action = :drop_table
|
29
|
+
parse_truncate_query
|
30
|
+
end
|
31
|
+
elsif type_token.insert?
|
32
|
+
parse_insert_query
|
33
|
+
elsif type_token.update?
|
34
|
+
parse_update_query
|
35
|
+
elsif type_token.select?
|
36
|
+
parse_select_query
|
37
|
+
elsif type_token.delete?
|
38
|
+
next_token
|
39
|
+
parse_table_and_filter
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def fill_params(params)
|
44
|
+
Statement.allocate.tap do |statement|
|
45
|
+
statement.cql = cql
|
46
|
+
statement.action = action
|
47
|
+
statement.args = args.dup
|
48
|
+
params = param_queue(params)
|
49
|
+
case action
|
50
|
+
when :insert
|
51
|
+
parameterize_args!(:values, params, statement)
|
52
|
+
when :select, :delete
|
53
|
+
parameterize_args!(:filter, params, statement)
|
54
|
+
when :update
|
55
|
+
parameterize_args!(:values, params, statement)
|
56
|
+
parameterize_args!(:filter, params, statement)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def bind(*args)
|
62
|
+
fill_params(args)
|
63
|
+
end
|
64
|
+
|
65
|
+
def ==(rhs)
|
66
|
+
rhs.is_a?(Statement) &&
|
67
|
+
rhs.action == action &&
|
68
|
+
rhs.args == args
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
attr_writer :cql, :action, :args
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
attr_reader :last_token
|
78
|
+
|
79
|
+
def parameterize_args!(key, params, statement)
|
80
|
+
values = args[key].inject({}) do |memo, (column, value)|
|
81
|
+
updated_value = if value.is_a?(Arithmetic)
|
82
|
+
Arithmetic.new(value.operation, value.column, pending_value(value.amount, params))
|
83
|
+
elsif value.is_a?(Comparitor)
|
84
|
+
if value.value.is_a?(Array)
|
85
|
+
new_values = value.value.map { |value| pending_value(value, params) }
|
86
|
+
Comparitor.new(value.operation, value.column, new_values)
|
87
|
+
else
|
88
|
+
Comparitor.new(value.operation, value.column, pending_value(value.value, params))
|
89
|
+
end
|
90
|
+
elsif value.is_a?(Array)
|
91
|
+
value.map { |value| pending_value(value, params) }
|
92
|
+
else
|
93
|
+
pending_value(value, params)
|
94
|
+
end
|
95
|
+
memo.merge!(column => updated_value)
|
96
|
+
end
|
97
|
+
statement.args.merge!(key => values)
|
98
|
+
end
|
99
|
+
|
100
|
+
def pending_value(value, params)
|
101
|
+
if value == :value_pending
|
102
|
+
raise Errors::InvalidError.new('Not enough params provided to #fill_params', 'MockStatement') if params.empty?
|
103
|
+
params.pop
|
104
|
+
else
|
105
|
+
value
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def tokens
|
110
|
+
@tokens ||= Tokenizer.new(@cql).token_queue
|
111
|
+
end
|
112
|
+
|
113
|
+
def next_token
|
114
|
+
if tokens.empty?
|
115
|
+
Token.new(:eof, nil)
|
116
|
+
else
|
117
|
+
@last_token = tokens.pop
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def parse_create_table
|
122
|
+
table_name_token = next_token
|
123
|
+
check_exists = if table_name_token.if?
|
124
|
+
next_token
|
125
|
+
next_token
|
126
|
+
table_name_token = next_token
|
127
|
+
end
|
128
|
+
table_name = table_name_token.value
|
129
|
+
|
130
|
+
next_token
|
131
|
+
column_name = next_token.value
|
132
|
+
column_type = next_token.value
|
133
|
+
primary_key = nil
|
134
|
+
if next_token.primary?
|
135
|
+
primary_key = [[column_name]]
|
136
|
+
2.times { next_token }
|
137
|
+
end
|
138
|
+
|
139
|
+
additional_columns = if tokens.empty?
|
140
|
+
{}
|
141
|
+
else
|
142
|
+
parenthesis_values(:rparen, :primary).each_slice(2).inject({}) do |memo, (name, type)|
|
143
|
+
memo.merge!(name => type)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
if !tokens.empty? && next_token.key?
|
148
|
+
next_token
|
149
|
+
primary_key_parts = parenthesis_values(:rparen)
|
150
|
+
partition_key = primary_key_parts.shift
|
151
|
+
partition_key = [partition_key] unless partition_key.is_a?(Array)
|
152
|
+
primary_key = [partition_key, *primary_key_parts]
|
153
|
+
end
|
154
|
+
|
155
|
+
@args = {table: table_name, check_exists: !!check_exists, columns: additional_columns.merge({column_name => column_type}), primary_key: primary_key}
|
156
|
+
end
|
157
|
+
|
158
|
+
def parse_truncate_query
|
159
|
+
keyspace_name, table_name = parsed_keyspace_and_table
|
160
|
+
|
161
|
+
@args = {keyspace: keyspace_name, table: table_name}
|
162
|
+
end
|
163
|
+
|
164
|
+
def parse_insert_query
|
165
|
+
next_token
|
166
|
+
|
167
|
+
keyspace_name, table_name = parsed_keyspace_and_table
|
168
|
+
next_token unless keyspace_name.nil?
|
169
|
+
|
170
|
+
insert_keys = parenthesis_values(:rparen)
|
171
|
+
2.times { next_token }
|
172
|
+
insert_values = parenthesis_values(:rparen)
|
173
|
+
|
174
|
+
values = insert_args(insert_keys, insert_values)
|
175
|
+
if next_token.if?
|
176
|
+
@args = {keyspace: keyspace_name, table: table_name, values: values, check_exists: true}
|
177
|
+
else
|
178
|
+
@args = {keyspace: keyspace_name, table: table_name, values: values}
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def parse_update_query
|
183
|
+
keyspace_name, table_name = parsed_keyspace_and_table
|
184
|
+
next_token if keyspace_name
|
185
|
+
values = parsed_filter(:where)
|
186
|
+
filter = parsed_filter(:eof)
|
187
|
+
@args = {keyspace: keyspace_name, table: table_name, values: values, filter: filter}
|
188
|
+
end
|
189
|
+
|
190
|
+
def parse_select_query
|
191
|
+
select_columns = parenthesis_values(:from)
|
192
|
+
parse_table_and_filter
|
193
|
+
@args.merge!(columns: select_columns)
|
194
|
+
end
|
195
|
+
|
196
|
+
def parse_table_and_filter
|
197
|
+
keyspace_name, table_name = parsed_keyspace_and_table
|
198
|
+
next_token if keyspace_name
|
199
|
+
|
200
|
+
filter = parsed_filter(:limit, :order)
|
201
|
+
|
202
|
+
@args = {keyspace: keyspace_name, table: table_name, filter: filter}
|
203
|
+
|
204
|
+
if last_token.order?
|
205
|
+
next_token
|
206
|
+
@args.merge!(order: parse_select_order)
|
207
|
+
end
|
208
|
+
|
209
|
+
if last_token.limit?
|
210
|
+
limit = next_token.normalized_value
|
211
|
+
@args = @args.merge!(limit: limit)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def parse_select_order
|
216
|
+
order = {}
|
217
|
+
prev_column = nil
|
218
|
+
token = next_token
|
219
|
+
until token.eof? || token.limit?
|
220
|
+
if token.desc?
|
221
|
+
order[prev_column] = :desc
|
222
|
+
elsif token.asc?
|
223
|
+
order[prev_column] = :asc
|
224
|
+
elsif token.comma?
|
225
|
+
else
|
226
|
+
order[token.value] = :asc
|
227
|
+
prev_column = token.value
|
228
|
+
end
|
229
|
+
token = next_token
|
230
|
+
end
|
231
|
+
order
|
232
|
+
end
|
233
|
+
|
234
|
+
def parsed_filter(*end_tokens)
|
235
|
+
filter_keys = []
|
236
|
+
filter_values = []
|
237
|
+
until tokens.empty? || end_tokens.include?(last_token.type)
|
238
|
+
filter_keys << next_filter_key(next_token)
|
239
|
+
|
240
|
+
restrictor_token = next_token
|
241
|
+
if restrictor_token.type == :in
|
242
|
+
next_token
|
243
|
+
filter_values << parenthesis_values(:rparen)
|
244
|
+
next_token
|
245
|
+
elsif restrictor_token.ltri? || restrictor_token.rtri?
|
246
|
+
parse_comparison_restriction(filter_keys, filter_values, restrictor_token)
|
247
|
+
else
|
248
|
+
parse_single_restriction(filter_values)
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
insert_args(filter_keys, filter_values)
|
253
|
+
end
|
254
|
+
|
255
|
+
def parse_comparison_restriction(filter_keys, filter_values, restrictor_token)
|
256
|
+
value_token = next_token
|
257
|
+
eql_comparison = if value_token.type == :eql
|
258
|
+
value_token = next_token
|
259
|
+
true
|
260
|
+
end
|
261
|
+
value = next_filter_key(value_token)
|
262
|
+
|
263
|
+
comparison_operator = comparison_operator(eql_comparison, restrictor_token)
|
264
|
+
filter_values << Comparitor.new(comparison_operator, filter_keys.last, value)
|
265
|
+
next_token
|
266
|
+
end
|
267
|
+
|
268
|
+
def next_filter_key(next_key)
|
269
|
+
if next_key.lparen?
|
270
|
+
parenthesis_values(:rparen)
|
271
|
+
else
|
272
|
+
next_key.normalized_value
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def parse_single_restriction(filter_values)
|
277
|
+
value_token = next_token
|
278
|
+
next_token
|
279
|
+
value = value_token.normalized_value
|
280
|
+
update_value = update_value(last_token, value)
|
281
|
+
if update_value
|
282
|
+
value = update_value
|
283
|
+
next_token
|
284
|
+
end
|
285
|
+
filter_values << value
|
286
|
+
end
|
287
|
+
|
288
|
+
def comparison_operator(eql_comparison, restrictor_token)
|
289
|
+
if eql_comparison
|
290
|
+
restrictor_token.rtri? ? :ge : :le
|
291
|
+
else
|
292
|
+
restrictor_token.rtri? ? :gt : :lt
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
def update_value(prev_token, value)
|
297
|
+
if prev_token.plus? || prev_token.minus?
|
298
|
+
column = value
|
299
|
+
amount = next_token.normalized_value
|
300
|
+
Arithmetic.new(prev_token.type, column, amount)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def parsed_keyspace_and_table
|
305
|
+
keyspace_name = nil
|
306
|
+
table_name = next_token.value
|
307
|
+
if next_token.dot?
|
308
|
+
keyspace_name = table_name
|
309
|
+
table_name = next_token.value
|
310
|
+
end
|
311
|
+
[keyspace_name, table_name]
|
312
|
+
end
|
313
|
+
|
314
|
+
def parenthesis_values(*terminators)
|
315
|
+
[].tap do |insert_values|
|
316
|
+
until terminators.include?((key = next_token).type)
|
317
|
+
if key.lparen?
|
318
|
+
insert_values << parenthesis_values(:rparen)
|
319
|
+
elsif !key.comma?
|
320
|
+
insert_values << parameterized_value(key.normalized_value) unless key.comma?
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def insert_args(insert_keys, insert_values)
|
327
|
+
insert_keys.count.times.inject({}) do |memo, index|
|
328
|
+
value = mapped_value(insert_values[index])
|
329
|
+
memo.merge!(insert_keys[index] => value)
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def mapped_value(value)
|
334
|
+
if value.is_a?(Array)
|
335
|
+
value.map { |value| parameterized_value(value) }
|
336
|
+
elsif value.is_a?(Arithmetic)
|
337
|
+
updated_amount = parameterized_value(value.amount)
|
338
|
+
Arithmetic.new(value.operation, value.column, updated_amount)
|
339
|
+
elsif value.is_a?(Comparitor)
|
340
|
+
updated_amount = parameterized_value(value.value)
|
341
|
+
Comparitor.new(value.operation, value.column, updated_amount)
|
342
|
+
else
|
343
|
+
parameterized_value(value)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
def parameterized_value(value)
|
348
|
+
if value == '?'
|
349
|
+
if @input_args.empty?
|
350
|
+
:value_pending
|
351
|
+
else
|
352
|
+
@input_args.pop
|
353
|
+
end
|
354
|
+
else
|
355
|
+
value
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
def param_queue(args)
|
360
|
+
Queue.new.tap { |queue| args.each { |arg| queue << arg } }
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
module Cassandra
|
2
|
+
module Mocks
|
3
|
+
class Table < Cassandra::Table
|
4
|
+
def initialize(keyspace, name, partition_key, clustering_key, fields)
|
5
|
+
compaction = Cassandra::Table::Compaction.new('mock', {})
|
6
|
+
options = Cassandra::Table::Options.new({}, compaction, {}, false, 'mock')
|
7
|
+
column_map = column_map(partition_key, clustering_key, fields)
|
8
|
+
super(keyspace, name, partition_key, clustering_key, column_map, options, [])
|
9
|
+
end
|
10
|
+
|
11
|
+
def insert(attributes, options = {})
|
12
|
+
validate_columns!(attributes)
|
13
|
+
validate_primary_key_presence!(attributes)
|
14
|
+
|
15
|
+
prev_row_index = rows.find_index do |row|
|
16
|
+
row.slice(*primary_key_names) == attributes.slice(*primary_key_names)
|
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
|
26
|
+
end
|
27
|
+
|
28
|
+
def select(*columns)
|
29
|
+
filter = select_filter(columns)
|
30
|
+
limit = filter.delete(:limit)
|
31
|
+
order = select_order(filter)
|
32
|
+
unless filter.empty?
|
33
|
+
validate_partion_key_filter!(filter)
|
34
|
+
validate_clustering_column_filter!(filter)
|
35
|
+
end
|
36
|
+
|
37
|
+
filtered_rows = filtered_rows(filter)
|
38
|
+
sorted_rows = filtered_rows.sort do |lhs, rhs|
|
39
|
+
compare_rows(0, lhs, rhs, order)
|
40
|
+
end
|
41
|
+
|
42
|
+
sorted_rows = sorted_rows[0...limit] if limit
|
43
|
+
|
44
|
+
result_rows = sorted_rows.map do |row|
|
45
|
+
(columns.first == '*') ? row : row.slice(*columns)
|
46
|
+
end
|
47
|
+
ResultPage.new(result_rows)
|
48
|
+
end
|
49
|
+
|
50
|
+
def delete(filter)
|
51
|
+
rows_to_remove = select('*', filter)
|
52
|
+
@rows.reject! { |row| rows_to_remove.include?(row) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def rows
|
56
|
+
@rows ||= []
|
57
|
+
end
|
58
|
+
|
59
|
+
# make #partition_key public
|
60
|
+
def partition_key
|
61
|
+
super
|
62
|
+
end
|
63
|
+
|
64
|
+
# make #clustering_columns public
|
65
|
+
def clustering_columns
|
66
|
+
super
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def select_filter(columns)
|
72
|
+
columns.last.is_a?(Hash) ? columns.pop : {}
|
73
|
+
end
|
74
|
+
|
75
|
+
def select_order(filter)
|
76
|
+
(filter.delete(:order) || {}).inject(Hash.new(1)) do |memo, (key, value)|
|
77
|
+
memo.merge!(key => value == :asc ? 1 : -1)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def validate_columns!(attributes)
|
82
|
+
attributes.each do |column_name, value|
|
83
|
+
column = find_column(column_name)
|
84
|
+
unless column
|
85
|
+
raise Errors::InvalidError.new(%Q{Invalid column, "#{column_name}", specified}, 'MockStatement')
|
86
|
+
end
|
87
|
+
|
88
|
+
if value
|
89
|
+
case column.type
|
90
|
+
when 'double'
|
91
|
+
raise_unless_valid_type(column_name, Float, value)
|
92
|
+
when 'string'
|
93
|
+
raise_unless_valid_type(column_name, String, value)
|
94
|
+
when 'text'
|
95
|
+
raise_unless_valid_type(column_name, String, value)
|
96
|
+
when 'varchar'
|
97
|
+
raise_unless_valid_type(column_name, String, value)
|
98
|
+
when 'blob'
|
99
|
+
raise_unless_valid_type(column_name, String, value)
|
100
|
+
when 'int'
|
101
|
+
raise_unless_valid_type(column_name, Fixnum, value)
|
102
|
+
when 'uuid'
|
103
|
+
raise_unless_valid_type(column_name, Cassandra::Uuid, value)
|
104
|
+
when 'timeuuid'
|
105
|
+
raise_unless_valid_type(column_name, Cassandra::TimeUuid, value)
|
106
|
+
when 'timestamp'
|
107
|
+
raise_unless_valid_type(column_name, Time, value)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def raise_unless_valid_type(column_name, ruby_type, value)
|
114
|
+
unless value.is_a?(ruby_type)
|
115
|
+
raise Errors::InvalidError.new(%Q{Expected column "#{column_name}" to be of type "#{ruby_type}", got a(n) "#{value.class}"}, 'MockStatement')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def validate_primary_key_presence!(attributes)
|
120
|
+
primary_key_names.each do |column|
|
121
|
+
raise Errors::InvalidError.new(%Q{Invalid null primary key part, "#{column}"}, 'MockStatement') unless filter_has_column?(attributes, column)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def validate_clustering_column_filter!(filter)
|
126
|
+
prev_columns = []
|
127
|
+
clustering_key_names.inject(true) do |hit_column, column|
|
128
|
+
if filter_has_column?(filter, column)
|
129
|
+
raise Cassandra::Errors::InvalidError.new("Clustering key part(s) #{prev_columns.map(&:inspect) * ', '} must be restricted", 'MockStatement') unless hit_column
|
130
|
+
else
|
131
|
+
prev_columns << column
|
132
|
+
end
|
133
|
+
filter_has_column?(filter, column)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def filter_has_column?(filter, column)
|
138
|
+
filter[column]
|
139
|
+
end
|
140
|
+
|
141
|
+
def filtered_rows(filter)
|
142
|
+
filter ? apply_filter(filter) : rows
|
143
|
+
end
|
144
|
+
|
145
|
+
def apply_filter(filter)
|
146
|
+
rows.select do |row|
|
147
|
+
partial_row = filter_slices_row(filter, row)
|
148
|
+
filter.all? do |column, value|
|
149
|
+
if value.is_a?(Statement::Comparitor)
|
150
|
+
value.check_against(partial_row)
|
151
|
+
elsif value.is_a?(Array)
|
152
|
+
value.include?(partial_row[column])
|
153
|
+
else
|
154
|
+
partial_row[column] == value
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def filter_slices_row(filter, row)
|
161
|
+
filter.keys.inject({}) do |memo, key, _|
|
162
|
+
value = if key.is_a?(Array)
|
163
|
+
row.values_at(*key)
|
164
|
+
else
|
165
|
+
row[key]
|
166
|
+
end
|
167
|
+
memo.merge!(key => value)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def validate_partion_key_filter!(filter)
|
172
|
+
missing_partition_keys = Set.new(partition_key_names) - filter.keys
|
173
|
+
raise Cassandra::Errors::InvalidError.new("Missing partition key part(s) #{missing_partition_keys.map(&:inspect) * ', '}", 'MockStatement') unless missing_partition_keys.empty?
|
174
|
+
end
|
175
|
+
|
176
|
+
def compare_rows(primary_key_index, lhs, rhs, order)
|
177
|
+
return 0 if primary_key_names[primary_key_index].nil?
|
178
|
+
|
179
|
+
if primary_key_part(lhs, primary_key_index) == primary_key_part(rhs, primary_key_index)
|
180
|
+
compare_rows(primary_key_index + 1, lhs, rhs, order)
|
181
|
+
else
|
182
|
+
comparison = primary_key_part(lhs, primary_key_index) <=> primary_key_part(rhs, primary_key_index)
|
183
|
+
order_comparison(comparison, order, primary_key_names[primary_key_index])
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def order_comparison(comparison, order, primary_key)
|
188
|
+
comparison * order[primary_key]
|
189
|
+
end
|
190
|
+
|
191
|
+
def primary_key_part(row, primary_key_index)
|
192
|
+
row[primary_key_names[primary_key_index]]
|
193
|
+
end
|
194
|
+
|
195
|
+
def primary_key_names
|
196
|
+
partition_key_names + clustering_key_names
|
197
|
+
end
|
198
|
+
|
199
|
+
def partition_key_names
|
200
|
+
partition_key.map(&:name)
|
201
|
+
end
|
202
|
+
|
203
|
+
def clustering_key_names
|
204
|
+
clustering_columns.map(&:name)
|
205
|
+
end
|
206
|
+
|
207
|
+
def column_names
|
208
|
+
columns.map(&:name)
|
209
|
+
end
|
210
|
+
|
211
|
+
def find_column(name)
|
212
|
+
columns.find { |column| column.name == name }
|
213
|
+
end
|
214
|
+
|
215
|
+
def column_map(partition_key, clustering_key, fields)
|
216
|
+
(partition_key + clustering_key + fields).inject({}) do |memo, column|
|
217
|
+
memo.merge!(column.name => column)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright 2014-2015 Thomas RM Rogers
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'cassandra'
|
18
|
+
require 'active_support/all'
|
19
|
+
require 'active_support/core_ext/class/attribute_accessors'
|
20
|
+
|
21
|
+
require 'cassandra_mocks/result_page'
|
22
|
+
require 'cassandra_mocks/table'
|
23
|
+
require 'cassandra_mocks/statement/batch'
|
24
|
+
require 'cassandra_mocks/statement/arithmetic'
|
25
|
+
require 'cassandra_mocks/statement/comparitor'
|
26
|
+
require 'cassandra_mocks/statement/token'
|
27
|
+
require 'cassandra_mocks/statement/tokenizer'
|
28
|
+
require 'cassandra_mocks/statement'
|
29
|
+
require 'cassandra_mocks/keyspace'
|
30
|
+
require 'cassandra_mocks/cluster'
|
31
|
+
require 'cassandra_mocks/session'
|
32
|
+
require 'cassandra_mocks/uuid'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module CassandraModel
|
2
|
+
class RawConnection
|
3
|
+
def cluster
|
4
|
+
@cluster ||= begin
|
5
|
+
Cassandra::Mocks::Cluster.new.tap do |cluster|
|
6
|
+
cluster.add_keyspace(config[:keyspace])
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def session
|
12
|
+
@session ||= Cassandra::Mocks::Session.new(config[:keyspace], cluster)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cassandra_mocks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.8.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thomas RM Rogers
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-12-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: cassandra-driver
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.0'
|
41
|
+
description: |-
|
42
|
+
Cassandra mocking framework intended to replace having
|
43
|
+
a full blown Cassandra running for a unit testing environment. Aims to be a fast
|
44
|
+
and accurate representation of running CQL 3 against the Ruby Cassandra driver
|
45
|
+
email: thomasrogers03@gmail.com
|
46
|
+
executables: []
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- LICENSE.txt
|
51
|
+
- README.md
|
52
|
+
- lib/cassandra_mocks.rb
|
53
|
+
- lib/cassandra_mocks/cluster.rb
|
54
|
+
- lib/cassandra_mocks/keyspace.rb
|
55
|
+
- lib/cassandra_mocks/result_page.rb
|
56
|
+
- lib/cassandra_mocks/session.rb
|
57
|
+
- lib/cassandra_mocks/statement.rb
|
58
|
+
- lib/cassandra_mocks/statement/arithmetic.rb
|
59
|
+
- lib/cassandra_mocks/statement/batch.rb
|
60
|
+
- lib/cassandra_mocks/statement/comparitor.rb
|
61
|
+
- lib/cassandra_mocks/statement/token.rb
|
62
|
+
- lib/cassandra_mocks/statement/tokenizer.rb
|
63
|
+
- lib/cassandra_mocks/table.rb
|
64
|
+
- lib/cassandra_mocks/uuid.rb
|
65
|
+
- lib/cassandra_model/mock_connection.rb
|
66
|
+
homepage: https://www.github.com/thomasrogers03/cassandra_mocks
|
67
|
+
licenses:
|
68
|
+
- Apache License 2.0
|
69
|
+
metadata: {}
|
70
|
+
post_install_message:
|
71
|
+
rdoc_options: []
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - '>='
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
requirements: []
|
85
|
+
rubyforge_project:
|
86
|
+
rubygems_version: 2.4.8
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
89
|
+
summary: Cassandra mocking framework
|
90
|
+
test_files: []
|