migrator 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.
- 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
|