rom-cassandra 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,140 @@
1
+ # encoding: utf-8
2
+
3
+ describe ROM::Cassandra::Gateway do
4
+
5
+ let(:gateway) { described_class.new(uri) }
6
+ let(:uri) { { hosts: ["127.0.0.1"], port: 9042 } }
7
+
8
+ describe ".new" do
9
+ after { described_class.new("127.0.0.2", port: 9042) }
10
+
11
+ let(:session_class) { ROM::Cassandra::Session }
12
+
13
+ it "creates the session with uri" do
14
+ allow(session_class).to receive(:new)
15
+ expect(session_class).to receive(:new).with("127.0.0.2", port: 9042)
16
+ end
17
+ end # describe .new
18
+
19
+ describe "#options" do
20
+ subject { gateway.options }
21
+
22
+ it "returns a uri" do
23
+ expect(subject).to eql(uri)
24
+ end
25
+ end # describe #options
26
+
27
+ describe "#session" do
28
+ subject { gateway.session }
29
+
30
+ it "is a session" do
31
+ expect(subject).to be_instance_of ROM::Cassandra::Session
32
+ end
33
+
34
+ it "has a proper uri" do
35
+ expect(subject.uri).to eql uri
36
+ end
37
+ end # describe #session
38
+
39
+ describe "#[]" do
40
+ subject { gateway["foo.bar"] }
41
+
42
+ context "by default" do
43
+ it { is_expected.to be_nil }
44
+ end
45
+
46
+ context "registered dataset" do
47
+ before { gateway.dataset "foo.bar" }
48
+
49
+ it { is_expected.to be_instance_of ROM::Cassandra::Dataset }
50
+ end
51
+ end # describe #[]
52
+
53
+ describe "#dataset?" do
54
+ subject { gateway.dataset? "foo.bar" }
55
+
56
+ context "by default" do
57
+ it { is_expected.to eql false }
58
+ end
59
+
60
+ context "registered dataset" do
61
+ before { gateway.dataset "foo.bar" }
62
+
63
+ it { is_expected.to eql true }
64
+ end
65
+ end # describe #dataset?
66
+
67
+ describe "#dataset" do
68
+ subject { gateway.dataset(name) }
69
+
70
+ context "with valid name" do
71
+ let(:name) { :"foo.bar" }
72
+
73
+ it "registers the dataset for given table" do
74
+ subject
75
+ dataset = gateway["foo.bar"]
76
+
77
+ expect(dataset.session).to eql(gateway.session)
78
+ expect(dataset.keyspace).to eql :foo
79
+ expect(dataset.table).to eql :bar
80
+ end
81
+ end
82
+
83
+ context "with a string name" do
84
+ let(:name) { "foo.bar" }
85
+
86
+ it "registers the dataset for given table" do
87
+ expect { subject }.to change { gateway[:"foo.bar"] }
88
+ end
89
+ end
90
+
91
+ context "with invalid name" do
92
+ let(:name) { "foo" }
93
+
94
+ it "fails" do
95
+ expect { subject }.to raise_error do |error|
96
+ expect(error).to be_kind_of ArgumentError
97
+ expect(error.message)
98
+ .to eql "'foo' is not a valid full name of a table. " \
99
+ "Use format 'keyspace.table' with both keyspace and table parts."
100
+ end
101
+ end
102
+ end
103
+ end # describe #dataset
104
+
105
+ describe "#migrate" do
106
+ let(:klass) { ROM::Cassandra::Migrations::Migrator }
107
+ let(:session) { gateway.session }
108
+ let(:migrator) { double :migrator, apply: nil }
109
+ let(:path) { double :path }
110
+ let(:version) { double :version }
111
+ let(:logger) { double :logger }
112
+
113
+ before { allow(klass).to receive(:new) { migrator } }
114
+
115
+ context "with options" do
116
+ after { gateway.migrate path: path, version: version, logger: logger }
117
+
118
+ it "initializes the migrator" do
119
+ expect(klass).to receive(:new).with(session, path: path, logger: logger)
120
+ end
121
+
122
+ it "runs the migrator" do
123
+ expect(migrator).to receive(:apply).with(version: version)
124
+ end
125
+ end
126
+
127
+ context "without options" do
128
+ after { gateway.migrate }
129
+
130
+ it "initializes the migrator" do
131
+ expect(klass).to receive(:new).with(session, {})
132
+ end
133
+
134
+ it "runs the migrator" do
135
+ expect(migrator).to receive(:apply).with({})
136
+ end
137
+ end
138
+ end # describe #migrate
139
+
140
+ end # describe ROM:Cassandra::Gateway
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+
3
+ require "shared/fake_migrate_folder"
4
+ require "timecop"
5
+
6
+ describe ROM::Cassandra::Migrations::Generator do
7
+
8
+ include_context :fake_migrate_folder, "context"
9
+
10
+ let(:name) { "createFoo" }
11
+ let(:time) { Time.utc(2017, "dec", 31, 23, 59, 59) }
12
+ let(:target) { "#{path}/db/20171231235959_create_foo.rb" }
13
+ let(:content) { File.read(target) }
14
+
15
+ describe ".call" do
16
+ subject { described_class.call(name, "#{path}/db") }
17
+
18
+ before { Timecop.freeze(time) }
19
+ after { Timecop.return }
20
+
21
+ it "creates the migration" do
22
+ subject
23
+ expect(content).to include "CreateFoo"
24
+ end
25
+
26
+ it "returns the name of the migration" do
27
+ expect(subject).to eql(target)
28
+ end
29
+ end # describe .call
30
+
31
+ end # describe ROM::Cassandra::Migrations::Generator
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ describe ROM::Cassandra::Migrations::Logger do
4
+
5
+ let(:logger) { described_class.new }
6
+ let(:stdout) { StringIO.new }
7
+
8
+ before { $stdout = stdout }
9
+ after { $stdout = STDOUT }
10
+
11
+ describe ".new" do
12
+ subject { logger }
13
+
14
+ it { is_expected.to be_kind_of ::Logger }
15
+
16
+ it "sends messages to $stdout" do
17
+ expect { subject.info "text" }.to change { stdout.string }.to "text\n"
18
+ end
19
+ end # describe .new
20
+
21
+ end # describe ROM::Cassandra::Migrations::Logger
@@ -0,0 +1,59 @@
1
+ # encoding: utf-8
2
+
3
+ describe ROM::Cassandra::Migrations::Migration do
4
+
5
+ let(:migration) { described_class.new session }
6
+ let(:session) { double :session, call: nil, freeze: nil }
7
+
8
+ describe "#session" do
9
+ subject { migration.session }
10
+
11
+ it { is_expected.to eql session }
12
+ end # describe #session
13
+
14
+ describe "#up" do
15
+ subject { migration.up }
16
+
17
+ it { is_expected.to be_nil }
18
+ end # describe #up
19
+
20
+ describe "#down" do
21
+ subject { migration.down }
22
+
23
+ it { is_expected.to be_nil }
24
+ end # describe #down
25
+
26
+ describe "#call" do
27
+ subject { migration.call query }
28
+ let(:query) { double :query, to_s: :foo }
29
+
30
+ it "is delegated to #session" do
31
+ expect(session).to receive(:call).with(:foo)
32
+ subject
33
+ end
34
+ end # describe #call
35
+
36
+ describe "#keyspace" do
37
+ subject { migration.keyspace(:foo) }
38
+
39
+ it "starts building query" do
40
+ query = subject.create.if_not_exists.to_s
41
+
42
+ expect(query).to eql "CREATE KEYSPACE IF NOT EXISTS foo;"
43
+ end
44
+ end # describe #keyspace
45
+
46
+ describe ".inherited" do
47
+ subject { Class.new described_class }
48
+
49
+ it "hides helpers" do
50
+ expect(subject.private_instance_methods)
51
+ .to include(:call, :keyspace, :up, :down)
52
+ end
53
+
54
+ it "keeps parent class' helpers unchanged" do
55
+ expect { subject }.not_to change { described_class.new(session).methods }
56
+ end
57
+ end # describe .inherited
58
+
59
+ end # describe ROM::Cassandra::Migrations::Migration
@@ -0,0 +1,120 @@
1
+ # encoding: utf-8
2
+
3
+ require "shared/fake_migrate_folder"
4
+
5
+ describe ROM::Cassandra::Migrations::Migrator do
6
+
7
+ include_context :fake_migrate_folder, "context"
8
+
9
+ let(:migrator) { described_class.new session, logger: logger, path: path }
10
+ let(:logger) { double :logger }
11
+ let(:session) { double :session, call: nil }
12
+
13
+ describe "#session" do
14
+ subject { migrator.session }
15
+
16
+ it "returns the session by uri" do
17
+ expect(subject).to eql(session)
18
+ end
19
+ end # describe #session
20
+
21
+ describe "#logger" do
22
+ subject { migrator.logger }
23
+
24
+ context "when logger is specified" do
25
+ it { is_expected.to eql logger }
26
+ end
27
+
28
+ context "when logger isn't specified" do
29
+ let(:migrator) { described_class.new session }
30
+
31
+ it { is_expected.to be_kind_of ROM::Cassandra::Migrations::Logger }
32
+ end
33
+ end # describe #logger
34
+
35
+ describe "#root" do
36
+ subject { migrator.root }
37
+
38
+ context "when path is specified" do
39
+ it { is_expected.to eql path }
40
+ end
41
+
42
+ context "when path isn't specified" do
43
+ let(:migrator) { described_class.new session }
44
+
45
+ it { is_expected.to eql("db/migrate") }
46
+ end
47
+ end # describe #root
48
+
49
+ describe "#paths" do
50
+ subject { migrator.paths }
51
+
52
+ it "returns the ordered list of migrations" do
53
+ expect(subject).to eql(files)
54
+ end
55
+ end # describe #paths
56
+
57
+ describe "#apply" do
58
+ before { allow(ROM::Cassandra::Migrations::Runner).to receive(:apply) }
59
+
60
+ let(:runner_up) { ROM::Cassandra::Migrations::RunnerUp }
61
+ let(:runner_down) { ROM::Cassandra::Migrations::RunnerDown }
62
+
63
+ context "without a version" do
64
+ after { migrator.apply }
65
+
66
+ it "applies all migrations in direct order" do
67
+ files.each do |path|
68
+ expect(runner_up)
69
+ .to receive(:apply)
70
+ .with(session, logger, path)
71
+ .ordered
72
+ end
73
+ end
74
+
75
+ it "doesn't rollback migrations" do
76
+ expect(runner_down).not_to receive(:apply)
77
+ end
78
+ end
79
+
80
+ context "with the zero version" do
81
+ after { migrator.apply version: 0 }
82
+
83
+ it "rolls back all migrations in reverse order" do
84
+ files.reverse_each do |path|
85
+ expect(runner_down)
86
+ .to receive(:apply)
87
+ .with(session, logger, path)
88
+ .ordered
89
+ end
90
+ end
91
+
92
+ it "doesn't apply migrations" do
93
+ expect(runner_up).not_to receive(:apply)
94
+ end
95
+ end
96
+
97
+ context "with a non-zero version" do
98
+ after { migrator.apply version: 20160101000000 }
99
+
100
+ it "applies migrations up to the version in direct order" do
101
+ files[0..1].each do |path|
102
+ expect(runner_up)
103
+ .to receive(:apply)
104
+ .with(session, logger, path)
105
+ .ordered
106
+ end
107
+ end
108
+
109
+ it "rolls back migrations with greater versions in reverse order" do
110
+ files[2..-1].reverse_each do |path|
111
+ expect(runner_down)
112
+ .to receive(:apply)
113
+ .with(session, logger, path)
114
+ .ordered
115
+ end
116
+ end
117
+ end
118
+ end # describe #apply
119
+
120
+ end # describe ROM::Cassandra::Migrations::Migrator
@@ -0,0 +1,65 @@
1
+ # encoding: utf-8
2
+
3
+ describe ROM::Cassandra::Migrations::RunnerDown do
4
+
5
+ let(:klass) { Class.new described_class }
6
+ let(:runner) { klass.new session, logger, path }
7
+ let(:session) { double :session, call: nil }
8
+ let(:logger) { double :logger, info: nil }
9
+ let(:path) { "foo/20150824103059_add_foo.rb" }
10
+
11
+ before do
12
+ klass.send(:define_method, :require) do |_|
13
+ AddFoo = Class.new(ROM::Cassandra::Migrations::Migration)
14
+ end
15
+ klass.send(:define_method, :select_version) { [{ id: 20150824103059 }] }
16
+ end
17
+
18
+ describe "#migrate?" do
19
+ subject { runner.migrate? }
20
+
21
+ context "when select_version isn't empty" do
22
+ it { is_expected.to eql true }
23
+ end
24
+
25
+ context "when select_version is empty" do
26
+ before { klass.send(:define_method, :select_version) { [] } }
27
+
28
+ it { is_expected.to eql false }
29
+ end
30
+ end # describe #migrate?
31
+
32
+ describe "#apply" do
33
+ after { runner.apply }
34
+ before { allow(runner).to receive(:migration) { migration } }
35
+
36
+ let(:migration) { double :migration, down: nil }
37
+
38
+ it "rolls back migration" do
39
+ expect(migration).to receive(:down).once
40
+ end
41
+ end # describe #apply
42
+
43
+ describe "#register" do
44
+ after { runner.register }
45
+
46
+ it "sends the DELETE query to session" do
47
+ expect(session)
48
+ .to receive(:call)
49
+ .with "DELETE FROM rom.migrations WHERE version = '20150824103059';"
50
+ end
51
+ end # describe #register
52
+
53
+ describe "#log" do
54
+ after { runner.log }
55
+
56
+ it "logs the message" do
57
+ expect(logger)
58
+ .to receive(:info)
59
+ .with "Roll back migration 20150824103059\n"
60
+ end
61
+ end # describe #log
62
+
63
+ after { Object.send :remove_const, :AddFoo if Object.const_defined? :AddFoo }
64
+
65
+ end # describe ROM::Cassandra::Migrations::RunnerDown
@@ -0,0 +1,142 @@
1
+ # encoding: utf-8
2
+
3
+ describe ROM::Cassandra::Migrations::Runner do
4
+
5
+ let(:klass) { Class.new described_class }
6
+ let(:runner) { klass.new session, logger, path }
7
+ let(:session) { double :session, call: nil }
8
+ let(:logger) { double :logger }
9
+ let(:path) { "foo/20150824103059_add_foo.rb" }
10
+
11
+ before do
12
+ klass.send(:define_method, :migrate?) { true }
13
+ klass.send(:define_method, :require) do |name|
14
+ AddFoo = Class.new(ROM::Cassandra::Migrations::Migration) if name == path
15
+ true
16
+ end
17
+ end
18
+
19
+ describe "#session" do
20
+ subject { runner.session }
21
+
22
+ it { is_expected.to eql session }
23
+ end # describe #session
24
+
25
+ describe "#logger" do
26
+ subject { runner.logger }
27
+
28
+ it { is_expected.to eql logger }
29
+ end # describe #logger
30
+
31
+ describe "#path" do
32
+ subject { runner.path }
33
+
34
+ it { is_expected.to eql path }
35
+ end # describe #path
36
+
37
+ describe "#version" do
38
+ subject { runner.version }
39
+
40
+ context "from valid timestamp" do
41
+ let(:path) { "foo/12345678901234/20150824103059_add_foo.rb" }
42
+
43
+ it { is_expected.to eql 20150824103059 }
44
+ end
45
+
46
+ context "from invalid timestamp" do
47
+ let(:path) { "foo/2015082410305_add_foo.rb" }
48
+
49
+ it "fails" do
50
+ expect { subject }.to raise_error do |error|
51
+ expect(error).to be_kind_of NameError
52
+ expect(error.message)
53
+ .to eql("invalid version number: 'foo/2015082410305_add_foo.rb'")
54
+ end
55
+ end
56
+ end
57
+ end # describe #version
58
+
59
+ describe "#migration" do
60
+ subject { runner.migration }
61
+
62
+ it "creates migration for #session" do
63
+ expect(subject).to be_kind_of(AddFoo)
64
+ expect(subject.session).to eql(session)
65
+ end
66
+
67
+ context "when #migrate? is falsey" do
68
+ before { klass.send(:define_method, :migrate?) { nil } }
69
+
70
+ it { is_expected.to eql nil }
71
+ end
72
+ end # describe #migration
73
+
74
+ describe "#clear" do
75
+ subject { runner.clear }
76
+
77
+ before { runner }
78
+ end # describe #clear
79
+
80
+ describe "#call" do
81
+ before do
82
+ allow(runner).to receive(:apply)
83
+ allow(runner).to receive(:register)
84
+ allow(runner).to receive(:log)
85
+ end
86
+
87
+ subject { runner.call }
88
+
89
+ it "calls commands in the right order" do
90
+ expect(runner).to receive(:apply).ordered
91
+ expect(runner).to receive(:register).ordered
92
+ expect(runner).to receive(:log).ordered
93
+ subject
94
+ end
95
+
96
+ context "when #migration isn't set" do
97
+ before { allow(runner).to receive(:migration) }
98
+
99
+ it "skips all commands" do
100
+ expect(runner).not_to receive(:apply)
101
+ expect(runner).not_to receive(:register)
102
+ expect(runner).not_to receive(:log)
103
+ subject
104
+ end
105
+ end
106
+ end # describe #call
107
+
108
+ describe "#select_version" do
109
+ after { runner.select_version }
110
+
111
+ let(:queries) do
112
+ [
113
+ "CREATE KEYSPACE IF NOT EXISTS rom WITH REPLICATION =" \
114
+ " {'class': 'SimpleStrategy', 'replication_factor': 3};",
115
+ "CREATE TABLE IF NOT EXISTS rom.migrations " \
116
+ "(version text, PRIMARY KEY (version));",
117
+ "SELECT * FROM rom.migrations WHERE version = '20150824103059';"
118
+ ]
119
+ end
120
+
121
+ it "sends requests to Cassandra cluster" do
122
+ queries.each do |query|
123
+ expect(session).to receive(:call).with(query).ordered
124
+ end
125
+ end
126
+ end # describe #select_version
127
+
128
+ describe ".apply" do
129
+ let(:runner) { double :runner, call: nil }
130
+
131
+ before { allow(described_class).to receive(:new) { runner } }
132
+ after { described_class.apply(session, logger, path) }
133
+
134
+ it "creates and calls the runner" do
135
+ expect(described_class).to receive(:new).with(session, logger, path)
136
+ expect(runner).to receive(:call)
137
+ end
138
+ end # describe .apply
139
+
140
+ after { Object.send :remove_const, :AddFoo if Object.const_defined? :AddFoo }
141
+
142
+ end # describe ROM::Cassandra::Migrations::Runner