cassava_rb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 608975429084fed43ea970713fc2ba91497d061b
4
+ data.tar.gz: 8c782e434e9172dd8d9a23ac286186de9ba155b4
5
+ SHA512:
6
+ metadata.gz: 2116e9edaff4ac4ef6d91f105a41028b1d217b57f689f26ee96594792dff87c68ec201f28f4fe062d2819fa719d60726a19011c0e1a3de2b06e2a761ae18c8ba
7
+ data.tar.gz: b027b99912c044a853d0d47bc76308df2714dfba43da863ff866f63d7d603f47c16b14a6251993da834c41e8cb49d8142166e0e359bde53eac84fd4d4924b85c
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cassava.gemspec
4
+ gemspec
5
+
6
+ group :development, :test do
7
+ gem "pry"
8
+ gem "awesome_print"
9
+ gem 'm', :git => 'git@github.com:ANorwell/m.git', :branch => 'minitest_5'
10
+ end
11
+
12
+ group :test do
13
+ gem 'minitest_should', :git => 'git@github.com:citrus/minitest_should.git'
14
+ gem "mocha"
15
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Datto
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,112 @@
1
+ # Cassava
2
+
3
+ [![Code Climate](https://codeclimate.com/github/backupify/cassava/badges/gpa.svg)](https://codeclimate.com/github/backupify/cassava)
4
+
5
+ An unopinionated Cassandra client built on top of the Datastax Cassandra Driver. Cassava provides a higher-level statement execution interface while still supporting asynchronous queries and the ability to connect to multiple clusters.
6
+
7
+ _If prepared incorrectly, the cassava plant can produce cyanide, a deadly compound when consumed._
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'cassava_rb', github: 'backupify/cassava'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install cassava_rb
24
+
25
+ ## Usage
26
+
27
+ Creating a client requires a `Cassandra::Session` object:
28
+
29
+ ```ruby
30
+ require 'cassava'
31
+ cluster = Cassandra.cluster
32
+ session = cluster.connect('a_keyspace')
33
+ client = Cassava::Client.new(session)
34
+ ```
35
+
36
+ ### Insert
37
+
38
+ ```ruby
39
+ client.insert(:table, :id => 'id', :a => 1, :b => 'b')
40
+
41
+ ```
42
+
43
+ ### Select
44
+
45
+ A select statement is built and then executed. To create a statement that will
46
+ select all columns:
47
+
48
+ ```ruby
49
+ statement = client.select(:table)
50
+ ```
51
+
52
+ This statement can then be further refined:
53
+
54
+ ```ruby
55
+ new_statement = statement.where(:id => 'id').limit(2)
56
+ ```
57
+
58
+ and then executed:
59
+
60
+ ```ruby
61
+ result = new_statement.execute
62
+ ```
63
+
64
+ or executed asynchronously:
65
+
66
+ ```ruby
67
+ promise = new_statement.execute_async
68
+ ```
69
+
70
+ To select only certain rows, provide those rows to the select method:
71
+
72
+ ```ruby
73
+ client.select(:table, [:id, :a, :b]).execute
74
+ ```
75
+
76
+ Ordering can be specified using the order method:
77
+
78
+ ```ruby
79
+ client.select(:table).where('id = ? AND a > ?', 1, 'b').order(:a, :desc).execute
80
+ ```
81
+
82
+ Filtering is permitting with the `allow_filtering` method.
83
+
84
+ Multiple records can be specified by passing an array of values, but this will generate an CQL IN query and should be used with caution:
85
+
86
+ ```ruby
87
+ client.select(:table).where(:id => 1, :a => [1, 2])
88
+ ```
89
+
90
+ ### Delete
91
+
92
+ To delete an entire record:
93
+
94
+ ```ruby
95
+ client.delete(table).where(:id => 1, :a => 1).execute
96
+ ```
97
+
98
+ To delete only certain columns:
99
+
100
+ ```ruby
101
+ client.delete(table, [:c, :d]).where(:id => 1, :a => 1).execute
102
+ ```
103
+
104
+ Note here that :c and :d must not be part of the primary key.
105
+
106
+ ## Contributing
107
+
108
+ 1. Fork it ( https://github.com/backupify/cassava/fork )
109
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
110
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
111
+ 4. Push to the branch (`git push origin my-new-feature`)
112
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.pattern = 'test/**/*_test.rb'
7
+ t.libs.push 'test'
8
+ end
9
+
10
+ task :setup do
11
+ require 'bundler/setup'
12
+ Bundler.require(:default, :development)
13
+ end
14
+
15
+
16
+ task :console => [:setup] do
17
+ Pry.start
18
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cassava/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cassava_rb"
8
+ spec.version = Cassava::VERSION
9
+ spec.authors = ["Arron Norwell"]
10
+ spec.email = ["anorwell@datto.com"]
11
+ spec.summary = %q{An unopinionated wrapper for the datastax cassandra gem.}
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "minitest"
23
+ spec.add_development_dependency "minitest-reporters"
24
+
25
+ spec.add_dependency 'cassandra-driver'
26
+ end
data/lib/cassava.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'cassandra'
2
+ require "cassava/version"
3
+ require 'cassava/client'
4
+
5
+ module Cassava; end
@@ -0,0 +1,264 @@
1
+ module Cassava
2
+ class Client
3
+ attr_reader :session, :executor
4
+
5
+ # @param session [Cassandra::Session] The session object
6
+ # @option opts [Object] :logger responding to :debug, :info, :warn, :error, :fatal
7
+ def initialize(session, opts = {})
8
+ @session = session
9
+ logger = opts[:logger] || NullLogger.new
10
+ @executor = Executor.new(session, logger)
11
+ end
12
+
13
+ # @see #insert
14
+ def insert_async(table, data)
15
+ executor.execute_async(insert_statement(table, data), :arguments => data.values)
16
+ end
17
+
18
+ # @param table [Symbol] the table name
19
+ # @param data [Hash] A hash of column names to data, which will be inserted into the table
20
+ def insert(table, data)
21
+ statement = insert_statement(table, data)
22
+ executor.execute(statement, :arguments => data.values)
23
+ end
24
+
25
+ # @param table [Symbol] the table name
26
+ # @param columns [Array<Symbol>] An optional list of column names (as symbols), to only select those columns
27
+ # @return [StatementBuilder] A statement builder representing the partially completed statement.
28
+ def select(table, columns = nil)
29
+ StatementBuilder.new(executor).select(table, columns)
30
+ end
31
+
32
+ # @param table [Symbol] the table name
33
+ # @param columns [Array<String] A list of columns that will be deleted. If nil, all columns will be deleted.
34
+ # @return [StatementBuilder] A statement builder representing the partially completed statement.
35
+ def delete(table, columns = nil)
36
+ StatementBuilder.new(executor).delete(table, columns)
37
+ end
38
+
39
+ # Pass a raw query to execute synchronously to the underlying session object.
40
+ # @param statement[String] The statment to execute
41
+ # @param opts [Hash] options accepted by Cassandra::Session
42
+ def execute_async(statement, opts = {})
43
+ executor.execute_async(statement, opts)
44
+ end
45
+
46
+ # Pass a raw query to execute asynchronously to the underlying session object.
47
+ # @param statement [String] The statment to execute
48
+ # @param opts [Hash] options accepted by Cassandra::Session
49
+ def execute(statement, opts = {})
50
+ executor.execute(statement, opts)
51
+ end
52
+
53
+ private
54
+
55
+ def insert_statement(table, data)
56
+ column_names = data.keys
57
+ statement_cql = "INSERT INTO #{table} (#{column_names.join(', ')}) VALUES (#{column_names.map { |x| '?' }.join(',')})"
58
+ executor.prepare(statement_cql)
59
+ end
60
+ end
61
+
62
+ class StatementBuilder
63
+ attr_reader :executor, :table, :clauses
64
+
65
+ CLAUSE_ORDERING = {
66
+ :main => 0,
67
+ :from => 1,
68
+ :where => 2,
69
+ :order => 3,
70
+ :limit => 4,
71
+ :allow_filtering => 5
72
+ }
73
+
74
+ def initialize(executor, clauses = {})
75
+ @executor = executor
76
+ @table = table
77
+ @clauses = clauses
78
+ end
79
+
80
+ # Execute the statement synchronously
81
+ # @param opts [Hash] options accepted by Cassandra::Session
82
+ def execute(opts = {})
83
+ options = opts.dup.merge(:arguments => prepared_arguments)
84
+ executor.execute(prepared_statement, options)
85
+ end
86
+
87
+ # Execute the statement asynchronously
88
+ # @param opts [Hash] options accepted by Cassandra::Session
89
+ def execute_async(opts = {})
90
+ options = opts.dup.merge(:arguments => prepared_arguments)
91
+ executor.execute_async(prepared_statement, options)
92
+ end
93
+
94
+ # @param table [Symbol] table to select data from
95
+ # @param columns [Array<Symbol>] Columns to select -- defaults to all.
96
+ # @return [StatementBuilder]
97
+ def select(table, columns = nil)
98
+ add_clause(SelectClause.new(table, columns), :main)
99
+ end
100
+
101
+ # @param table [Symbol] table to delete data from
102
+ # @param columns [Array<Symbol>] Columns to delete -- defaults to all.
103
+ # @return [StatementBuilder]
104
+ def delete(table, columns = nil)
105
+ add_clause(DeleteClause.new(table, columns), :main)
106
+ end
107
+
108
+ # Condition the query based on a condition
109
+ # Provide either a String and a list of arguments, or a hash.
110
+ # @example
111
+ # statement.where('id = ? and field > ?', 1, 'a')
112
+ # @example
113
+ # statement.where(:id => 1, :field => 'x')
114
+ # @param args [Array] arguments representing the where condition
115
+ # @return [StatementBuilder]
116
+ def where(*args)
117
+ clause = clauses[:where] || WhereClause.new([], [])
118
+ add_clause(clause.where(*args), :where)
119
+ end
120
+
121
+ # Allow filtering for this query
122
+ # @return [StatementBuilder]
123
+ def allow_filtering
124
+ add_clause('ALLOW FILTERING', :allow_filtering)
125
+ end
126
+
127
+ # @param clustering_column [Symbol] clustering_column to order by
128
+ # @param direction [:asc|:desc] the direction to order by, defaults to :asc
129
+ # @return [StatementBuilder]
130
+ def order(clustering_column, direction = :asc)
131
+ add_clause("ORDER BY #{clustering_column.to_s} #{direction.to_s}", :order)
132
+ end
133
+
134
+ # @param n [Integer] maximum number of results to return
135
+ # @return [StatementBuilder]
136
+ def limit(n)
137
+ add_clause("LIMIT #{n.to_i}", :limit)
138
+ end
139
+
140
+ # Return the count of objects rather than the objects themselves
141
+ # @return [StatementBuilder]
142
+ def count
143
+ add_clause(clauses[:main].count, :main)
144
+ end
145
+
146
+ # @return [String] the CQL statement that this StatementBuilder represents
147
+ def statement
148
+ clauses.sort_by { |s| CLAUSE_ORDERING[s[0]] }.map { |s| s[1] }.join(' ')
149
+ end
150
+
151
+ private
152
+
153
+ def prepared_statement
154
+ executor.prepare(statement)
155
+ end
156
+
157
+ def prepared_arguments
158
+ clauses[:where] ? clauses[:where].arguments : []
159
+ end
160
+
161
+ # Adds a clause of a given type.
162
+ # @return [StatementBuilder] A new StatementBuilder with the added clause
163
+ def add_clause(clause, type)
164
+ clauses_copy = clauses.dup
165
+ clauses_copy[type] = clause
166
+ self.class.new(executor, clauses_copy)
167
+ end
168
+ end
169
+
170
+ SelectClause = Struct.new(:table, :columns, :count_boolean) do
171
+ def count
172
+ self.class.new(table, _columns = nil, _count_boolean = true)
173
+ end
174
+
175
+ def to_s
176
+ if count_boolean
177
+ "SELECT COUNT(*) FROM #{table}"
178
+ else
179
+ self.columns ||= ['*']
180
+ "SELECT #{columns.join(', ')} from #{table}"
181
+ end
182
+ end
183
+ end
184
+
185
+ DeleteClause = Struct.new(:table, :columns) do
186
+ def to_s
187
+ if columns
188
+ "DELETE #{columns.join(', ')} from #{table}"
189
+ else
190
+ "DELETE from #{table}"
191
+ end
192
+ end
193
+ end
194
+
195
+ WhereClause = Struct.new(:parts, :arguments) do
196
+ def where(*args)
197
+ new_parts = self.parts.dup || []
198
+ new_arguments = self.arguments.dup || []
199
+
200
+ case args[0]
201
+ when String
202
+ new_parts << args[0]
203
+ new_arguments += args[1..-1]
204
+ when Hash
205
+ new_parts += args[0].map { |key, value| "#{key} #{where_string(value)}" }
206
+ new_arguments += args[0].values.flatten
207
+ end
208
+ self.class.new(new_parts, new_arguments)
209
+ end
210
+
211
+ def to_s
212
+ "WHERE #{parts.join(' AND ')}"
213
+ end
214
+
215
+ private
216
+
217
+ def where_string(value)
218
+ case value
219
+ when Array
220
+ quoted_values = value.map { |v| type_quote(v) }
221
+ "IN(#{quoted_values.map { |_| '?' }.join(', ')})"
222
+ else "= ?"
223
+ end
224
+ end
225
+
226
+ def type_quote(value)
227
+ case value
228
+ when Numeric then value.to_s
229
+ when String then "'#{value}'"
230
+ end
231
+ end
232
+ end
233
+
234
+ Executor = Struct.new(:session, :logger) do
235
+ def execute(statement, opts = {})
236
+ log_statement(statement, opts)
237
+ session.execute(statement, opts)
238
+ rescue => e
239
+ log_error(e, statement, opts)
240
+ raise e
241
+ end
242
+
243
+ def execute_async(statement, opts = {})
244
+ log_statement(statement, opts)
245
+ session.execute_async(statement, opts)
246
+ end
247
+
248
+ def prepare(*args)
249
+ session.prepare(*args)
250
+ end
251
+
252
+ def log_statement(statement, opts)
253
+ logger.debug("Executing Cassandra request #{statement.to_s} with options #{opts}")
254
+ end
255
+
256
+ def log_error(e, statement, opts)
257
+ logger.debug("Error #{e} executing Cassandra request #{statement.to_s} with options #{opts}")
258
+ end
259
+ end
260
+
261
+ class NullLogger
262
+ def method_missing(*); end
263
+ end
264
+ end
@@ -0,0 +1,3 @@
1
+ module Cassava
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,195 @@
1
+ require_relative '../test_helper'
2
+
3
+ module Cassava
4
+ class ClientTest < Minitest::Should::TestCase
5
+ setup do
6
+ initialize_test_table
7
+ @session = session_for_keyspace
8
+ @client = Cassava::Client.new(@session)
9
+ end
10
+
11
+ def string_keys(hash)
12
+ Hash[hash.map { |k, v| [k.to_s, v] }]
13
+ end
14
+
15
+ context 'insert' do
16
+ should 'be able to insert an item' do
17
+ item = { :id => 'i', :a => 1, :b => 'b', :c => 'c', :d => 1}
18
+ @client.insert(:test, item)
19
+ assert_equal string_keys(item), @client.select(:test).execute.first
20
+ end
21
+
22
+ should 'insert an item with some fields missing' do
23
+ item = { :id => 'i', :a => 1, :b => 'b', :c => 'c'}
24
+ expected = item.merge(:d => nil)
25
+ @client.insert(:test, item)
26
+ assert_equal string_keys(expected), @client.select(:test).execute.first
27
+ end
28
+
29
+ should 'raise an error if the row key is missing' do
30
+ item = { :a => 1, :b => 'b', :c => 'c'}
31
+ assert_raises(Cassandra::Errors::InvalidError) { @client.insert(:test, item) }
32
+ end
33
+
34
+ should 'raise an error if a primary key part is missing' do
35
+ item = { :id => 'i', :a => 1, :c => 'c'}
36
+ assert_raises(Cassandra::Errors::InvalidError) { @client.insert(:test, item) }
37
+ end
38
+
39
+ should 'allow the insertion of columns with mismatched quotes' do
40
+ item = { :id => 'i', :a => 1, :b => 'b', :c => "'\"item(", :d => 1}
41
+ @client.insert(:test, item)
42
+ assert_equal string_keys(item), @client.select(:test).execute.first
43
+ end
44
+ end
45
+
46
+ context 'select' do
47
+
48
+ setup do
49
+ @client.insert(:test, :id => 'i', :a => 1, :b => 'b', :c => '1')
50
+ @client.insert(:test, :id => 'i', :a => 2, :b => 'a', :c => '1')
51
+ @client.insert(:test, :id => 'i', :a => 3, :b => 'c', :c => '1')
52
+ @client.insert(:test, :id => 'i2', :a => 4, :b => 'b', :c => '1')
53
+ end
54
+
55
+ should 'select all columns for all items' do
56
+ items = @client.select(:test).execute
57
+ assert_equal 4, items.count
58
+ assert_equal [1,2,3,4].to_set, items.map { |x| x['a'] }.to_set
59
+ assert_equal %w(id a b c d).to_set, items.first.keys.to_set
60
+ end
61
+
62
+ should 'select certain columns for all items' do
63
+ items = @client.select(:test, %w(id a c)).execute
64
+ assert_equal 4, items.count
65
+ assert_equal [1,2,3,4].to_set, items.map { |x| x['a'] }.to_set
66
+ assert_equal %w(id a c).to_set, items.first.keys.to_set
67
+ end
68
+
69
+ context 'where' do
70
+ should 'allow where clause' do
71
+ items = @client.select(:test).where(:id => 'i').execute
72
+ assert_equal [1,2,3].to_set, items.map { |x| x['a'] }.to_set
73
+ end
74
+
75
+ should 'allow string-based where clauses' do
76
+ items = @client.select(:test).where("id = 'i' and a > 1").execute
77
+ assert_equal [2,3].to_set, items.map { |x| x['a'] }.to_set
78
+ end
79
+
80
+ should 'allow string-based where clauses with arguments' do
81
+ items = @client.select(:test).where("id = ? and a > ?", 'i', 1).execute
82
+ assert_equal [2,3].to_set, items.map { |x| x['a'] }.to_set
83
+ end
84
+
85
+ should 'allow multiple where clauses to be chained' do
86
+ items = @client.select(:test).where(:id => 'i').where('a > 1').execute
87
+ assert_equal [2,3].to_set, items.map { |x| x['a'] }.to_set
88
+ end
89
+
90
+ should 'create an IN clause when a list of values is passed' do
91
+ items = @client.select(:test).where(:id => 'i', :a => 1, :b => %w(a b)).execute
92
+ assert_equal [1], items.map { |x| x['a'] }
93
+ end
94
+
95
+ context 'hash arguments' do
96
+ should 'allow single and double quotes in the value' do
97
+ items = @client.select(:test).where(:id => "'\"abc").execute
98
+ assert_equal [], items.to_a
99
+ end
100
+ end
101
+
102
+ context 'string arguments' do
103
+ should 'allow single and double quotes in the value' do
104
+ items = @client.select(:test).where('id = ?', "'\"abc").execute
105
+ assert_equal [], items.to_a
106
+ end
107
+ end
108
+ end
109
+
110
+ should 'order by ascending primary key by default' do
111
+ items = @client.select(:test).where(:id => 'i').execute
112
+ assert_equal [1,2,3], items.map { |x| x['a'] }
113
+ end
114
+
115
+ should 'allow order to specify ordering' do
116
+ items = @client.select(:test).where(:id => 'i').order(:a, :desc).execute
117
+ assert_equal [3, 2, 1], items.map { |x| x['a'] }
118
+ end
119
+
120
+ should 'allow limiting the result count' do
121
+ items = @client.select(:test).where(:id => 'i').limit(2).execute
122
+ assert_equal [1, 2], items.map { |x| x['a'] }
123
+ end
124
+
125
+ should 'not allow queries across multiple rows if allow_filtering is not set' do
126
+ assert_raises(Cassandra::Errors::InvalidError) { @client.select(:test).where(:a => 1).execute }
127
+ end
128
+
129
+ should 'allow queries across multiple rows if allow_filtering is set' do
130
+ items = @client.select(:test).where('a >= 3').allow_filtering.execute
131
+ assert_equal [3, 4].to_set, items.map { |x| x['a'] }.to_set
132
+ end
133
+
134
+ should 'allow clauses to be chained in any order' do
135
+ items = @client.select(:test).limit(2).allow_filtering.where('a >= 2').execute
136
+ assert_equal [2, 4].to_set, items.map { |x| x['a'] }.to_set
137
+ end
138
+
139
+ should 'allow select statements to be modified without affecting the original statement' do
140
+ partial_query = @client.select(:test).allow_filtering.where(:a => 1)
141
+
142
+ items = partial_query.where(:b => 'missing').execute
143
+ assert_equal 0, items.count
144
+
145
+ original_items = partial_query.execute
146
+ assert_equal 1, original_items.count
147
+ end
148
+
149
+ should 'support count queries' do
150
+ count = @client.select(:test).where("id = ? and a > ?", 'i', 1).count.execute
151
+ assert_equal 2, count.first["count"]
152
+ end
153
+ end
154
+
155
+ context 'delete' do
156
+ setup do
157
+ @client.insert(:test, :id => 'i', :a => 2, :b => 'a', :c => '1', :d => 1)
158
+ @client.insert(:test, :id => 'i', :a => 3, :b => 'c', :c => '1', :d => 1)
159
+ end
160
+
161
+ should 'delete entire rows' do
162
+ @client.delete(:test).where(:id => 'i', :a => 2).execute
163
+ items = @client.select(:test).where(:id => 'i', :a => 2).execute
164
+ assert_equal 0, items.count
165
+ end
166
+
167
+ should 'delete multiple elements in a partition' do
168
+ @client.delete(:test).where(:id => 'i').execute
169
+ items = @client.select(:test).where(:id => 'i').execute
170
+ assert_equal 0, items.count
171
+ end
172
+
173
+ should 'delete individual columns' do
174
+ @client.delete(:test, [:c, :d]).where(:id => 'i', :a => 2, :b => 'a').execute
175
+ items = @client.select(:test).where(:id => 'i', :a => 2).execute
176
+ assert_equal nil, items.first['c']
177
+ assert_equal nil, items.first['d']
178
+ end
179
+
180
+ context 'hash arguments' do
181
+ should 'allow single and double quotes in the value' do
182
+ # no error raised
183
+ @client.delete(:test).where(:id => "'\"abc").execute
184
+ end
185
+ end
186
+
187
+ context 'string arguments' do
188
+ should 'allow single and double quotes in the value' do
189
+ # no error raised
190
+ @client.delete(:test).where('id = ?', "'\"abc").execute
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'pry'
3
+ require 'minitest/autorun'
4
+ require 'minitest/should'
5
+
6
+
7
+ require 'cassava'
8
+
9
+ class Minitest::Should::TestCase
10
+ def self.xshould(*args)
11
+ puts "Disabled test: #{args}"
12
+ end
13
+ end
14
+
15
+ def session_for_keyspace(keyspace = 'test_cassava')
16
+ c = Cassandra.cluster(port: 9242)
17
+ c.connect(keyspace)
18
+ end
19
+
20
+ def initialize_test_table
21
+ sess = session_for_keyspace(nil)
22
+ sess.execute('DROP KEYSPACE test_cassava') rescue nil
23
+ sess.execute("CREATE KEYSPACE test_cassava with replication = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 }")
24
+ session_for_keyspace.execute('CREATE TABLE test(id text, a int, b text, c text, d int, primary key ((id), a, b))')
25
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cassava_rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Arron Norwell
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-01-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-reporters
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: cassandra-driver
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - anorwell@datto.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - cassava_rb.gemspec
96
+ - lib/cassava.rb
97
+ - lib/cassava/client.rb
98
+ - lib/cassava/version.rb
99
+ - test/cassava/client_test.rb
100
+ - test/test_helper.rb
101
+ homepage: ''
102
+ licenses:
103
+ - MIT
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.2.2
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: An unopinionated wrapper for the datastax cassandra gem.
125
+ test_files:
126
+ - test/cassava/client_test.rb
127
+ - test/test_helper.rb
128
+ has_rdoc: