mince_migrator 0.0.1 → 1.0.0

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