migrext 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/features/import.feature +60 -0
- data/features/step_definitions/import.rb +85 -0
- data/features/support/base.rb +7 -0
- data/lib/migrext.rb +33 -0
- data/lib/migrext/importer.rb +25 -0
- data/lib/migrext/migration.rb +63 -0
- data/lib/migrext/migration_set.rb +28 -0
- data/lib/migrext/migrator.rb +14 -0
- data/lib/migrext/rails.rb +73 -0
- data/lib/tasks/migrext.rake +8 -0
- data/rails/init.rb +1 -0
- metadata +71 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
Feature: Import migrations
|
3
|
+
|
4
|
+
Scenario Outline: Support for Rails versions
|
5
|
+
Given a Rails <version> application
|
6
|
+
And a migration #1 in root
|
7
|
+
And a migration #2 in blue
|
8
|
+
And a migration #3 in red
|
9
|
+
When import all migrations
|
10
|
+
And migrate the database
|
11
|
+
Then migrations 1, 2 and 3 are loaded
|
12
|
+
|
13
|
+
Examples:
|
14
|
+
| version |
|
15
|
+
| 2 |
|
16
|
+
| 3 |
|
17
|
+
|
18
|
+
Scenario: migration versions have to be unique
|
19
|
+
If two extensions has migrations with the same number
|
20
|
+
the importer have to increment them to avoid collisions.
|
21
|
+
|
22
|
+
Given a Rails 3 application
|
23
|
+
And a migration #1 named "foo" in root
|
24
|
+
And a migration #1 named "bar" in blue
|
25
|
+
And a migration #1 named "baz" in red
|
26
|
+
When import all migrations
|
27
|
+
Then migration #1 is named "foo"
|
28
|
+
And migration #2 is named "blue_bar"
|
29
|
+
And migration #3 is named "red_baz"
|
30
|
+
|
31
|
+
Scenario: migration versions have to be greater than database version
|
32
|
+
If we try to import a migration with a number smaller than the
|
33
|
+
database version the importer has to increment migrations versions
|
34
|
+
|
35
|
+
Given a Rails 3 application
|
36
|
+
And a migration #30 named "one" in root
|
37
|
+
And a migration #100 named "two" in root
|
38
|
+
And a migration #15 named "three" in red
|
39
|
+
And a migration #50 named "four" in red
|
40
|
+
When migrate the database
|
41
|
+
And import all migrations
|
42
|
+
Then migration #30 is named "one"
|
43
|
+
And migration #100 is named "two"
|
44
|
+
And migration #101 is named "red_three"
|
45
|
+
And migration #102 is named "red_four"
|
46
|
+
|
47
|
+
Scenario: migration versions are kept if database version is smaller
|
48
|
+
The same migrations in the previous scenario, but now database version
|
49
|
+
is smaller than the imported migration versions
|
50
|
+
|
51
|
+
Given a Rails 3 application
|
52
|
+
And a migration #30 named "one" in root
|
53
|
+
And a migration #100 named "two" in root
|
54
|
+
And a migration #15 named "three" in red
|
55
|
+
And a migration #50 named "four" in red
|
56
|
+
When import all migrations
|
57
|
+
Then migration #15 is named "red_three"
|
58
|
+
And migration #30 is named "one"
|
59
|
+
And migration #50 is named "red_four"
|
60
|
+
And migration #100 is named "two"
|
@@ -0,0 +1,85 @@
|
|
1
|
+
|
2
|
+
Given /^a Rails (\S+) application$/ do |version|
|
3
|
+
|
4
|
+
@app_path = "/tmp/rails-migrext/test#$$#{rand(10000)}"
|
5
|
+
|
6
|
+
Process.wait(fork do
|
7
|
+
gem_version = "~> #{version}"
|
8
|
+
require 'rubygems'
|
9
|
+
gem 'rails', gem_version
|
10
|
+
|
11
|
+
ARGV.clear
|
12
|
+
case version
|
13
|
+
when /^2/
|
14
|
+
ARGV << @app_path
|
15
|
+
when /^3/
|
16
|
+
ARGV << "new" << @app_path
|
17
|
+
end
|
18
|
+
ARGV << "-d" << "sqlite3"
|
19
|
+
|
20
|
+
STDOUT.reopen "/dev/null", "w"
|
21
|
+
load Gem.bin_path('rails', 'rails', gem_version)
|
22
|
+
end)
|
23
|
+
|
24
|
+
# Load MigrExt
|
25
|
+
@lib_initializer = "#@app_path/config/initializers/migrext-sources.rb"
|
26
|
+
File.open(@lib_initializer, "a") {|f| f.puts "$: << #{File.expand_path("../../../lib/", __FILE__).inspect}\nrequire 'migrext/migrator'" }
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
Given /^a migration \#(\d+) in (\w+)$/ do |migration_number, source|
|
31
|
+
name = "ext_test_#{migration_number.scan(/./).map {|d| (d.to_i + 10).to_s(32) }.join }"
|
32
|
+
Given "a migration ##{migration_number} named \"#{name}\" in #{source}"
|
33
|
+
end
|
34
|
+
|
35
|
+
Given /^a migration \#(\d+) named "(\w+)" in (\w+)$/ do |migration_number, migration_name, source|
|
36
|
+
|
37
|
+
@migrations ||= []
|
38
|
+
|
39
|
+
if source == "root"
|
40
|
+
base_dir = "#@app_path/db/migrate"
|
41
|
+
else
|
42
|
+
base_dir = "#@app_path/exts-migrations-#{source}/"
|
43
|
+
end
|
44
|
+
|
45
|
+
if not File.directory?(base_dir)
|
46
|
+
Dir.mkdir base_dir
|
47
|
+
|
48
|
+
if source != "root"
|
49
|
+
File.open(@lib_initializer, "a") {|f| f.puts "ActiveRecord::Migrator.add_external_sources '#{source}', '#{base_dir}'" }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
migration_file = "#{base_dir}/#{migration_number}_#{migration_name}.rb"
|
54
|
+
File.exist?(migration_file).should be_false
|
55
|
+
|
56
|
+
File.open(migration_file, "w") do |f|
|
57
|
+
f.puts "class #{migration_name.camelize} < ActiveRecord::Migration\ndef self.down\nend\ndef self.up\nend\nend"
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
When /^import all migrations$/ do
|
63
|
+
MigrExt::RailsApplication.new(@app_path).import_externals!
|
64
|
+
end
|
65
|
+
|
66
|
+
When /^migrate the database$/ do
|
67
|
+
Process.wait(fork do
|
68
|
+
STDOUT.reopen "/dev/null", "w"
|
69
|
+
Dir.chdir @app_path
|
70
|
+
exec "rake", "db:migrate"
|
71
|
+
end)
|
72
|
+
end
|
73
|
+
|
74
|
+
Then /^migrations (.*) are loaded$/ do |migration_numbers|
|
75
|
+
migrations = MigrExt::RailsApplication.new(@app_path).current_migrations.map {|m| m.original_version }
|
76
|
+
migration_numbers.scan(/\d+/).each do |version|
|
77
|
+
migrations.should include(version.to_i)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
Then /^migration \#(\d+) is named "([^"]*)"$/ do |version, name|
|
82
|
+
versions = Dir["#@app_path/db/migrate/#{version}_*.rb"]
|
83
|
+
versions.length.should eql(1)
|
84
|
+
(versions.first =~ /\d+_(\w+)\.rb$/ && $1).should eql(name)
|
85
|
+
end
|
data/lib/migrext.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
require 'migrext/importer'
|
3
|
+
require 'migrext/rails'
|
4
|
+
require 'migrext/migration'
|
5
|
+
require 'migrext/migration_set'
|
6
|
+
|
7
|
+
if not "".respond_to?(:camelize)
|
8
|
+
class String
|
9
|
+
def camelize
|
10
|
+
gsub(/(?:^|_)(.)/) { $1.upcase }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
if not "".respond_to?(:underscore)
|
16
|
+
class String
|
17
|
+
def underscore
|
18
|
+
gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
if defined? Rails::Railtie
|
24
|
+
class Migrext < Rails::Railtie
|
25
|
+
rake_tasks do
|
26
|
+
load "tasks/migrext.rake"
|
27
|
+
end
|
28
|
+
|
29
|
+
initializer :before, "migrext.loader" do
|
30
|
+
require "migrext/migrator"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module MigrExt
|
2
|
+
class Importer
|
3
|
+
attr_reader :application
|
4
|
+
|
5
|
+
def initialize(application)
|
6
|
+
@application = application
|
7
|
+
end
|
8
|
+
|
9
|
+
def run!
|
10
|
+
# Find available migrations
|
11
|
+
application.external_sources.map do |source|
|
12
|
+
set = MigrExt::MigrationSet.new(application, source[:name], source[:path])
|
13
|
+
|
14
|
+
set.current_migrations.each do |migration|
|
15
|
+
unless migration.imported?
|
16
|
+
migration.create_rooted_content!
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#def
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module MigrExt
|
2
|
+
class Migration
|
3
|
+
attr_reader :set, :filename
|
4
|
+
|
5
|
+
attr_reader :version, :name, :plugin
|
6
|
+
attr_reader :original_version, :original_name, :original_plugin
|
7
|
+
|
8
|
+
def initialize(set, filename)
|
9
|
+
@set = set
|
10
|
+
@filename = filename
|
11
|
+
|
12
|
+
if filename =~ /(\d+)_(\w+)\.rb$/i
|
13
|
+
@original_version = @version = $1.to_i
|
14
|
+
@original_name = @name = $2
|
15
|
+
@original_plugin = @plugin = @set.name
|
16
|
+
end
|
17
|
+
|
18
|
+
if File.read(filename) =~ /#\s+imported\s+migration\s+(\d+)\s+(\w+)\s+from\s+(\w+)/i
|
19
|
+
@version = $1.to_i
|
20
|
+
@name = $2
|
21
|
+
@plugin = $3
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other_migration)
|
26
|
+
version == other_migration.version && name == other_migration.name && plugin == other_migration.plugin
|
27
|
+
end
|
28
|
+
|
29
|
+
def imported?
|
30
|
+
set.app.current_migrations.include?(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_rooted_content!
|
34
|
+
if set.root?
|
35
|
+
raise RuntimeError, "Set has to be from an extension root"
|
36
|
+
end
|
37
|
+
|
38
|
+
content = File.read(filename)
|
39
|
+
name = set.name + "_" + self.name
|
40
|
+
content.gsub! /^class \w+/, "class #{name.camelize}"
|
41
|
+
content << "\n\n# imported migration #@version #@name from #@plugin\n"
|
42
|
+
|
43
|
+
version = self.version.to_i
|
44
|
+
|
45
|
+
if version < set.app.database_version
|
46
|
+
version = set.app.database_version + 1
|
47
|
+
end
|
48
|
+
|
49
|
+
while true
|
50
|
+
break unless set.app.current_migrations.any? {|migration| migration.original_version == version }
|
51
|
+
version = version + 1
|
52
|
+
end
|
53
|
+
|
54
|
+
set.app.create_migration! version, name, content
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
def inspect
|
59
|
+
"#<MigrExt::Migration #@version/#@name/#@plugin original[#@original_version/#@original_name/#@original_plugin] @set=#{@set.name.inspect}>"
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module MigrExt
|
2
|
+
class MigrationSet
|
3
|
+
attr_reader :app, :name, :path
|
4
|
+
|
5
|
+
def initialize(app, name, path)
|
6
|
+
@app = app
|
7
|
+
@name = name
|
8
|
+
@path = path
|
9
|
+
end
|
10
|
+
|
11
|
+
def root?
|
12
|
+
@name.nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
def current_migrations
|
16
|
+
@current_migrations ||= begin
|
17
|
+
Dir["#@path/*"].map do |filename|
|
18
|
+
if filename =~ /\d+_\w+\.rb$/
|
19
|
+
MigrExt::Migration.new self, filename
|
20
|
+
else
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end.compact
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
if defined?(ActiveRecord::Migrator)
|
4
|
+
|
5
|
+
class ActiveRecord::Migrator
|
6
|
+
cattr_accessor :external_sources
|
7
|
+
self.external_sources = []
|
8
|
+
|
9
|
+
def self.add_external_sources(name, path)
|
10
|
+
external_sources << { :name => name, :path => path }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module MigrExt
|
5
|
+
class RailsApplication
|
6
|
+
|
7
|
+
def initialize(base_dir)
|
8
|
+
@base_dir = base_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
def import_externals!
|
12
|
+
MigrExt::Importer.new(self).run!
|
13
|
+
end
|
14
|
+
|
15
|
+
def current_migrations
|
16
|
+
@current_migrations ||= MigrExt::MigrationSet.new(self, nil, "#@base_dir/db/migrate").current_migrations
|
17
|
+
end
|
18
|
+
|
19
|
+
def external_sources
|
20
|
+
@external_sources ||= begin
|
21
|
+
eval_in_application("ActiveRecord::Migrator.external_sources")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_migration!(version, name, content)
|
26
|
+
filename = "#@base_dir/db/migrate/#{version}_#{name.underscore}.rb"
|
27
|
+
raise RuntimeError, "Migration #{version}:#{name} exist" if File.exist?(filename)
|
28
|
+
|
29
|
+
File.open(filename, "w") {|f| f.write content }
|
30
|
+
@current_migrations = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def database_version
|
34
|
+
eval_in_application "ActiveRecord::Migrator.current_version"
|
35
|
+
end
|
36
|
+
|
37
|
+
def eval_in_application(code)
|
38
|
+
@drb_rails ||= begin
|
39
|
+
require 'drb'
|
40
|
+
rd, wr = IO.pipe
|
41
|
+
|
42
|
+
@rails_pid = fork do
|
43
|
+
rd.close
|
44
|
+
Dir.chdir @base_dir
|
45
|
+
|
46
|
+
def eval_code(code); eval code; end
|
47
|
+
|
48
|
+
require "#{@base_dir}/config/environment.rb"
|
49
|
+
|
50
|
+
DRb.start_service nil, self
|
51
|
+
wr.print DRb.uri
|
52
|
+
wr.close
|
53
|
+
|
54
|
+
DRb.thread.join
|
55
|
+
exit 1 # unreachable
|
56
|
+
end
|
57
|
+
wr.close
|
58
|
+
at_exit do
|
59
|
+
begin
|
60
|
+
Process.kill 15, @rails_pid
|
61
|
+
rescue Errno::ESRCH
|
62
|
+
# Process was died some time ago. Ignore the error.
|
63
|
+
true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
DRbObject.new(nil, rd.read)
|
68
|
+
end
|
69
|
+
|
70
|
+
@drb_rails.eval_code code
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "migrext/migrator"
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: migrext
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
version: "0.1"
|
9
|
+
platform: ruby
|
10
|
+
authors:
|
11
|
+
- Ayose Cazorla
|
12
|
+
autorequire:
|
13
|
+
bindir: bin
|
14
|
+
cert_chain: []
|
15
|
+
|
16
|
+
date: 2010-08-15 00:00:00 +01:00
|
17
|
+
default_executable:
|
18
|
+
dependencies: []
|
19
|
+
|
20
|
+
description: Manage migrations from external sources
|
21
|
+
email: setepo@gmail.com
|
22
|
+
executables: []
|
23
|
+
|
24
|
+
extensions: []
|
25
|
+
|
26
|
+
extra_rdoc_files: []
|
27
|
+
|
28
|
+
files:
|
29
|
+
- lib/migrext.rb
|
30
|
+
- lib/migrext/migrator.rb
|
31
|
+
- lib/migrext/migration_set.rb
|
32
|
+
- lib/migrext/migration.rb
|
33
|
+
- lib/migrext/importer.rb
|
34
|
+
- lib/migrext/rails.rb
|
35
|
+
- lib/tasks/migrext.rake
|
36
|
+
- rails/init.rb
|
37
|
+
- features/import.feature
|
38
|
+
- features/step_definitions/import.rb
|
39
|
+
- features/support/base.rb
|
40
|
+
has_rdoc: true
|
41
|
+
homepage:
|
42
|
+
licenses: []
|
43
|
+
|
44
|
+
post_install_message:
|
45
|
+
rdoc_options: []
|
46
|
+
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.6
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: Manage migrations from external sources
|
70
|
+
test_files: []
|
71
|
+
|