whi-cassie 1.1.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/dependabot.yml +12 -0
- data/.github/workflows/continuous_integration.yml +64 -0
- data/.gitignore +7 -15
- data/.ruby-version +1 -0
- data/.standard.yml +11 -0
- data/Appraisals +17 -0
- data/Gemfile +10 -0
- data/HISTORY.md +12 -0
- data/README.md +14 -10
- data/Rakefile +13 -12
- data/VERSION +1 -1
- data/gemfiles/.bundle/config +2 -0
- data/gemfiles/activemodel_4.gemfile +14 -0
- data/gemfiles/activemodel_5.gemfile +14 -0
- data/gemfiles/activemodel_6.gemfile +14 -0
- data/gemfiles/activemodel_7.gemfile +14 -0
- data/lib/cassie/config.rb +8 -7
- data/lib/cassie/model.rb +143 -111
- data/lib/cassie/railtie.rb +7 -5
- data/lib/cassie/schema.rb +35 -28
- data/lib/cassie/subscribers.rb +10 -12
- data/lib/cassie/testing.rb +22 -20
- data/lib/cassie.rb +55 -57
- data/lib/whi-cassie.rb +2 -0
- data/spec/cassie/config_spec.rb +20 -22
- data/spec/cassie/model_spec.rb +333 -311
- data/spec/cassie/subscribers_spec.rb +19 -21
- data/spec/cassie_spec.rb +86 -87
- data/spec/models/thing.rb +11 -11
- data/spec/models/type_tester.rb +7 -7
- data/spec/spec_helper.rb +32 -13
- data/whi-cassie.gemspec +17 -19
- metadata +23 -27
data/lib/cassie/schema.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This class can be used to create, drop, or get information about the cassandra schemas. This class
|
2
4
|
# is intended only to provide support for creating schemas in development and test environments. You
|
3
5
|
# should not use this class with your production environment since some of the methods can be destructive.
|
@@ -8,39 +10,42 @@
|
|
8
10
|
# definition files live. The files should be named "#{abstract_keyspace}.cql". The actual keyspace name will
|
9
11
|
# be looked from the keyspace mapping in the configuration.
|
10
12
|
class Cassie::Schema
|
11
|
-
TABLES_CQL = "SELECT
|
12
|
-
|
13
|
+
TABLES_CQL = "SELECT table_name FROM system_schema.tables WHERE keyspace_name = ?"
|
14
|
+
VERSION_2_TABLES_CQL = "SELECT columnfamily_name FROM system.schema_columnfamilies WHERE keyspace_name = ?"
|
15
|
+
|
16
|
+
# rubocop:disable Lint/MixedRegexpCaptureTypes
|
13
17
|
CREATE_MATCHER = /\A(?<create>CREATE (TABLE|((CUSTOM )?INDEX)|TYPE|TRIGGER))(?<exist>( IF NOT EXISTS)?) (?<object>[a-z0-9_.]+)/i.freeze
|
14
18
|
DROP_MATCHER = /\A(?<drop>DROP (TABLE|INDEX|TYPE|TRIGGER))(?<exist>( IF EXISTS)?) (?<object>[a-z0-9_.]+)/i.freeze
|
15
|
-
|
19
|
+
# rubocop:enable Lint/MixedRegexpCaptureTypes
|
20
|
+
|
16
21
|
attr_reader :keyspace
|
17
|
-
|
22
|
+
|
18
23
|
class << self
|
19
24
|
# Get all the defined schemas.
|
20
25
|
def all
|
21
26
|
schemas.values
|
22
27
|
end
|
23
|
-
|
28
|
+
|
24
29
|
# Find the schema for a keyspace using the abstract name.
|
25
30
|
def find(keyspace)
|
26
31
|
schemas[keyspace]
|
27
32
|
end
|
28
|
-
|
33
|
+
|
29
34
|
# Throw out the cached schemas so they can be reloaded from the configuration.
|
30
35
|
def reset!
|
31
36
|
@schemas = nil
|
32
37
|
end
|
33
|
-
|
38
|
+
|
34
39
|
# Drop a specified keyspace by abstract name. The actual keyspace name will be looked up
|
35
40
|
# from the keyspaces in the configuration.
|
36
41
|
def drop!(keyspace_name)
|
37
42
|
keyspace = Cassie.instance.config.keyspace(keyspace_name)
|
38
43
|
raise ArgumentError.new("#{keyspace_name} is not defined as keyspace in the configuration") unless keyspace
|
39
|
-
|
44
|
+
|
40
45
|
drop_keyspace_cql = "DROP KEYSPACE IF EXISTS #{keyspace}"
|
41
46
|
Cassie.instance.execute(drop_keyspace_cql)
|
42
47
|
end
|
43
|
-
|
48
|
+
|
44
49
|
# Load a specified keyspace by abstract name. The actual keyspace name will be looked up
|
45
50
|
# from the keyspaces in the configuration.
|
46
51
|
def load!(keyspace_name)
|
@@ -49,24 +54,24 @@ class Cassie::Schema
|
|
49
54
|
|
50
55
|
schema_file = File.join(Cassie.instance.config.schema_directory, "#{keyspace_name}.cql")
|
51
56
|
raise ArgumentError.new("#{keyspace_name} schema file does not exist at #{schema_file}") unless File.exist?(schema_file)
|
52
|
-
schema_statements = File.read(schema_file).split(
|
53
|
-
|
57
|
+
schema_statements = File.read(schema_file).split(";").collect { |s| s.strip.chomp(";") }
|
58
|
+
|
54
59
|
create_keyspace_cql = "CREATE KEYSPACE IF NOT EXISTS #{keyspace} WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 1}"
|
55
60
|
Cassie.instance.execute(create_keyspace_cql)
|
56
|
-
|
61
|
+
|
57
62
|
schema_statements.each do |statement|
|
58
|
-
statement = statement.gsub(/#(.*)$/,
|
63
|
+
statement = statement.gsub(/#(.*)$/, "").gsub(/\s+/, " ").strip
|
59
64
|
create_match = statement.match(CREATE_MATCHER)
|
60
65
|
if create_match
|
61
66
|
object = create_match["object"]
|
62
|
-
object = "#{keyspace}.#{object}" unless object.include?(
|
63
|
-
statement = statement.sub(create_match.to_s, "#{create_match[
|
67
|
+
object = "#{keyspace}.#{object}" unless object.include?(".")
|
68
|
+
statement = statement.sub(create_match.to_s, "#{create_match["create"]} IF NOT EXISTS #{object}")
|
64
69
|
else
|
65
70
|
drop_match = statement.match(DROP_MATCHER)
|
66
71
|
if drop_match
|
67
72
|
object = drop_match["object"]
|
68
|
-
object = "#{keyspace}.#{object}" unless object.include?(
|
69
|
-
statement = statement.sub(drop_match.to_s, "#{drop_match[
|
73
|
+
object = "#{keyspace}.#{object}" unless object.include?(".")
|
74
|
+
statement = statement.sub(drop_match.to_s, "#{drop_match["drop"]} IF EXISTS #{object}")
|
70
75
|
end
|
71
76
|
end
|
72
77
|
unless statement.blank?
|
@@ -75,23 +80,23 @@ class Cassie::Schema
|
|
75
80
|
end
|
76
81
|
nil
|
77
82
|
end
|
78
|
-
|
83
|
+
|
79
84
|
# Drop all keyspaces defined in the configuration.
|
80
85
|
def drop_all!
|
81
86
|
Cassie.instance.config.keyspace_names.each do |keyspace|
|
82
87
|
drop!(keyspace)
|
83
88
|
end
|
84
89
|
end
|
85
|
-
|
90
|
+
|
86
91
|
# Drop all keyspaces defined in the configuration.
|
87
92
|
def load_all!
|
88
93
|
Cassie.instance.config.keyspace_names.each do |keyspace|
|
89
94
|
load!(keyspace)
|
90
95
|
end
|
91
96
|
end
|
92
|
-
|
97
|
+
|
93
98
|
private
|
94
|
-
|
99
|
+
|
95
100
|
def schemas
|
96
101
|
unless defined?(@schemas) && @schemas
|
97
102
|
schemas = {}
|
@@ -103,24 +108,26 @@ class Cassie::Schema
|
|
103
108
|
@schemas
|
104
109
|
end
|
105
110
|
end
|
106
|
-
|
111
|
+
|
107
112
|
def initialize(keyspace)
|
108
113
|
@keyspace = keyspace
|
109
114
|
end
|
110
|
-
|
115
|
+
|
111
116
|
# Returns a list of tables defined for the schema.
|
112
117
|
def tables
|
113
118
|
unless defined?(@tables) && @tables
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
119
|
+
results = nil
|
120
|
+
begin
|
121
|
+
results = Cassie.instance.execute(TABLES_CQL, keyspace)
|
122
|
+
rescue Cassandra::Errors::InvalidError
|
123
|
+
results = Cassie.instance.execute(VERSION_2_TABLES_CQL, keyspace)
|
118
124
|
end
|
125
|
+
tables = results.collect { |row| row.values.first }
|
119
126
|
@tables = tables
|
120
127
|
end
|
121
128
|
@tables
|
122
129
|
end
|
123
|
-
|
130
|
+
|
124
131
|
# Truncate the data from a table.
|
125
132
|
def truncate!(table)
|
126
133
|
statement = Cassie.instance.prepare("TRUNCATE #{keyspace}.#{table}")
|
data/lib/cassie/subscribers.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Thread safe list of subscribers. Each subscriber must respond to the :call method.
|
2
4
|
class Cassie::Subscribers
|
3
|
-
|
4
5
|
def initialize(parent_subscribers = nil)
|
5
6
|
@array = [].freeze
|
6
7
|
@lock = Mutex.new
|
7
8
|
@parent_subscribers = parent_subscribers
|
8
9
|
end
|
9
|
-
|
10
|
+
|
10
11
|
def add(subscriber)
|
11
12
|
@lock.synchronize do
|
12
13
|
new_array = @array.dup
|
@@ -15,7 +16,7 @@ class Cassie::Subscribers
|
|
15
16
|
end
|
16
17
|
end
|
17
18
|
alias_method :<<, :add
|
18
|
-
|
19
|
+
|
19
20
|
def remove(subscriber)
|
20
21
|
removed = nil
|
21
22
|
@lock.synchronize do
|
@@ -26,28 +27,25 @@ class Cassie::Subscribers
|
|
26
27
|
removed
|
27
28
|
end
|
28
29
|
alias_method :delete, :remove
|
29
|
-
|
30
|
+
|
30
31
|
def clear
|
31
32
|
@array = []
|
32
33
|
end
|
33
|
-
|
34
|
+
|
34
35
|
def size
|
35
36
|
@array.size + (@parent_subscribers ? @parent_subscribers.size : 0)
|
36
37
|
end
|
37
|
-
|
38
|
+
|
38
39
|
def empty?
|
39
40
|
size == 0
|
40
41
|
end
|
41
|
-
|
42
|
+
|
42
43
|
def each(&block)
|
43
44
|
@array.each(&block)
|
44
|
-
|
45
|
-
@parent_subscribers.each(&block)
|
46
|
-
end
|
45
|
+
@parent_subscribers&.each(&block)
|
47
46
|
end
|
48
|
-
|
47
|
+
|
49
48
|
def include?(subscriber)
|
50
49
|
@array.include?(subscriber)
|
51
50
|
end
|
52
|
-
|
53
51
|
end
|
data/lib/cassie/testing.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This class provides helper methods for testing.
|
2
4
|
module Cassie::Testing
|
3
5
|
extend ActiveSupport::Concern
|
4
|
-
|
6
|
+
|
5
7
|
included do
|
6
|
-
|
8
|
+
prepend OverrideMethods
|
7
9
|
end
|
8
|
-
|
10
|
+
|
9
11
|
class << self
|
10
12
|
# Prepare the test environment. This method must be called before running the test suite.
|
11
13
|
def prepare!
|
@@ -16,31 +18,31 @@ module Cassie::Testing
|
|
16
18
|
end
|
17
19
|
end
|
18
20
|
end
|
19
|
-
|
21
|
+
|
20
22
|
# Wrap test cases as a block in this method. After the test case finishes, all tables
|
21
23
|
# that had data inserted into them will be truncated so that the data state will be clean
|
22
24
|
# for the next test case.
|
23
25
|
def cleanup!
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
schema.truncate!(table) if schema
|
33
|
-
end
|
26
|
+
yield
|
27
|
+
ensure
|
28
|
+
if Thread.current[:cassie_inserted].present?
|
29
|
+
Cassie.instance.batch do
|
30
|
+
Thread.current[:cassie_inserted].each do |table|
|
31
|
+
keyspace, table = table.split(".", 2)
|
32
|
+
schema = Cassie::Schema.find(keyspace)
|
33
|
+
schema&.truncate!(table)
|
34
34
|
end
|
35
|
-
Thread.current[:cassie_inserted] = nil
|
36
35
|
end
|
36
|
+
Thread.current[:cassie_inserted] = nil
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
|
41
|
+
module OverrideMethods
|
42
|
+
def insert(table, *args)
|
43
|
+
Thread.current[:cassie_inserted] ||= Set.new
|
44
|
+
Thread.current[:cassie_inserted] << table
|
45
|
+
super(table, *args)
|
46
|
+
end
|
45
47
|
end
|
46
48
|
end
|
data/lib/cassie.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "cassandra"
|
2
4
|
|
3
5
|
# This class provides a lightweight wrapper around the Cassandra driver. It provides
|
4
6
|
# a foundation for maintaining a connection and constructing CQL statements.
|
@@ -9,35 +11,35 @@ class Cassie
|
|
9
11
|
require File.expand_path("../cassie/schema.rb", __FILE__)
|
10
12
|
require File.expand_path("../cassie/testing.rb", __FILE__)
|
11
13
|
require File.expand_path("../cassie/railtie.rb", __FILE__) if defined?(Rails)
|
12
|
-
|
14
|
+
|
13
15
|
class RecordNotFound < StandardError
|
14
16
|
end
|
15
|
-
|
17
|
+
|
16
18
|
class RecordInvalid < StandardError
|
17
19
|
attr_reader :record
|
18
|
-
|
20
|
+
|
19
21
|
def initialize(record)
|
20
22
|
super("Errors on #{record.class.name}: #{record.errors.to_hash.inspect}")
|
21
23
|
@record = record
|
22
24
|
end
|
23
25
|
end
|
24
|
-
|
26
|
+
|
25
27
|
# Message passed to subscribers with the statement, options, and time for each statement
|
26
28
|
# to execute. Note that if statements are batched they will be packed into one message
|
27
29
|
# with a Cassandra::Statements::Batch statement and empty options.
|
28
30
|
class Message
|
29
31
|
attr_reader :statement, :options, :elapsed_time
|
30
|
-
|
32
|
+
|
31
33
|
def initialize(statement, options, elapsed_time)
|
32
34
|
@statement = statement
|
33
35
|
@options = options
|
34
36
|
@elapsed_time = elapsed_time
|
35
37
|
end
|
36
38
|
end
|
37
|
-
|
39
|
+
|
38
40
|
attr_reader :config, :subscribers
|
39
41
|
attr_accessor :consistency
|
40
|
-
|
42
|
+
|
41
43
|
class << self
|
42
44
|
# A singleton instance that can be shared to communicate with a Cassandra cluster.
|
43
45
|
def instance
|
@@ -47,7 +49,7 @@ class Cassie
|
|
47
49
|
end
|
48
50
|
@instance
|
49
51
|
end
|
50
|
-
|
52
|
+
|
51
53
|
# Call this method to load the Cassie::Config from the specified file for the
|
52
54
|
# specified environment.
|
53
55
|
def configure!(options)
|
@@ -58,7 +60,7 @@ class Cassie
|
|
58
60
|
end
|
59
61
|
@config = Cassie::Config.new(options)
|
60
62
|
end
|
61
|
-
|
63
|
+
|
62
64
|
# This method can be used to set a consistency level for all Cassandra queries
|
63
65
|
# within a block that don't explicitly define them. It can be used where consistency
|
64
66
|
# is important (i.e. on validation queries) but where a higher level method
|
@@ -72,18 +74,16 @@ class Cassie
|
|
72
74
|
Thread.current[:cassie_consistency] = save_val
|
73
75
|
end
|
74
76
|
end
|
75
|
-
|
77
|
+
|
76
78
|
# Get a Logger compatible object if it has been set.
|
77
79
|
def logger
|
78
80
|
@logger if defined?(@logger)
|
79
81
|
end
|
80
|
-
|
82
|
+
|
81
83
|
# Set a logger with a Logger compatible object.
|
82
|
-
|
83
|
-
@logger = value
|
84
|
-
end
|
84
|
+
attr_writer :logger
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
87
|
def initialize(config)
|
88
88
|
@config = config
|
89
89
|
@monitor = Monitor.new
|
@@ -93,14 +93,14 @@ class Cassie
|
|
93
93
|
@subscribers = Subscribers.new
|
94
94
|
@consistency = ((config.cluster || {})[:consistency] || :local_one)
|
95
95
|
end
|
96
|
-
|
96
|
+
|
97
97
|
# Open a connection to the Cassandra cluster.
|
98
98
|
def connect
|
99
99
|
start_time = Time.now
|
100
100
|
cluster_config = config.cluster
|
101
|
-
cluster_config = cluster_config.merge(:
|
101
|
+
cluster_config = cluster_config.merge(logger: logger) if logger
|
102
102
|
cluster = Cassandra.cluster(cluster_config)
|
103
|
-
logger
|
103
|
+
logger&.info("Cassie.connect with #{config.sanitized_cluster} in #{((Time.now - start_time) * 1000).round}ms")
|
104
104
|
@monitor.synchronize do
|
105
105
|
@session = cluster.connect(config.default_keyspace)
|
106
106
|
@prepared_statements = {}
|
@@ -109,19 +109,19 @@ class Cassie
|
|
109
109
|
|
110
110
|
# Close the connections to the Cassandra cluster.
|
111
111
|
def disconnect
|
112
|
-
logger
|
112
|
+
logger&.info("Cassie.disconnect from #{config.sanitized_cluster}")
|
113
113
|
@monitor.synchronize do
|
114
|
-
@session
|
114
|
+
@session&.close
|
115
115
|
@session = nil
|
116
116
|
@prepared_statements = {}
|
117
117
|
end
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
# Return true if the connection to the Cassandra cluster has been established.
|
121
121
|
def connected?
|
122
122
|
!!@session
|
123
123
|
end
|
124
|
-
|
124
|
+
|
125
125
|
# Force reconnection. If you're using this code in conjunction in a forking server environment
|
126
126
|
# like passenger or unicorn you should call this method after forking.
|
127
127
|
def reconnect
|
@@ -150,13 +150,13 @@ class Cassie
|
|
150
150
|
end
|
151
151
|
end
|
152
152
|
end
|
153
|
-
|
153
|
+
|
154
154
|
if cache_filled_up && logger && Time.now > @last_prepare_warning + 10
|
155
155
|
# Set a throttle on how often this message is logged so we don't kill performance enven more.
|
156
156
|
@last_prepare_warning = Time.now
|
157
157
|
logger.warn("Cassie.prepare cache filled up. Consider increasing the size from #{config.max_prepared_statements}.")
|
158
158
|
end
|
159
|
-
|
159
|
+
|
160
160
|
statement
|
161
161
|
end
|
162
162
|
|
@@ -210,22 +210,22 @@ class Cassie
|
|
210
210
|
columns = []
|
211
211
|
values = []
|
212
212
|
values_hash.each do |column, value|
|
213
|
-
|
213
|
+
unless value.nil?
|
214
214
|
columns << column
|
215
215
|
values << value
|
216
216
|
end
|
217
217
|
end
|
218
|
-
cql = "INSERT INTO #{table} (#{columns.join(
|
219
|
-
|
220
|
-
if options
|
218
|
+
cql = "INSERT INTO #{table} (#{columns.join(", ")}) VALUES (#{question_marks(columns.size)})"
|
219
|
+
|
220
|
+
if options&.include?(:ttl)
|
221
221
|
options = options.dup
|
222
222
|
ttl = options.delete(:ttl)
|
223
223
|
if ttl
|
224
|
-
cql
|
224
|
+
cql += " USING TTL ?"
|
225
225
|
values << Integer(ttl)
|
226
226
|
end
|
227
227
|
end
|
228
|
-
|
228
|
+
|
229
229
|
batch_or_execute(cql, values, options)
|
230
230
|
end
|
231
231
|
|
@@ -248,20 +248,20 @@ class Cassie
|
|
248
248
|
end
|
249
249
|
end
|
250
250
|
values = update_values + key_values
|
251
|
-
|
251
|
+
|
252
252
|
cql = "UPDATE #{table}"
|
253
|
-
|
254
|
-
if options
|
253
|
+
|
254
|
+
if options&.include?(:ttl)
|
255
255
|
options = options.dup
|
256
256
|
ttl = options.delete(:ttl)
|
257
257
|
if ttl
|
258
|
-
cql
|
258
|
+
cql += " USING TTL ?"
|
259
259
|
values.unshift(Integer(ttl))
|
260
260
|
end
|
261
261
|
end
|
262
262
|
|
263
|
-
cql
|
264
|
-
|
263
|
+
cql += " SET #{update_cql.join(", ")} WHERE #{key_cql}"
|
264
|
+
|
265
265
|
batch_or_execute(cql, values, options)
|
266
266
|
end
|
267
267
|
|
@@ -281,31 +281,31 @@ class Cassie
|
|
281
281
|
start_time = Time.now
|
282
282
|
begin
|
283
283
|
statement = nil
|
284
|
-
if cql.is_a?(String)
|
284
|
+
statement = if cql.is_a?(String)
|
285
285
|
if values.present?
|
286
|
-
|
286
|
+
prepare(cql)
|
287
287
|
else
|
288
|
-
|
288
|
+
Cassandra::Statements::Simple.new(cql)
|
289
289
|
end
|
290
290
|
else
|
291
|
-
|
291
|
+
cql
|
292
292
|
end
|
293
|
-
|
293
|
+
|
294
294
|
if values.present?
|
295
295
|
values = Array(values)
|
296
|
-
options = (options ? options.merge(:
|
296
|
+
options = (options ? options.merge(arguments: values) : {arguments: values})
|
297
297
|
end
|
298
|
-
|
298
|
+
|
299
299
|
# Set a default consistency from a block context if it isn't explicitly set.
|
300
300
|
statement_consistency = current_consistency
|
301
301
|
if statement_consistency
|
302
302
|
if options
|
303
|
-
options = options.merge(:
|
303
|
+
options = options.merge(consistency: statement_consistency) if options[:consistency].nil?
|
304
304
|
else
|
305
|
-
options = {:
|
305
|
+
options = {consistency: statement_consistency}
|
306
306
|
end
|
307
307
|
end
|
308
|
-
|
308
|
+
|
309
309
|
session.execute(statement, options || {})
|
310
310
|
rescue Cassandra::Errors::IOError => e
|
311
311
|
disconnect
|
@@ -313,18 +313,18 @@ class Cassie
|
|
313
313
|
ensure
|
314
314
|
if statement.is_a?(Cassandra::Statement) && !subscribers.empty?
|
315
315
|
payload = Message.new(statement, options, Time.now - start_time)
|
316
|
-
subscribers.each{|subscriber| subscriber.call(payload)}
|
316
|
+
subscribers.each { |subscriber| subscriber.call(payload) }
|
317
317
|
end
|
318
318
|
end
|
319
319
|
end
|
320
|
-
|
320
|
+
|
321
321
|
# Return the current consistency level that has been set for statements.
|
322
322
|
def current_consistency
|
323
323
|
Thread.current[:cassie_consistency] || consistency
|
324
324
|
end
|
325
325
|
|
326
326
|
private
|
327
|
-
|
327
|
+
|
328
328
|
def logger
|
329
329
|
self.class.logger
|
330
330
|
end
|
@@ -333,7 +333,7 @@ class Cassie
|
|
333
333
|
connect unless connected?
|
334
334
|
@session
|
335
335
|
end
|
336
|
-
|
336
|
+
|
337
337
|
def batch_or_execute(cql, values, options = nil)
|
338
338
|
batch = Thread.current[:cassie_batch]
|
339
339
|
if batch
|
@@ -345,9 +345,7 @@ class Cassie
|
|
345
345
|
end
|
346
346
|
|
347
347
|
def question_marks(size)
|
348
|
-
|
349
|
-
(size - 1).times{ q << ',?' }
|
350
|
-
q
|
348
|
+
"?#{",?" * (size - 1)}"
|
351
349
|
end
|
352
350
|
|
353
351
|
def key_clause(key_hash)
|
@@ -357,9 +355,9 @@ class Cassie
|
|
357
355
|
cql << "#{key} = ?"
|
358
356
|
values << value
|
359
357
|
end
|
360
|
-
[cql.join(
|
358
|
+
[cql.join(" AND "), values]
|
361
359
|
end
|
362
|
-
|
360
|
+
|
363
361
|
# Extract the CQL from a statement
|
364
362
|
def statement_cql(statement, previous = nil)
|
365
363
|
cql = nil
|
@@ -368,7 +366,7 @@ class Cassie
|
|
368
366
|
elsif statement.respond_to?(:statements) && (previous.nil? || !previous.include?(statement))
|
369
367
|
previous ||= []
|
370
368
|
previous << statement
|
371
|
-
cql = statement.statements.collect{|s| statement_cql(s, previous)}.join(
|
369
|
+
cql = statement.statements.collect { |s| statement_cql(s, previous) }.join("; ")
|
372
370
|
end
|
373
371
|
cql
|
374
372
|
end
|
data/lib/whi-cassie.rb
CHANGED
data/spec/cassie/config_spec.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe Cassie::Config do
|
4
|
-
|
5
4
|
let(:options) do
|
6
5
|
{
|
7
6
|
"cluster" => {
|
@@ -14,43 +13,42 @@ describe Cassie::Config do
|
|
14
13
|
"default_keyspace" => "another"
|
15
14
|
}
|
16
15
|
end
|
17
|
-
|
16
|
+
|
18
17
|
it "should handle empty options" do
|
19
18
|
config = Cassie::Config.new({})
|
20
|
-
config.cluster.
|
21
|
-
config.keyspace_names.
|
22
|
-
config.default_keyspace.
|
23
|
-
config.schema_directory.
|
24
|
-
config.max_prepared_statements.
|
19
|
+
expect(config.cluster).to eq({})
|
20
|
+
expect(config.keyspace_names).to eq([])
|
21
|
+
expect(config.default_keyspace).to eq(nil)
|
22
|
+
expect(config.schema_directory).to eq(nil)
|
23
|
+
expect(config.max_prepared_statements).to eq(1000)
|
25
24
|
end
|
26
|
-
|
25
|
+
|
27
26
|
it "should have cluster options" do
|
28
27
|
config = Cassie::Config.new(options)
|
29
|
-
config.cluster.
|
28
|
+
expect(config.cluster).to eq({consistency: :one, timeout: 15})
|
30
29
|
end
|
31
|
-
|
30
|
+
|
32
31
|
it "should have keyspaces" do
|
33
32
|
config = Cassie::Config.new(options)
|
34
|
-
config.keyspace(:default).
|
35
|
-
config.keyspace("other").
|
36
|
-
config.keyspace_names.
|
33
|
+
expect(config.keyspace(:default)).to start_with("test_default")
|
34
|
+
expect(config.keyspace("other")).to start_with("test_other")
|
35
|
+
expect(config.keyspace_names).to match_array(["default", "other"])
|
37
36
|
end
|
38
|
-
|
37
|
+
|
39
38
|
it "should have a default_keyspace" do
|
40
39
|
config = Cassie::Config.new(options)
|
41
|
-
config.default_keyspace.
|
40
|
+
expect(config.default_keyspace).to eq("another")
|
42
41
|
end
|
43
|
-
|
42
|
+
|
44
43
|
it "should get the schema_directory" do
|
45
44
|
config = Cassie::Config.new(options)
|
46
|
-
config.schema_directory.
|
47
|
-
Cassie::Config.new({}).schema_directory.
|
45
|
+
expect(config.schema_directory).to eq("/tmp")
|
46
|
+
expect(Cassie::Config.new({}).schema_directory).to eq(nil)
|
48
47
|
end
|
49
|
-
|
48
|
+
|
50
49
|
it "should get the max_prepared_statements" do
|
51
50
|
config = Cassie::Config.new(options)
|
52
|
-
config.max_prepared_statements.
|
53
|
-
Cassie::Config.new({}).max_prepared_statements.
|
51
|
+
expect(config.max_prepared_statements).to eq(100)
|
52
|
+
expect(Cassie::Config.new({}).max_prepared_statements).to eq(1000)
|
54
53
|
end
|
55
|
-
|
56
54
|
end
|