rom-cassandra 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +9 -0
  4. data/.metrics +9 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +2 -0
  7. data/.travis.yml +26 -0
  8. data/.yardopts +3 -0
  9. data/CHANGELOG.md +3 -0
  10. data/Gemfile +7 -0
  11. data/Guardfile +14 -0
  12. data/LICENSE +21 -0
  13. data/README.md +83 -0
  14. data/Rakefile +34 -0
  15. data/config/metrics/STYLEGUIDE +230 -0
  16. data/config/metrics/cane.yml +5 -0
  17. data/config/metrics/churn.yml +6 -0
  18. data/config/metrics/flay.yml +2 -0
  19. data/config/metrics/metric_fu.yml +14 -0
  20. data/config/metrics/reek.yml +1 -0
  21. data/config/metrics/roodi.yml +24 -0
  22. data/config/metrics/rubocop.yml +84 -0
  23. data/config/metrics/saikuro.yml +3 -0
  24. data/config/metrics/simplecov.yml +6 -0
  25. data/config/metrics/yardstick.yml +37 -0
  26. data/lib/rom-cassandra.rb +3 -0
  27. data/lib/rom/cassandra.rb +33 -0
  28. data/lib/rom/cassandra/commands.rb +53 -0
  29. data/lib/rom/cassandra/commands/batch.rb +54 -0
  30. data/lib/rom/cassandra/commands/create.rb +38 -0
  31. data/lib/rom/cassandra/commands/delete.rb +38 -0
  32. data/lib/rom/cassandra/commands/update.rb +38 -0
  33. data/lib/rom/cassandra/dataset.rb +102 -0
  34. data/lib/rom/cassandra/gateway.rb +115 -0
  35. data/lib/rom/cassandra/migrations.rb +30 -0
  36. data/lib/rom/cassandra/migrations/generator.rb +68 -0
  37. data/lib/rom/cassandra/migrations/generator/migration.erb +32 -0
  38. data/lib/rom/cassandra/migrations/logger.rb +28 -0
  39. data/lib/rom/cassandra/migrations/migration.rb +107 -0
  40. data/lib/rom/cassandra/migrations/migrator.rb +103 -0
  41. data/lib/rom/cassandra/migrations/runner.rb +119 -0
  42. data/lib/rom/cassandra/migrations/runner_down.rb +49 -0
  43. data/lib/rom/cassandra/migrations/runner_up.rb +50 -0
  44. data/lib/rom/cassandra/query.rb +43 -0
  45. data/lib/rom/cassandra/relation.rb +88 -0
  46. data/lib/rom/cassandra/session.rb +50 -0
  47. data/lib/rom/cassandra/tasks.rb +6 -0
  48. data/lib/rom/cassandra/version.rb +15 -0
  49. data/lib/tasks/db.rake +16 -0
  50. data/rom-cassandra.gemspec +33 -0
  51. data/spec/config/reset_cluster.rb +28 -0
  52. data/spec/config/rom.rb +3 -0
  53. data/spec/config/test_module.rb +7 -0
  54. data/spec/integration/batch_spec.rb +36 -0
  55. data/spec/integration/create_spec.rb +33 -0
  56. data/spec/integration/delete_spec.rb +33 -0
  57. data/spec/integration/migrate/20150825142003_create_users.rb +24 -0
  58. data/spec/integration/migrate/20150825142024_create_logs.rb +17 -0
  59. data/spec/integration/migrate_spec.rb +47 -0
  60. data/spec/integration/relation_spec.rb +27 -0
  61. data/spec/integration/update_spec.rb +33 -0
  62. data/spec/shared/fake_migrate_folder.rb +21 -0
  63. data/spec/shared/users.rb +20 -0
  64. data/spec/spec_helper.rb +17 -0
  65. data/spec/unit/commands/batch_spec.rb +86 -0
  66. data/spec/unit/commands/create_spec.rb +77 -0
  67. data/spec/unit/commands/delete_spec.rb +77 -0
  68. data/spec/unit/commands/update_spec.rb +77 -0
  69. data/spec/unit/dataset_spec.rb +130 -0
  70. data/spec/unit/gateway_spec.rb +140 -0
  71. data/spec/unit/migrations/generator_spec.rb +31 -0
  72. data/spec/unit/migrations/logger_spec.rb +21 -0
  73. data/spec/unit/migrations/migration_spec.rb +59 -0
  74. data/spec/unit/migrations/migrator_spec.rb +120 -0
  75. data/spec/unit/migrations/runner_down_spec.rb +65 -0
  76. data/spec/unit/migrations/runner_spec.rb +142 -0
  77. data/spec/unit/migrations/runner_up_spec.rb +67 -0
  78. data/spec/unit/query_spec.rb +21 -0
  79. data/spec/unit/relation_spec.rb +142 -0
  80. data/spec/unit/session_spec.rb +55 -0
  81. data/spec/unit/tasks/create_migration_spec.rb +41 -0
  82. 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
@@ -0,0 +1,6 @@
1
+ # encoding: utf-8
2
+
3
+ require "rake"
4
+ require "rom-cassandra"
5
+
6
+ load "tasks/db.rake"
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ module ROM
4
+
5
+ module Cassandra
6
+
7
+ # The semantic version of the module.
8
+ #
9
+ # @see http://semver.org/ Semantic versioning 2.0
10
+ #
11
+ VERSION = "0.0.1".freeze
12
+
13
+ end # module Cassandra
14
+
15
+ end # module ROM
@@ -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
@@ -0,0 +1,3 @@
1
+ # encoding: utf-8
2
+
3
+ ROM.use :auto_registration
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ # Recreates ROM::Cassandra::Test before every spec
4
+ RSpec.configure do |config|
5
+ config.before { module ROM::Cassandra::Test; end }
6
+ config.after { ROM::Cassandra.send :remove_const, :Test }
7
+ end
@@ -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