cassanity 0.2.2 → 0.3.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 (35) hide show
  1. data/Changelog.md +5 -0
  2. data/Guardfile +8 -1
  3. data/README.md +8 -0
  4. data/examples/_shared.rb +1 -0
  5. data/examples/batch.rb +4 -1
  6. data/examples/column_families.rb +3 -1
  7. data/examples/counters.rb +90 -0
  8. data/examples/keyspaces.rb +3 -1
  9. data/examples/select_range.rb +118 -0
  10. data/lib/cassanity/argument_generators/set_clause.rb +7 -8
  11. data/lib/cassanity/argument_generators/where_clause.rb +19 -1
  12. data/lib/cassanity/decrement.rb +27 -0
  13. data/lib/cassanity/executors/cassandra_cql.rb +1 -1
  14. data/lib/cassanity/increment.rb +27 -0
  15. data/lib/cassanity/operator.rb +27 -0
  16. data/lib/cassanity/operators/eq.rb +15 -0
  17. data/lib/cassanity/operators/gt.rb +15 -0
  18. data/lib/cassanity/operators/gte.rb +15 -0
  19. data/lib/cassanity/operators/lt.rb +15 -0
  20. data/lib/cassanity/operators/lte.rb +15 -0
  21. data/lib/cassanity/version.rb +1 -1
  22. data/lib/cassanity.rb +84 -0
  23. data/spec/integration/cassanity/column_family_spec.rb +69 -2
  24. data/spec/unit/cassanity/argument_generators/set_clause_spec.rb +9 -46
  25. data/spec/unit/cassanity/argument_generators/where_clause_spec.rb +87 -8
  26. data/spec/unit/cassanity/decrement_spec.rb +68 -0
  27. data/spec/unit/cassanity/increment_spec.rb +68 -0
  28. data/spec/unit/cassanity/operator_spec.rb +58 -0
  29. data/spec/unit/cassanity/operators/eq_spec.rb +24 -0
  30. data/spec/unit/cassanity/operators/gt_spec.rb +24 -0
  31. data/spec/unit/cassanity/operators/gte_spec.rb +24 -0
  32. data/spec/unit/cassanity/operators/lt_spec.rb +24 -0
  33. data/spec/unit/cassanity/operators/lte_spec.rb +24 -0
  34. data/spec/unit/cassanity_spec.rb +118 -0
  35. metadata +30 -4
@@ -181,6 +181,51 @@ describe Cassanity::ColumnFamily do
181
181
  ])
182
182
  end
183
183
 
184
+ context "selecting a range of data" do
185
+ let(:name) { 'rollups_minute' }
186
+
187
+ subject {
188
+ described_class.new({
189
+ keyspace: keyspace,
190
+ name: name,
191
+ })
192
+ }
193
+
194
+ before do
195
+ client.execute("CREATE COLUMNFAMILY #{name} (id text, ts int, value counter, PRIMARY KEY(id, ts))")
196
+ @id = 'foo'
197
+ client.execute("UPDATE #{name} SET value = value + 1 WHERE id = ? AND ts = ?", @id, 1)
198
+ client.execute("UPDATE #{name} SET value = value + 1 WHERE id = ? AND ts = ?", @id, 2)
199
+ client.execute("UPDATE #{name} SET value = value + 1 WHERE id = ? AND ts = ?", @id, 3)
200
+ client.execute("UPDATE #{name} SET value = value + 1 WHERE id = ? AND ts = ?", @id, 4)
201
+ end
202
+
203
+ it "works including end" do
204
+ subject.select({
205
+ where: {
206
+ id: @id,
207
+ ts: Range.new(1, 3),
208
+ }
209
+ }).should eq([
210
+ {'id' => 'foo', 'ts' => 1, 'value' => 1},
211
+ {'id' => 'foo', 'ts' => 2, 'value' => 1},
212
+ {'id' => 'foo', 'ts' => 3, 'value' => 1},
213
+ ])
214
+ end
215
+
216
+ it "works excluding end" do
217
+ subject.select({
218
+ where: {
219
+ id: @id,
220
+ ts: Range.new(1, 3, true),
221
+ }
222
+ }).should eq([
223
+ {'id' => 'foo', 'ts' => 1, 'value' => 1},
224
+ {'id' => 'foo', 'ts' => 2, 'value' => 1},
225
+ ])
226
+ end
227
+ end
228
+
184
229
  it "can insert data" do
185
230
  subject.insert({
186
231
  data: {
@@ -219,7 +264,7 @@ describe Cassanity::ColumnFamily do
219
264
  row['name'].should eq('gist')
220
265
  end
221
266
 
222
- describe "updating a counter column" do
267
+ describe "incrementing a counter column" do
223
268
  subject {
224
269
  described_class.new({
225
270
  keyspace: keyspace,
@@ -229,7 +274,7 @@ describe Cassanity::ColumnFamily do
229
274
 
230
275
  it "works" do
231
276
  subject.update({
232
- set: {views: 'views + 2'},
277
+ set: {views: Cassanity::Increment.new(2)},
233
278
  where: {id: '1'},
234
279
  })
235
280
 
@@ -241,6 +286,28 @@ describe Cassanity::ColumnFamily do
241
286
  end
242
287
  end
243
288
 
289
+ describe "decrementing a counter column" do
290
+ subject {
291
+ described_class.new({
292
+ keyspace: keyspace,
293
+ name: counters_column_family_name,
294
+ })
295
+ }
296
+
297
+ it "works" do
298
+ subject.update({
299
+ set: {views: Cassanity::Decrement.new(2)},
300
+ where: {id: '1'},
301
+ })
302
+
303
+ result = client.execute("SELECT * FROM #{counters_column_family_name} WHERE id = '1'")
304
+ result.rows.should eq(1)
305
+ row = result.fetch_hash
306
+ row['id'].should eq('1')
307
+ row['views'].should be(-2)
308
+ end
309
+ end
310
+
244
311
  it "can delete data" do
245
312
  client.execute("INSERT INTO #{column_family_name} (id, name) VALUES (?, ?)", '1', 'github')
246
313
  client.execute("INSERT INTO #{column_family_name} (id, name) VALUES (?, ?)", '2', 'gist')
@@ -31,54 +31,17 @@ describe Cassanity::ArgumentGenerators::SetClause do
31
31
  end
32
32
  end
33
33
 
34
- context "with counter update" do
35
- it "returns array of arguments where counter SET is correct" do
36
- subject.call(set: {views: 'views + 5'}).
37
- should eq([" SET views = views + 5"])
38
- end
39
-
40
- it "works with no spaces" do
41
- subject.call(set: {views: 'views+5'}).
42
- should eq([" SET views = views+5"])
43
- end
44
-
45
- it "works with one or more spaces before the operator" do
46
- subject.call(set: {views: 'views +5'}).
47
- should eq([" SET views = views +5"])
48
-
49
- subject.call(set: {views: 'views +5'}).
50
- should eq([" SET views = views +5"])
51
- end
52
-
53
- it "works with one or more spaces after the operator" do
54
- subject.call(set: {views: 'views+ 5'}).
55
- should eq([" SET views = views+ 5"])
56
-
57
- subject.call(set: {views: 'views+ 5'}).
58
- should eq([" SET views = views+ 5"])
59
- end
60
-
61
- it "works with spaces after before the key" do
62
- subject.call(set: {views: ' views + 5'}).
63
- should eq([" SET views = views + 5"])
64
- end
65
-
66
- it "works with spaces after the number" do
67
- subject.call(set: {views: 'views + 5 '}).
68
- should eq([" SET views = views + 5 "])
69
-
70
- subject.call(set: {views: 'views + 5 '}).
71
- should eq([" SET views = views + 5 "])
72
- end
73
-
74
- it "works with negative operator" do
75
- subject.call(set: {views: 'views - 5'}).
76
- should eq([" SET views = views - 5"])
34
+ context "with increment" do
35
+ it "returns array of arguments with SET including counter increment" do
36
+ subject.call(set: {views: Cassanity::Increment.new(5)}).
37
+ should eq([" SET views = views + ?", 5])
77
38
  end
39
+ end
78
40
 
79
- it "works with multiple digit numbers" do
80
- subject.call(set: {views: 'views - 52737237'}).
81
- should eq([" SET views = views - 52737237"])
41
+ context "with decrement" do
42
+ it "returns array of arguments with SET including counter decrement" do
43
+ subject.call(set: {views: Cassanity::Decrement.new(3)}).
44
+ should eq([" SET views = views - ?", 3])
82
45
  end
83
46
  end
84
47
  end
@@ -26,6 +26,21 @@ describe Cassanity::ArgumentGenerators::WhereClause do
26
26
  end
27
27
  end
28
28
 
29
+ context "with multiple where values" do
30
+ it "returns array of arguments with AND separating where keys" do
31
+ subject.call({
32
+ where: {
33
+ bucket: '2012',
34
+ id: '1',
35
+ }
36
+ }).should eq([
37
+ " WHERE bucket = ? AND id = ?",
38
+ '2012',
39
+ '1',
40
+ ])
41
+ end
42
+ end
43
+
29
44
  context "with array value for a where" do
30
45
  it "returns array of arguments using IN for key with array value" do
31
46
  subject.call({
@@ -39,19 +54,83 @@ describe Cassanity::ArgumentGenerators::WhereClause do
39
54
  end
40
55
  end
41
56
 
42
- context "with multiple where values" do
43
- it "returns array of arguments with AND separating where keys" do
57
+ context "with inclusive range value for a where" do
58
+ it "returns range comparison including end of range" do
44
59
  subject.call({
45
60
  where: {
46
- bucket: '2012',
47
- id: '1',
48
- }
61
+ timestamp: Range.new(1, 3)
62
+ },
49
63
  }).should eq([
50
- " WHERE bucket = ? AND id = ?",
51
- '2012',
52
- '1',
64
+ " WHERE timestamp >= ? AND timestamp <= ?",
65
+ 1, 3
53
66
  ])
54
67
  end
55
68
  end
69
+
70
+ context "with exclusive range value for a where" do
71
+ it "returns range comparison including end of range" do
72
+ subject.call({
73
+ where: {
74
+ timestamp: Range.new(1, 3, true)
75
+ },
76
+ }).should eq([
77
+ " WHERE timestamp >= ? AND timestamp < ?",
78
+ 1, 3
79
+ ])
80
+ end
81
+ end
82
+
83
+ context "with a cassanity operator value" do
84
+ it "returns correct cql" do
85
+ subject.call({
86
+ where: {
87
+ timestamp: Cassanity::Operator.new('<', 10)
88
+ },
89
+ }).should eq([
90
+ " WHERE timestamp < ?",
91
+ 10
92
+ ])
93
+ end
94
+ end
95
+
96
+ context "with a cassanity less than operator value" do
97
+ it "returns correct cql" do
98
+ subject.call({
99
+ where: {timestamp: Cassanity::Operators::Lt.new(10)},
100
+ }).should eq([" WHERE timestamp < ?", 10])
101
+ end
102
+ end
103
+
104
+ context "with a cassanity less than or equal to operator value" do
105
+ it "returns correct cql" do
106
+ subject.call({
107
+ where: {timestamp: Cassanity::Operators::Lte.new(10)},
108
+ }).should eq([" WHERE timestamp <= ?", 10])
109
+ end
110
+ end
111
+
112
+ context "with a cassanity greater than operator value" do
113
+ it "returns correct cql" do
114
+ subject.call({
115
+ where: {timestamp: Cassanity::Operators::Gt.new(10)},
116
+ }).should eq([" WHERE timestamp > ?", 10])
117
+ end
118
+ end
119
+
120
+ context "with a cassanity greater than or equal to operator value" do
121
+ it "returns correct cql" do
122
+ subject.call({
123
+ where: {timestamp: Cassanity::Operators::Gte.new(10)},
124
+ }).should eq([" WHERE timestamp >= ?", 10])
125
+ end
126
+ end
127
+
128
+ context "with a cassanity equal to operator value" do
129
+ it "returns correct cql" do
130
+ subject.call({
131
+ where: {timestamp: Cassanity::Operators::Eq.new(10)},
132
+ }).should eq([" WHERE timestamp = ?", 10])
133
+ end
134
+ end
56
135
  end
57
136
  end
@@ -0,0 +1,68 @@
1
+ require 'helper'
2
+ require 'cassanity/decrement'
3
+
4
+ describe Cassanity::Decrement do
5
+ describe "self named helper method" do
6
+ it "returns instance" do
7
+ Cassanity::Decrement(5).should eq(described_class.new(5))
8
+ end
9
+ end
10
+
11
+ describe "#initialize" do
12
+ context "with value" do
13
+ before do
14
+ @instance = described_class.new(5)
15
+ end
16
+
17
+ it "sets value" do
18
+ @instance.value.should be(5)
19
+ end
20
+
21
+ it "sets symbol" do
22
+ @instance.symbol.should be(:-)
23
+ end
24
+ end
25
+
26
+ context "without value" do
27
+ it "defaults value to 1" do
28
+ subject.value.should be(1)
29
+ end
30
+ end
31
+
32
+ context "with nil" do
33
+ it "raises error" do
34
+ expect {
35
+ described_class.new(nil)
36
+ }.to raise_error(ArgumentError, "value cannot be nil")
37
+ end
38
+ end
39
+ end
40
+
41
+ shared_examples_for "decrement equality" do |method_name|
42
+ it "returns true for same class and value" do
43
+ instance = described_class.new(5)
44
+ other = described_class.new(5)
45
+ instance.send(method_name, other).should be_true
46
+ end
47
+
48
+ it "returns false for same class and different value" do
49
+ instance = described_class.new(5)
50
+ other = described_class.new(7)
51
+ instance.send(method_name, other).should be_false
52
+ end
53
+
54
+ it "returns false for different class" do
55
+ instance = described_class.new(5)
56
+ other = Object.new
57
+ instance.send(method_name, other).should be_false
58
+ end
59
+ end
60
+
61
+ describe "#eql?" do
62
+ include_examples "decrement equality", :eql?
63
+ end
64
+
65
+ describe "#==" do
66
+ include_examples "decrement equality", :==
67
+ end
68
+ end
@@ -0,0 +1,68 @@
1
+ require 'helper'
2
+ require 'cassanity/increment'
3
+
4
+ describe Cassanity::Increment do
5
+ describe "self named helper method" do
6
+ it "returns instance" do
7
+ Cassanity::Increment(5).should eq(described_class.new(5))
8
+ end
9
+ end
10
+
11
+ describe "#initialize" do
12
+ context "with value" do
13
+ before do
14
+ @instance = described_class.new(5)
15
+ end
16
+
17
+ it "sets value" do
18
+ @instance.value.should be(5)
19
+ end
20
+
21
+ it "sets symbol" do
22
+ @instance.symbol.should be(:+)
23
+ end
24
+ end
25
+
26
+ context "without value" do
27
+ it "defaults value to 1" do
28
+ subject.value.should be(1)
29
+ end
30
+ end
31
+
32
+ context "with nil" do
33
+ it "raises error" do
34
+ expect {
35
+ described_class.new(nil)
36
+ }.to raise_error(ArgumentError, "value cannot be nil")
37
+ end
38
+ end
39
+ end
40
+
41
+ shared_examples_for "increment equality" do |method_name|
42
+ it "returns true for same class and value" do
43
+ instance = described_class.new(5)
44
+ other = described_class.new(5)
45
+ instance.send(method_name, other).should be_true
46
+ end
47
+
48
+ it "returns false for same class and different value" do
49
+ instance = described_class.new(5)
50
+ other = described_class.new(7)
51
+ instance.send(method_name, other).should be_false
52
+ end
53
+
54
+ it "returns false for different class" do
55
+ instance = described_class.new(5)
56
+ other = Object.new
57
+ instance.send(method_name, other).should be_false
58
+ end
59
+ end
60
+
61
+ describe "#eql?" do
62
+ include_examples "increment equality", :eql?
63
+ end
64
+
65
+ describe "#==" do
66
+ include_examples "increment equality", :==
67
+ end
68
+ end
@@ -0,0 +1,58 @@
1
+ require 'helper'
2
+ require 'cassanity/operator'
3
+
4
+ describe Cassanity::Operator do
5
+ describe "self named helper method" do
6
+ it "returns instance" do
7
+ Cassanity::Operator(:<, 5).should eq(described_class.new(:<, 5))
8
+ end
9
+ end
10
+
11
+ describe "#initialize" do
12
+ before do
13
+ @instance = described_class.new('<', 5)
14
+ end
15
+
16
+ it "sets symbol" do
17
+ @instance.symbol.should eq('<')
18
+ end
19
+
20
+ it "sets value" do
21
+ @instance.value.should be(5)
22
+ end
23
+ end
24
+
25
+ shared_examples_for "operator equality" do |method_name|
26
+ it "returns true for same class, symbol and value" do
27
+ instance = described_class.new(:<, 5)
28
+ other = described_class.new(:<, 5)
29
+ instance.send(method_name, other).should be_true
30
+ end
31
+
32
+ it "returns false for same class/value and different symbol" do
33
+ instance = described_class.new(:<, 5)
34
+ other = described_class.new(:>, 5)
35
+ instance.send(method_name, other).should be_false
36
+ end
37
+
38
+ it "returns false for same class/symbol and different value" do
39
+ instance = described_class.new(:<, 5)
40
+ other = described_class.new(:>, 7)
41
+ instance.send(method_name, other).should be_false
42
+ end
43
+
44
+ it "returns false for different class" do
45
+ instance = described_class.new(:<, 5)
46
+ other = Object.new
47
+ instance.send(method_name, other).should be_false
48
+ end
49
+ end
50
+
51
+ describe "#eql?" do
52
+ include_examples "operator equality", :eql?
53
+ end
54
+
55
+ describe "#==" do
56
+ include_examples "operator equality", :==
57
+ end
58
+ end
@@ -0,0 +1,24 @@
1
+ require 'helper'
2
+ require 'cassanity/operators/eq'
3
+
4
+ describe Cassanity::Operators::Eq do
5
+ describe "self named helper method" do
6
+ it "returns instance" do
7
+ Cassanity::Operators::Eq(5).should eq(described_class.new(5))
8
+ end
9
+ end
10
+
11
+ describe "#initialize" do
12
+ subject {
13
+ described_class.new('John')
14
+ }
15
+
16
+ it "sets symbol" do
17
+ subject.symbol.should be(:"=")
18
+ end
19
+
20
+ it "sets value" do
21
+ subject.value.should eq('John')
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'helper'
2
+ require 'cassanity/operators/gt'
3
+
4
+ describe Cassanity::Operators::Gt do
5
+ describe "self named helper method" do
6
+ it "returns instance" do
7
+ Cassanity::Operators::Gt(5).should eq(described_class.new(5))
8
+ end
9
+ end
10
+
11
+ describe "#initialize" do
12
+ subject {
13
+ described_class.new(5)
14
+ }
15
+
16
+ it "sets symbol" do
17
+ subject.symbol.should be(:>)
18
+ end
19
+
20
+ it "sets value" do
21
+ subject.value.should eq(5)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'helper'
2
+ require 'cassanity/operators/gte'
3
+
4
+ describe Cassanity::Operators::Gte do
5
+ describe "self named helper method" do
6
+ it "returns instance" do
7
+ Cassanity::Operators::Gte(5).should eq(described_class.new(5))
8
+ end
9
+ end
10
+
11
+ describe "#initialize" do
12
+ subject {
13
+ described_class.new(5)
14
+ }
15
+
16
+ it "sets symbol" do
17
+ subject.symbol.should be(:>=)
18
+ end
19
+
20
+ it "sets value" do
21
+ subject.value.should eq(5)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'helper'
2
+ require 'cassanity/operators/lt'
3
+
4
+ describe Cassanity::Operators::Lt do
5
+ describe "self named helper method" do
6
+ it "returns instance" do
7
+ Cassanity::Operators::Lt(5).should eq(described_class.new(5))
8
+ end
9
+ end
10
+
11
+ describe "#initialize" do
12
+ subject {
13
+ described_class.new(5)
14
+ }
15
+
16
+ it "sets symbol" do
17
+ subject.symbol.should be(:<)
18
+ end
19
+
20
+ it "sets value" do
21
+ subject.value.should eq(5)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'helper'
2
+ require 'cassanity/operators/lte'
3
+
4
+ describe Cassanity::Operators::Lte do
5
+ describe "self named helper method" do
6
+ it "returns instance" do
7
+ Cassanity::Operators::Lte(5).should eq(described_class.new(5))
8
+ end
9
+ end
10
+
11
+ describe "#initialize" do
12
+ subject {
13
+ described_class.new(5)
14
+ }
15
+
16
+ it "sets symbol" do
17
+ subject.symbol.should be(:<=)
18
+ end
19
+
20
+ it "sets value" do
21
+ subject.value.should eq(5)
22
+ end
23
+ end
24
+ end