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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -0
  3. data/doc/Migrations.md +4 -15
  4. data/examples/collections.rb +82 -0
  5. data/lib/cassanity.rb +54 -0
  6. data/lib/cassanity/addition.rb +15 -0
  7. data/lib/cassanity/argument_generators/batch.rb +8 -1
  8. data/lib/cassanity/argument_generators/column_family_create.rb +11 -1
  9. data/lib/cassanity/argument_generators/column_family_delete.rb +19 -4
  10. data/lib/cassanity/argument_generators/keyspace_create.rb +5 -1
  11. data/lib/cassanity/argument_generators/set_clause.rb +4 -1
  12. data/lib/cassanity/collection_item.rb +39 -0
  13. data/lib/cassanity/removal.rb +15 -0
  14. data/lib/cassanity/schema.rb +5 -2
  15. data/lib/cassanity/set_addition.rb +16 -0
  16. data/lib/cassanity/set_removal.rb +16 -0
  17. data/lib/cassanity/statement.rb +4 -1
  18. data/lib/cassanity/version.rb +1 -1
  19. data/spec/unit/cassanity/addition_spec.rb +80 -0
  20. data/spec/unit/cassanity/argument_generators/batch_spec.rb +45 -0
  21. data/spec/unit/cassanity/argument_generators/column_family_create_spec.rb +20 -0
  22. data/spec/unit/cassanity/argument_generators/column_family_delete_spec.rb +42 -0
  23. data/spec/unit/cassanity/argument_generators/keyspace_create_spec.rb +11 -0
  24. data/spec/unit/cassanity/argument_generators/set_clause_spec.rb +35 -0
  25. data/spec/unit/cassanity/collection_item_spec.rb +84 -0
  26. data/spec/unit/cassanity/removal_spec.rb +80 -0
  27. data/spec/unit/cassanity/schema_spec.rb +49 -0
  28. data/spec/unit/cassanity/set_addition_spec.rb +80 -0
  29. data/spec/unit/cassanity/set_removal_spec.rb +80 -0
  30. data/spec/unit/cassanity/statement_spec.rb +9 -2
  31. data/spec/unit/cassanity_spec.rb +42 -0
  32. metadata +31 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7f0756deb7322a0613027e49cf30ec6d37f9643d
4
- data.tar.gz: c7f1f8679988d87049daeb929e657223ea2747f0
3
+ metadata.gz: 8463dccda7b4fa69e8920bf632ff5436c752c8c0
4
+ data.tar.gz: 72fb90eb87e6fdff894d017f4be9cdc4c1c4c09f
5
5
  SHA512:
6
- metadata.gz: d68305b3a2395732a858d158212234667b8b2cf16353dde999d6774e7e214ac6e0be240416eb9b9b649d46be53bba9f7bd89f7e6d7b7f5fa3b8d70af84ed5076
7
- data.tar.gz: edf5a6e070675b6994f9f31e5a0c746e93216a584409ae0856f4a26266210b5122de97c81f473946ff17ae20f72e81f0f3bebf3e00dcb499f5993736ab1bbd85
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
 
@@ -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/001_create_users.rb
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
- ## Migration Version Numbers
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'})
@@ -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 = Array(args.fetch(: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
- unless columns.empty?
28
- column_clause = " #{columns.join(', ')}"
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 = default_replication_options.merge(args[: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
@@ -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
- shared_columns = column_names & @primary_keys
61
- shared_columns == @primary_keys
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