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,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