migration_collapser 0.1.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/.gitignore +6 -0
- data/GPL_LICENSE +674 -0
- data/MIT_LICENSE +22 -0
- data/README.rdoc +21 -0
- data/Rakefile +6 -0
- data/VERSION +1 -0
- data/bin/collapse_migrations +4 -0
- data/lib/migration_collapser/file_replacer.rb +64 -0
- data/lib/migration_collapser/rails_loader.rb +19 -0
- data/lib/migration_collapser/revision_finder.rb +13 -0
- data/lib/migration_collapser/runner.rb +9 -0
- data/lib/migration_collapser/templates/initial_schema_migration.rb +70 -0
- data/lib/migration_collapser/version.rb +9 -0
- data/lib/migration_collapser.rb +11 -0
- data/rake_tasks/flog.rb +10 -0
- data/rake_tasks/gem.rb +27 -0
- data/rake_tasks/rdoc.rb +16 -0
- data/rake_tasks/rspec.rb +20 -0
- data/rake_tasks/sloc.rb +16 -0
- data/rake_tasks/tags.rb +23 -0
- data/spec/migration_collapser/file_replacer_spec.rb +130 -0
- data/spec/migration_collapser/rails_loader_spec.rb +14 -0
- data/spec/migration_collapser/revision_finder_spec.rb +33 -0
- data/spec/migration_collapser/runner_spec.rb +45 -0
- data/spec/migration_collapser/version_spec.rb +21 -0
- data/spec/mysql_connection.yml +6 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +19 -0
- metadata +96 -0
data/MIT_LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2009 Scott Taylor (smtlaissezfaire) <scott@railsnewbie.com>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
= Migration Collapser
|
2
|
+
|
3
|
+
Collapse your rails migrations
|
4
|
+
|
5
|
+
== Installation
|
6
|
+
|
7
|
+
gem install migration_collapser
|
8
|
+
|
9
|
+
== Usage
|
10
|
+
|
11
|
+
First, load your production database:
|
12
|
+
|
13
|
+
mysql -u root my_dev_db < my_production_db.sql
|
14
|
+
|
15
|
+
Dump your production database (without data) in db/migrate/initial_schema.sql
|
16
|
+
|
17
|
+
mysqldump -u root --no-data my_dev_db > db/migrate/initial_schema.sql
|
18
|
+
|
19
|
+
Run the migration collapser from RAILS_ROOT:
|
20
|
+
|
21
|
+
collapse_migrations
|
data/Rakefile
ADDED
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module MigrationCollapser
|
2
|
+
class FileReplacer
|
3
|
+
PATTERN = /(\d+)\_.+\.rb/
|
4
|
+
|
5
|
+
def initialize(revision)
|
6
|
+
self.revision = revision
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :revision
|
10
|
+
|
11
|
+
def revision=(num)
|
12
|
+
@revision = num.to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
def replace
|
16
|
+
delete_files
|
17
|
+
create_migration
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete_files
|
21
|
+
files = Dir.glob("db/migrate/*.rb")
|
22
|
+
file_map = new_migration_map(files)
|
23
|
+
|
24
|
+
check_files(file_map)
|
25
|
+
|
26
|
+
file_map.each do |file, number|
|
27
|
+
if number <= revision
|
28
|
+
FileUtils.rm(file)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def create_migration
|
34
|
+
cp "templates/initial_schema_migration.rb", "db/migrate/#{revision}_initial_schema.rb"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def cp(template, path)
|
40
|
+
FileUtils.cp(File.dirname(__FILE__) + "/#{template}", path)
|
41
|
+
end
|
42
|
+
|
43
|
+
def check_files(file_map)
|
44
|
+
raise "No files in db/migrate" if file_map.empty?
|
45
|
+
|
46
|
+
if file_map.none? { |_, number| number == revision }
|
47
|
+
raise "No migration matching revision #{revision}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def new_migration_map(files)
|
52
|
+
hash = {}
|
53
|
+
|
54
|
+
files.each do |file|
|
55
|
+
file =~ PATTERN
|
56
|
+
number = $1.to_i
|
57
|
+
|
58
|
+
hash[file] = number
|
59
|
+
end
|
60
|
+
|
61
|
+
hash
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MigrationCollapser
|
2
|
+
module RailsLoader
|
3
|
+
class << self
|
4
|
+
def load
|
5
|
+
require expand_path(Dir.getwd, "config", "environment")
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def expand_path(*dirs)
|
11
|
+
File.expand_path(join(*dirs))
|
12
|
+
end
|
13
|
+
|
14
|
+
def join(*dirs)
|
15
|
+
File.join(*dirs)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module MigrationCollapser
|
4
|
+
class RevisionFinder
|
5
|
+
class SchemaInfo < ActiveRecord::Base
|
6
|
+
set_table_name "schema_migrations"
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.find
|
10
|
+
SchemaInfo.find(:first, :order => "version DESC").version
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
class InitialSchema < ActiveRecord::Migration
|
2
|
+
class << self
|
3
|
+
def up
|
4
|
+
class << connection = ActiveRecord::Base.connection
|
5
|
+
def username
|
6
|
+
@config[:username]
|
7
|
+
end
|
8
|
+
|
9
|
+
def host
|
10
|
+
@config[:host]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
file = File.read(File.dirname(__FILE__) + "/initial_schema.sql")
|
15
|
+
|
16
|
+
file.split(";").each do |sql_line|
|
17
|
+
execute(sql_line, true)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def down
|
22
|
+
drop_views
|
23
|
+
drop_tables
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def execute(sql_string, verbose = false)
|
29
|
+
if verbose
|
30
|
+
puts "* Executing: #{sql_string}"
|
31
|
+
end
|
32
|
+
|
33
|
+
ActiveRecord::Base.connection.execute(sql_string)
|
34
|
+
end
|
35
|
+
|
36
|
+
def drop_tables
|
37
|
+
tables.each do |table|
|
38
|
+
drop_table(table)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def drop_views
|
43
|
+
views.each do |view|
|
44
|
+
drop_view(view)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def drop_table(table_name)
|
49
|
+
puts "* Dropping table: #{table_name}"
|
50
|
+
execute "DROP TABLE `#{table_name}`"
|
51
|
+
end
|
52
|
+
|
53
|
+
def drop_view(table_name)
|
54
|
+
puts "* Dropping view: #{table_name}"
|
55
|
+
execute "DROP VIEW `#{table_name}`"
|
56
|
+
end
|
57
|
+
|
58
|
+
def tables
|
59
|
+
@tables ||= all_tables - ["schema_migrations"]
|
60
|
+
end
|
61
|
+
|
62
|
+
def all_tables
|
63
|
+
execute("SHOW FULL TABLES WHERE table_type = 'VIEW'").all_hashes.map { |x| x.values.last }
|
64
|
+
end
|
65
|
+
|
66
|
+
def views
|
67
|
+
@views ||= execute("SHOW FULL TABLES WHERE table_type = 'VIEW'").all_hashes.map { |x| x.values.last }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/rake_tasks/flog.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
desc "Feel the pain of my code, and submit a refactoring patch"
|
2
|
+
task :flog do
|
3
|
+
puts %x(find lib | grep ".rb$" | xargs flog)
|
4
|
+
end
|
5
|
+
|
6
|
+
task :flog_to_disk => :create_doc_directory do
|
7
|
+
puts "Flogging..."
|
8
|
+
%x(find lib | grep ".rb$" | xargs flog > doc/flog.txt)
|
9
|
+
puts "Done Flogging...\n"
|
10
|
+
end
|
data/rake_tasks/gem.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/migration_collapser")
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'jeweler'
|
5
|
+
|
6
|
+
def set_version_for_jewler
|
7
|
+
version = MigrationCollapser::Version::STRING
|
8
|
+
|
9
|
+
File.open(File.expand_path(File.dirname(__FILE__) + "/../VERSION"), "w") do |f|
|
10
|
+
f << version
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
Jeweler::Tasks.new do |gemspec|
|
15
|
+
set_version_for_jewler
|
16
|
+
|
17
|
+
gemspec.name = "migration_collapser"
|
18
|
+
gemspec.summary = "Collapse rails migrations"
|
19
|
+
gemspec.description = "Collapse migrations already run in production"
|
20
|
+
gemspec.email = "scott@railsnewbie.com"
|
21
|
+
gemspec.homepage = "http://github.com/smtlaissezfaire/migration_collapser"
|
22
|
+
gemspec.authors = ["Scott Taylor"]
|
23
|
+
gemspec.add_dependency "using"
|
24
|
+
end
|
25
|
+
rescue LoadError
|
26
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
27
|
+
end
|
data/rake_tasks/rdoc.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'hanna/rdoctask'
|
3
|
+
|
4
|
+
DOC_DIRECTORY = File.dirname(__FILE__) + "/../doc"
|
5
|
+
|
6
|
+
Rake::RDocTask.new do |rdoc|
|
7
|
+
rdoc.rdoc_dir = DOC_DIRECTORY
|
8
|
+
rdoc.title = 'migration_collapser'
|
9
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
10
|
+
|
11
|
+
rdoc.options << '--webcvs=http://github.com/smtlaissezfaire/migration_collapser'
|
12
|
+
|
13
|
+
["README.rdoc", "GPL_LICENSE", "MIT_LICENSE", "lib/**/*.rb"].each do |file|
|
14
|
+
rdoc.rdoc_files.include(file)
|
15
|
+
end
|
16
|
+
end
|
data/rake_tasks/rspec.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
require 'spec/rake/verify_rcov'
|
3
|
+
|
4
|
+
desc 'Run the specs'
|
5
|
+
Spec::Rake::SpecTask.new do |t|
|
6
|
+
t.warning = false
|
7
|
+
t.spec_opts = ["--color"]
|
8
|
+
end
|
9
|
+
|
10
|
+
desc "Create the html specdoc"
|
11
|
+
Spec::Rake::SpecTask.new(:specdoc) do |t|
|
12
|
+
t.spec_opts = ["--format", "html:doc/specdoc.html"]
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Run all examples with RCov"
|
16
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
17
|
+
t.rcov = true
|
18
|
+
t.rcov_opts = ['--exclude', 'spec', "--exclude", "gems"]
|
19
|
+
t.rcov_dir = "doc/rcov"
|
20
|
+
end
|
data/rake_tasks/sloc.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
def sloc
|
2
|
+
`sloccount #{File.dirname(__FILE__)}/../lib #{File.dirname(__FILE__)}/../ext`
|
3
|
+
end
|
4
|
+
|
5
|
+
desc "Output sloccount report. You'll need sloccount installed."
|
6
|
+
task :sloc do
|
7
|
+
puts "Counting lines of code"
|
8
|
+
puts sloc
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Write sloccount report"
|
12
|
+
task :output_sloc => :create_doc_directory do
|
13
|
+
File.open(File.dirname(__FILE__) + "/doc/lines_of_code.txt", "w") do |f|
|
14
|
+
f << sloc
|
15
|
+
end
|
16
|
+
end
|
data/rake_tasks/tags.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Build the TAGS file for Emacs
|
2
|
+
# Taken with slight modifications from
|
3
|
+
# http://blog.lathi.net/articles/2007/11/07/navigating-your-projects-in-emacs
|
4
|
+
#
|
5
|
+
# Thanks Jim Weirich
|
6
|
+
|
7
|
+
module Emacs
|
8
|
+
module Tags
|
9
|
+
def self.ruby_files
|
10
|
+
@ruby_files ||= FileList['**/*.rb'].exclude("pkg")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
namespace :tags do
|
16
|
+
task :emacs do
|
17
|
+
puts "Making Emacs TAGS file"
|
18
|
+
sh "ctags -e #{Emacs::Tags.ruby_files}", :verbose => false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "Build the emacs tags file"
|
23
|
+
task :tags => ["tags:emacs"]
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "fakefs/spec_helpers"
|
3
|
+
|
4
|
+
module MigrationCollapser
|
5
|
+
describe FileReplacer do
|
6
|
+
describe "replace" do
|
7
|
+
before do
|
8
|
+
@replacer = FileReplacer.new("revision")
|
9
|
+
@replacer.stub!(:delete_files)
|
10
|
+
@replacer.stub!(:create_migration)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should call delete_files" do
|
14
|
+
@replacer.should_receive(:delete_files)
|
15
|
+
@replacer.replace
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should call create_migration" do
|
19
|
+
@replacer.should_receive(:create_migration)
|
20
|
+
@replacer.replace
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def setup_rails
|
25
|
+
Dir.mkdir "/rails_root"
|
26
|
+
Dir.mkdir "/rails_root/db"
|
27
|
+
Dir.mkdir "/rails_root/db/migrate"
|
28
|
+
|
29
|
+
Dir.chdir "/rails_root"
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "deleting files" do
|
33
|
+
include FakeFS::SpecHelpers
|
34
|
+
|
35
|
+
before do
|
36
|
+
setup_rails
|
37
|
+
|
38
|
+
@revision = 123456
|
39
|
+
@replacer = FileReplacer.new(@revision)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should raise an error if it has no files" do
|
43
|
+
lambda {
|
44
|
+
@replacer.delete_files
|
45
|
+
}.should raise_error("No files in db/migrate")
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should delete the file if it it exists" do
|
49
|
+
FileUtils.touch "/rails_root/db/migrate/123456_my_migration.rb"
|
50
|
+
@replacer.delete_files
|
51
|
+
File.exists?("/rails_root/db/migrate/123456_my_migration.rb").should be_false
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should delete the migration with any name" do
|
55
|
+
FileUtils.touch "/rails_root/db/migrate/123456_foo_bar.rb"
|
56
|
+
@replacer.delete_files
|
57
|
+
File.exists?("/rails_root/db/migrate/123456_foo_bar.rb").should be_false
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should delete the migration with the correct number" do
|
61
|
+
@replacer.revision = 123
|
62
|
+
FileUtils.touch "/rails_root/db/migrate/123_foo_bar.rb"
|
63
|
+
@replacer.delete_files
|
64
|
+
File.exists?("/rails_root/db/migrate/123_foo_bar.rb").should be_false
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should delete all the migrations before (and including) the given number" do
|
68
|
+
@replacer.revision = 002
|
69
|
+
|
70
|
+
FileUtils.touch "/rails_root/db/migrate/001_foo_bar.rb"
|
71
|
+
FileUtils.touch "/rails_root/db/migrate/002_foo_bar.rb"
|
72
|
+
|
73
|
+
@replacer.delete_files
|
74
|
+
|
75
|
+
File.exists?("/rails_root/db/migrate/001_foo_bar.rb").should be_false
|
76
|
+
File.exists?("/rails_root/db/migrate/002_foo_bar.rb").should be_false
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should not delete migrations before the number" do
|
80
|
+
@replacer.revision = 2
|
81
|
+
|
82
|
+
FileUtils.touch "/rails_root/db/migrate/001_foo_bar.rb"
|
83
|
+
FileUtils.touch "/rails_root/db/migrate/002_foo_bar.rb"
|
84
|
+
FileUtils.touch "/rails_root/db/migrate/003_foo_bar.rb"
|
85
|
+
|
86
|
+
@replacer.delete_files
|
87
|
+
|
88
|
+
File.exists?("/rails_root/db/migrate/001_foo_bar.rb").should be_false
|
89
|
+
File.exists?("/rails_root/db/migrate/002_foo_bar.rb").should be_false
|
90
|
+
File.exists?("/rails_root/db/migrate/003_foo_bar.rb").should be_true
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should raise an error if the file does not exist" do
|
94
|
+
FileUtils.touch "/rails_root/db/migrate/0001_my_migration.rb"
|
95
|
+
|
96
|
+
@replacer.revision = 123
|
97
|
+
|
98
|
+
lambda {
|
99
|
+
@replacer.delete_files
|
100
|
+
}.should raise_error
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "replacing the migration" do
|
105
|
+
before do
|
106
|
+
@replacer = FileReplacer.new(1234)
|
107
|
+
@file = File.expand_path(File.dirname(__FILE__) + "/../../lib/migration_collapser/templates/initial_schema_migration.rb")
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should copy the contents of the default migration" do
|
111
|
+
FileUtils.should_receive(:cp).with(@file, "db/migrate/999_initial_schema.rb")
|
112
|
+
@replacer.revision = 999
|
113
|
+
|
114
|
+
@replacer.create_migration
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "revision" do
|
119
|
+
it "should have it as an int when assigned in the constructor" do
|
120
|
+
FileReplacer.new("1234").revision.should == 1234
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should cast it to an int when provided through the accessor" do
|
124
|
+
replacer = FileReplacer.new("1234")
|
125
|
+
replacer.revision = "1111"
|
126
|
+
replacer.revision.should == 1111
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|