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.
Files changed (76) hide show
  1. data/.gitignore +19 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +10 -0
  4. data/Gemfile +11 -0
  5. data/Guardfile +17 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +99 -0
  8. data/Rakefile +16 -0
  9. data/cassanity.gemspec +21 -0
  10. data/examples/_shared.rb +8 -0
  11. data/examples/batch.rb +40 -0
  12. data/examples/column_families.rb +68 -0
  13. data/examples/keyspaces.rb +57 -0
  14. data/lib/cassanity/argument_generators/batch.rb +52 -0
  15. data/lib/cassanity/argument_generators/column_family_alter.rb +47 -0
  16. data/lib/cassanity/argument_generators/column_family_create.rb +44 -0
  17. data/lib/cassanity/argument_generators/column_family_delete.rb +65 -0
  18. data/lib/cassanity/argument_generators/column_family_drop.rb +18 -0
  19. data/lib/cassanity/argument_generators/column_family_insert.rb +53 -0
  20. data/lib/cassanity/argument_generators/column_family_select.rb +34 -0
  21. data/lib/cassanity/argument_generators/column_family_truncate.rb +18 -0
  22. data/lib/cassanity/argument_generators/column_family_update.rb +69 -0
  23. data/lib/cassanity/argument_generators/index_create.rb +22 -0
  24. data/lib/cassanity/argument_generators/index_drop.rb +13 -0
  25. data/lib/cassanity/argument_generators/keyspace_create.rb +51 -0
  26. data/lib/cassanity/argument_generators/keyspace_drop.rb +13 -0
  27. data/lib/cassanity/argument_generators/keyspace_use.rb +13 -0
  28. data/lib/cassanity/argument_generators/keyspaces.rb +12 -0
  29. data/lib/cassanity/argument_generators/set_clause.rb +30 -0
  30. data/lib/cassanity/argument_generators/using_clause.rb +24 -0
  31. data/lib/cassanity/argument_generators/where_clause.rb +29 -0
  32. data/lib/cassanity/argument_generators/with_clause.rb +32 -0
  33. data/lib/cassanity/column_family.rb +233 -0
  34. data/lib/cassanity/connection.rb +88 -0
  35. data/lib/cassanity/error.rb +28 -0
  36. data/lib/cassanity/executors/cassandra_cql.rb +120 -0
  37. data/lib/cassanity/keyspace.rb +118 -0
  38. data/lib/cassanity/result_transformers/column_family_select.rb +15 -0
  39. data/lib/cassanity/result_transformers/mirror.rb +12 -0
  40. data/lib/cassanity/schema.rb +26 -0
  41. data/lib/cassanity/version.rb +3 -0
  42. data/lib/cassanity.rb +5 -0
  43. data/spec/helper.rb +27 -0
  44. data/spec/integration/cassanity/column_family_spec.rb +243 -0
  45. data/spec/integration/cassanity/connection_spec.rb +87 -0
  46. data/spec/integration/cassanity/keyspace_spec.rb +64 -0
  47. data/spec/support/cassanity_helpers.rb +35 -0
  48. data/spec/unit/cassanity/argument_generators/batch_spec.rb +36 -0
  49. data/spec/unit/cassanity/argument_generators/column_family_alter_spec.rb +85 -0
  50. data/spec/unit/cassanity/argument_generators/column_family_create_spec.rb +107 -0
  51. data/spec/unit/cassanity/argument_generators/column_family_delete_spec.rb +92 -0
  52. data/spec/unit/cassanity/argument_generators/column_family_drop_spec.rb +25 -0
  53. data/spec/unit/cassanity/argument_generators/column_family_insert_spec.rb +70 -0
  54. data/spec/unit/cassanity/argument_generators/column_family_select_spec.rb +113 -0
  55. data/spec/unit/cassanity/argument_generators/column_family_truncate_spec.rb +25 -0
  56. data/spec/unit/cassanity/argument_generators/column_family_update_spec.rb +109 -0
  57. data/spec/unit/cassanity/argument_generators/index_create_spec.rb +39 -0
  58. data/spec/unit/cassanity/argument_generators/index_drop_spec.rb +14 -0
  59. data/spec/unit/cassanity/argument_generators/keyspace_create_spec.rb +53 -0
  60. data/spec/unit/cassanity/argument_generators/keyspace_drop_spec.rb +14 -0
  61. data/spec/unit/cassanity/argument_generators/keyspace_use_spec.rb +14 -0
  62. data/spec/unit/cassanity/argument_generators/keyspaces_spec.rb +12 -0
  63. data/spec/unit/cassanity/argument_generators/set_clause_spec.rb +85 -0
  64. data/spec/unit/cassanity/argument_generators/using_clause_spec.rb +44 -0
  65. data/spec/unit/cassanity/argument_generators/where_clause_spec.rb +57 -0
  66. data/spec/unit/cassanity/argument_generators/with_clause_spec.rb +63 -0
  67. data/spec/unit/cassanity/column_family_spec.rb +250 -0
  68. data/spec/unit/cassanity/connection_spec.rb +75 -0
  69. data/spec/unit/cassanity/error_spec.rb +35 -0
  70. data/spec/unit/cassanity/executors/cassandra_cql_spec.rb +178 -0
  71. data/spec/unit/cassanity/keyspace_spec.rb +137 -0
  72. data/spec/unit/cassanity/result_transformers/column_family_select_spec.rb +0 -0
  73. data/spec/unit/cassanity/result_transformers/mirror_spec.rb +12 -0
  74. data/spec/unit/cassanity/schema_spec.rb +23 -0
  75. data/spec/unit/cassanity_spec.rb +5 -0
  76. 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,15 @@
1
+ module Cassanity
2
+ module ResultTransformers
3
+ class ColumnFamilySelect
4
+
5
+ # Internal: Turns result into Array of Hashes.
6
+ def call(result)
7
+ rows = []
8
+ result.fetch_hash do |row|
9
+ rows << row
10
+ end
11
+ rows
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ module Cassanity
2
+ module ResultTransformers
3
+ class Mirror
4
+
5
+ # Internal: Returns whatever result is passed to it. This is used as the
6
+ # default result transformer when a command does not have one.
7
+ def call(result)
8
+ result
9
+ end
10
+ end
11
+ end
12
+ 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
@@ -0,0 +1,3 @@
1
+ module Cassanity
2
+ VERSION = "0.1.0"
3
+ end
data/lib/cassanity.rb ADDED
@@ -0,0 +1,5 @@
1
+ module Cassanity
2
+ end
3
+
4
+ require 'cassanity/connection'
5
+ require 'cassanity/executors/cassandra_cql'
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