cassanity 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|