cassanity 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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