migrator 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/.autotest +29 -0
- data/History.txt +5 -0
- data/Manifest.txt +13 -0
- data/README.rdoc +209 -0
- data/Rakefile +23 -0
- data/lib/aef/migrator.rb +27 -0
- data/lib/aef/migrator/abstract_adapter.rb +26 -0
- data/lib/aef/migrator/adapter.rb +40 -0
- data/lib/aef/migrator/migrator.rb +170 -0
- data/spec/migrator_spec.rb +214 -0
- data/spec/mock_adapter.rb +47 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +25 -0
- metadata +116 -0
- metadata.gz.sig +0 -0
data.tar.gz.sig
ADDED
Binary file
|
data/.autotest
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
module Autotest::GnomeNotify
|
4
|
+
|
5
|
+
# Time notification will be displayed before disappearing automatically
|
6
|
+
EXPIRATION_IN_SECONDS = 2
|
7
|
+
ERROR_STOCK_ICON = "gtk-dialog-error"
|
8
|
+
SUCCESS_STOCK_ICON = "gtk-dialog-info"
|
9
|
+
|
10
|
+
# Convenience method to send an error notification message
|
11
|
+
#
|
12
|
+
# [stock_icon] Stock icon name of icon to display
|
13
|
+
# [title] Notification message title
|
14
|
+
# [message] Core message for the notification
|
15
|
+
def self.notify stock_icon, title, message
|
16
|
+
options = "-t #{EXPIRATION_IN_SECONDS * 1000} -i #{stock_icon}"
|
17
|
+
system "notify-send #{options} '#{title}' '#{message}'"
|
18
|
+
end
|
19
|
+
|
20
|
+
Autotest.add_hook :red do |at|
|
21
|
+
notify ERROR_STOCK_ICON, "Tests failed", "#{at.files_to_test.size} tests failed"
|
22
|
+
end
|
23
|
+
|
24
|
+
Autotest.add_hook :green do |at|
|
25
|
+
notify SUCCESS_STOCK_ICON, "All tests passed, good job!", ""
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
lib/aef/migrator/adapter.rb
|
2
|
+
lib/aef/migrator/migrator.rb
|
3
|
+
lib/aef/migrator/abstract_adapter.rb
|
4
|
+
lib/aef/migrator.rb
|
5
|
+
Manifest.txt
|
6
|
+
History.txt
|
7
|
+
Rakefile
|
8
|
+
.autotest
|
9
|
+
spec/spec_helper.rb
|
10
|
+
spec/mock_adapter.rb
|
11
|
+
spec/spec.opts
|
12
|
+
spec/migrator_spec.rb
|
13
|
+
README.rdoc
|
data/README.rdoc
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
= Migrator
|
2
|
+
|
3
|
+
* Project: https://rubyforge.org/projects/migrator/
|
4
|
+
* RDoc: http://migrator.rubyforge.org/
|
5
|
+
* Github: http://github.com/aef/migrator/
|
6
|
+
|
7
|
+
== DESCRIPTION:
|
8
|
+
|
9
|
+
Migrator is a Ruby library for building general purpose versioning systems in
|
10
|
+
the style of ActiveRecord's migrations.
|
11
|
+
|
12
|
+
== FEATURES/PROBLEMS:
|
13
|
+
|
14
|
+
* Completely written in Ruby
|
15
|
+
* Tested and fully working on:
|
16
|
+
* Ubuntu Linux 8.10 amd64/x86_64
|
17
|
+
* Ruby 1.8.7p72
|
18
|
+
* Ruby 1.9.1p129
|
19
|
+
* Debian GNU/Linux 4.0 x86
|
20
|
+
* Ruby 1.8.6
|
21
|
+
* On Windows XP x86
|
22
|
+
* Ruby 1.8.6
|
23
|
+
|
24
|
+
== SYNOPSIS:
|
25
|
+
|
26
|
+
Load the gem:
|
27
|
+
|
28
|
+
require 'aef/migrator'
|
29
|
+
|
30
|
+
Build an adapter:
|
31
|
+
|
32
|
+
class Adapter
|
33
|
+
include Aef::Migrator::Adapter
|
34
|
+
|
35
|
+
# version() has to reflect the current version. Also version=() must exist
|
36
|
+
# to set the current version
|
37
|
+
attr_accessor :version,
|
38
|
+
|
39
|
+
# versions() has to provide a list of all versions in correct order
|
40
|
+
attr_accessor :versions
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
self.versions = %w{a b c d e}
|
44
|
+
self.version = 'c'
|
45
|
+
end
|
46
|
+
|
47
|
+
# process() has to do the work behind a migration in forward direction. Will
|
48
|
+
# be called multiple times for multistep migrations.
|
49
|
+
def process(target_version)
|
50
|
+
puts "Migrating from #{version} to #{target_version}"
|
51
|
+
|
52
|
+
self.version = target_version
|
53
|
+
end
|
54
|
+
|
55
|
+
# revert() has to do the work behind a migration in backward direction. Will
|
56
|
+
# be called multiple times for multistep migrations.
|
57
|
+
def revert(target_version)
|
58
|
+
puts "Unmigrating from #{version} to #{target_version}"
|
59
|
+
|
60
|
+
self.version = target_version
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Notice that the module Aef::Migrator::Adapter and the class
|
65
|
+
Aef::Migrator::AbstractAdapter both provide the basic method definitions for an
|
66
|
+
adapter but no functionality. The advantage are better error messages when you
|
67
|
+
forget to implement something, but you don't have to use either, as
|
68
|
+
Aef::Migrator defines an adapter only by checking for required methods.
|
69
|
+
|
70
|
+
Now we setup the migrator:
|
71
|
+
|
72
|
+
migrator = Aef::Migrator.new(Adapter.new)
|
73
|
+
|
74
|
+
After that we can use the versioning backend provided by the adapter to do
|
75
|
+
migrations:
|
76
|
+
|
77
|
+
migrator.version
|
78
|
+
# => "c"
|
79
|
+
|
80
|
+
migrator.up
|
81
|
+
migrator.version
|
82
|
+
# => "d"
|
83
|
+
|
84
|
+
migrator.up.version
|
85
|
+
# => "e"
|
86
|
+
|
87
|
+
migrator.down('b')
|
88
|
+
migrator.version
|
89
|
+
# => "b"
|
90
|
+
|
91
|
+
migrator.migrate('e').version
|
92
|
+
# => "e"
|
93
|
+
|
94
|
+
When migrations cannot be done, exceptions of the baseclass Aef::Migrator::Error
|
95
|
+
are raised. If there is a problem with the Adapter the exceptions will be of
|
96
|
+
Aef::Migrator::AdapterError which is a subclass of Aef::Migrator::Error.
|
97
|
+
Exceptions informing about unmatched migration constraints are from the subclass
|
98
|
+
Aef::Migrator::MigrationError.
|
99
|
+
|
100
|
+
We can also check if a migration is possible before running it. This way we
|
101
|
+
don't have to deal with exceptions when a migration cannot be done.
|
102
|
+
|
103
|
+
migrator.migratable?('e')
|
104
|
+
# => false
|
105
|
+
|
106
|
+
migrator.upable?
|
107
|
+
# => false
|
108
|
+
|
109
|
+
migrator.downable?('c')
|
110
|
+
# => true
|
111
|
+
|
112
|
+
See the files in the spec subdirectory for further examples of usage.
|
113
|
+
|
114
|
+
== REQUIREMENTS:
|
115
|
+
|
116
|
+
* rubygems
|
117
|
+
|
118
|
+
=== Additional for automated testing
|
119
|
+
* hoe (>= 2.3.2)
|
120
|
+
* rspec (>= 1.2.8)
|
121
|
+
|
122
|
+
== INSTALL:
|
123
|
+
|
124
|
+
On *nix systems you may need to prefix the command with sudo to get root
|
125
|
+
privileges.
|
126
|
+
|
127
|
+
=== High security (recommended)
|
128
|
+
|
129
|
+
There is a high security installation option available through rubygems. It is
|
130
|
+
highly recommended over the normal installation, although it may be a bit less
|
131
|
+
comfortable. To use the installation method, you will need my public key, which
|
132
|
+
I use for cryptographic signatures on all my gems. You can find the public key
|
133
|
+
and more detailed verification information in the aef-certificates section of my
|
134
|
+
rubyforge project[https://rubyforge.org/frs/?group_id=7890&release_id=31749]
|
135
|
+
|
136
|
+
Add the key to your rubygems' trusted certificates by the following command:
|
137
|
+
|
138
|
+
gem cert --add aef.pem
|
139
|
+
|
140
|
+
Now you can install the gem while automatically verifying it's signature by the
|
141
|
+
following command:
|
142
|
+
|
143
|
+
gem install migrator --ignore-dependencies -P HighSecurity
|
144
|
+
|
145
|
+
Please notice that you may need other keys for dependent libraries, so you may
|
146
|
+
have to install dependencies manually.
|
147
|
+
|
148
|
+
=== Normal (insecure)
|
149
|
+
|
150
|
+
gem install migrator
|
151
|
+
|
152
|
+
=== Github (also insecure)
|
153
|
+
|
154
|
+
Alternatively you could install migrator from github which may be a bit more
|
155
|
+
up to date. The version may however not be as stable as the normal gem and there
|
156
|
+
is no way to install the gem securely. Therefore this is not recommended.
|
157
|
+
|
158
|
+
gem install aef-migrator --source http://gems.github.com
|
159
|
+
|
160
|
+
=== Automated testing
|
161
|
+
|
162
|
+
You can test this package through rspec on your system. First find the path
|
163
|
+
where the gem was installed to:
|
164
|
+
|
165
|
+
gem which aef/migrator
|
166
|
+
|
167
|
+
Go into the root directory of the installed gem and run the following command
|
168
|
+
to start the test runner:
|
169
|
+
|
170
|
+
rake spec
|
171
|
+
|
172
|
+
If something goes wrong you should be noticed through failing examples.
|
173
|
+
|
174
|
+
== DEVELOPMENT:
|
175
|
+
|
176
|
+
This software is developed in the source code management system git hosted
|
177
|
+
at github.com. You can download the most recent sourcecode through the following
|
178
|
+
command:
|
179
|
+
|
180
|
+
git clone git://github.com/aef/migrator.git
|
181
|
+
|
182
|
+
Help on making this software better is always very appreciated. If you want your
|
183
|
+
changes to be included in the official release, please send me a patch through
|
184
|
+
the project's tracker[https://rubyforge.org/tracker/?group_id=7890] at
|
185
|
+
rubyforge.org. You can generate a patch-file by the following command:
|
186
|
+
|
187
|
+
git diff > patch.diff
|
188
|
+
|
189
|
+
Please make sure to write tests for your changes and notice that I can't promise
|
190
|
+
to include your changes before reviewing them.
|
191
|
+
|
192
|
+
== LICENSE:
|
193
|
+
|
194
|
+
Copyright 2009 Alexander E. Fischer <aef@raxys.net>
|
195
|
+
|
196
|
+
This file is part of Migrator.
|
197
|
+
|
198
|
+
Migrator is free software: you can redistribute it and/or modify
|
199
|
+
it under the terms of the GNU General Public License as published by
|
200
|
+
the Free Software Foundation, either version 3 of the License, or
|
201
|
+
(at your option) any later version.
|
202
|
+
|
203
|
+
This program is distributed in the hope that it will be useful,
|
204
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
205
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
206
|
+
GNU General Public License for more details.
|
207
|
+
|
208
|
+
You should have received a copy of the GNU General Public License
|
209
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
|
6
|
+
Hoe.spec('migrator') do
|
7
|
+
developer('Alexander E. Fischer', 'aef@raxys.net')
|
8
|
+
|
9
|
+
self.rubyforge_name = 'migrator'
|
10
|
+
self.extra_dev_deps = {
|
11
|
+
'rspec' => '>= 1.2.8'
|
12
|
+
}
|
13
|
+
self.url = 'https://rubyforge.org/projects/migrator/'
|
14
|
+
self.readme_file = 'README.rdoc'
|
15
|
+
self.extra_rdoc_files = %w{README.rdoc}
|
16
|
+
self.spec_extras = {
|
17
|
+
:rdoc_options => ['--main', 'README.rdoc', '--inline-source', '--line-numbers', '--title', 'Migrator']
|
18
|
+
}
|
19
|
+
self.rspec_options = ['--options', 'spec/spec.opts']
|
20
|
+
self.remote_rdoc_dir = ''
|
21
|
+
end
|
22
|
+
|
23
|
+
# vim: syntax=Ruby
|
data/lib/aef/migrator.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# Copyright 2009 Alexander E. Fischer <aef@raxys.net>
|
2
|
+
#
|
3
|
+
# This file is part of Migrator.
|
4
|
+
#
|
5
|
+
# Migrator is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
# Namespace for projects of Alexander E. Fischer <aef@raxys.net>
|
19
|
+
#
|
20
|
+
# If you want to be able to simply type Example instead of Aef::Example to
|
21
|
+
# address classes in this namespace simply write the following before using the
|
22
|
+
# classes:
|
23
|
+
#
|
24
|
+
# include Aef
|
25
|
+
module Aef
|
26
|
+
autoload :Migrator, 'aef/migrator/migrator'
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Copyright 2009 Alexander E. Fischer <aef@raxys.net>
|
2
|
+
#
|
3
|
+
# This file is part of Migrator.
|
4
|
+
#
|
5
|
+
# Migrator is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
# This class implements the interface for an adapter required by Aef::Migrator
|
19
|
+
# and could be used as an abstract base class. See also: Adapter
|
20
|
+
class Aef::Migrator::AbstractAdapter
|
21
|
+
include Aef::Migrator::Adapter
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
raise NotImplementedError, 'Abstract class' if self.class == Aef::Migrator::AbstractAdapter
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# Copyright 2009 Alexander E. Fischer <aef@raxys.net>
|
2
|
+
#
|
3
|
+
# This file is part of Migrator.
|
4
|
+
#
|
5
|
+
# Migrator is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
# This module implements the interface for an adapter required by Aef::Migrator.
|
19
|
+
# See also: AbstractAdapter.
|
20
|
+
module Aef::Migrator::Adapter
|
21
|
+
def process(target_version)
|
22
|
+
raise NotImplementedError, 'The process method needs to be implemented'
|
23
|
+
end
|
24
|
+
|
25
|
+
def revert(target_version)
|
26
|
+
raise NotImplementedError, 'The revert method needs to be implemented'
|
27
|
+
end
|
28
|
+
|
29
|
+
def version
|
30
|
+
raise NotImplementedError, 'The version method needs to be implemented'
|
31
|
+
end
|
32
|
+
|
33
|
+
def version=(versions)
|
34
|
+
raise NotImplementedError, 'The version= method needs to be implemented'
|
35
|
+
end
|
36
|
+
|
37
|
+
def versions
|
38
|
+
raise NotImplementedError, 'The versions method needs to be implemented'
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# Copyright 2009 Alexander E. Fischer <aef@raxys.net>
|
2
|
+
#
|
3
|
+
# This file is part of Migrator.
|
4
|
+
#
|
5
|
+
# Migrator is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
# Class for building general purpose versioning systems in the style of
|
19
|
+
# ActiveRecord's migrations. An adapter object is needed for any instance of
|
20
|
+
# this class to have any use. See Adapter and AbstractAdapter for the required
|
21
|
+
# interface.
|
22
|
+
class Aef::Migrator
|
23
|
+
VERSION = '1.0.0'
|
24
|
+
|
25
|
+
# Baseclass for all Migrator specific exceptions
|
26
|
+
class Error < RuntimeError; end
|
27
|
+
|
28
|
+
# Baseclass for exceptions describing adapter errors
|
29
|
+
class AdapterError < Error; end
|
30
|
+
class AdapterMissingError < AdapterError; end
|
31
|
+
class AdapterMethodMissingError < AdapterError; end
|
32
|
+
class CurrentVersionInvalidError < AdapterError; end
|
33
|
+
|
34
|
+
# Baseclass for exceptions describing errors while processing migrations
|
35
|
+
class MigrationError < Error; end
|
36
|
+
class AlreadyOnTopError < MigrationError; end
|
37
|
+
class AlreadyOnBottomError < MigrationError; end
|
38
|
+
class TargetVersionInvalidError < MigrationError; end
|
39
|
+
class UpMigrationInvalidError < MigrationError; end
|
40
|
+
class DownMigrationInvalidError < MigrationError; end
|
41
|
+
class MigrationUnneccessaryError < MigrationError; end
|
42
|
+
|
43
|
+
autoload :Adapter, 'aef/migrator/adapter'
|
44
|
+
autoload :AbstractAdapter, 'aef/migrator/abstract_adapter'
|
45
|
+
|
46
|
+
attr_accessor :adapter
|
47
|
+
|
48
|
+
def initialize(adapter)
|
49
|
+
@adapter = adapter
|
50
|
+
end
|
51
|
+
|
52
|
+
# Checks if a migration is possible
|
53
|
+
def migratable?(target_version, direction = nil)
|
54
|
+
!!migration_steps(target_version, direction)
|
55
|
+
rescue MigrationError
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
# Checks if an up migration is possible
|
60
|
+
def upable?(target_version = nil)
|
61
|
+
migratable?(target_version, :up)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Checks if a down migration is possible
|
65
|
+
def downable?(target_version = nil)
|
66
|
+
migratable?(target_version, :down)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Process a migration
|
70
|
+
#
|
71
|
+
# Raises MigratorError in case of invalid actions
|
72
|
+
def migrate(target_version, direction = nil)
|
73
|
+
params = migration_steps(target_version, direction)
|
74
|
+
|
75
|
+
params[:steps].each do |version|
|
76
|
+
@adapter.send(params[:action], version)
|
77
|
+
end
|
78
|
+
|
79
|
+
self
|
80
|
+
end
|
81
|
+
|
82
|
+
# Process an up migration
|
83
|
+
#
|
84
|
+
# Raises MigratorError in case of invalid actions
|
85
|
+
def up(target_version = nil)
|
86
|
+
migrate(target_version, :up)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Process a down migration
|
90
|
+
#
|
91
|
+
# Raises MigratorError in case of invalid actions
|
92
|
+
def down(target_version = nil)
|
93
|
+
migrate(target_version, :down)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Current version
|
97
|
+
def version
|
98
|
+
@adapter.version
|
99
|
+
end
|
100
|
+
|
101
|
+
# All possible versions in correct order
|
102
|
+
def versions
|
103
|
+
@adapter.versions
|
104
|
+
end
|
105
|
+
|
106
|
+
protected
|
107
|
+
|
108
|
+
# Returns a hash with an :action value and a :steps array with all versions
|
109
|
+
# to reach the target
|
110
|
+
def migration_steps(target_version, direction)
|
111
|
+
raise ArgumentError, 'Invalid direction' unless [nil, :up, :down].include?(direction)
|
112
|
+
|
113
|
+
unless @adapter
|
114
|
+
raise AdapterMissingError, 'No adapter set'
|
115
|
+
else
|
116
|
+
Adapter.instance_methods(false).each do |method|
|
117
|
+
raise AdapterMethodMissingError, "Adapter must respond to #{method}" unless @adapter.respond_to?(method)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
version_list = versions
|
122
|
+
current_version = version
|
123
|
+
|
124
|
+
indices = {:current => version_list.index(current_version)}
|
125
|
+
|
126
|
+
raise CurrentVersionInvalidError, "Current version not in version list: " +
|
127
|
+
"'#{current_version}'" unless indices[:current]
|
128
|
+
|
129
|
+
# When direction is set and target_version is unset step up/down by one
|
130
|
+
unless target_version
|
131
|
+
case direction
|
132
|
+
when :up
|
133
|
+
step_up = indices[:current] + 1
|
134
|
+
raise AlreadyOnTopError, 'Already on highest version' unless step_up < version_list.size
|
135
|
+
indices[:target] = step_up
|
136
|
+
target_version = version_list[step_up]
|
137
|
+
when :down
|
138
|
+
step_down = indices[:current] - 1
|
139
|
+
raise AlreadyOnBottomError, 'Already on lowest version' unless step_down >= 0
|
140
|
+
indices[:target] = step_down
|
141
|
+
target_version = version_list[step_down]
|
142
|
+
else
|
143
|
+
raise ArgumentError, 'No target version specified'
|
144
|
+
end
|
145
|
+
else
|
146
|
+
indices[:target] = version_list.index(target_version)
|
147
|
+
|
148
|
+
raise TargetVersionInvalidError, 'Target version not in version list: ' +
|
149
|
+
"'#{target_version}'" unless indices[:target]
|
150
|
+
end
|
151
|
+
|
152
|
+
if indices[:current] < indices[:target]
|
153
|
+
raise DownMigrationInvalidError, "Current version '#{current_version}' " +
|
154
|
+
"is lower than '#{target_version}'" if direction == :down
|
155
|
+
|
156
|
+
indices[:current] += 1
|
157
|
+
|
158
|
+
{:action => :process, :steps => version_list[indices.values.min..indices.values.max]}
|
159
|
+
elsif indices[:current] > indices[:target]
|
160
|
+
raise UpMigrationInvalidError, "Current version '#{current_version}' " +
|
161
|
+
"is higher than '#{target_version}'" if direction == :up
|
162
|
+
|
163
|
+
indices[:current] -= 1
|
164
|
+
|
165
|
+
{:action => :revert, :steps => version_list[indices.values.min..indices.values.max].reverse}
|
166
|
+
else
|
167
|
+
raise MigrationUnneccessaryError, "Current version equals target version: '#{current_version}'"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,214 @@
|
|
1
|
+
# Copyright 2009 Alexander E. Fischer <aef@raxys.net>
|
2
|
+
#
|
3
|
+
# This file is part of Migrator.
|
4
|
+
#
|
5
|
+
# Migrator is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
require 'spec/spec_helper'
|
19
|
+
|
20
|
+
describe Aef::Migrator do
|
21
|
+
before(:all) do
|
22
|
+
class BrokenAdapter < described_class::AbstractAdapter
|
23
|
+
undef :process
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
before(:each) do
|
28
|
+
@adapter = MockAdapter.new
|
29
|
+
@adapter.verbose = false
|
30
|
+
@migrator = described_class.new(@adapter)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should complain about invalid direction params" do
|
34
|
+
lambda {
|
35
|
+
@migrator.migrate('a', :invalid)
|
36
|
+
}.should raise_error(ArgumentError)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should complain when no adapter is set" do
|
40
|
+
lambda {
|
41
|
+
@migrator.adapter = nil
|
42
|
+
@migrator.migrate('a')
|
43
|
+
}.should raise_error(described_class::AdapterMissingError)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should complain about missing adapter methods" do
|
47
|
+
lambda {
|
48
|
+
@migrator.adapter = BrokenAdapter.new
|
49
|
+
@migrator.migrate('a')
|
50
|
+
}.should raise_error(described_class::AdapterMethodMissingError)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should complain about current versions which are not in versions" do
|
54
|
+
lambda {
|
55
|
+
@adapter.version = 'f'
|
56
|
+
@migrator.migrate('a')
|
57
|
+
}.should raise_error(described_class::CurrentVersionInvalidError)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should complain about target versions which are not in versions" do
|
61
|
+
lambda {
|
62
|
+
@migrator.migrate('invalid')
|
63
|
+
}.should raise_error(described_class::TargetVersionInvalidError)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should complain about migrating to the current version" do
|
67
|
+
lambda {
|
68
|
+
@migrator.migrate('c')
|
69
|
+
}.should raise_error(described_class::MigrationUnneccessaryError)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should be able to implicitly migrate one version up" do
|
73
|
+
lambda {
|
74
|
+
@migrator.up
|
75
|
+
}.should change{@migrator.version}.from('c').to('d')
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should be able to implicitly migrate one version down" do
|
79
|
+
lambda {
|
80
|
+
@migrator.down
|
81
|
+
}.should change{@migrator.version}.from('c').to('b')
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should be able to explicitly migrate one version up" do
|
85
|
+
lambda {
|
86
|
+
@migrator.up('d')
|
87
|
+
}.should change{@migrator.version}.from('c').to('d')
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should be able to explicitly migrate one version down" do
|
91
|
+
lambda {
|
92
|
+
@migrator.down('b')
|
93
|
+
}.should change{@migrator.version}.from('c').to('b')
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should be able to migrate multiple version up with one call" do
|
97
|
+
lambda {
|
98
|
+
@migrator.up('e')
|
99
|
+
}.should change{@migrator.version}.from('c').to('e')
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should be able to migrate multiple version down with one call" do
|
103
|
+
lambda {
|
104
|
+
@migrator.down('a')
|
105
|
+
}.should change{@migrator.version}.from('c').to('a')
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should prohibit migration in wrong direction when up method is used" do
|
109
|
+
lambda {
|
110
|
+
@migrator.up('a')
|
111
|
+
}.should raise_error(described_class::UpMigrationInvalidError)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should prohibit migration in wrong direction when down method is used" do
|
115
|
+
lambda {
|
116
|
+
@migrator.down('e')
|
117
|
+
}.should raise_error(described_class::DownMigrationInvalidError)
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should allow up migration with generic migrate method" do
|
121
|
+
lambda {
|
122
|
+
@migrator.migrate('e')
|
123
|
+
}.should change{@migrator.version}.from('c').to('e')
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should allow down migration with generic migrate method" do
|
127
|
+
lambda {
|
128
|
+
@migrator.migrate('a')
|
129
|
+
}.should change{@migrator.version}.from('c').to('a')
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should complain about invalid direction params when migratable? is called" do
|
133
|
+
lambda {
|
134
|
+
@migrator.migratable?('a', :invalid)
|
135
|
+
}.should raise_error(ArgumentError)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should complain when no adapter is set when migratable? is called" do
|
139
|
+
lambda {
|
140
|
+
@migrator.adapter = nil
|
141
|
+
@migrator.migrate('a')
|
142
|
+
}.should raise_error(described_class::AdapterMissingError)
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should complain about missing adapter methods when migratable? is called" do
|
146
|
+
lambda {
|
147
|
+
@migrator.adapter = BrokenAdapter.new
|
148
|
+
@migrator.migratable?('a')
|
149
|
+
}.should raise_error(described_class::AdapterMethodMissingError)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should complain about current versions which are not in versions when migratable? is called" do
|
153
|
+
lambda {
|
154
|
+
@adapter.version = 'f'
|
155
|
+
@migrator.migratable?('a')
|
156
|
+
}.should raise_error(described_class::CurrentVersionInvalidError)
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should indicate that a migration to an invalid target version is impossible" do
|
160
|
+
@migrator.migratable?('invalid').should be_false
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should indicate that implicitly migrating one version up is possible" do
|
164
|
+
@migrator.upable?.should be_true
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should indicate that implicitly migrating one version down is possible" do
|
168
|
+
@migrator.downable?.should be_true
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should indicate that migrating one version up is possible" do
|
172
|
+
@migrator.upable?('d').should be_true
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should indicate that migrating one version down is possible" do
|
176
|
+
@migrator.downable?('b').should be_true
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should indicate that a multi-step migration up is possible" do
|
180
|
+
@migrator.upable?('e').should be_true
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should indicate that a multi-step migration down is possible" do
|
184
|
+
@migrator.downable?('a').should be_true
|
185
|
+
end
|
186
|
+
|
187
|
+
it "should indicate a wrong direction migration is impossible when upable? method is used" do
|
188
|
+
@migrator.upable?('a').should be_false
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should indicate a wrong direction migration is impossible when downable? method is used" do
|
192
|
+
@migrator.downable?('e').should be_false
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should indicate that up migration is possible when migratable? method is used" do
|
196
|
+
@migrator.migratable?('e').should be_true
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should indicate that down migration is possible when migratable? method is used" do
|
200
|
+
@migrator.migratable?('a').should be_true
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should return itself for method changing through method migrate" do
|
204
|
+
@migrator.migrate('d').should eql(@migrator)
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should return itself for method changing through method up" do
|
208
|
+
@migrator.up.should eql(@migrator)
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should return itself for method changing through method down" do
|
212
|
+
@migrator.down.should eql(@migrator)
|
213
|
+
end
|
214
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright 2009 Alexander E. Fischer <aef@raxys.net>
|
2
|
+
#
|
3
|
+
# This file is part of Migrator.
|
4
|
+
#
|
5
|
+
# Migrator is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
class MockAdapter < Aef::Migrator::AbstractAdapter
|
19
|
+
attr_accessor :version, :versions_hash, :verbose
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
@versions_hash = {
|
23
|
+
'a' => 'a.version',
|
24
|
+
'b' => 'b.version',
|
25
|
+
'c' => 'c.version',
|
26
|
+
'd' => 'd.version',
|
27
|
+
'e' => 'e.version'
|
28
|
+
}
|
29
|
+
self.version = 'c'
|
30
|
+
end
|
31
|
+
|
32
|
+
def process(target_version)
|
33
|
+
puts "Migrating from #{version} to #{target_version} (#{versions_hash[target_version]})" if @verbose
|
34
|
+
|
35
|
+
self.version = target_version
|
36
|
+
end
|
37
|
+
|
38
|
+
def revert(target_version)
|
39
|
+
puts "Unmigrating from #{version} to #{target_version} (#{versions_hash[version]})" if @verbose
|
40
|
+
|
41
|
+
self.version = target_version
|
42
|
+
end
|
43
|
+
|
44
|
+
def versions
|
45
|
+
versions_hash.keys.sort
|
46
|
+
end
|
47
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Copyright 2009 Alexander E. Fischer <aef@raxys.net>
|
2
|
+
#
|
3
|
+
# This file is part of Migrator.
|
4
|
+
#
|
5
|
+
# Migrator is free software: you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation, either version 3 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License
|
16
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
17
|
+
|
18
|
+
$LOAD_PATH.unshift('lib')
|
19
|
+
|
20
|
+
require 'aef/migrator'
|
21
|
+
require 'spec/mock_adapter'
|
22
|
+
|
23
|
+
Spec::Runner.configure do |config|
|
24
|
+
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: migrator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexander E. Fischer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDKDCCAhCgAwIBAgIBADANBgkqhkiG9w0BAQUFADA6MQwwCgYDVQQDDANhZWYx
|
14
|
+
FTATBgoJkiaJk/IsZAEZFgVyYXh5czETMBEGCgmSJomT8ixkARkWA25ldDAeFw0w
|
15
|
+
OTAyMjUyMDM5MDhaFw0xMDAyMjUyMDM5MDhaMDoxDDAKBgNVBAMMA2FlZjEVMBMG
|
16
|
+
CgmSJomT8ixkARkWBXJheHlzMRMwEQYKCZImiZPyLGQBGRYDbmV0MIIBIjANBgkq
|
17
|
+
hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoYtj0qad5/MpbdttITzBH0h1SNe6eO7R
|
18
|
+
7qVeqNYu6qDQAQ0rYc0JhubJCWYrZEJorHEBhUFU6cdQgQOs78wiJaDgkeU7YfXB
|
19
|
+
q2l125kJ8aHkA1ukrK2/DRzp2AHEmzxHIYpXV5/63h+NWjCP+uKvTELYsotS2MKt
|
20
|
+
3d43E0QajsPZu2ZuNFwkroqeue872gMHUldGOVy5WtSd9ajw2xI/CociY6746dL+
|
21
|
+
pYriV3QaYtR/ezeaLpKBLsc5T1UQ07t7Xs7mI281tdmRvpLdP5dQhjzInfio0CJv
|
22
|
+
1Pf5bZUjGG0K9RW2Gb/tGPSYEETiLMubjH61OwBooXKiWR5cs4/1BQIDAQABozkw
|
23
|
+
NzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUSYvjhG2EWnR5kx5l
|
24
|
+
DAewXCkJOVEwDQYJKoZIhvcNAQEFBQADggEBAB2ryDbU4bQtnunKv/AXq4CuO3LS
|
25
|
+
kik9Xhye8E/5dTcsgitCZJXAqx0rHcK0u2EHnjA5CDcdF5JB7XgSvRrQkFWoW/9K
|
26
|
+
lCB4ih+sB2AI2IUiYBeoCGctXdBQ020prqop/oTQEudzFk/gyQ686lp06HdLRt+O
|
27
|
+
HoQjTIab8vmfgIubjPdIRzokMfHbelvhLi+mQfWVghRhs2jpEfdXbL0w5nNw+trp
|
28
|
+
rO70Dw59hduDUOpgpxew+PLbs4vP1tb1QKPG+39C+PZtosbbf1fai0hqYV1txMCx
|
29
|
+
55akF+N8NbO6tpVDy6TMagqa10LfEpiQH6dvDHe/xdAqYOCrXKpmqzwu2PI=
|
30
|
+
-----END CERTIFICATE-----
|
31
|
+
|
32
|
+
date: 2009-09-17 00:00:00 +02:00
|
33
|
+
default_executable:
|
34
|
+
dependencies:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: rspec
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.2.8
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: hoe
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.3.2
|
54
|
+
version:
|
55
|
+
description: |-
|
56
|
+
Migrator is a Ruby library for building general purpose versioning systems in
|
57
|
+
the style of ActiveRecord's migrations.
|
58
|
+
email:
|
59
|
+
- aef@raxys.net
|
60
|
+
executables: []
|
61
|
+
|
62
|
+
extensions: []
|
63
|
+
|
64
|
+
extra_rdoc_files:
|
65
|
+
- Manifest.txt
|
66
|
+
- History.txt
|
67
|
+
- README.rdoc
|
68
|
+
files:
|
69
|
+
- lib/aef/migrator/adapter.rb
|
70
|
+
- lib/aef/migrator/migrator.rb
|
71
|
+
- lib/aef/migrator/abstract_adapter.rb
|
72
|
+
- lib/aef/migrator.rb
|
73
|
+
- Manifest.txt
|
74
|
+
- History.txt
|
75
|
+
- Rakefile
|
76
|
+
- .autotest
|
77
|
+
- spec/spec_helper.rb
|
78
|
+
- spec/mock_adapter.rb
|
79
|
+
- spec/spec.opts
|
80
|
+
- spec/migrator_spec.rb
|
81
|
+
- README.rdoc
|
82
|
+
has_rdoc: true
|
83
|
+
homepage: https://rubyforge.org/projects/migrator/
|
84
|
+
licenses: []
|
85
|
+
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options:
|
88
|
+
- --main
|
89
|
+
- README.rdoc
|
90
|
+
- --inline-source
|
91
|
+
- --line-numbers
|
92
|
+
- --title
|
93
|
+
- Migrator
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: "0"
|
101
|
+
version:
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: "0"
|
107
|
+
version:
|
108
|
+
requirements: []
|
109
|
+
|
110
|
+
rubyforge_project: migrator
|
111
|
+
rubygems_version: 1.3.5
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: Migrator is a Ruby library for building general purpose versioning systems in the style of ActiveRecord's migrations.
|
115
|
+
test_files: []
|
116
|
+
|
metadata.gz.sig
ADDED
Binary file
|