mince_migrator 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +20 -58
  3. data/bin/mince_migrator +125 -0
  4. data/lib/mince_migrator/cli_helper.rb +72 -0
  5. data/lib/mince_migrator/config.rb +30 -0
  6. data/lib/mince_migrator/creator.rb +48 -0
  7. data/lib/mince_migrator/deleter.rb +42 -0
  8. data/lib/mince_migrator/list.rb +46 -0
  9. data/lib/mince_migrator/list_report.rb +67 -0
  10. data/lib/mince_migrator/migration.rb +66 -0
  11. data/lib/mince_migrator/migrations/file.rb +74 -0
  12. data/lib/mince_migrator/migrations/loader.rb +52 -0
  13. data/lib/mince_migrator/migrations/name.rb +48 -0
  14. data/lib/mince_migrator/migrations/runner.rb +39 -0
  15. data/lib/mince_migrator/migrations/runner_validator.rb +47 -0
  16. data/lib/mince_migrator/migrations/template.mustache +24 -0
  17. data/lib/mince_migrator/migrations/template.rb +16 -0
  18. data/lib/mince_migrator/migrations/versioned_file.rb +38 -0
  19. data/lib/mince_migrator/ran_migration.rb +27 -0
  20. data/lib/mince_migrator/reverter.rb +57 -0
  21. data/lib/mince_migrator/status_report.rb +41 -0
  22. data/lib/mince_migrator/version.rb +18 -1
  23. data/lib/mince_migrator.rb +5 -4
  24. data/spec/integration/create_a_migration_spec.rb +57 -0
  25. data/spec/integration/deleting_a_migration_spec.rb +40 -0
  26. data/spec/integration/list_all_migrations_spec.rb +57 -0
  27. data/spec/integration/reverting_a_migration_spec.rb +59 -0
  28. data/spec/integration/running_a_migration_spec.rb +66 -0
  29. data/spec/integration_helper.rb +17 -0
  30. data/spec/support/db/a_second_migration.rb +24 -0
  31. data/spec/support/db/a_second_migration_2.rb +24 -0
  32. data/spec/support/db/create_seeded_admin_users.rb +24 -0
  33. data/spec/support/db/first_migration.rb +24 -0
  34. data/spec/support/db/first_migration_2.rb +24 -0
  35. data/spec/support/db/first_migration_3.rb +24 -0
  36. data/spec/support/db/first_migration_4.rb +24 -0
  37. data/spec/support/db/name_of_migration.rb +24 -0
  38. data/spec/support/invalid_interface_migration.rb +7 -0
  39. data/spec/support/not_a_migration.rb +2 -0
  40. data/spec/support/test_migration.rb +30 -0
  41. data/spec/units/mince_migrator/cli_helper_spec.rb +147 -0
  42. data/spec/units/mince_migrator/creator_spec.rb +80 -0
  43. data/spec/units/mince_migrator/deleter_spec.rb +52 -0
  44. data/spec/units/mince_migrator/list_spec.rb +53 -0
  45. data/spec/units/mince_migrator/migration_spec.rb +119 -0
  46. data/spec/units/mince_migrator/migrations/file_spec.rb +85 -0
  47. data/spec/units/mince_migrator/migrations/loader_spec.rb +47 -0
  48. data/spec/units/mince_migrator/migrations/name_spec.rb +47 -0
  49. data/spec/units/mince_migrator/migrations/runner_spec.rb +70 -0
  50. data/spec/units/mince_migrator/migrations/runner_validator_spec.rb +46 -0
  51. data/spec/units/mince_migrator/migrations/template_spec.rb +42 -0
  52. data/spec/units/mince_migrator/migrations/versioned_file_spec.rb +48 -0
  53. data/spec/units/mince_migrator/ran_migration_spec.rb +60 -0
  54. data/spec/units/mince_migrator/reverter_spec.rb +78 -0
  55. metadata +298 -26
  56. data/.gitignore +0 -17
  57. data/Gemfile +0 -4
  58. data/Rakefile +0 -12
  59. data/mince_migrator.gemspec +0 -19
@@ -0,0 +1,66 @@
1
+ require_relative '../integration_helper'
2
+
3
+ describe 'Running a migration' do
4
+ subject { MinceMigrator::Migrations::Runner.new(name: name) }
5
+
6
+ context 'when the migration could not be found' do
7
+ let(:name) { 'migration that does not exist' }
8
+
9
+ before do
10
+ subject.can_run_migration?
11
+ end
12
+
13
+ its(:reasons_for_failure) { should == "Migration does not exist" }
14
+ its(:can_run_migration?) { should be_false }
15
+ end
16
+
17
+ context 'when the migration exists' do
18
+ let(:name) { "test migration" }
19
+ let(:spec_migration) { File.expand_path('../../support/test_migration.rb', __FILE__) }
20
+ let(:db_dir) { MinceMigrator::Config.migration_dir }
21
+ let(:data_model) { MinceMigrator::Migrations::TestMigration::Temporary::UserDataModel }
22
+
23
+ its(:reasons_for_failure) { should be_empty }
24
+ its(:can_run_migration?) { should be_true }
25
+
26
+ before do
27
+ FileUtils.mkdir_p(db_dir)
28
+ FileUtils.cp(spec_migration, db_dir)
29
+ subject.can_run_migration?
30
+ end
31
+
32
+ it 'can run the migration' do
33
+ subject.run_migration.should be_true
34
+
35
+ users = data_model.all
36
+ users.size.should == 1
37
+ users.first[:username].should == 'matt'
38
+ end
39
+
40
+ context 'and the migration has already been ran' do
41
+ before do
42
+ subject.run_migration
43
+ subject.can_run_migration?
44
+ end
45
+
46
+ its(:reasons_for_failure) { should == "Migration has already ran" }
47
+ its(:can_run_migration?) { should be_false }
48
+ end
49
+ end
50
+
51
+ context 'when Mince interface has not been set' do
52
+ let(:name) { 'name' }
53
+
54
+ before do
55
+ Mince::Config.interface = nil
56
+ subject.can_run_migration?
57
+ end
58
+
59
+ after do
60
+ Mince::Config.interface = Mince::HashyDb::Interface
61
+ end
62
+
63
+ its(:reasons_for_failure) { should == "Mince interface is not set" }
64
+ its(:can_run_migration?) { should be_false }
65
+ end
66
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../lib/mince_migrator'
2
+ require_relative '../lib/mince_migrator/config'
3
+ require 'mince'
4
+ require 'hashy_db'
5
+
6
+ Mince::Config.interface = Mince::HashyDb::Interface
7
+ MinceMigrator::Config.instance.migration_relative_dir = "tmp/db_for_integration_specs"
8
+
9
+ RSpec.configure do |config|
10
+ config.after(:each) do
11
+ if File.exists?(MinceMigrator::Config.migration_dir)
12
+ FileUtils.rm_r MinceMigrator::Config.migration_dir
13
+ end
14
+
15
+ Mince::Config.interface.clear
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module MinceMigrator
2
+ module Migrations
3
+ require 'time'
4
+
5
+ module ASecondMigration
6
+ def self.run
7
+ # Actual migration goes here
8
+ end
9
+
10
+ def self.revert
11
+ # In case you need to revert this one migration
12
+ end
13
+
14
+ # So you can change the order to run more easily
15
+ def self.time_created
16
+ Time.parse "2013-03-28 05:03:26 UTC"
17
+ end
18
+
19
+ module Temporary
20
+ # Migration dependent classes go here
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module MinceMigrator
2
+ module Migrations
3
+ require 'time'
4
+
5
+ module ASecondMigration2
6
+ def self.run
7
+ # Actual migration goes here
8
+ end
9
+
10
+ def self.revert
11
+ # In case you need to revert this one migration
12
+ end
13
+
14
+ # So you can change the order to run more easily
15
+ def self.time_created
16
+ Time.parse "2013-03-28 05:03:26 UTC"
17
+ end
18
+
19
+ module Temporary
20
+ # Migration dependent classes go here
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module MinceMigrator
2
+ module Migrations
3
+ require 'time'
4
+
5
+ module CreateSeededAdminUsers
6
+ def self.run
7
+ # Actual migration goes here
8
+ end
9
+
10
+ def self.revert
11
+ # In case you need to revert this one migration
12
+ end
13
+
14
+ # So you can change the order to run more easily
15
+ def self.time_created
16
+ Time.parse "2013-03-28 05:03:26 UTC"
17
+ end
18
+
19
+ module Temporary
20
+ # Migration dependent classes go here
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module MinceMigrator
2
+ module Migrations
3
+ require 'time'
4
+
5
+ module FirstMigration
6
+ def self.run
7
+ # Actual migration goes here
8
+ end
9
+
10
+ def self.revert
11
+ # In case you need to revert this one migration
12
+ end
13
+
14
+ # So you can change the order to run more easily
15
+ def self.time_created
16
+ Time.parse "2013-03-28 05:03:26 UTC"
17
+ end
18
+
19
+ module Temporary
20
+ # Migration dependent classes go here
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module MinceMigrator
2
+ module Migrations
3
+ require 'time'
4
+
5
+ module FirstMigration2
6
+ def self.run
7
+ # Actual migration goes here
8
+ end
9
+
10
+ def self.revert
11
+ # In case you need to revert this one migration
12
+ end
13
+
14
+ # So you can change the order to run more easily
15
+ def self.time_created
16
+ Time.parse "2013-03-28 05:03:26 UTC"
17
+ end
18
+
19
+ module Temporary
20
+ # Migration dependent classes go here
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module MinceMigrator
2
+ module Migrations
3
+ require 'time'
4
+
5
+ module FirstMigration3
6
+ def self.run
7
+ # Actual migration goes here
8
+ end
9
+
10
+ def self.revert
11
+ # In case you need to revert this one migration
12
+ end
13
+
14
+ # So you can change the order to run more easily
15
+ def self.time_created
16
+ Time.parse "2013-03-28 05:03:26 UTC"
17
+ end
18
+
19
+ module Temporary
20
+ # Migration dependent classes go here
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module MinceMigrator
2
+ module Migrations
3
+ require 'time'
4
+
5
+ module FirstMigration4
6
+ def self.run
7
+ # Actual migration goes here
8
+ end
9
+
10
+ def self.revert
11
+ # In case you need to revert this one migration
12
+ end
13
+
14
+ # So you can change the order to run more easily
15
+ def self.time_created
16
+ Time.parse "2013-03-28 05:03:26 UTC"
17
+ end
18
+
19
+ module Temporary
20
+ # Migration dependent classes go here
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ module MinceMigrator
2
+ module Migrations
3
+ require 'time'
4
+
5
+ module NameOfMigration
6
+ def self.run
7
+ # Actual migration goes here
8
+ end
9
+
10
+ def self.revert
11
+ # In case you need to revert this one migration
12
+ end
13
+
14
+ # So you can change the order to run more easily
15
+ def self.time_created
16
+ Time.parse "2013-03-28 05:03:26 UTC"
17
+ end
18
+
19
+ module Temporary
20
+ # Migration dependent classes go here
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ module MinceMigrator
2
+ module Migrations
3
+ module InvalidInterfaceMigration
4
+ end
5
+ end
6
+ end
7
+
@@ -0,0 +1,2 @@
1
+ "What is this? It is definitely not a mince migrator migration.
2
+ But that's the point, because we are using this file as a test for a bad migration file."
@@ -0,0 +1,30 @@
1
+ module MinceMigrator
2
+ module Migrations
3
+ require 'time'
4
+
5
+ module TestMigration
6
+ def self.run
7
+ Temporary::UserDataModel.add(username: 'matt')
8
+ end
9
+
10
+ def self.revert
11
+ Temporary::UserDataModel.delete_by_params(username: 'matt')
12
+ end
13
+
14
+ # So you can change the order to run more easily
15
+ def self.time_created
16
+ Time.parse '2013-03-04 09:31:28 UTC'
17
+ end
18
+
19
+ module Temporary
20
+ class UserDataModel
21
+ include Mince::DataModel
22
+
23
+ data_collection :users
24
+ data_fields :username, :email, :first_name, :last_name
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,147 @@
1
+ require_relative '../../../lib/mince_migrator/cli_helper'
2
+
3
+ module MinceMigrator
4
+ describe CliHelper do
5
+ subject { klass.new }
6
+
7
+ let(:klass) do
8
+ Class.new do
9
+ include CliHelper
10
+ end
11
+ end
12
+ let(:options) { mock }
13
+ let(:reasons_for_failure) { mock }
14
+
15
+ before do
16
+ subject.stub(:puts)
17
+ end
18
+
19
+ describe 'deleting a migration' do
20
+ let(:deleter) { mock }
21
+
22
+ before do
23
+ Deleter.stub(:new).with(options).and_return(deleter)
24
+ end
25
+
26
+ context 'when the migration can be deleted' do
27
+ before do
28
+ deleter.stub(can_delete_migration?: true, name: mock)
29
+ end
30
+
31
+ it 'deletes it' do
32
+ deleter.should_receive(:delete_migration)
33
+
34
+ subject.delete_migration(options)
35
+ end
36
+ end
37
+
38
+ context 'when the migration cannot be deleted' do
39
+ before do
40
+ deleter.stub(can_delete_migration?: false, reasons_for_failure: reasons_for_failure)
41
+ end
42
+
43
+ it 'fails with an error message' do
44
+ subject.should_receive(:help_now!).with(reasons_for_failure)
45
+
46
+ subject.delete_migration(options)
47
+ end
48
+ end
49
+ end
50
+
51
+ describe 'running a migration' do
52
+ let(:runner) { mock }
53
+
54
+ before do
55
+ Migrations::Runner.stub(:new).with(options).and_return(runner)
56
+ end
57
+
58
+ context 'when the migration can be ran' do
59
+ before do
60
+ runner.stub(can_run_migration?: true, name: mock)
61
+ end
62
+
63
+ it 'runs it' do
64
+ runner.should_receive(:run_migration)
65
+
66
+ subject.run_migration(options)
67
+ end
68
+ end
69
+
70
+ context 'when the migration cannot be ran' do
71
+ before do
72
+ runner.stub(can_run_migration?: false, reasons_for_failure: reasons_for_failure)
73
+ end
74
+
75
+ it 'fails with an error message' do
76
+ subject.should_receive(:help_now!).with(reasons_for_failure)
77
+
78
+ subject.run_migration(options)
79
+ end
80
+ end
81
+ end
82
+
83
+ describe 'creating a migration' do
84
+ let(:creator) { mock migration_file_relative_path: mock }
85
+
86
+ before do
87
+ Creator.stub(:new).with(options).and_return(creator)
88
+ end
89
+
90
+ context 'when the migration can be created' do
91
+ before do
92
+ creator.stub(can_create_migration?: true, name: mock)
93
+ end
94
+
95
+ it 'creates it' do
96
+ creator.should_receive(:create_migration)
97
+
98
+ subject.create_migration(options)
99
+ end
100
+ end
101
+
102
+ context 'when the migration cannot be created' do
103
+ before do
104
+ creator.stub(can_create_migration?: false, reasons_for_failure: reasons_for_failure)
105
+ end
106
+
107
+ it 'fails with an error message' do
108
+ subject.should_receive(:help_now!).with(reasons_for_failure)
109
+
110
+ subject.create_migration(options)
111
+ end
112
+ end
113
+ end
114
+
115
+ describe 'reverting a migration' do
116
+ let(:reverter) { mock }
117
+
118
+ before do
119
+ Reverter.stub(:new).with(options).and_return(reverter)
120
+ end
121
+
122
+ context 'when the migration can be reverted' do
123
+ before do
124
+ reverter.stub(can_revert_migration?: true, name: mock)
125
+ end
126
+
127
+ it 'reverts it' do
128
+ reverter.should_receive(:revert_migration)
129
+
130
+ subject.revert_migration(options)
131
+ end
132
+ end
133
+
134
+ context 'when the migration cannot be reverted' do
135
+ before do
136
+ reverter.stub(can_revert_migration?: false, reasons_for_failure: reasons_for_failure)
137
+ end
138
+
139
+ it 'fails with an error message' do
140
+ subject.should_receive(:help_now!).with(reasons_for_failure)
141
+
142
+ subject.revert_migration(options)
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,80 @@
1
+ require_relative '../../../lib/mince_migrator/creator'
2
+
3
+ describe MinceMigrator::Creator do
4
+ context 'when a name is not provided' do
5
+ let(:migration_name) { mock valid?: false, reasons_for_failure: mock, value: mock }
6
+
7
+ before do
8
+ MinceMigrator::Migrations::Name.stub(:new).with(nil).and_return(migration_name)
9
+ end
10
+
11
+ its(:can_create_migration?) { should be_false }
12
+ its(:reasons_for_failure) { should == migration_name.reasons_for_failure }
13
+ end
14
+
15
+ context 'when a name is provided' do
16
+ subject { described_class.new(name) }
17
+
18
+ let(:name) { mock }
19
+ let(:migration_name) { mock value: mock}
20
+
21
+ before do
22
+ MinceMigrator::Migrations::Name.stub(:new).with(name).and_return(migration_name)
23
+ end
24
+
25
+ context 'when the name is invalid' do
26
+ let(:reasons_for_failure) { mock }
27
+
28
+ before do
29
+ migration_name.stub(valid?: false, reasons_for_failure: reasons_for_failure)
30
+ end
31
+
32
+ its(:can_create_migration?) { should be_false }
33
+ its(:reasons_for_failure) { should == reasons_for_failure }
34
+ end
35
+
36
+ context 'when the name is valid' do
37
+ let(:opened_file) { mock write: nil, close: nil }
38
+ let(:migration_file) { mock path: mock, full_path: mock, body: mock, full_relative_path: mock }
39
+ let(:versioned_file) { mock next_unused_version: migration_file }
40
+
41
+ before do
42
+ migration_name.stub(valid?: true, value: mock)
43
+ FileUtils.stub(:mkdir_p).with(MinceMigrator::Config.migration_dir)
44
+ MinceMigrator::Migrations::VersionedFile.stub(:new).with(migration_name.value).and_return(versioned_file)
45
+ ::File.stub(:open).with(migration_file.full_path, 'w+').and_return(opened_file)
46
+ ::File.stub(:exists?).with(migration_file.full_path).and_return(false)
47
+ end
48
+
49
+ its(:migration_file_relative_path){ should == migration_file.full_relative_path }
50
+
51
+ it 'insures the path to the migraiton file exists' do
52
+ FileUtils.should_receive(:mkdir_p).with(MinceMigrator::Config.migration_dir)
53
+
54
+ subject.create_migration
55
+ end
56
+
57
+ it 'can create the migration' do
58
+ subject.can_create_migration?.should be_true
59
+ end
60
+
61
+ it 'creates a migration file' do
62
+ opened_file.should_receive(:write).with(migration_file.body)
63
+ opened_file.should_receive(:close)
64
+
65
+ subject.create_migration
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ describe MinceMigrator::Creator, 'Class level methods' do
72
+ it 'can create a migration' do
73
+ name = mock
74
+ creator = mock can_create_migration?: true
75
+ described_class.should_receive(:new).with(name).and_return(creator)
76
+ creator.should_receive(:create_migration)
77
+
78
+ described_class.create(name)
79
+ end
80
+ end
@@ -0,0 +1,52 @@
1
+ require_relative '../../../lib/mince_migrator/deleter'
2
+
3
+ describe MinceMigrator::Deleter do
4
+ let(:name) { mock }
5
+ let(:migration_name) { mock filename: mock, value: mock }
6
+ let(:migration_path) { ::File.join(path, migration_name.filename) }
7
+ let(:path) { mock }
8
+
9
+ subject { described_class.new(name: name) }
10
+
11
+ before do
12
+ MinceMigrator::Config.stub(migration_dir: path)
13
+ MinceMigrator::Migrations::Name.stub(:new).with(name).and_return(migration_name)
14
+ end
15
+
16
+ context 'when the migration does not exist' do
17
+ before do
18
+ ::File.stub(:exists?).with(migration_path).and_return(false)
19
+ end
20
+
21
+ its(:can_delete_migration?) { should be_false }
22
+ its(:reasons_for_failure) { should == "Migration does not exist with name '#{migration_name.value}'" }
23
+ end
24
+
25
+ context 'when the migration exists' do
26
+ let(:ran_migration) { nil }
27
+
28
+ before do
29
+ ::File.stub(:exists?).with(migration_path).and_return(true)
30
+ MinceMigrator::RanMigration.stub(:find_by_name).with(migration_name.value).and_return(ran_migration)
31
+ FileUtils.stub(:rm)
32
+ end
33
+
34
+ its(:can_delete_migration?) { should be_true }
35
+
36
+ it 'deletes the migration' do
37
+ FileUtils.should_receive(:rm).with(migration_path)
38
+
39
+ subject.delete_migration
40
+ end
41
+
42
+ context 'when it has been ran' do
43
+ let(:ran_migration) { mock }
44
+
45
+ it 'deletes the migration for the database' do
46
+ ran_migration.should_receive(:delete)
47
+
48
+ subject.delete_migration
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,53 @@
1
+ require_relative '../../../lib/mince_migrator/list'
2
+
3
+ describe MinceMigrator::List do
4
+ let(:migration_dir) { mock }
5
+
6
+ before do
7
+ MinceMigrator::Config.stub(migration_dir: migration_dir)
8
+ end
9
+
10
+ context 'getting all migrations' do
11
+ context 'when there are no migrations' do
12
+ before do
13
+ Dir.stub(:glob).with("#{migration_dir}/*").and_return([])
14
+ end
15
+
16
+ it 'is empty' do
17
+ subject.all.should be_empty
18
+ end
19
+
20
+ its(:number_of_migrations) { should == 0 }
21
+ end
22
+
23
+ context 'when some migrations exist' do
24
+ let(:migration_paths) { [migration_path1, migration_path2] }
25
+ let(:migration_path1) { mock }
26
+ let(:migration_path2) { mock }
27
+ let(:migration1) { mock time_created: Time.now.utc, status: 'ran' }
28
+ let(:migration2) { mock time_created: Time.now.utc - 500000, status: 'not ran' }
29
+
30
+ before do
31
+ Dir.stub(:glob).with("#{migration_dir}/*").and_return(migration_paths)
32
+ MinceMigrator::Migration.stub(:load_from_file).with(migration_path1).and_return(migration1)
33
+ MinceMigrator::Migration.stub(:load_from_file).with(migration_path2).and_return(migration2)
34
+ end
35
+
36
+ its(:number_of_migrations) { should == 2 }
37
+
38
+ it 'can load all migrations' do
39
+ subject.all.should == [migration2, migration1]
40
+ end
41
+
42
+ context 'when only wanting a list of migrations that have not yet ran' do
43
+ subject { described_class.new 'not ran' }
44
+
45
+ its(:number_of_migrations) { should == 1 }
46
+
47
+ it 'can load those migrations' do
48
+ subject.all.should == [migration2]
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end