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.
@@ -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
@@ -0,0 +1,7 @@
1
+
2
+ @migrext_lib = File.expand_path("../../../lib/", __FILE__)
3
+ if not $:.include?(@migrext_lib)
4
+ $: << @migrext_lib
5
+ end
6
+
7
+ require 'migrext'
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
@@ -0,0 +1,8 @@
1
+
2
+ namespace :migrext do
3
+ desc "Import all external migrations using MigrExt"
4
+ task :import do
5
+ require 'migrext'
6
+ MigrExt::RailsApplication.new(Rake.original_dir).import_externals!
7
+ end
8
+ 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
+