cassanity 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.travis.yml +10 -0
- data/Gemfile +11 -0
- data/Guardfile +17 -0
- data/LICENSE.txt +22 -0
- data/README.md +99 -0
- data/Rakefile +16 -0
- data/cassanity.gemspec +21 -0
- data/examples/_shared.rb +8 -0
- data/examples/batch.rb +40 -0
- data/examples/column_families.rb +68 -0
- data/examples/keyspaces.rb +57 -0
- data/lib/cassanity/argument_generators/batch.rb +52 -0
- data/lib/cassanity/argument_generators/column_family_alter.rb +47 -0
- data/lib/cassanity/argument_generators/column_family_create.rb +44 -0
- data/lib/cassanity/argument_generators/column_family_delete.rb +65 -0
- data/lib/cassanity/argument_generators/column_family_drop.rb +18 -0
- data/lib/cassanity/argument_generators/column_family_insert.rb +53 -0
- data/lib/cassanity/argument_generators/column_family_select.rb +34 -0
- data/lib/cassanity/argument_generators/column_family_truncate.rb +18 -0
- data/lib/cassanity/argument_generators/column_family_update.rb +69 -0
- data/lib/cassanity/argument_generators/index_create.rb +22 -0
- data/lib/cassanity/argument_generators/index_drop.rb +13 -0
- data/lib/cassanity/argument_generators/keyspace_create.rb +51 -0
- data/lib/cassanity/argument_generators/keyspace_drop.rb +13 -0
- data/lib/cassanity/argument_generators/keyspace_use.rb +13 -0
- data/lib/cassanity/argument_generators/keyspaces.rb +12 -0
- data/lib/cassanity/argument_generators/set_clause.rb +30 -0
- data/lib/cassanity/argument_generators/using_clause.rb +24 -0
- data/lib/cassanity/argument_generators/where_clause.rb +29 -0
- data/lib/cassanity/argument_generators/with_clause.rb +32 -0
- data/lib/cassanity/column_family.rb +233 -0
- data/lib/cassanity/connection.rb +88 -0
- data/lib/cassanity/error.rb +28 -0
- data/lib/cassanity/executors/cassandra_cql.rb +120 -0
- data/lib/cassanity/keyspace.rb +118 -0
- data/lib/cassanity/result_transformers/column_family_select.rb +15 -0
- data/lib/cassanity/result_transformers/mirror.rb +12 -0
- data/lib/cassanity/schema.rb +26 -0
- data/lib/cassanity/version.rb +3 -0
- data/lib/cassanity.rb +5 -0
- data/spec/helper.rb +27 -0
- data/spec/integration/cassanity/column_family_spec.rb +243 -0
- data/spec/integration/cassanity/connection_spec.rb +87 -0
- data/spec/integration/cassanity/keyspace_spec.rb +64 -0
- data/spec/support/cassanity_helpers.rb +35 -0
- data/spec/unit/cassanity/argument_generators/batch_spec.rb +36 -0
- data/spec/unit/cassanity/argument_generators/column_family_alter_spec.rb +85 -0
- data/spec/unit/cassanity/argument_generators/column_family_create_spec.rb +107 -0
- data/spec/unit/cassanity/argument_generators/column_family_delete_spec.rb +92 -0
- data/spec/unit/cassanity/argument_generators/column_family_drop_spec.rb +25 -0
- data/spec/unit/cassanity/argument_generators/column_family_insert_spec.rb +70 -0
- data/spec/unit/cassanity/argument_generators/column_family_select_spec.rb +113 -0
- data/spec/unit/cassanity/argument_generators/column_family_truncate_spec.rb +25 -0
- data/spec/unit/cassanity/argument_generators/column_family_update_spec.rb +109 -0
- data/spec/unit/cassanity/argument_generators/index_create_spec.rb +39 -0
- data/spec/unit/cassanity/argument_generators/index_drop_spec.rb +14 -0
- data/spec/unit/cassanity/argument_generators/keyspace_create_spec.rb +53 -0
- data/spec/unit/cassanity/argument_generators/keyspace_drop_spec.rb +14 -0
- data/spec/unit/cassanity/argument_generators/keyspace_use_spec.rb +14 -0
- data/spec/unit/cassanity/argument_generators/keyspaces_spec.rb +12 -0
- data/spec/unit/cassanity/argument_generators/set_clause_spec.rb +85 -0
- data/spec/unit/cassanity/argument_generators/using_clause_spec.rb +44 -0
- data/spec/unit/cassanity/argument_generators/where_clause_spec.rb +57 -0
- data/spec/unit/cassanity/argument_generators/with_clause_spec.rb +63 -0
- data/spec/unit/cassanity/column_family_spec.rb +250 -0
- data/spec/unit/cassanity/connection_spec.rb +75 -0
- data/spec/unit/cassanity/error_spec.rb +35 -0
- data/spec/unit/cassanity/executors/cassandra_cql_spec.rb +178 -0
- data/spec/unit/cassanity/keyspace_spec.rb +137 -0
- data/spec/unit/cassanity/result_transformers/column_family_select_spec.rb +0 -0
- data/spec/unit/cassanity/result_transformers/mirror_spec.rb +12 -0
- data/spec/unit/cassanity/schema_spec.rb +23 -0
- data/spec/unit/cassanity_spec.rb +5 -0
- metadata +172 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
module Cassanity
|
2
|
+
class Error < Exception
|
3
|
+
# Public: The original error this exception is wrapping.
|
4
|
+
attr_reader :original
|
5
|
+
|
6
|
+
# Public: Initializes an Error.
|
7
|
+
#
|
8
|
+
# args - The Hash of arguments.
|
9
|
+
# :original - The Exception being wrapped (optional).
|
10
|
+
#
|
11
|
+
# Returns the duplicated String.
|
12
|
+
def initialize(args = {})
|
13
|
+
@original = args.fetch(:original) { $! }
|
14
|
+
@message = args.fetch(:message) {
|
15
|
+
if @original
|
16
|
+
"Original Exception: #{@original.class}: #{@original.message}"
|
17
|
+
else
|
18
|
+
"Something truly horrible went wrong"
|
19
|
+
end
|
20
|
+
}
|
21
|
+
|
22
|
+
super @message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class UnknownCommand < Error
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'cassandra-cql'
|
2
|
+
require 'cassanity/error'
|
3
|
+
require 'cassanity/argument_generators/keyspaces'
|
4
|
+
require 'cassanity/argument_generators/keyspace_create'
|
5
|
+
require 'cassanity/argument_generators/keyspace_drop'
|
6
|
+
require 'cassanity/argument_generators/keyspace_use'
|
7
|
+
require 'cassanity/argument_generators/column_family_create'
|
8
|
+
require 'cassanity/argument_generators/column_family_drop'
|
9
|
+
require 'cassanity/argument_generators/column_family_truncate'
|
10
|
+
require 'cassanity/argument_generators/column_family_select'
|
11
|
+
require 'cassanity/argument_generators/column_family_insert'
|
12
|
+
require 'cassanity/argument_generators/column_family_update'
|
13
|
+
require 'cassanity/argument_generators/column_family_delete'
|
14
|
+
require 'cassanity/argument_generators/column_family_alter'
|
15
|
+
require 'cassanity/argument_generators/index_create'
|
16
|
+
require 'cassanity/argument_generators/index_drop'
|
17
|
+
require 'cassanity/argument_generators/batch'
|
18
|
+
require 'cassanity/result_transformers/column_family_select'
|
19
|
+
require 'cassanity/result_transformers/mirror'
|
20
|
+
|
21
|
+
module Cassanity
|
22
|
+
module Executors
|
23
|
+
class CassandraCql
|
24
|
+
|
25
|
+
# Private: Hash of commands to related argument generators.
|
26
|
+
ArgumentGenerators = {
|
27
|
+
keyspaces: Cassanity::ArgumentGenerators::Keyspaces.new,
|
28
|
+
keyspace_create: Cassanity::ArgumentGenerators::KeyspaceCreate.new,
|
29
|
+
keyspace_drop: Cassanity::ArgumentGenerators::KeyspaceDrop.new,
|
30
|
+
keyspace_use: Cassanity::ArgumentGenerators::KeyspaceUse.new,
|
31
|
+
column_family_create: Cassanity::ArgumentGenerators::ColumnFamilyCreate.new,
|
32
|
+
column_family_drop: Cassanity::ArgumentGenerators::ColumnFamilyDrop.new,
|
33
|
+
column_family_truncate: Cassanity::ArgumentGenerators::ColumnFamilyTruncate.new,
|
34
|
+
column_family_select: Cassanity::ArgumentGenerators::ColumnFamilySelect.new,
|
35
|
+
column_family_insert: Cassanity::ArgumentGenerators::ColumnFamilyInsert.new,
|
36
|
+
column_family_update: Cassanity::ArgumentGenerators::ColumnFamilyUpdate.new,
|
37
|
+
column_family_delete: Cassanity::ArgumentGenerators::ColumnFamilyDelete.new,
|
38
|
+
column_family_alter: Cassanity::ArgumentGenerators::ColumnFamilyAlter.new,
|
39
|
+
index_create: Cassanity::ArgumentGenerators::IndexCreate.new,
|
40
|
+
index_drop: Cassanity::ArgumentGenerators::IndexDrop.new,
|
41
|
+
batch: Cassanity::ArgumentGenerators::Batch.new,
|
42
|
+
}
|
43
|
+
|
44
|
+
# Private: Hash of commands to related result transformers.
|
45
|
+
ResultTransformers = {
|
46
|
+
column_family_select: Cassanity::ResultTransformers::ColumnFamilySelect.new,
|
47
|
+
}
|
48
|
+
|
49
|
+
# Private: Default result transformer for commands that do not have one.
|
50
|
+
Mirror = Cassanity::ResultTransformers::Mirror.new
|
51
|
+
|
52
|
+
# Private
|
53
|
+
attr_reader :client
|
54
|
+
|
55
|
+
# Private
|
56
|
+
attr_reader :argument_generators
|
57
|
+
|
58
|
+
# Private
|
59
|
+
attr_reader :result_transformers
|
60
|
+
|
61
|
+
# Internal: Initializes a cassandra-cql based CQL executor.
|
62
|
+
#
|
63
|
+
# args - The Hash of arguments.
|
64
|
+
# :client - The CassandraCQL::Database connection instance.
|
65
|
+
# :argument_generators - A Hash where each key is a command name
|
66
|
+
# and each value is the related argument
|
67
|
+
# generator that responds to `call`
|
68
|
+
# (optional).
|
69
|
+
# :result_transformers - A Hash where each key is a command name
|
70
|
+
# and each value is the related result
|
71
|
+
# transformer that responds to `call`
|
72
|
+
# (optional).
|
73
|
+
#
|
74
|
+
# Examples
|
75
|
+
#
|
76
|
+
# client = CassandraCQL::Database.new('host')
|
77
|
+
# Cassanity::Executors::CassandraCql.new(client: client)
|
78
|
+
#
|
79
|
+
def initialize(args = {})
|
80
|
+
@client = args.fetch(:client)
|
81
|
+
@argument_generators = args.fetch(:argument_generators) { ArgumentGenerators }
|
82
|
+
@result_transformers = args.fetch(:result_transformers) { ResultTransformers }
|
83
|
+
end
|
84
|
+
|
85
|
+
# Internal: Execute a CQL query.
|
86
|
+
#
|
87
|
+
# args - One or more arguments to send to execute. First should always be
|
88
|
+
# String CQL query. The rest should be the bound variables if any
|
89
|
+
# are needed.
|
90
|
+
#
|
91
|
+
# Examples
|
92
|
+
#
|
93
|
+
# call({
|
94
|
+
# command: :keyspaces,
|
95
|
+
# })
|
96
|
+
#
|
97
|
+
# call({
|
98
|
+
# command: :keyspace_create,
|
99
|
+
# arguments: {name: 'analytics'},
|
100
|
+
# })
|
101
|
+
#
|
102
|
+
# Returns the result of execution.
|
103
|
+
# Raises Cassanity::Error if anything goes wrong during execution.
|
104
|
+
def call(args = {})
|
105
|
+
command = args.fetch(:command)
|
106
|
+
generator = @argument_generators.fetch(command)
|
107
|
+
execute_arguments = generator.call(args[:arguments])
|
108
|
+
|
109
|
+
result = @client.execute(*execute_arguments)
|
110
|
+
|
111
|
+
transformer = @result_transformers.fetch(command) { Mirror }
|
112
|
+
transformer.call(result)
|
113
|
+
rescue KeyError
|
114
|
+
raise Cassanity::UnknownCommand
|
115
|
+
rescue Exception => e
|
116
|
+
raise Cassanity::Error
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'cassanity/column_family'
|
2
|
+
|
3
|
+
module Cassanity
|
4
|
+
class Keyspace
|
5
|
+
# Public
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
# Internal
|
9
|
+
attr_reader :executor
|
10
|
+
|
11
|
+
# Internal
|
12
|
+
attr_reader :strategy_class
|
13
|
+
|
14
|
+
# Internal
|
15
|
+
attr_reader :strategy_options
|
16
|
+
|
17
|
+
# Public: Initializes a Keyspace.
|
18
|
+
#
|
19
|
+
# args - The Hash of arguments (default: {}).
|
20
|
+
# :name - The String name of the keyspace.
|
21
|
+
# :executor - What will execute the queries. Must respond to `call`.
|
22
|
+
# :strategy_class - The String strategy class name to use when
|
23
|
+
# creating keyspace.
|
24
|
+
# :strategy_options - The Hash of strategy options to use when
|
25
|
+
# creating keyspace.
|
26
|
+
#
|
27
|
+
def initialize(args = {})
|
28
|
+
@name = args.fetch(:name)
|
29
|
+
@executor = args.fetch(:executor)
|
30
|
+
@strategy_class = args[:strategy_class]
|
31
|
+
@strategy_options = args[:strategy_options]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Public: Creates the keyspace
|
35
|
+
#
|
36
|
+
# args - The Hash of arguments to pass to the argument generator
|
37
|
+
# (default: {}). :name is always included.
|
38
|
+
#
|
39
|
+
# Examples
|
40
|
+
#
|
41
|
+
# create # uses options from initialization
|
42
|
+
#
|
43
|
+
# # override options from initialization
|
44
|
+
# create({
|
45
|
+
# strategy_class: 'NetworkTopologyStrategy',
|
46
|
+
# strategy_options: {
|
47
|
+
# dc1: 1,
|
48
|
+
# dc2: 3,
|
49
|
+
# }
|
50
|
+
# })
|
51
|
+
#
|
52
|
+
# Returns whatever is returned by executor.
|
53
|
+
def create(args = {})
|
54
|
+
@executor.call({
|
55
|
+
command: :keyspace_create,
|
56
|
+
arguments: args.merge({
|
57
|
+
name: @name,
|
58
|
+
})
|
59
|
+
})
|
60
|
+
end
|
61
|
+
|
62
|
+
# Public: Uses a keyspace
|
63
|
+
#
|
64
|
+
# args - The Hash of arguments to pass to the argument generator
|
65
|
+
# (default: {}). :name is always included.
|
66
|
+
#
|
67
|
+
# Examples
|
68
|
+
#
|
69
|
+
# use # you shouldn't really ever need more than this
|
70
|
+
#
|
71
|
+
# Returns whatever is returned by executor.
|
72
|
+
def use(args = {})
|
73
|
+
@executor.call({
|
74
|
+
command: :keyspace_use,
|
75
|
+
arguments: args.merge({
|
76
|
+
name: @name,
|
77
|
+
}),
|
78
|
+
})
|
79
|
+
end
|
80
|
+
|
81
|
+
# Public: Drops a keyspace
|
82
|
+
#
|
83
|
+
# args - The Hash of arguments to pass to the argument generator
|
84
|
+
# (default: {}). :name is always included.
|
85
|
+
#
|
86
|
+
# Examples
|
87
|
+
#
|
88
|
+
# drop # you shouldn't really ever need more than this
|
89
|
+
#
|
90
|
+
# Returns whatever is returned by executor.
|
91
|
+
def drop(args = {})
|
92
|
+
@executor.call({
|
93
|
+
command: :keyspace_drop,
|
94
|
+
arguments: args.merge({
|
95
|
+
name: @name,
|
96
|
+
}),
|
97
|
+
})
|
98
|
+
end
|
99
|
+
|
100
|
+
# Public: Get a column family instance
|
101
|
+
#
|
102
|
+
# name - The String name of the column family.
|
103
|
+
# args - The Hash of arguments to use for ColumnFamily initialization
|
104
|
+
# (optional, default: {}). :name and :keyspace are always included.
|
105
|
+
#
|
106
|
+
# Returns a Cassanity::ColumnFamily instance.
|
107
|
+
def column_family(name, args = {})
|
108
|
+
column_family_args = args.merge({
|
109
|
+
name: name,
|
110
|
+
keyspace: self,
|
111
|
+
})
|
112
|
+
|
113
|
+
ColumnFamily.new(column_family_args)
|
114
|
+
end
|
115
|
+
alias_method :table, :column_family
|
116
|
+
alias_method :[], :column_family
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Cassanity
|
2
|
+
class Schema
|
3
|
+
# Internal
|
4
|
+
attr_reader :primary_key
|
5
|
+
|
6
|
+
# Internal
|
7
|
+
attr_reader :columns
|
8
|
+
|
9
|
+
# Internal
|
10
|
+
attr_reader :with
|
11
|
+
|
12
|
+
# Public: Initializes a Schema.
|
13
|
+
#
|
14
|
+
# args - The Hash of arguments.
|
15
|
+
# :primary_key - The String or Symbol key or Array of String/Symbol
|
16
|
+
# keys to use as primary key.
|
17
|
+
# :columns - The Hash of columns where the name is the column name
|
18
|
+
# and the value is the column type.
|
19
|
+
# :with - The Hash of options for the WITH clause.
|
20
|
+
def initialize(args = {})
|
21
|
+
@primary_key = args.fetch(:primary_key)
|
22
|
+
@columns = args.fetch(:columns)
|
23
|
+
@with = args[:with] || {}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/cassanity.rb
ADDED
data/spec/helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
$:.unshift(File.expand_path('../../lib', __FILE__))
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
|
6
|
+
Bundler.require :default
|
7
|
+
|
8
|
+
require 'cassanity'
|
9
|
+
|
10
|
+
root = Pathname(__FILE__).dirname.join('..').expand_path
|
11
|
+
|
12
|
+
Dir[root.join("spec/support/**/*.rb")].each { |f| require f }
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.order = :random
|
16
|
+
|
17
|
+
config.filter_run :focused => true
|
18
|
+
config.alias_example_to :fit, :focused => true
|
19
|
+
config.alias_example_to :xit, :pending => true
|
20
|
+
config.run_all_when_everything_filtered = true
|
21
|
+
|
22
|
+
config.backtrace_clean_patterns = [
|
23
|
+
/lib\/rspec\/(core|expectations|matchers|mocks)/,
|
24
|
+
]
|
25
|
+
|
26
|
+
config.include CassanityHelpers
|
27
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'cassanity/keyspace'
|
3
|
+
require 'cassanity/executors/cassandra_cql'
|
4
|
+
|
5
|
+
describe Cassanity::ColumnFamily do
|
6
|
+
let(:keyspace_name) { 'cassanity_test' }
|
7
|
+
let(:column_family_name) { 'apps' }
|
8
|
+
let(:counters_column_family_name) { 'counters' }
|
9
|
+
|
10
|
+
let(:client) {
|
11
|
+
CassandraCQL::Database.new('127.0.0.1:9160', {
|
12
|
+
cql_version: '3.0.0',
|
13
|
+
})
|
14
|
+
}
|
15
|
+
|
16
|
+
let(:keyspace) {
|
17
|
+
Cassanity::Keyspace.new({
|
18
|
+
name: keyspace_name,
|
19
|
+
executor: executor,
|
20
|
+
})
|
21
|
+
}
|
22
|
+
|
23
|
+
let(:executor) {
|
24
|
+
Cassanity::Executors::CassandraCql.new({
|
25
|
+
client: client,
|
26
|
+
})
|
27
|
+
}
|
28
|
+
|
29
|
+
let(:schema) {
|
30
|
+
Cassanity::Schema.new({
|
31
|
+
primary_key: :id,
|
32
|
+
columns: {
|
33
|
+
id: :timeuuid,
|
34
|
+
name: :text,
|
35
|
+
},
|
36
|
+
with: {
|
37
|
+
comment: 'For storing things',
|
38
|
+
}
|
39
|
+
})
|
40
|
+
}
|
41
|
+
|
42
|
+
let(:arguments) {
|
43
|
+
{
|
44
|
+
keyspace: keyspace,
|
45
|
+
name: column_family_name,
|
46
|
+
schema: schema,
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
subject {
|
51
|
+
described_class.new(arguments)
|
52
|
+
}
|
53
|
+
|
54
|
+
before do
|
55
|
+
client_drop_keyspace(client, keyspace_name)
|
56
|
+
client_create_keyspace(client, keyspace_name)
|
57
|
+
client_create_column_family(client, column_family_name, "id text PRIMARY KEY, name text")
|
58
|
+
client_create_column_family(client, counters_column_family_name, "id text PRIMARY KEY, views counter")
|
59
|
+
end
|
60
|
+
|
61
|
+
after do
|
62
|
+
client_drop_keyspace(client, keyspace_name)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "can create itself" do
|
66
|
+
column_family = described_class.new(arguments.merge(name: 'people'))
|
67
|
+
column_family.create
|
68
|
+
|
69
|
+
apps_column_family = client.schema.column_families.fetch(column_family.name)
|
70
|
+
apps_column_family.comment.should eq('For storing things')
|
71
|
+
|
72
|
+
columns = apps_column_family.columns
|
73
|
+
columns.should have_key('id')
|
74
|
+
columns.should have_key('name')
|
75
|
+
columns['id'].should eq('org.apache.cassandra.db.marshal.TimeUUIDType')
|
76
|
+
columns['name'].should eq('org.apache.cassandra.db.marshal.UTF8Type')
|
77
|
+
end
|
78
|
+
|
79
|
+
it "can truncate" do
|
80
|
+
client.execute("INSERT INTO #{column_family_name} (id, name) VALUES (?, ?)", '1', 'github')
|
81
|
+
client.execute("INSERT INTO #{column_family_name} (id, name) VALUES (?, ?)", '2', 'gist')
|
82
|
+
result = client.execute("SELECT * FROM #{column_family_name}")
|
83
|
+
result.rows.should eq(2)
|
84
|
+
|
85
|
+
subject.truncate
|
86
|
+
|
87
|
+
result = client.execute("SELECT * FROM #{column_family_name}")
|
88
|
+
result.rows.should eq(0)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "can drop" do
|
92
|
+
client_column_family?(client, column_family_name).should be_true
|
93
|
+
subject.drop
|
94
|
+
client_column_family?(client, column_family_name).should be_false
|
95
|
+
end
|
96
|
+
|
97
|
+
it "can drop when using a different keyspace" do
|
98
|
+
client_column_family?(client, column_family_name).should be_true
|
99
|
+
client.execute('USE system')
|
100
|
+
subject.drop
|
101
|
+
client_column_family?(client, column_family_name).should be_false
|
102
|
+
end
|
103
|
+
|
104
|
+
it "can alter" do
|
105
|
+
subject.alter(add: {created_at: :timestamp})
|
106
|
+
|
107
|
+
apps_column_family = client.schema.column_families.fetch(column_family_name)
|
108
|
+
columns = apps_column_family.columns
|
109
|
+
columns.should have_key('created_at')
|
110
|
+
columns['created_at'].should eq('org.apache.cassandra.db.marshal.DateType')
|
111
|
+
|
112
|
+
subject.alter(alter: {created_at: :timeuuid})
|
113
|
+
|
114
|
+
apps_column_family = client.schema.column_families.fetch(column_family_name)
|
115
|
+
columns = apps_column_family.columns
|
116
|
+
columns.should have_key('created_at')
|
117
|
+
columns['created_at'].should eq('org.apache.cassandra.db.marshal.TimeUUIDType')
|
118
|
+
|
119
|
+
subject.alter(drop: :created_at)
|
120
|
+
|
121
|
+
apps_column_family = client.schema.column_families.fetch(column_family_name)
|
122
|
+
columns = apps_column_family.columns
|
123
|
+
columns.should_not have_key('created_at')
|
124
|
+
|
125
|
+
subject.alter(with: {comment: 'Some new comment'})
|
126
|
+
apps_column_family = client.schema.column_families.fetch(column_family_name)
|
127
|
+
apps_column_family.comment.should eq('Some new comment')
|
128
|
+
end
|
129
|
+
|
130
|
+
it "can create and drop indexes" do
|
131
|
+
subject.create_index({
|
132
|
+
name: :apps_name_index,
|
133
|
+
column_name: :name,
|
134
|
+
})
|
135
|
+
|
136
|
+
apps = client.schema.column_families['apps']
|
137
|
+
apps_meta = apps.column_metadata
|
138
|
+
index = apps_meta.detect { |c| c.index_name == 'apps_name_index' }
|
139
|
+
index.should_not be_nil
|
140
|
+
|
141
|
+
subject.drop_index({
|
142
|
+
name: :apps_name_index,
|
143
|
+
})
|
144
|
+
|
145
|
+
apps = client.schema.column_families['apps']
|
146
|
+
apps_meta = apps.column_metadata
|
147
|
+
index = apps_meta.detect { |c| c.index_name == 'apps_name_index' }
|
148
|
+
index.should be_nil
|
149
|
+
end
|
150
|
+
|
151
|
+
it "can select data" do
|
152
|
+
client.execute("INSERT INTO #{column_family_name} (id, name) VALUES (?, ?)", '1', 'github')
|
153
|
+
client.execute("INSERT INTO #{column_family_name} (id, name) VALUES (?, ?)", '2', 'gist')
|
154
|
+
result = subject.select({
|
155
|
+
select: :name,
|
156
|
+
where: {
|
157
|
+
id: '2',
|
158
|
+
},
|
159
|
+
})
|
160
|
+
result.should eq([
|
161
|
+
{'name' => 'gist'}
|
162
|
+
])
|
163
|
+
end
|
164
|
+
|
165
|
+
it "can insert data" do
|
166
|
+
subject.insert({
|
167
|
+
data: {
|
168
|
+
id: '1',
|
169
|
+
name: 'GitHub',
|
170
|
+
},
|
171
|
+
})
|
172
|
+
|
173
|
+
result = client.execute("SELECT * FROM #{column_family_name}")
|
174
|
+
result.rows.should eq(1)
|
175
|
+
row = result.fetch_hash
|
176
|
+
row['id'].should eq('1')
|
177
|
+
row['name'].should eq('GitHub')
|
178
|
+
end
|
179
|
+
|
180
|
+
it "can update data" do
|
181
|
+
client.execute("INSERT INTO #{column_family_name} (id, name) VALUES (?, ?)", '1', 'github')
|
182
|
+
client.execute("INSERT INTO #{column_family_name} (id, name) VALUES (?, ?)", '2', 'gist')
|
183
|
+
|
184
|
+
subject.update({
|
185
|
+
set: {name: 'New Name'},
|
186
|
+
where: {id: '1'},
|
187
|
+
})
|
188
|
+
|
189
|
+
result = client.execute("SELECT * FROM #{column_family_name} WHERE id = '1'")
|
190
|
+
result.rows.should eq(1)
|
191
|
+
row = result.fetch_hash
|
192
|
+
row['id'].should eq('1')
|
193
|
+
row['name'].should eq('New Name')
|
194
|
+
|
195
|
+
# does not update other rows
|
196
|
+
result = client.execute("SELECT * FROM #{column_family_name} WHERE id = '2'")
|
197
|
+
result.rows.should eq(1)
|
198
|
+
row = result.fetch_hash
|
199
|
+
row['id'].should eq('2')
|
200
|
+
row['name'].should eq('gist')
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "updating a counter column" do
|
204
|
+
subject {
|
205
|
+
described_class.new({
|
206
|
+
keyspace: keyspace,
|
207
|
+
name: counters_column_family_name,
|
208
|
+
})
|
209
|
+
}
|
210
|
+
|
211
|
+
it "works" do
|
212
|
+
subject.update({
|
213
|
+
set: {views: 'views + 2'},
|
214
|
+
where: {id: '1'},
|
215
|
+
})
|
216
|
+
|
217
|
+
result = client.execute("SELECT * FROM #{counters_column_family_name} WHERE id = '1'")
|
218
|
+
result.rows.should eq(1)
|
219
|
+
row = result.fetch_hash
|
220
|
+
row['id'].should eq('1')
|
221
|
+
row['views'].should be(2)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
it "can delete data" do
|
226
|
+
client.execute("INSERT INTO #{column_family_name} (id, name) VALUES (?, ?)", '1', 'github')
|
227
|
+
client.execute("INSERT INTO #{column_family_name} (id, name) VALUES (?, ?)", '2', 'gist')
|
228
|
+
|
229
|
+
result = client.execute("SELECT * FROM #{column_family_name}")
|
230
|
+
result.rows.should eq(2)
|
231
|
+
|
232
|
+
subject.delete({
|
233
|
+
where: {id: '1'},
|
234
|
+
})
|
235
|
+
|
236
|
+
result = client.execute("SELECT * FROM #{column_family_name} WHERE id = '1'")
|
237
|
+
result.rows.should eq(0)
|
238
|
+
|
239
|
+
# does not delete other rows
|
240
|
+
result = client.execute("SELECT * FROM #{column_family_name} WHERE id = '2'")
|
241
|
+
result.rows.should eq(1)
|
242
|
+
end
|
243
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'cassanity/connection'
|
3
|
+
require 'cassanity/executors/cassandra_cql'
|
4
|
+
|
5
|
+
describe Cassanity::Connection do
|
6
|
+
let(:keyspace_name) { 'cassanity_test' }
|
7
|
+
let(:column_family_name) { 'apps' }
|
8
|
+
|
9
|
+
let(:client) {
|
10
|
+
CassandraCQL::Database.new('127.0.0.1:9160', {
|
11
|
+
cql_version: '3.0.0',
|
12
|
+
})
|
13
|
+
}
|
14
|
+
|
15
|
+
let(:executor) {
|
16
|
+
Cassanity::Executors::CassandraCql.new({
|
17
|
+
client: client,
|
18
|
+
})
|
19
|
+
}
|
20
|
+
|
21
|
+
subject {
|
22
|
+
described_class.new({
|
23
|
+
executor: executor,
|
24
|
+
})
|
25
|
+
}
|
26
|
+
|
27
|
+
before do
|
28
|
+
client_drop_keyspace(client, keyspace_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
after do
|
32
|
+
client_drop_keyspace(client, keyspace_name)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "can batch" do
|
36
|
+
client_create_keyspace(client, keyspace_name)
|
37
|
+
client_create_column_family(client, column_family_name, "id text PRIMARY KEY, name text")
|
38
|
+
|
39
|
+
default_arguments = {
|
40
|
+
keyspace_name: keyspace_name,
|
41
|
+
name: column_family_name,
|
42
|
+
}
|
43
|
+
|
44
|
+
subject.batch({
|
45
|
+
:modifications => [
|
46
|
+
[:insert, default_arguments.merge(data: {id: '1', name: 'github'})],
|
47
|
+
[:insert, default_arguments.merge(data: {id: '2', name: 'gist'})],
|
48
|
+
[:update, default_arguments.merge(set: {name: 'github.com'}, where: {id: '1'})],
|
49
|
+
[:delete, default_arguments.merge(where: {id: '2'})],
|
50
|
+
]
|
51
|
+
})
|
52
|
+
|
53
|
+
result = client.execute("SELECT * FROM apps")
|
54
|
+
result.rows.should be(1)
|
55
|
+
|
56
|
+
rows = []
|
57
|
+
result.fetch_hash { |row| rows << row }
|
58
|
+
|
59
|
+
rows.should eq([
|
60
|
+
{'id' => '1', 'name' => 'github.com'},
|
61
|
+
])
|
62
|
+
end
|
63
|
+
|
64
|
+
it "knows keyspaces" do
|
65
|
+
client_create_keyspace(client, 'something1')
|
66
|
+
client_create_keyspace(client, 'something2')
|
67
|
+
|
68
|
+
result = subject.keyspaces
|
69
|
+
result.each do |keyspace|
|
70
|
+
keyspace.should be_instance_of(Cassanity::Keyspace)
|
71
|
+
keyspace.executor.should eq(subject.executor)
|
72
|
+
end
|
73
|
+
|
74
|
+
names = result.map(&:name)
|
75
|
+
names.should include('something1')
|
76
|
+
names.should include('something2')
|
77
|
+
|
78
|
+
client_drop_keyspace(client, 'something1')
|
79
|
+
client_drop_keyspace(client, 'something2')
|
80
|
+
end
|
81
|
+
|
82
|
+
it "knows if a keyspace exists" do
|
83
|
+
subject.keyspace?(keyspace_name).should be_false
|
84
|
+
client_create_keyspace(client, keyspace_name)
|
85
|
+
subject.keyspace?(keyspace_name).should be_true
|
86
|
+
end
|
87
|
+
end
|