rom-cassandra 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.metrics +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +26 -0
- data/.yardopts +3 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +7 -0
- data/Guardfile +14 -0
- data/LICENSE +21 -0
- data/README.md +83 -0
- data/Rakefile +34 -0
- data/config/metrics/STYLEGUIDE +230 -0
- data/config/metrics/cane.yml +5 -0
- data/config/metrics/churn.yml +6 -0
- data/config/metrics/flay.yml +2 -0
- data/config/metrics/metric_fu.yml +14 -0
- data/config/metrics/reek.yml +1 -0
- data/config/metrics/roodi.yml +24 -0
- data/config/metrics/rubocop.yml +84 -0
- data/config/metrics/saikuro.yml +3 -0
- data/config/metrics/simplecov.yml +6 -0
- data/config/metrics/yardstick.yml +37 -0
- data/lib/rom-cassandra.rb +3 -0
- data/lib/rom/cassandra.rb +33 -0
- data/lib/rom/cassandra/commands.rb +53 -0
- data/lib/rom/cassandra/commands/batch.rb +54 -0
- data/lib/rom/cassandra/commands/create.rb +38 -0
- data/lib/rom/cassandra/commands/delete.rb +38 -0
- data/lib/rom/cassandra/commands/update.rb +38 -0
- data/lib/rom/cassandra/dataset.rb +102 -0
- data/lib/rom/cassandra/gateway.rb +115 -0
- data/lib/rom/cassandra/migrations.rb +30 -0
- data/lib/rom/cassandra/migrations/generator.rb +68 -0
- data/lib/rom/cassandra/migrations/generator/migration.erb +32 -0
- data/lib/rom/cassandra/migrations/logger.rb +28 -0
- data/lib/rom/cassandra/migrations/migration.rb +107 -0
- data/lib/rom/cassandra/migrations/migrator.rb +103 -0
- data/lib/rom/cassandra/migrations/runner.rb +119 -0
- data/lib/rom/cassandra/migrations/runner_down.rb +49 -0
- data/lib/rom/cassandra/migrations/runner_up.rb +50 -0
- data/lib/rom/cassandra/query.rb +43 -0
- data/lib/rom/cassandra/relation.rb +88 -0
- data/lib/rom/cassandra/session.rb +50 -0
- data/lib/rom/cassandra/tasks.rb +6 -0
- data/lib/rom/cassandra/version.rb +15 -0
- data/lib/tasks/db.rake +16 -0
- data/rom-cassandra.gemspec +33 -0
- data/spec/config/reset_cluster.rb +28 -0
- data/spec/config/rom.rb +3 -0
- data/spec/config/test_module.rb +7 -0
- data/spec/integration/batch_spec.rb +36 -0
- data/spec/integration/create_spec.rb +33 -0
- data/spec/integration/delete_spec.rb +33 -0
- data/spec/integration/migrate/20150825142003_create_users.rb +24 -0
- data/spec/integration/migrate/20150825142024_create_logs.rb +17 -0
- data/spec/integration/migrate_spec.rb +47 -0
- data/spec/integration/relation_spec.rb +27 -0
- data/spec/integration/update_spec.rb +33 -0
- data/spec/shared/fake_migrate_folder.rb +21 -0
- data/spec/shared/users.rb +20 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/unit/commands/batch_spec.rb +86 -0
- data/spec/unit/commands/create_spec.rb +77 -0
- data/spec/unit/commands/delete_spec.rb +77 -0
- data/spec/unit/commands/update_spec.rb +77 -0
- data/spec/unit/dataset_spec.rb +130 -0
- data/spec/unit/gateway_spec.rb +140 -0
- data/spec/unit/migrations/generator_spec.rb +31 -0
- data/spec/unit/migrations/logger_spec.rb +21 -0
- data/spec/unit/migrations/migration_spec.rb +59 -0
- data/spec/unit/migrations/migrator_spec.rb +120 -0
- data/spec/unit/migrations/runner_down_spec.rb +65 -0
- data/spec/unit/migrations/runner_spec.rb +142 -0
- data/spec/unit/migrations/runner_up_spec.rb +67 -0
- data/spec/unit/query_spec.rb +21 -0
- data/spec/unit/relation_spec.rb +142 -0
- data/spec/unit/session_spec.rb +55 -0
- data/spec/unit/tasks/create_migration_spec.rb +41 -0
- metadata +242 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "cassandra"
|
4
|
+
|
5
|
+
module ROM::Cassandra
|
6
|
+
|
7
|
+
# Wraps the external driver, responsible for sending CQL requests
|
8
|
+
# to the Cassandra cluster
|
9
|
+
#
|
10
|
+
class Session
|
11
|
+
|
12
|
+
# The regexp, describing the format of the allowed address of the cluster
|
13
|
+
FORMAT = /\d{1,3}(\.\d{1,3}){3}(\:\d+)?/
|
14
|
+
|
15
|
+
# @!attribute [r] uri
|
16
|
+
#
|
17
|
+
# @return [Hash] the settings for the session
|
18
|
+
#
|
19
|
+
attr_reader :uri
|
20
|
+
|
21
|
+
# Initializes a session to given cluster
|
22
|
+
#
|
23
|
+
# @param [Hash] options
|
24
|
+
#
|
25
|
+
def initialize(*options)
|
26
|
+
@uri = extract(*options)
|
27
|
+
@conn = ::Cassandra.cluster(uri).connect
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sends the query to the Cassandra syncronously
|
31
|
+
#
|
32
|
+
# @param [#to_s] query
|
33
|
+
#
|
34
|
+
# @return [Array<Hash>]
|
35
|
+
#
|
36
|
+
def call(query)
|
37
|
+
@conn.execute(query.to_s).to_a
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def extract(uri = { hosts: ["127.0.0.1"], port: 9042 }, hash = {})
|
43
|
+
return uri if uri.instance_of? Hash
|
44
|
+
hosts, port = uri[FORMAT].split(":")
|
45
|
+
{ hosts: [hosts], port: port.to_i }.merge hash
|
46
|
+
end
|
47
|
+
|
48
|
+
end # class Session
|
49
|
+
|
50
|
+
end # module ROM::Cassandra
|
data/lib/tasks/db.rake
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
namespace :db do
|
4
|
+
desc "Create a migration (required parameter: NAME)"
|
5
|
+
task :create_migration, [:name, :path] do |_, args|
|
6
|
+
unless (name = args[:name])
|
7
|
+
puts "No NAME specified.\n" \
|
8
|
+
"Usage example: `rake db:create_migration[create_users]`"
|
9
|
+
exit
|
10
|
+
end
|
11
|
+
path = args[:path] || "db/migrate"
|
12
|
+
|
13
|
+
result = ROM::Cassandra::Migrations::Generator.call(name, path)
|
14
|
+
puts "<= Created #{result}"
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "rom/cassandra/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
|
8
|
+
gem.name = "rom-cassandra"
|
9
|
+
gem.version = ROM::Cassandra::VERSION.dup
|
10
|
+
gem.author = ["Andrew Kozin"]
|
11
|
+
gem.email = ["andrew.kozin@gmail.com"]
|
12
|
+
gem.summary = "Cassandra support for Ruby Object Mapper"
|
13
|
+
gem.description = gem.summary
|
14
|
+
gem.homepage = "https://rom-rb.org"
|
15
|
+
gem.license = "MIT"
|
16
|
+
|
17
|
+
gem.files = `git ls-files -z`.split("\x0")
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
19
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
|
+
gem.extra_rdoc_files = Dir["README.md", "LICENSE"]
|
21
|
+
gem.require_paths = ["lib"]
|
22
|
+
|
23
|
+
gem.required_ruby_version = ">= 1.9.3"
|
24
|
+
|
25
|
+
gem.add_runtime_dependency "cassandra-driver", "~> 2.1"
|
26
|
+
gem.add_runtime_dependency "inflecto", "~> 0.0"
|
27
|
+
gem.add_runtime_dependency "query_builder", "~> 0.0"
|
28
|
+
gem.add_runtime_dependency "rom", "~> 0.9.1"
|
29
|
+
|
30
|
+
gem.add_development_dependency "hexx-rspec", "~> 0.5"
|
31
|
+
gem.add_development_dependency "timecop", "~> 0.8"
|
32
|
+
|
33
|
+
end # Gem::Specification
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "cassandra"
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
|
7
|
+
# Prepares table auth.users using official Datastax ruby driver for Cassandra
|
8
|
+
#
|
9
|
+
# Populates table records [{id: 1, name: "joe"}, {id: 2, name: "jane"}]
|
10
|
+
#
|
11
|
+
def reset_cluster
|
12
|
+
[
|
13
|
+
"DROP KEYSPACE IF EXISTS auth;",
|
14
|
+
"CREATE KEYSPACE auth WITH " \
|
15
|
+
"replication = {'class': 'SimpleStrategy', 'replication_factor': 1};",
|
16
|
+
"CREATE TABLE auth.users (id int, name text, PRIMARY KEY (id));",
|
17
|
+
"INSERT INTO auth.users (id, name) VALUES (1, 'joe');",
|
18
|
+
"INSERT INTO auth.users (id, name) VALUES (2, 'jane');"
|
19
|
+
].each(&::Cassandra.cluster.connect.method(:execute))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Prepares the cluster before the suit
|
23
|
+
reset_cluster
|
24
|
+
|
25
|
+
# Recreates cluster after every example, marked by :reset_cluster tag
|
26
|
+
config.after(:example, :reset_cluster) { reset_cluster }
|
27
|
+
|
28
|
+
end # RSpec.configure
|
data/spec/config/rom.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "shared/users"
|
4
|
+
|
5
|
+
module ROM::Cassandra::Test # the namespace for newly created classes
|
6
|
+
|
7
|
+
describe "command 'batch'", :reset_cluster do
|
8
|
+
|
9
|
+
include_context :users
|
10
|
+
|
11
|
+
before do
|
12
|
+
class Batch < ROM::Cassandra::Commands::Batch
|
13
|
+
relation :users
|
14
|
+
register_as :batch
|
15
|
+
|
16
|
+
def execute
|
17
|
+
super do
|
18
|
+
add("DELETE FROM auth.users WHERE id = 1;")
|
19
|
+
.add(keyspace(:auth).table(:users).insert(id: 3, name: "frank"))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
subject { rom.command(:users).batch }
|
26
|
+
|
27
|
+
it "works" do
|
28
|
+
expect { subject.call }
|
29
|
+
.to change { select.to_a }
|
30
|
+
.from([{ "id" => 1, "name" => "joe" }, { "id" => 2, "name" => "jane" }])
|
31
|
+
.to [{ "id" => 2, "name" => "jane" }, { "id" => 3, "name" => "frank" }]
|
32
|
+
end
|
33
|
+
|
34
|
+
end # describe command 'batch'
|
35
|
+
|
36
|
+
end # module ROM::Cassandra::Commands::Batch
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "shared/users"
|
4
|
+
|
5
|
+
module ROM::Cassandra::Test # the namespace for newly created classes
|
6
|
+
|
7
|
+
describe "command 'create'", :reset_cluster do
|
8
|
+
|
9
|
+
include_context :users
|
10
|
+
|
11
|
+
before do
|
12
|
+
class CreateUser < ROM::Commands::Create[:cassandra]
|
13
|
+
relation :users
|
14
|
+
register_as :create
|
15
|
+
|
16
|
+
def execute(id, name)
|
17
|
+
super { insert(id: id, name: name) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
subject { rom.command(:users).create }
|
23
|
+
|
24
|
+
it "works" do
|
25
|
+
expect { subject.call(3, "jill") }
|
26
|
+
.to change { select.by_id(3).to_a }
|
27
|
+
.from([])
|
28
|
+
.to [{ "id" => 3, "name" => "jill" }]
|
29
|
+
end
|
30
|
+
|
31
|
+
end # describe command 'create'
|
32
|
+
|
33
|
+
end # module ROM::Cassandra::Test
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "shared/users"
|
4
|
+
|
5
|
+
module ROM::Cassandra::Test # the namespace for newly created classes
|
6
|
+
|
7
|
+
describe "command 'delete'", :reset_cluster do
|
8
|
+
|
9
|
+
include_context :users
|
10
|
+
|
11
|
+
before do
|
12
|
+
class DeleteUser < ROM::Commands::Delete[:cassandra]
|
13
|
+
relation :users
|
14
|
+
register_as :delete
|
15
|
+
|
16
|
+
def execute(id)
|
17
|
+
super { where(id: 1) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
subject { rom.command(:users).delete }
|
23
|
+
|
24
|
+
it "works" do
|
25
|
+
expect { subject.call(1) }
|
26
|
+
.to change { select.by_id(1).to_a }
|
27
|
+
.from([{ "id" => 1, "name" => "joe" }])
|
28
|
+
.to []
|
29
|
+
end
|
30
|
+
|
31
|
+
end # describe command 'delete'
|
32
|
+
|
33
|
+
end # module ROM::Cassandra::Test
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class CreateUsers < ROM::Cassandra::Migrations::Migration
|
4
|
+
def up
|
5
|
+
replication = { class: :SimpleStrategy, replication_factor: 1 }
|
6
|
+
|
7
|
+
call keyspace(:logs)
|
8
|
+
.create
|
9
|
+
.if_not_exists
|
10
|
+
.with(replication: replication)
|
11
|
+
|
12
|
+
call keyspace(:logs)
|
13
|
+
.table(:users)
|
14
|
+
.create
|
15
|
+
.if_not_exists
|
16
|
+
.add(:id, :int)
|
17
|
+
.add(:name, :text)
|
18
|
+
.primary_key(:id)
|
19
|
+
end
|
20
|
+
|
21
|
+
def down
|
22
|
+
call keyspace(:logs).drop.if_exists
|
23
|
+
end
|
24
|
+
end # class CreateUsers
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class CreateLogs < ROM::Cassandra::Migrations::Migration
|
4
|
+
def up
|
5
|
+
call keyspace(:logs)
|
6
|
+
.table(:logs)
|
7
|
+
.create
|
8
|
+
.add(:id, :int)
|
9
|
+
.add(:name, :text)
|
10
|
+
.primary_key(:id)
|
11
|
+
.if_not_exists
|
12
|
+
end
|
13
|
+
|
14
|
+
def down
|
15
|
+
call "DROP TABLE IF EXISTS logs.logs;"
|
16
|
+
end
|
17
|
+
end # class CreateUsers
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe "migrate" do
|
4
|
+
|
5
|
+
# The helper function to check the db content
|
6
|
+
def check(query)
|
7
|
+
Cassandra.cluster.connect.execute query
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:stdout) { StringIO.new }
|
11
|
+
let(:logger) { Logger.new stdout }
|
12
|
+
let(:path) { File.expand_path("../migrate", __FILE__) }
|
13
|
+
let(:gateway) { ROM.finalize.env.gateways[:default] }
|
14
|
+
|
15
|
+
it "works" do
|
16
|
+
ROM.setup(:cassandra, "127.0.0.1:9042")
|
17
|
+
|
18
|
+
expect { check "SELECT * FROM logs.users;" }.to raise_error StandardError
|
19
|
+
expect { check "SELECT * FROM logs.logs" }.to raise_error StandardError
|
20
|
+
expect(stdout.string).to be_empty
|
21
|
+
|
22
|
+
gateway.migrate logger: logger, path: path, version: 20150825142003
|
23
|
+
|
24
|
+
expect { check "SELECT * FROM logs.users;" }.not_to raise_error
|
25
|
+
expect { check "SELECT * FROM logs.logs" }.to raise_error StandardError
|
26
|
+
expect(stdout.string).to include "Apply migration 20150825142003"
|
27
|
+
expect(stdout.string).not_to include "Apply migration 20150825142024"
|
28
|
+
|
29
|
+
gateway.migrate logger: logger, path: path
|
30
|
+
|
31
|
+
expect { check "SELECT * FROM logs.users;" }.not_to raise_error
|
32
|
+
expect { check "SELECT * FROM logs.logs" }.not_to raise_error
|
33
|
+
expect(stdout.string).to include "Apply migration 20150825142024"
|
34
|
+
expect(stdout.string).not_to include "Roll back"
|
35
|
+
|
36
|
+
gateway.migrate logger: logger, path: path, version: 0
|
37
|
+
|
38
|
+
expect { check "SELECT * FROM logs.users;" }.to raise_error StandardError
|
39
|
+
expect { check "SELECT * FROM logs.logs" }.to raise_error StandardError
|
40
|
+
expect(stdout.string).to include "Roll back migration 20150825142024"
|
41
|
+
expect(stdout.string).to include "Roll back migration 20150825142003"
|
42
|
+
end
|
43
|
+
|
44
|
+
after { check "DROP KEYSPACE IF EXISTS rom;" }
|
45
|
+
after { check "DROP KEYSPACE IF EXISTS logs;" }
|
46
|
+
|
47
|
+
end # describe migrate
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "shared/users"
|
4
|
+
|
5
|
+
module ROM::Cassandra::Test # the namespace for newly created classes
|
6
|
+
|
7
|
+
describe "relation" do
|
8
|
+
|
9
|
+
include_context :users
|
10
|
+
|
11
|
+
let(:subject) { rom.relation(:users) }
|
12
|
+
|
13
|
+
it "works" do
|
14
|
+
expect(subject.to_a).to eql [
|
15
|
+
{ "id" => 1, "name" => "joe" },
|
16
|
+
{ "id" => 2, "name" => "jane" }
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "works with modifiers" do
|
21
|
+
expect(subject.by_id(1).to_a)
|
22
|
+
.to eql [{ "id" => 1, "name" => "joe" }]
|
23
|
+
end
|
24
|
+
|
25
|
+
end # describe relation
|
26
|
+
|
27
|
+
end # module ROM::Cassandra::Test
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "shared/users"
|
4
|
+
|
5
|
+
module ROM::Cassandra::Test # the namespace for newly created classes
|
6
|
+
|
7
|
+
describe "command 'update'", :reset_cluster do
|
8
|
+
|
9
|
+
include_context :users
|
10
|
+
|
11
|
+
before do
|
12
|
+
class UpdateUser < ROM::Commands::Update[:cassandra]
|
13
|
+
relation :users
|
14
|
+
register_as :update
|
15
|
+
|
16
|
+
def execute(id, name)
|
17
|
+
super { update(name: name).where(id: 1) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
subject { rom.command(:users).update }
|
23
|
+
|
24
|
+
it "works" do
|
25
|
+
expect { subject.call(1, "frank") }
|
26
|
+
.to change { select.by_id(1).to_a }
|
27
|
+
.from([{ "id" => 1, "name" => "joe" }])
|
28
|
+
.to [{ "id" => 1, "name" => "frank" }]
|
29
|
+
end
|
30
|
+
|
31
|
+
end # describe command 'update'
|
32
|
+
|
33
|
+
end # module ROM::Cassandra::Test
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "fileutils"
|
4
|
+
|
5
|
+
shared_context :fake_migrate_folder do |folder|
|
6
|
+
before { FileUtils.mkdir_p path }
|
7
|
+
before { files.reverse_each(&FileUtils.method(:touch)) }
|
8
|
+
|
9
|
+
let(:root) { File.expand_path "../../sandbox", __FILE__ }
|
10
|
+
let(:path) { File.join(root, folder) }
|
11
|
+
let(:files) do
|
12
|
+
[
|
13
|
+
"20151231235959_create_auth.rb",
|
14
|
+
"20160101000000_create_users.rb",
|
15
|
+
"20160101000001_create_logs.rb",
|
16
|
+
"20160101000002_create_rights.rb"
|
17
|
+
].map { |name| File.join(path, name) }
|
18
|
+
end
|
19
|
+
|
20
|
+
after { FileUtils.rm_rf root }
|
21
|
+
end # shared context
|