cassanity 0.6.0.beta5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -0
- data/doc/Migrations.md +4 -15
- data/examples/collections.rb +82 -0
- data/lib/cassanity.rb +54 -0
- data/lib/cassanity/addition.rb +15 -0
- data/lib/cassanity/argument_generators/batch.rb +8 -1
- data/lib/cassanity/argument_generators/column_family_create.rb +11 -1
- data/lib/cassanity/argument_generators/column_family_delete.rb +19 -4
- data/lib/cassanity/argument_generators/keyspace_create.rb +5 -1
- data/lib/cassanity/argument_generators/set_clause.rb +4 -1
- data/lib/cassanity/collection_item.rb +39 -0
- data/lib/cassanity/removal.rb +15 -0
- data/lib/cassanity/schema.rb +5 -2
- data/lib/cassanity/set_addition.rb +16 -0
- data/lib/cassanity/set_removal.rb +16 -0
- data/lib/cassanity/statement.rb +4 -1
- data/lib/cassanity/version.rb +1 -1
- data/spec/unit/cassanity/addition_spec.rb +80 -0
- data/spec/unit/cassanity/argument_generators/batch_spec.rb +45 -0
- data/spec/unit/cassanity/argument_generators/column_family_create_spec.rb +20 -0
- data/spec/unit/cassanity/argument_generators/column_family_delete_spec.rb +42 -0
- data/spec/unit/cassanity/argument_generators/keyspace_create_spec.rb +11 -0
- data/spec/unit/cassanity/argument_generators/set_clause_spec.rb +35 -0
- data/spec/unit/cassanity/collection_item_spec.rb +84 -0
- data/spec/unit/cassanity/removal_spec.rb +80 -0
- data/spec/unit/cassanity/schema_spec.rb +49 -0
- data/spec/unit/cassanity/set_addition_spec.rb +80 -0
- data/spec/unit/cassanity/set_removal_spec.rb +80 -0
- data/spec/unit/cassanity/statement_spec.rb +9 -2
- data/spec/unit/cassanity_spec.rb +42 -0
- metadata +31 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8463dccda7b4fa69e8920bf632ff5436c752c8c0
|
4
|
+
data.tar.gz: 72fb90eb87e6fdff894d017f4be9cdc4c1c4c09f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fddf06889ca7c6be97a23c07708ec0d714aac31515e7d499a4ba41adb2fcd368f074b492408711b0710f61978971d5c9511b7af9fa3cca6d8ffeac17adb21cb
|
7
|
+
data.tar.gz: 280945481fa843e44c334269a5b632bcff9742a599e5ceb7b5fe40663824fbfb099a093eb04eb8822221099ace095ac60a10d5a8fd2345e1e2af4d9fa5c122a9
|
data/README.md
CHANGED
@@ -90,6 +90,7 @@ You can also do a lot more. Here are a few more [examples](https://github.com/jn
|
|
90
90
|
* [Keyspaces](https://github.com/jnunemaker/cassanity/tree/master/examples/keyspaces.rb)
|
91
91
|
* [Column Families](https://github.com/jnunemaker/cassanity/tree/master/examples/column_families.rb)
|
92
92
|
* [Select a Range](https://github.com/jnunemaker/cassanity/tree/master/examples/select_range.rb)
|
93
|
+
* [Collections](https://github.com/jnunemaker/cassanity/tree/master/examples/collections.rb)
|
93
94
|
|
94
95
|
## More Reading
|
95
96
|
|
data/doc/Migrations.md
CHANGED
@@ -18,9 +18,11 @@ mkdir -p db/migrate
|
|
18
18
|
Second, create a file for the create users migration:
|
19
19
|
|
20
20
|
```bash
|
21
|
-
touch db/migrate
|
21
|
+
touch db/migrate/`date +%s`_create_users.rb
|
22
22
|
```
|
23
23
|
|
24
|
+
This will create a file with the current timestamp as the prefix. This is the recommended naming convention.
|
25
|
+
|
24
26
|
Now, open the file up in your favorite editor and paste in the following:
|
25
27
|
|
26
28
|
```ruby
|
@@ -112,20 +114,7 @@ migrator.migrate_to(0, :down)
|
|
112
114
|
migrator.migrate_to(1, :up)
|
113
115
|
```
|
114
116
|
|
115
|
-
|
116
|
-
|
117
|
-
In the example above, I used a migration with a version of 001. The only requirement cassanity enforces on migration version numbers is that they be an integer. This means that you can you timestamped migrations if you prefer. For example, the following are all completely valid migration names.
|
118
|
-
|
119
|
-
```
|
120
|
-
touch db/migrate/001_create_users.rb
|
121
|
-
touch db/migrate/201303_create_users.rb
|
122
|
-
touch db/migrate/20130304_create_users.rb
|
123
|
-
touch db/migrate/20130304160000_create_users.rb
|
124
|
-
```
|
125
|
-
|
126
|
-
The version could be year, month, day, hour, minute, second (20130304160000, 4:00pm on March 4, 2013), as in the example I just showed, or you could standardize on year, month, day (20130304, March 4, 2013).
|
127
|
-
|
128
|
-
**Note**: Migrations will run in the order they appear on disk. They are sorted by their path, which means if you do not go with timestamped migrations, you should padd migrations with leading zero's, as Rails does (ie: "001" instead of "01" or "1").
|
117
|
+
**Note**: Migrations will run in the order they appear on disk (alphanumerically sorted), that's why we encourage you to use timestamped migrations.
|
129
118
|
|
130
119
|
## Rake Tasks
|
131
120
|
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require_relative '_shared'
|
2
|
+
require 'cassanity'
|
3
|
+
|
4
|
+
client = Cassanity::Client.new(['127.0.0.1'], 9042, {
|
5
|
+
instrumenter: ActiveSupport::Notifications,
|
6
|
+
})
|
7
|
+
|
8
|
+
keyspace = client['cassanity_examples']
|
9
|
+
keyspace.recreate
|
10
|
+
|
11
|
+
# setup column family
|
12
|
+
users = keyspace.column_family('users', {
|
13
|
+
schema: {
|
14
|
+
primary_key: :id,
|
15
|
+
columns: {
|
16
|
+
id: :text,
|
17
|
+
emails: :"set<text>",
|
18
|
+
top_places: :"list<text>",
|
19
|
+
todo: :"map<timestamp,text>",
|
20
|
+
},
|
21
|
+
},
|
22
|
+
})
|
23
|
+
users.create
|
24
|
+
|
25
|
+
|
26
|
+
## Sets
|
27
|
+
|
28
|
+
# insert a row
|
29
|
+
users.insert(data: {id: '1', emails: Set['f@baggings.com','baggins@gmail.com']})
|
30
|
+
|
31
|
+
# add an element to the set
|
32
|
+
users.update(set: {emails: Cassanity.set_add('a@b.com')}, where: {id: '1'})
|
33
|
+
|
34
|
+
# delete an element from the set
|
35
|
+
users.update(set: {emails: Cassanity.set_remove('f@baggings.com')}, where: {id: '1'})
|
36
|
+
|
37
|
+
# delete all elements from the set
|
38
|
+
users.delete(columns: :emails, where: {id: '1'})
|
39
|
+
# or
|
40
|
+
users.update(set: {emails: Set[]}, where: {id: '1'})
|
41
|
+
|
42
|
+
|
43
|
+
## Lists
|
44
|
+
|
45
|
+
# insert a row
|
46
|
+
users.insert(data: {id: '2', top_places: ['mordor','rivendell','rohan']})
|
47
|
+
|
48
|
+
# add an element to the list
|
49
|
+
users.update(set: {top_places: Cassanity.add('the shire')}, where: {id: '2'})
|
50
|
+
|
51
|
+
# update an element by its index
|
52
|
+
users.update(set: {top_places: Cassanity.item(0,'riddermark')}, where: {id: '2'})
|
53
|
+
|
54
|
+
# delete an element or more from the list
|
55
|
+
users.update(set: {top_places: Cassanity.remove('riddermark', 'rivendell')}, where: {id: '2'})
|
56
|
+
# or delete an element by its index
|
57
|
+
users.delete(columns: Cassanity.item(0, :top_places), where: {id: '2'})
|
58
|
+
|
59
|
+
# delete all elements from the list
|
60
|
+
users.delete(columns: :top_places, where: {id: '2'})
|
61
|
+
# or
|
62
|
+
users.update(set: {top_places: []}, where: {id: '2'})
|
63
|
+
|
64
|
+
|
65
|
+
## Maps
|
66
|
+
require 'date'
|
67
|
+
today = Date.today
|
68
|
+
tomorrow = today + 1
|
69
|
+
|
70
|
+
# insert a row
|
71
|
+
users.insert(data: {id: '3', todo: {today.to_time => 'enter mordor'}})
|
72
|
+
|
73
|
+
# add/update an element to the map
|
74
|
+
users.update(set: {todo: Cassanity.item(tomorrow.to_time, 'find water')}, where: {id: '3'})
|
75
|
+
|
76
|
+
# delete an element from the map
|
77
|
+
users.delete(columns: Cassanity.item(tomorrow.to_time, :todo), where: {id: '3'})
|
78
|
+
|
79
|
+
# delete all elements from the map
|
80
|
+
users.delete(columns: :todo, where: {id: '3'})
|
81
|
+
# or
|
82
|
+
users.update(set: {todo: {}}, where: {id: '3'})
|
data/lib/cassanity.rb
CHANGED
@@ -6,6 +6,11 @@ require 'cassanity/operators/lte'
|
|
6
6
|
require 'cassanity/increment'
|
7
7
|
require 'cassanity/decrement'
|
8
8
|
require 'cassanity/range'
|
9
|
+
require 'cassanity/addition'
|
10
|
+
require 'cassanity/removal'
|
11
|
+
require 'cassanity/set_addition'
|
12
|
+
require 'cassanity/set_removal'
|
13
|
+
require 'cassanity/collection_item'
|
9
14
|
|
10
15
|
module Cassanity
|
11
16
|
# Public: Shortcut for returning an equality operator.
|
@@ -83,6 +88,52 @@ module Cassanity
|
|
83
88
|
Cassanity::Range.new(start, finish, exclusive)
|
84
89
|
end
|
85
90
|
|
91
|
+
# Public: Shortcut for returning an addition value for a list collection.
|
92
|
+
#
|
93
|
+
# values - The values to add to the list.
|
94
|
+
#
|
95
|
+
# Returns an Cassanity::Addition instance.
|
96
|
+
def self.add(*values)
|
97
|
+
Addition.new(*values)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Public: Shortcut for returning a removal value for a list collection.
|
101
|
+
#
|
102
|
+
# values - The values to remove from the list.
|
103
|
+
#
|
104
|
+
# Returns an Cassanity::Removal instance.
|
105
|
+
def self.remove(*values)
|
106
|
+
Removal.new(*values)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Public: Shortcut for returning an addition value for a set collection.
|
110
|
+
#
|
111
|
+
# value - The values to add to the set.
|
112
|
+
#
|
113
|
+
# Returns an Cassanity::SetAddition instance.
|
114
|
+
def self.set_add(*values)
|
115
|
+
SetAddition.new(*values)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Public: Shortcut for returning a removal value for a set collection.
|
119
|
+
#
|
120
|
+
# values - The values to remove from the set.
|
121
|
+
#
|
122
|
+
# Returns an Cassanity::SetRemoval instance.
|
123
|
+
def self.set_remove(*values)
|
124
|
+
SetRemoval.new(*values)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Public: Shortcut for returning a collection item.
|
128
|
+
#
|
129
|
+
# key - The item key in the list/map collection
|
130
|
+
# value - The item value.
|
131
|
+
#
|
132
|
+
# Returns a Cassanity::CollectionItem instance.
|
133
|
+
def self.item(key, value)
|
134
|
+
CollectionItem.new(key, value)
|
135
|
+
end
|
136
|
+
|
86
137
|
class << self
|
87
138
|
alias_method :equal, :eq
|
88
139
|
|
@@ -97,6 +148,9 @@ module Cassanity
|
|
97
148
|
|
98
149
|
alias_method :decr, :dec
|
99
150
|
alias_method :decrement, :dec
|
151
|
+
|
152
|
+
alias_method :sadd, :set_add
|
153
|
+
alias_method :sremove, :set_remove
|
100
154
|
end
|
101
155
|
end
|
102
156
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Cassanity
|
2
|
+
def self.Addition(*args)
|
3
|
+
Addition.new(*args)
|
4
|
+
end
|
5
|
+
|
6
|
+
class Addition < Operator
|
7
|
+
# Public: Returns an addition instance
|
8
|
+
def initialize(*args)
|
9
|
+
values = args.flatten.compact
|
10
|
+
raise ArgumentError.new("value cannot be nil") if values.empty?
|
11
|
+
|
12
|
+
super :+, values
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -4,6 +4,9 @@ module Cassanity
|
|
4
4
|
module ArgumentGenerators
|
5
5
|
class Batch
|
6
6
|
|
7
|
+
# Private: List of supported batch types
|
8
|
+
BatchTypes = ['COUNTER','LOGGED','UNLOGGED']
|
9
|
+
|
7
10
|
# Private: Map of command to argument generator
|
8
11
|
Commands = {
|
9
12
|
insert: ColumnFamilyInsert.new,
|
@@ -19,11 +22,15 @@ module Cassanity
|
|
19
22
|
|
20
23
|
# Internal
|
21
24
|
def call(args = {})
|
25
|
+
type = args[:type].to_s.upcase
|
26
|
+
type = 'LOGGED' if type.empty?
|
27
|
+
raise ArgumentError.new("invalid batch type") unless BatchTypes.include?(type)
|
28
|
+
|
22
29
|
using = args[:using]
|
23
30
|
modifications_argument = args.fetch(:modifications) { [] }
|
24
31
|
|
25
32
|
variables = []
|
26
|
-
cql = "BEGIN BATCH"
|
33
|
+
cql = type == 'LOGGED' ? "BEGIN BATCH" : "BEGIN #{type} BATCH"
|
27
34
|
|
28
35
|
using_cql, *using_variables = @using_clause.call(using: using)
|
29
36
|
cql << using_cql
|
@@ -27,7 +27,7 @@ module Cassanity
|
|
27
27
|
definitions << "#{name} #{type}"
|
28
28
|
end
|
29
29
|
|
30
|
-
definitions << "PRIMARY KEY (#{primary_keys.join(', ')})"
|
30
|
+
definitions << "PRIMARY KEY (#{compose_primary_key(primary_keys).join(', ')})"
|
31
31
|
|
32
32
|
cql_definition = definitions.join(', ')
|
33
33
|
|
@@ -39,6 +39,16 @@ module Cassanity
|
|
39
39
|
|
40
40
|
[cql, *variables]
|
41
41
|
end
|
42
|
+
|
43
|
+
def compose_primary_key(primary_keys)
|
44
|
+
primary_keys.map do |key|
|
45
|
+
if key.is_a? Array
|
46
|
+
"(#{key.join(', ')})"
|
47
|
+
else
|
48
|
+
key
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
42
52
|
end
|
43
53
|
end
|
44
54
|
end
|
@@ -15,7 +15,7 @@ module Cassanity
|
|
15
15
|
def call(args = {})
|
16
16
|
name = args.fetch(:column_family_name)
|
17
17
|
where = args.fetch(:where)
|
18
|
-
columns =
|
18
|
+
columns = args.fetch(:columns) { [] }
|
19
19
|
using = args[:using]
|
20
20
|
|
21
21
|
if (keyspace_name = args[:keyspace_name])
|
@@ -23,10 +23,25 @@ module Cassanity
|
|
23
23
|
end
|
24
24
|
|
25
25
|
column_clause, variables = '', []
|
26
|
-
|
27
|
-
|
28
|
-
|
26
|
+
columns ||= []
|
27
|
+
|
28
|
+
cols = []
|
29
|
+
[columns].flatten.each do |c|
|
30
|
+
if Hash === c
|
31
|
+
column_name = c.keys.first
|
32
|
+
keys = c.values.first
|
33
|
+
[keys].flatten.each do |k|
|
34
|
+
cols << "#{column_name}[?]"
|
35
|
+
variables << k
|
36
|
+
end
|
37
|
+
elsif Cassanity::CollectionItem === c
|
38
|
+
cols << "#{c.value}[?]"
|
39
|
+
variables << c.key
|
40
|
+
else
|
41
|
+
cols << c
|
42
|
+
end
|
29
43
|
end
|
44
|
+
column_clause = " #{cols.join(', ')}".rstrip
|
30
45
|
|
31
46
|
cql = "DELETE#{column_clause} FROM #{name}"
|
32
47
|
|
@@ -14,7 +14,11 @@ module Cassanity
|
|
14
14
|
name = args.fetch(:keyspace_name)
|
15
15
|
cql = "CREATE KEYSPACE #{name}"
|
16
16
|
|
17
|
-
replication =
|
17
|
+
replication = args.fetch(:replication, {})
|
18
|
+
|
19
|
+
if replication.empty? || replication[:class] !~ /NetworkTopologyStrategy/
|
20
|
+
replication = default_replication_options.merge(replication)
|
21
|
+
end
|
18
22
|
|
19
23
|
with_cql, *with_variables = @with_clause.call(with: { replication: replication })
|
20
24
|
cql << with_cql
|
@@ -12,9 +12,12 @@ module Cassanity
|
|
12
12
|
|
13
13
|
set.each do |key, value|
|
14
14
|
case value
|
15
|
-
when Cassanity::Increment, Cassanity::Decrement
|
15
|
+
when Cassanity::Increment, Cassanity::Decrement, Cassanity::Addition, Cassanity::Removal, Cassanity::SetAddition, Cassanity::SetRemoval
|
16
16
|
sets << "#{key} = #{key} #{value.symbol} ?"
|
17
17
|
variables << value.value
|
18
|
+
when Cassanity::CollectionItem
|
19
|
+
sets << "#{key}[?] = ?"
|
20
|
+
variables << value.key << value.value
|
18
21
|
else
|
19
22
|
sets << "#{key} = ?"
|
20
23
|
variables << value
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Cassanity
|
2
|
+
def self.CollectionItem(*args)
|
3
|
+
CollectionItem.new(*args)
|
4
|
+
end
|
5
|
+
|
6
|
+
class CollectionItem
|
7
|
+
# Internal
|
8
|
+
attr_reader :key
|
9
|
+
|
10
|
+
# Internal
|
11
|
+
attr_reader :value
|
12
|
+
|
13
|
+
# Public: Returns a collection item instance
|
14
|
+
def initialize(key, value)
|
15
|
+
raise ArgumentError.new("key cannot be nil") if key.nil?
|
16
|
+
raise ArgumentError.new("value cannot be nil") if value.nil?
|
17
|
+
|
18
|
+
@key = key
|
19
|
+
@value = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def eql?(other)
|
23
|
+
self.class.eql?(other.class) &&
|
24
|
+
value == other.value &&
|
25
|
+
key == other.key
|
26
|
+
end
|
27
|
+
|
28
|
+
alias_method :==, :eql?
|
29
|
+
|
30
|
+
# Public
|
31
|
+
def inspect
|
32
|
+
attributes = [
|
33
|
+
"key=#{key.inspect}",
|
34
|
+
"value=#{value.inspect}",
|
35
|
+
]
|
36
|
+
"#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Cassanity
|
2
|
+
def self.Removal(*args)
|
3
|
+
Removal.new(*args)
|
4
|
+
end
|
5
|
+
|
6
|
+
class Removal < Operator
|
7
|
+
# Public: Returns a removal instance
|
8
|
+
def initialize(*args)
|
9
|
+
values = args.flatten.compact
|
10
|
+
raise ArgumentError.new("value cannot be nil") if values.empty?
|
11
|
+
|
12
|
+
super :-, values
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/cassanity/schema.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Cassanity
|
2
4
|
class Schema
|
3
5
|
# Internal
|
@@ -57,8 +59,9 @@ module Cassanity
|
|
57
59
|
|
58
60
|
# Private
|
59
61
|
def primary_keys_are_defined_as_columns?
|
60
|
-
|
61
|
-
shared_columns
|
62
|
+
flattened_primary_keys = @primary_keys.flatten
|
63
|
+
shared_columns = column_names & flattened_primary_keys
|
64
|
+
shared_columns.to_set == flattened_primary_keys.to_set
|
62
65
|
end
|
63
66
|
|
64
67
|
# Public
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'cassanity/addition'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Cassanity
|
5
|
+
def self.SetAddition(*args)
|
6
|
+
SetAddition.new(*args)
|
7
|
+
end
|
8
|
+
|
9
|
+
class SetAddition < Addition
|
10
|
+
# Public: Returns a set_addition instance
|
11
|
+
def initialize(*args)
|
12
|
+
super(*args)
|
13
|
+
@value = @value.to_set
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'cassanity/removal'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Cassanity
|
5
|
+
def self.SetRemoval(*args)
|
6
|
+
SetRemoval.new(*args)
|
7
|
+
end
|
8
|
+
|
9
|
+
class SetRemoval < Removal
|
10
|
+
# Public: Returns a set_removal instance
|
11
|
+
def initialize(*args)
|
12
|
+
super(*args)
|
13
|
+
@value = @value.to_set
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|