cassandro 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gems +1 -1
- data/{LICENSE → LICENSE.md} +0 -0
- data/README.md +30 -303
- data/cassandro.gemspec +2 -2
- data/docs/advanced_features.md +59 -0
- data/docs/getting_started.md +70 -0
- data/docs/migrations.md +60 -0
- data/docs/modeling.md +93 -0
- data/docs/querying.md +120 -0
- data/lib/cassandro/core.rb +9 -0
- data/lib/cassandro/model.rb +53 -22
- data/test/cassandro_model_test.rb +29 -1
- data/test/support/tables.rb +3 -0
- metadata +12 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ca78baede197271950dd4171393ec32c9f3698f
|
4
|
+
data.tar.gz: 59a76cb0577d8b8293a1925d5ca071c889d61cbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b0810673c9631f7e60e0e9b7e4796b579c4c1b8b943f76f7dc0890a631f2b5fd0fbd08ca507d0abfaad198697206167ef6fcd84451123b759ca52d96abd6c2e
|
7
|
+
data.tar.gz: a5f4d40a976c593e3e6b149cf28573955b5e60381c24d2c4b8a04a92afcba110e3787784db5c127600bac5e9da7335a72c6ba0be7db021d8a8792509139f84b5
|
data/.gems
CHANGED
@@ -1 +1 @@
|
|
1
|
-
cassandra-driver -v 1.
|
1
|
+
cassandra-driver -v 2.1.3
|
data/{LICENSE → LICENSE.md}
RENAMED
File without changes
|
data/README.md
CHANGED
@@ -1,329 +1,56 @@
|
|
1
1
|
# Cassandro [![Gem Version](https://badge.fury.io/rb/cassandro.svg)](http://badge.fury.io/rb/cassandro)
|
2
2
|
|
3
|
-
Cassandro is a small Ruby ORM for Apache Cassandra 2.0 and CQL 3.0. Cassandro uses the new Datastax Ruby Driver
|
3
|
+
Cassandro is a small Ruby ORM for Apache Cassandra 2.0 and CQL 3.0. Cassandro uses the new [Datastax Ruby Driver](https://github.com/datastax/ruby-driver)
|
4
4
|
|
5
5
|
## Install
|
6
6
|
|
7
7
|
`gem install cassandro`
|
8
8
|
|
9
|
-
##
|
9
|
+
## Changelog
|
10
10
|
|
11
|
-
|
11
|
+
### v2.0
|
12
|
+
* Support `cassandra-driver` >= 2.0
|
13
|
+
* Allow registering indexes in model's definition
|
14
|
+
* Add `Model#ttl` method
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
Creating a new keyspace. For full details of keyspace creation visit [CLI keyspace](http://www.datastax.com/documentation/cassandra/2.0/cassandra/reference/referenceStorage_r.html)
|
21
|
-
|
22
|
-
```ruby
|
23
|
-
# < 1.0.0
|
24
|
-
Cassandro.create_keyspace('new_keyspace', 'SimpleStrategy', 1)
|
25
|
-
|
26
|
-
# >= 1.0.0, allow more options
|
27
|
-
Cassandro.create_keyspace('new_keyspace', {
|
28
|
-
replication: {
|
29
|
-
class: 'NetworkTopologyStrategy',
|
30
|
-
dc_1: 2,
|
31
|
-
dc_2: 2
|
32
|
-
},
|
33
|
-
durable_writes: true
|
34
|
-
})
|
35
|
-
```
|
36
|
-
|
37
|
-
Select keyspace outside `#connect`
|
38
|
-
|
39
|
-
```ruby
|
40
|
-
Cassandro.use('keyspace_name')
|
41
|
-
```
|
42
|
-
|
43
|
-
Create table.
|
44
|
-
```ruby
|
45
|
-
table = <<-TABLEDEF
|
46
|
-
CREATE TABLE IF NOT EXISTS users (
|
47
|
-
email VARCHAR,
|
48
|
-
first_name VARCHAR,
|
49
|
-
age INT,
|
50
|
-
created_at TIMESTAMP,
|
51
|
-
PRIMARY KEY(email,created_at)
|
52
|
-
)
|
53
|
-
TABLEDEF
|
54
|
-
|
55
|
-
Cassandro.execute(table)
|
56
|
-
```
|
57
|
-
|
58
|
-
Execute queries.
|
59
|
-
```ruby
|
60
|
-
result = Cassandro.execute("SELECT * FROM table_name;")
|
61
|
-
```
|
62
|
-
|
63
|
-
Using Driver directly.
|
64
|
-
```ruby
|
65
|
-
statement = Cassandro.client.prepare("SELECT * FROM table_name WHERE colname = ?;")
|
66
|
-
result = Cassandro.client.execute(statement, id)
|
67
|
-
```
|
68
|
-
|
69
|
-
## Cassandro::Model
|
70
|
-
|
71
|
-
### Creating model
|
72
|
-
Creating new model: make you class inherits form `Cassandro::Model`
|
73
|
-
|
74
|
-
```ruby
|
75
|
-
class User < Cassandro::Model
|
76
|
-
end
|
77
|
-
```
|
78
|
-
|
79
|
-
Specifying table name using the method `table(table_name)`:
|
80
|
-
|
81
|
-
```ruby
|
82
|
-
class User < Cassandro::Model
|
83
|
-
|
84
|
-
table 'users'
|
85
|
-
end
|
86
|
-
```
|
16
|
+
### v1.2
|
17
|
+
* TTL
|
18
|
+
* Model-wide TTL
|
19
|
+
* Single record TTL
|
20
|
+
* Support `:set` datatype
|
21
|
+
* Ignore columns not definied on model
|
87
22
|
|
88
|
-
|
23
|
+
## Example
|
89
24
|
|
90
25
|
```ruby
|
91
|
-
class
|
92
|
-
|
93
|
-
|
94
|
-
attribute :first_name, :text
|
95
|
-
attribute :age, :integer
|
96
|
-
attribute :created_at, :datetime
|
97
|
-
end
|
98
|
-
```
|
99
|
-
|
100
|
-
types: :uuid, :text, :integer, :float, :timestamp, :datetime
|
101
|
-
|
102
|
-
Setting the primary key using the method `primary_key(pk_name | Array)`:
|
103
|
-
|
104
|
-
```ruby
|
105
|
-
class User < Cassandro::Model
|
106
|
-
|
107
|
-
primary_key [:email, :created_at]
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
```
|
112
|
-
|
113
|
-
Setting unique field using the method `unique(field | Array)`:
|
114
|
-
|
115
|
-
```ruby
|
116
|
-
class User < Cassandro::Model
|
117
|
-
|
118
|
-
unique :email
|
119
|
-
end
|
120
|
-
```
|
121
|
-
|
122
|
-
|
123
|
-
### Setting TTL to a model
|
124
|
-
|
125
|
-
```Ruby
|
126
|
-
class Person < Cassandro::Model
|
127
|
-
table :people
|
128
|
-
ttl 60
|
129
|
-
end
|
130
|
-
```
|
131
|
-
This will make all the instances of the People class will have a Time To Live of `60` seconds in the database.
|
132
|
-
|
133
|
-
Creating a single record with a given TTL:
|
134
|
-
|
135
|
-
```Ruby
|
136
|
-
class Person < Cassandro::Model
|
137
|
-
table :people
|
138
|
-
attribute :first_name, :text
|
139
|
-
attribute :last_name, :text
|
140
|
-
end
|
141
|
-
|
142
|
-
Person.create_with_ttl(20, :first_name => "Eddie", :last_name => "Vedder")
|
143
|
-
```
|
144
|
-
|
145
|
-
This will create a record in the `people` table with a TTL of `20` seconds. It doesn't matter if the model has a different TTL set, this will override that TTL for _this record only_
|
146
|
-
|
147
|
-
|
148
|
-
__A complete example__
|
149
|
-
|
150
|
-
```ruby
|
151
|
-
class User < Cassandro::Model
|
152
|
-
|
153
|
-
table 'users'
|
154
|
-
|
26
|
+
class Developer < Cassandro::Model
|
27
|
+
table :developers
|
28
|
+
|
155
29
|
attribute :email, :text
|
156
|
-
attribute :
|
157
|
-
attribute :
|
158
|
-
attribute :created_at, :datetime
|
30
|
+
attribute :repos, :integer
|
31
|
+
attribute :nickname, :text
|
159
32
|
|
160
|
-
primary_key [:
|
161
|
-
|
162
|
-
|
163
|
-
end
|
164
|
-
```
|
165
|
-
|
166
|
-
### Interacting
|
167
|
-
|
168
|
-
Creating a new row:
|
169
|
-
|
170
|
-
```ruby
|
171
|
-
user = User.create(email: 'test1@example.com', first_name: 'Test', age: 30, created_at: DateTime.now)
|
172
|
-
=> #<User:0x00000001b9dc40
|
173
|
-
@attributes=
|
174
|
-
{:email=>"test1@example.com",
|
175
|
-
:first_name=>"Test",
|
176
|
-
:age=>30,
|
177
|
-
:created_at=>
|
178
|
-
#<DateTime: 2014-11-03T11:34:47-03:00 ((2456965j,52487s,201385585n),-10800s,2299161j)>},
|
179
|
-
@errors={},
|
180
|
-
@insert_statement=
|
181
|
-
#<Cassandra::Statements::Prepared:0xdcd4f8 @cql=" INSERT INTO users(email,first_name,age,created_at)\n VALUES(?,?,?,?)\n IF NOT EXISTS\n">,
|
182
|
-
@persisted=true>
|
183
|
-
|
184
|
-
```
|
185
|
-
|
186
|
-
Find:
|
187
|
-
|
188
|
-
```ruby
|
189
|
-
User[email: 'test1@example.com']
|
190
|
-
=> #<User:0x00000001cc59d8
|
191
|
-
@attributes=
|
192
|
-
{:email=>"test1@example.com",
|
193
|
-
:created_at=>2014-11-03 11:34:47 -0300,
|
194
|
-
:age=>30,
|
195
|
-
:first_name=>"Test"},
|
196
|
-
@errors={},
|
197
|
-
@persisted=true>
|
198
|
-
|
199
|
-
User.where('email','test1@example.com')
|
200
|
-
=> #<User:0x00000001cc59d8
|
201
|
-
@attributes=
|
202
|
-
{:email=>"test1@example.com",
|
203
|
-
:created_at=>2014-11-03 11:34:47 -0300,
|
204
|
-
:age=>30,
|
205
|
-
:first_name=>"Test"},
|
206
|
-
@errors={},
|
207
|
-
@persisted=true>
|
208
|
-
|
209
|
-
```
|
210
|
-
|
211
|
-
```ruby
|
212
|
-
User.all
|
213
|
-
=> [#<User:0x00000002bc75f8
|
214
|
-
@attributes=
|
215
|
-
{:email=>"test@example.com",
|
216
|
-
:created_at=>2014-11-03 11:30:52 -0300,
|
217
|
-
:age=>30,
|
218
|
-
:first_name=>"Test"},
|
219
|
-
@errors={},
|
220
|
-
@persisted=true>,
|
221
|
-
#<User:0x00000002bc6b30
|
222
|
-
@attributes=
|
223
|
-
{:email=>"test1@example.com",
|
224
|
-
:created_at=>2014-11-03 11:34:47 -0300,
|
225
|
-
:age=>30,
|
226
|
-
:first_name=>"Test"},
|
227
|
-
@errors={},
|
228
|
-
@persisted=true>]
|
229
|
-
```
|
230
|
-
|
231
|
-
```ruby
|
232
|
-
User.query('created_at > ?', Time.now.to_i)
|
233
|
-
=> #<Cassandra::Result:0x1fcb254 @rows=[{"email"=>"test@example.com", "created_at"=>2014-11-03 11:30:52 -0300, "age"=>30, "first_name"=>"Test"}, {"email"=>"test1@example.com", "created_at"=>2014-11-03 11:34:47 -0300, "age"=>30, "first_name"=>"Test"}] @last_page=true>
|
234
|
-
```
|
235
|
-
|
236
|
-
Count:
|
237
|
-
|
238
|
-
```ruby
|
239
|
-
User.count('email', 'test@example.com')
|
240
|
-
=> 1
|
241
|
-
```
|
242
|
-
|
243
|
-
Checking errors:
|
244
|
-
```ruby
|
245
|
-
user = User.create(email: 'test1@example.com', first_name: 'Test', age: 30, created_at: DateTime.now)
|
246
|
-
=> #<User:0x00000001dc7a48
|
247
|
-
@attributes=
|
248
|
-
{:email=>"test1@example.com",
|
249
|
-
:first_name=>"Test",
|
250
|
-
:age=>30,
|
251
|
-
:created_at=>
|
252
|
-
#<DateTime: 2014-11-03T11:36:40-03:00 ((2456965j,52600s,972972939n),-10800s,2299161j)>},
|
253
|
-
@errors={:unique=>"user_not_unique"},
|
254
|
-
@persisted=false>
|
255
|
-
|
256
|
-
user.persisted?
|
257
|
-
=> false
|
258
|
-
user.errors
|
259
|
-
=> {:unique=>"user_not_unique"}
|
260
|
-
|
261
|
-
```
|
262
|
-
|
263
|
-
## Migrations
|
264
|
-
|
265
|
-
Define your migrations by extending from `Cassandro::Migration`
|
266
|
-
|
267
|
-
```ruby
|
268
|
-
class UserMigration < Cassandro::Migration
|
269
|
-
version 1
|
270
|
-
|
271
|
-
def up
|
272
|
-
execute <<-TABLEDEF
|
273
|
-
CREATE TABLE users (
|
274
|
-
id UUID,
|
275
|
-
first_name VARCHAR,
|
276
|
-
last_name VARCHAR,
|
277
|
-
email VARCHAR,
|
278
|
-
PRIMARY KEY(id, email)
|
279
|
-
)
|
280
|
-
TABLEDEF
|
281
|
-
end
|
282
|
-
|
283
|
-
def down
|
284
|
-
execute <<-QUERY
|
285
|
-
DROP TABLE users;
|
286
|
-
QUERY
|
287
|
-
end
|
33
|
+
primary_key [:id, :repos]
|
34
|
+
|
35
|
+
index :nickname
|
288
36
|
end
|
289
37
|
|
290
|
-
|
291
|
-
|
292
|
-
version 2
|
38
|
+
Cassandro.connect(hosts: ['127.0.0.1'], keyspace: 'little_cassandro')
|
293
39
|
|
294
|
-
|
295
|
-
execute <<-TABLEUPDATE
|
296
|
-
ALTER TABLE users ADD gender VARCHAR
|
297
|
-
TABLEUPDATE
|
298
|
-
end
|
299
|
-
|
300
|
-
def down
|
301
|
-
execute <<-QUERY
|
302
|
-
ALTER TABLE users DROP gender
|
303
|
-
QUERY
|
304
|
-
end
|
305
|
-
end
|
40
|
+
Developer.create(email: 'developer@dev.com', repos: 10, nickname: 'cassandro')
|
306
41
|
```
|
307
42
|
|
308
|
-
|
309
|
-
|
310
|
-
```ruby
|
311
|
-
Cassandro.connect(hosts: ['127.0.0.1'], keyspace: 'some_keyspace')
|
43
|
+
## Documentation
|
312
44
|
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
migrator.migrate!(:up, 2) #migrates up to version 2
|
319
|
-
migrator.migrate!(:down, 1) #migrates down to version 1
|
320
|
-
```
|
45
|
+
* [Getting Started](docs/getting_started.md)
|
46
|
+
* [Migrations](docs/migrations.md)
|
47
|
+
* [Modeling](docs/modeling.md)
|
48
|
+
* [Querying](docs/querying.md)
|
49
|
+
* [Advanced Features](docs/advanced_features.md)
|
321
50
|
|
322
51
|
## TODO
|
323
52
|
|
324
|
-
*
|
325
|
-
* Better queries
|
326
|
-
* Better documentation
|
53
|
+
* Improve querying
|
327
54
|
|
328
55
|
## How to collaborate
|
329
56
|
|
data/cassandro.gemspec
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "cassandro"
|
5
|
-
s.version = "
|
5
|
+
s.version = "2.0.0"
|
6
6
|
s.summary = "Ruby ORM for Apache Cassandra"
|
7
7
|
s.license = "MIT"
|
8
8
|
s.description = "Lightweight Apache Cassandra ORM for Ruby"
|
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.email = ["orazile@gmail.com", "leonardomateo@gmail.com"]
|
11
11
|
s.homepage = "https://github.com/tarolandia/cassandro"
|
12
12
|
s.require_paths = ["lib"]
|
13
|
-
s.add_dependency "cassandra-driver", '~> 1.
|
13
|
+
s.add_dependency "cassandra-driver", '~> 2.1.3', '>= 2.0.0'
|
14
14
|
s.add_development_dependency "protest", '~> 0.5', '>= 0.5.3'
|
15
15
|
s.add_development_dependency "rack-test", '~> 0.6', '>= 0.6.3'
|
16
16
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
## TTL
|
2
|
+
|
3
|
+
### Setting TTL to a model
|
4
|
+
|
5
|
+
```Ruby
|
6
|
+
class Person < Cassandro::Model
|
7
|
+
table :people
|
8
|
+
ttl 60
|
9
|
+
end
|
10
|
+
```
|
11
|
+
This will make all the instances of the People class will have a Time To Live of `60` seconds in the database.
|
12
|
+
|
13
|
+
### Creating a single record with a given TTL:
|
14
|
+
|
15
|
+
```Ruby
|
16
|
+
class Person < Cassandro::Model
|
17
|
+
table :people
|
18
|
+
attribute :first_name, :text
|
19
|
+
attribute :last_name, :text
|
20
|
+
end
|
21
|
+
|
22
|
+
Person.create_with_ttl(20, :first_name => "Eddie", :last_name => "Vedder")
|
23
|
+
```
|
24
|
+
|
25
|
+
This will create a record in the `people` table with a TTL of `20` seconds. It doesn't matter if the model has a different TTL set, this will override that TTL for _this record only_
|
26
|
+
|
27
|
+
### Getting TTL
|
28
|
+
|
29
|
+
After creating a record with a TTL you can use method `Model#ttl` to get the value.
|
30
|
+
|
31
|
+
```Ruby
|
32
|
+
person = Person.create_with_ttl(20, :first_name => "John", :last_name => "Lennon")
|
33
|
+
|
34
|
+
person.ttl # => 20
|
35
|
+
|
36
|
+
# ...
|
37
|
+
|
38
|
+
person.ttl # => 18
|
39
|
+
```
|
40
|
+
|
41
|
+
### Enabling Soft Delete
|
42
|
+
|
43
|
+
|
44
|
+
```Ruby
|
45
|
+
class Person < Cassandro::Model
|
46
|
+
include Cassandro::SoftDelete
|
47
|
+
|
48
|
+
table :people
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
This will add an attribute `:delete` to your model. Next time you use `Model#destroy` your data will not be deleted from database but marked as deleted. You can then use `Model#restore` to unmark it.
|
53
|
+
|
54
|
+
Data marked as deleted is not included within `Model#all` method by default. In order to include deleted records you have to send a boolean parameter to `all`.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
Person.all # => list all but deleted
|
58
|
+
Person.all(true) # => list all, deleted included
|
59
|
+
```
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# Getting Started with Cassandro ORM
|
2
|
+
|
3
|
+
## Connection
|
4
|
+
|
5
|
+
Connecting to Cassandra DB: `Cassandro.connect(Hash options)`.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
# Connect
|
9
|
+
Cassandro.connect(hosts: ['192.168.2.100', '192.168.2.101'])
|
10
|
+
|
11
|
+
# Connect specifying keyspace
|
12
|
+
Cassandro.connect(hosts: ['192.168.2.100', '192.168.2.101'], keyspace: 'some_keyspace')
|
13
|
+
```
|
14
|
+
|
15
|
+
_For full list of options visit [Ruby Driver Documentation](http://datastax.github.io/ruby-driver/api/#cluster-class_method)_
|
16
|
+
|
17
|
+
## Keyspace
|
18
|
+
|
19
|
+
### Create Keyspace
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
Cassandro.create_keyspace('new_keyspace', {
|
23
|
+
replication: {
|
24
|
+
class: 'NetworkTopologyStrategy',
|
25
|
+
dc_1: 2,
|
26
|
+
dc_2: 2
|
27
|
+
},
|
28
|
+
durable_writes: true
|
29
|
+
})
|
30
|
+
```
|
31
|
+
|
32
|
+
_For full details of keyspace creation visit [CLI keyspace](http://www.datastax.com/documentation/cassandra/2.0/cassandra/reference/referenceStorage_r.html)_
|
33
|
+
|
34
|
+
|
35
|
+
### Select keyspace
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
Cassandro.use('keyspace_name')
|
39
|
+
```
|
40
|
+
|
41
|
+
## Execute
|
42
|
+
|
43
|
+
### Execute queries
|
44
|
+
```ruby
|
45
|
+
result = Cassandro.execute("SELECT * FROM table_name;")
|
46
|
+
```
|
47
|
+
### Create table
|
48
|
+
```ruby
|
49
|
+
table = <<-TABLEDEF
|
50
|
+
CREATE TABLE IF NOT EXISTS users (
|
51
|
+
email VARCHAR,
|
52
|
+
first_name VARCHAR,
|
53
|
+
age INT,
|
54
|
+
created_at TIMESTAMP,
|
55
|
+
PRIMARY KEY(email,created_at)
|
56
|
+
)
|
57
|
+
TABLEDEF
|
58
|
+
|
59
|
+
Cassandro.execute(table)
|
60
|
+
```
|
61
|
+
|
62
|
+
## Cassandra Client
|
63
|
+
|
64
|
+
Cassandro provides access to `cassandra-driver` instance through `Cassandro.client`
|
65
|
+
|
66
|
+
### Using Driver directly
|
67
|
+
```ruby
|
68
|
+
statement = Cassandro.client.prepare("SELECT * FROM table_name WHERE colname = ?;")
|
69
|
+
result = Cassandro.client.execute(statement, id)
|
70
|
+
```
|
data/docs/migrations.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
## Migrations
|
2
|
+
|
3
|
+
Define your migrations by extending from `Cassandro::Migration`
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class UserMigration < Cassandro::Migration
|
7
|
+
version 1
|
8
|
+
|
9
|
+
def up
|
10
|
+
execute <<-TABLEDEF
|
11
|
+
CREATE TABLE users (
|
12
|
+
id UUID,
|
13
|
+
first_name VARCHAR,
|
14
|
+
last_name VARCHAR,
|
15
|
+
email VARCHAR,
|
16
|
+
PRIMARY KEY(id, email)
|
17
|
+
)
|
18
|
+
TABLEDEF
|
19
|
+
end
|
20
|
+
|
21
|
+
def down
|
22
|
+
execute <<-QUERY
|
23
|
+
DROP TABLE users;
|
24
|
+
QUERY
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class UserGenderMigration < Cassandro::Migration
|
29
|
+
|
30
|
+
version 2
|
31
|
+
|
32
|
+
def up
|
33
|
+
execute <<-TABLEUPDATE
|
34
|
+
ALTER TABLE users ADD gender VARCHAR
|
35
|
+
TABLEUPDATE
|
36
|
+
end
|
37
|
+
|
38
|
+
def down
|
39
|
+
execute <<-QUERY
|
40
|
+
ALTER TABLE users DROP gender
|
41
|
+
QUERY
|
42
|
+
end
|
43
|
+
end
|
44
|
+
```
|
45
|
+
|
46
|
+
Then use `Cassandro::Migrator` to run your migrations
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
Cassandro.connect(hosts: ['127.0.0.1'], keyspace: 'some_keyspace')
|
50
|
+
|
51
|
+
migrator = Cassandro::Migrator.new('./path/to/migrations', Logger.new(STDOUT))
|
52
|
+
|
53
|
+
migrator.migrate!(:up) #migrates to last version
|
54
|
+
migrator.migrate!(:down) #apply all downgrades
|
55
|
+
|
56
|
+
migrator.migrate!(:up, 2) #migrates up to version 2
|
57
|
+
migrator.migrate!(:down, 1) #migrates down to version 1
|
58
|
+
```
|
59
|
+
|
60
|
+
|
data/docs/modeling.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
## Cassandro::Model
|
2
|
+
|
3
|
+
### Creating model
|
4
|
+
|
5
|
+
#### Creating new model
|
6
|
+
|
7
|
+
Make you class inherits form `Cassandro::Model`
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class User < Cassandro::Model
|
11
|
+
end
|
12
|
+
```
|
13
|
+
#### Table name
|
14
|
+
|
15
|
+
Specify table name using the method `table(table_name)`:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
class User < Cassandro::Model
|
19
|
+
|
20
|
+
table 'users'
|
21
|
+
end
|
22
|
+
```
|
23
|
+
|
24
|
+
#### Attributes
|
25
|
+
|
26
|
+
Add attributes using the method `attribute(name, type, options)`:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
class User < Cassandro::Model
|
30
|
+
|
31
|
+
attribute :email, :text
|
32
|
+
attribute :first_name, :text
|
33
|
+
attribute :age, :integer
|
34
|
+
attribute :created_at, :datetime
|
35
|
+
end
|
36
|
+
```
|
37
|
+
|
38
|
+
Types: `:uuid`, `:text`, `:integer`, `:float`, `:timestamp`, `:datetime`, `:set`
|
39
|
+
|
40
|
+
#### Primary Key
|
41
|
+
|
42
|
+
Set the primary key using the method `primary_key(pk_name | Array)`:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
class User < Cassandro::Model
|
46
|
+
|
47
|
+
primary_key [:email, :created_at]
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
```
|
52
|
+
|
53
|
+
#### Unique
|
54
|
+
|
55
|
+
Set unique fields using the method `unique(field | Array)`:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
class User < Cassandro::Model
|
59
|
+
|
60
|
+
unique :email
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
#### Index
|
65
|
+
|
66
|
+
Set indexes using the method `index(field | Array)`. Note registering indexes in your model is only a refence.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
class User < Cassandro::Model
|
70
|
+
|
71
|
+
index :age
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
#### A complete example
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class User < Cassandro::Model
|
79
|
+
|
80
|
+
table 'users'
|
81
|
+
|
82
|
+
attribute :email, :text
|
83
|
+
attribute :first_name, :text
|
84
|
+
attribute :age, :integer
|
85
|
+
attribute :created_at, :datetime
|
86
|
+
|
87
|
+
primary_key [:email, :created_at]
|
88
|
+
|
89
|
+
unique :email
|
90
|
+
|
91
|
+
index :age
|
92
|
+
end
|
93
|
+
```
|
data/docs/querying.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
## Querying
|
2
|
+
|
3
|
+
```ruby
|
4
|
+
class User < Cassandro::Model
|
5
|
+
table :users
|
6
|
+
|
7
|
+
attribute :email, :text
|
8
|
+
attribute :first_name, :text
|
9
|
+
attribute :age, :integer
|
10
|
+
attribute :created_at, :datetime
|
11
|
+
|
12
|
+
primary_key :email
|
13
|
+
|
14
|
+
index :first_name
|
15
|
+
end
|
16
|
+
```
|
17
|
+
|
18
|
+
### Creating a new row:
|
19
|
+
|
20
|
+
```ruby
|
21
|
+
user = User.create(email: 'test1@example.com', first_name: 'Test', age: 30, created_at: DateTime.now)
|
22
|
+
=> #<User:0x00000001b9dc40 ... @persisted=true>
|
23
|
+
|
24
|
+
```
|
25
|
+
|
26
|
+
### Updating attributes
|
27
|
+
|
28
|
+
#### Using `#save`
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
user.age = 31
|
32
|
+
user.save
|
33
|
+
=> true
|
34
|
+
```
|
35
|
+
|
36
|
+
#### Using `#update_attributes`
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
user.update_attributes(first_name: 'Test 1', age: 31)
|
40
|
+
=> true
|
41
|
+
```
|
42
|
+
|
43
|
+
### Selecting records
|
44
|
+
|
45
|
+
#### Find
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
User[email: 'test1@example.com']
|
49
|
+
=> #<User:0x00000001cc59d8 ... @persisted=true>
|
50
|
+
```
|
51
|
+
```ruby
|
52
|
+
User[email: 'test1@example.com', first_name: 'No Test']
|
53
|
+
=> nil
|
54
|
+
```
|
55
|
+
|
56
|
+
#### Find all
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
User.all
|
60
|
+
=> [#<User:0x00000001cc59d8 ... @persisted=true>, #<User:0x00000001cc59d9 ... @persisted=true>, ...]
|
61
|
+
```
|
62
|
+
#### Where / Query
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
User.where('first_name','Test 1')
|
66
|
+
=> [#<User:0x00000001cc59d8 ... @persisted=true>]
|
67
|
+
```
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
User.query('first_name = ?', 'Test')
|
71
|
+
=> [#<User:0x00000001cc59d8 ... @persisted=true>, ...]
|
72
|
+
```
|
73
|
+
|
74
|
+
#### Count:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
User.count('email', 'test@example.com')
|
78
|
+
=> 1
|
79
|
+
|
80
|
+
User.count
|
81
|
+
=> 2
|
82
|
+
```
|
83
|
+
|
84
|
+
### Deleting
|
85
|
+
|
86
|
+
#### Destroy
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
user = User[email: 'test1@example.com']
|
90
|
+
user.destroy
|
91
|
+
```
|
92
|
+
|
93
|
+
#### Destroy All (truncate table)
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
User.destroy_all
|
97
|
+
```
|
98
|
+
|
99
|
+
### Checking errors:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
user = User.create(email: 'test1@example.com', first_name: 'Test', age: 30, created_at: DateTime.now)
|
103
|
+
=> #<User:0x00000001dc7a48
|
104
|
+
@attributes=
|
105
|
+
{:email=>"test1@example.com",
|
106
|
+
:first_name=>"Test",
|
107
|
+
:age=>30,
|
108
|
+
:created_at=>
|
109
|
+
#<DateTime: 2014-11-03T11:36:40-03:00 ((2456965j,52600s,972972939n),-10800s,2299161j)>},
|
110
|
+
@errors={:unique=>"user_not_unique"},
|
111
|
+
@persisted=false>
|
112
|
+
|
113
|
+
user.persisted?
|
114
|
+
=> false
|
115
|
+
user.errors
|
116
|
+
=> {:unique=>"user_not_unique"}
|
117
|
+
|
118
|
+
```
|
119
|
+
|
120
|
+
|
data/lib/cassandro/core.rb
CHANGED
data/lib/cassandro/model.rb
CHANGED
@@ -70,7 +70,7 @@ module Cassandro
|
|
70
70
|
|
71
71
|
begin
|
72
72
|
st = Cassandro.client.prepare(query)
|
73
|
-
Cassandro.client.execute(st,
|
73
|
+
Cassandro.client.execute(st, arguments: native_attributes(attrs))
|
74
74
|
@attributes.merge!(attrs)
|
75
75
|
true
|
76
76
|
rescue Exception => e
|
@@ -101,7 +101,7 @@ module Cassandro
|
|
101
101
|
st = self.statement_for(:insert, :insert_check => insert_check)
|
102
102
|
|
103
103
|
begin
|
104
|
-
r = Cassandro.client.execute(st,
|
104
|
+
r = Cassandro.client.execute(st, arguments: self.native_attributes)
|
105
105
|
raise ModelException.new('not_applied') unless !insert_check || (insert_check && r.first["[applied]"])
|
106
106
|
@persisted = true
|
107
107
|
rescue => e
|
@@ -119,11 +119,21 @@ module Cassandro
|
|
119
119
|
Cassandro.execute(query)
|
120
120
|
end
|
121
121
|
|
122
|
+
def ttl
|
123
|
+
query = <<-QUERY
|
124
|
+
SELECT TTL(#{@attributes.keys.last}) FROM #{self.class.table_name}
|
125
|
+
WHERE #{self.class.pk.flatten.map { |k| "#{k.to_s} = #{self.class.cast_as(k, @attributes[k])}" }.join(' AND ')}
|
126
|
+
QUERY
|
127
|
+
result = Cassandro.execute(query)
|
128
|
+
result.any? ? result.first.values.first : nil
|
129
|
+
end
|
130
|
+
|
122
131
|
def self.table(name)
|
123
132
|
self.table_name = name.to_s
|
124
133
|
end
|
125
134
|
|
126
135
|
def self.attribute(name, type = String, options = {})
|
136
|
+
return if attributes.include?(name)
|
127
137
|
attributes << name
|
128
138
|
casts[name] = type
|
129
139
|
|
@@ -138,17 +148,25 @@ module Cassandro
|
|
138
148
|
|
139
149
|
def self.primary_key(keys)
|
140
150
|
if keys.is_a?(Array)
|
141
|
-
pk.
|
151
|
+
pk.merge(keys)
|
142
152
|
else
|
143
|
-
pk
|
153
|
+
pk.add(keys)
|
144
154
|
end
|
145
155
|
end
|
146
156
|
|
147
157
|
def self.unique(keys)
|
148
158
|
if keys.is_a?(Array)
|
149
|
-
uniques.
|
159
|
+
uniques.merge(keys)
|
150
160
|
else
|
151
|
-
uniques
|
161
|
+
uniques.add(keys)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.index(keys)
|
166
|
+
if keys.is_a?(Array)
|
167
|
+
indexes.merge(keys)
|
168
|
+
else
|
169
|
+
indexes.add(keys)
|
152
170
|
end
|
153
171
|
end
|
154
172
|
|
@@ -170,7 +188,7 @@ module Cassandro
|
|
170
188
|
QUERY
|
171
189
|
|
172
190
|
st = Cassandro.client.prepare(query)
|
173
|
-
result = Cassandro.client.execute(st,
|
191
|
+
result = Cassandro.client.execute(st, arguments: values)
|
174
192
|
|
175
193
|
return nil unless result.any?
|
176
194
|
|
@@ -184,6 +202,16 @@ module Cassandro
|
|
184
202
|
model
|
185
203
|
end
|
186
204
|
|
205
|
+
def self.create_with_ttl(seconds, attrs = {})
|
206
|
+
old_ttl = self.options[:ttl]
|
207
|
+
self.options[:ttl] = seconds.to_i
|
208
|
+
|
209
|
+
result = self.create(attrs)
|
210
|
+
|
211
|
+
old_ttl ? self.options[:ttl] = old_ttl : self.options.delete(:ttl)
|
212
|
+
result
|
213
|
+
end
|
214
|
+
|
187
215
|
def self.all
|
188
216
|
query = "SELECT * FROM #{self.table_name}"
|
189
217
|
|
@@ -202,7 +230,7 @@ module Cassandro
|
|
202
230
|
query = "SELECT * FROM #{table_name} WHERE #{key} = ? ALLOW FILTERING"
|
203
231
|
|
204
232
|
st = Cassandro.client.prepare(query)
|
205
|
-
rows = Cassandro.client.execute(st, value)
|
233
|
+
rows = Cassandro.client.execute(st, arguments: [value])
|
206
234
|
|
207
235
|
rows.each do |result|
|
208
236
|
results << new(result, true)
|
@@ -218,7 +246,7 @@ module Cassandro
|
|
218
246
|
key = key.to_sym
|
219
247
|
query << " WHERE #{key} = ? ALLOW FILTERING"
|
220
248
|
st = Cassandro.client.prepare(query)
|
221
|
-
results = Cassandro.client.execute(st, value)
|
249
|
+
results = Cassandro.client.execute(st, arguments: [value])
|
222
250
|
else
|
223
251
|
results = Cassandro.client.execute(query)
|
224
252
|
end
|
@@ -237,9 +265,18 @@ module Cassandro
|
|
237
265
|
end
|
238
266
|
|
239
267
|
def self.query(where, *values)
|
268
|
+
results = []
|
269
|
+
|
240
270
|
query = "SELECT * FROM #{table_name} WHERE #{where} ALLOW FILTERING"
|
241
271
|
st = Cassandro.client.prepare(query)
|
242
|
-
Cassandro.client.execute(st,
|
272
|
+
rows = Cassandro.client.execute(st, arguments: values)
|
273
|
+
|
274
|
+
|
275
|
+
rows.each do |result|
|
276
|
+
results << new(result, true)
|
277
|
+
end
|
278
|
+
|
279
|
+
results
|
243
280
|
end
|
244
281
|
|
245
282
|
protected
|
@@ -248,7 +285,7 @@ module Cassandro
|
|
248
285
|
end
|
249
286
|
|
250
287
|
def self.pk
|
251
|
-
@pk ||=
|
288
|
+
@pk ||= Set.new
|
252
289
|
end
|
253
290
|
|
254
291
|
def self.table_name
|
@@ -264,7 +301,11 @@ module Cassandro
|
|
264
301
|
end
|
265
302
|
|
266
303
|
def self.uniques
|
267
|
-
@unique ||=
|
304
|
+
@unique ||= Set.new
|
305
|
+
end
|
306
|
+
|
307
|
+
def self.indexes
|
308
|
+
@indexes ||= Set.new
|
268
309
|
end
|
269
310
|
|
270
311
|
def self.uniqueness_defined?
|
@@ -298,16 +339,6 @@ module Cassandro
|
|
298
339
|
self.options[:ttl] = seconds.to_i
|
299
340
|
end
|
300
341
|
|
301
|
-
def self.create_with_ttl(seconds, attrs = {})
|
302
|
-
old_ttl = self.options[:ttl]
|
303
|
-
self.options[:ttl] = seconds.to_i
|
304
|
-
|
305
|
-
result = self.create(attrs)
|
306
|
-
|
307
|
-
old_ttl ? self.options[:ttl] = old_ttl : self.options.delete(:ttl)
|
308
|
-
result
|
309
|
-
end
|
310
|
-
|
311
342
|
def statement_for(operation, options = {})
|
312
343
|
case operation
|
313
344
|
when :insert
|
@@ -44,6 +44,28 @@ Protest.describe "Cassandro Model" do
|
|
44
44
|
assert Test.uniqueness_defined?
|
45
45
|
end
|
46
46
|
|
47
|
+
test "adds index" do
|
48
|
+
class Test < Cassandro::Model
|
49
|
+
index :test_col_2
|
50
|
+
end
|
51
|
+
|
52
|
+
assert Test.indexes.include?(:test_col_2)
|
53
|
+
end
|
54
|
+
|
55
|
+
test "adds multiple indexes" do
|
56
|
+
class Test < Cassandro::Model
|
57
|
+
attribute :test_col_3, :text
|
58
|
+
attribute :test_col_4, :text
|
59
|
+
attribute :test_col_5, :text
|
60
|
+
|
61
|
+
index :test_col_3
|
62
|
+
index [:test_col_4, :test_col_5]
|
63
|
+
end
|
64
|
+
assert Test.indexes.include?(:test_col_3)
|
65
|
+
assert Test.indexes.include?(:test_col_4)
|
66
|
+
assert Test.indexes.include?(:test_col_5)
|
67
|
+
end
|
68
|
+
|
47
69
|
test "allows setting and getting attributes" do
|
48
70
|
uuid = SecureRandom.uuid
|
49
71
|
test = Test.new(test_col_1: uuid, test_col_2: 'test_value_2')
|
@@ -159,7 +181,7 @@ Protest.describe "Cassandro Model" do
|
|
159
181
|
Test.create(test_col_1: uuid, test_col_2: 'test_value_2')
|
160
182
|
|
161
183
|
tests = Test[uuid]
|
162
|
-
assert_equal false, tests.respond_to?("
|
184
|
+
assert_equal false, tests.respond_to?("test_col_ignored")
|
163
185
|
end
|
164
186
|
end
|
165
187
|
|
@@ -247,5 +269,11 @@ Protest.describe "Cassandro Model" do
|
|
247
269
|
assert results.first["ttl(address)"] > 0
|
248
270
|
assert results.first["ttl(address)"] <= 30
|
249
271
|
end
|
272
|
+
|
273
|
+
test "should get TTL" do
|
274
|
+
patient = Patient.create_with_ttl(20, :name => "Cassandro Get", :address => "cassandstreet", :age => 1)
|
275
|
+
assert patient.ttl > 0
|
276
|
+
assert patient.ttl <= 20
|
277
|
+
end
|
250
278
|
end
|
251
279
|
end
|
data/test/support/tables.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cassandro
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lautaro Orazi
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-
|
12
|
+
date: 2015-04-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: cassandra-driver
|
@@ -17,20 +17,20 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version:
|
20
|
+
version: 2.1.3
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version:
|
23
|
+
version: 2.0.0
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
27
27
|
requirements:
|
28
28
|
- - "~>"
|
29
29
|
- !ruby/object:Gem::Version
|
30
|
-
version:
|
30
|
+
version: 2.1.3
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 2.0.0
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: protest
|
36
36
|
requirement: !ruby/object:Gem::Requirement
|
@@ -82,9 +82,14 @@ files:
|
|
82
82
|
- ".gems"
|
83
83
|
- ".gems-test"
|
84
84
|
- ".gitignore"
|
85
|
-
- LICENSE
|
85
|
+
- LICENSE.md
|
86
86
|
- README.md
|
87
87
|
- cassandro.gemspec
|
88
|
+
- docs/advanced_features.md
|
89
|
+
- docs/getting_started.md
|
90
|
+
- docs/migrations.md
|
91
|
+
- docs/modeling.md
|
92
|
+
- docs/querying.md
|
88
93
|
- lib/cassandro.rb
|
89
94
|
- lib/cassandro/core.rb
|
90
95
|
- lib/cassandro/ext/migration.rb
|