also_migrate 0.3.0 → 0.3.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ .DS_Store
2
+ *.gem
3
+ coverage
4
+ pkg
5
+ spec/config/database.yml
6
+ spec/log
7
+ tmp
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2010
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,43 @@
1
+ AlsoMigrate
2
+ ===========
3
+
4
+ Migrate multiple tables with similar schema at once.
5
+
6
+ Requirements
7
+ ------------
8
+
9
+ <pre>
10
+ gem install also_migrate
11
+ </pre>
12
+
13
+ Define the model
14
+ ----------------
15
+
16
+ <pre>
17
+ class Article &lt; ActiveRecord::Base
18
+ also_migrate(
19
+ :article_archives,
20
+ :add => [
21
+ # Parameters to ActiveRecord::ConnectionAdapters::SchemaStatements#add_column
22
+ [ 'deleted_at', :datetime, {} ]
23
+ ],
24
+ :subtract => 'restored_at',
25
+ :ignore => 'deleted_at',
26
+ :indexes => 'id'
27
+ )
28
+ end
29
+ </pre>
30
+
31
+ Options:
32
+
33
+ * <code>add</code> Create columns that the original table doesn't have (defaults to none)
34
+ * <code>subtract</code> Exclude columns from the original table (defaults to none)
35
+ * <code>ignore</code> Ignore migrations that apply to certain columns (defaults to none)
36
+ * <code>indexes</code> Only index certain columns (duplicates all indexes by default)
37
+
38
+ That's it!
39
+ ----------
40
+
41
+ Next time you migrate, <code>article_archives</code> is created if it doesn't exist.
42
+
43
+ Any new migration applied to <code>articles</code> is automatically applied to <code>article_archives</code>.
data/Rakefile ADDED
@@ -0,0 +1,90 @@
1
+ require File.dirname(__FILE__) + '/lib/also_migrate/gems'
2
+
3
+ AlsoMigrate::Gems.activate %w(rake rspec)
4
+
5
+ require 'rake'
6
+ require 'spec/rake/spectask'
7
+
8
+ def gemspec
9
+ @gemspec ||= begin
10
+ file = File.expand_path('../also_migrate.gemspec', __FILE__)
11
+ eval(File.read(file), binding, file)
12
+ end
13
+ end
14
+
15
+ if defined?(Spec::Rake::SpecTask)
16
+ desc "Run specs"
17
+ Spec::Rake::SpecTask.new do |t|
18
+ t.spec_files = FileList['spec/**/*_spec.rb']
19
+ t.spec_opts = %w(-fs --color)
20
+ t.warning = true
21
+ end
22
+ task :spec
23
+ task :default => :spec
24
+ end
25
+
26
+ desc "Build gem(s)"
27
+ task :gem do
28
+ old_gemset = ENV['GEMSET']
29
+ root = File.expand_path('../', __FILE__)
30
+ pkg = "#{root}/pkg"
31
+ system "rm -Rf #{pkg}"
32
+ AlsoMigrate::Gems.gemset_names.each do |gemset|
33
+ ENV['GEMSET'] = gemset.to_s
34
+ system "cd #{root} && gem build also_migrate.gemspec"
35
+ system "mkdir -p #{pkg} && mv *.gem pkg"
36
+ end
37
+ ENV['GEMSET'] = old_gemset
38
+ end
39
+
40
+ namespace :gem do
41
+ desc "Install gem(s)"
42
+ task :install do
43
+ Rake::Task['gem'].invoke
44
+ Dir["#{File.dirname(__FILE__)}/pkg/*.gem"].each do |pkg|
45
+ system "gem install #{pkg} --no-ri --no-rdoc"
46
+ end
47
+ end
48
+
49
+ desc "Push gem(s)"
50
+ task :push do
51
+ Rake::Task['gem'].invoke
52
+ Dir["#{File.dirname(__FILE__)}/pkg/*.gem"].each do |pkg|
53
+ system "gem push #{pkg}"
54
+ end
55
+ end
56
+ end
57
+
58
+ namespace :gems do
59
+ desc "Install gem dependencies (DEV=0 DOCS=0 GEMSPEC=default SUDO=0)"
60
+ task :install do
61
+ dev = ENV['DEV'] == '1'
62
+ docs = ENV['DOCS'] == '1' ? '' : '--no-ri --no-rdoc'
63
+ gemset = ENV['GEMSET']
64
+ sudo = ENV['SUDO'] == '1' ? 'sudo' : ''
65
+
66
+ AlsoMigrate::Gems.gemset = gemset if gemset
67
+
68
+ if dev
69
+ gems = AlsoMigrate::Gems.gemspec.development_dependencies
70
+ else
71
+ gems = AlsoMigrate::Gems.gemspec.dependencies
72
+ end
73
+
74
+ gems.each do |name|
75
+ name = name.to_s
76
+ version = AlsoMigrate::Gems.versions[name]
77
+ if Gem.source_index.find_name(name, version).empty?
78
+ version = version ? "-v #{version}" : ''
79
+ system "#{sudo} gem install #{name} #{version} #{docs}"
80
+ else
81
+ puts "already installed: #{name} #{version}"
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ desc "Validate the gemspec"
88
+ task :gemspec do
89
+ gemspec.validate
90
+ end
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ root = File.expand_path('../', __FILE__)
3
+ lib = "#{root}/lib"
4
+ $:.unshift lib unless $:.include?(lib)
5
+
6
+ require 'also_migrate/gems'
7
+ AlsoMigrate::Gems.gemset ||= ENV['GEMSET'] || :default
8
+
9
+ Gem::Specification.new do |s|
10
+ AlsoMigrate::Gems.gemspec.hash.each do |key, value|
11
+ if key == 'name' && AlsoMigrate::Gems.gemset != :default
12
+ s.name = "#{value}-#{AlsoMigrate::Gems.gemset}"
13
+ elsif key == 'summary' && AlsoMigrate::Gems.gemset == :solo
14
+ s.summary = value + " (no dependencies)"
15
+ elsif !%w(dependencies development_dependencies).include?(key)
16
+ s.send "#{key}=", value
17
+ end
18
+ end
19
+
20
+ AlsoMigrate::Gems.dependencies.each do |g|
21
+ s.add_dependency g.to_s, AlsoMigrate::Gems.versions[g]
22
+ end
23
+
24
+ AlsoMigrate::Gems.development_dependencies.each do |g|
25
+ s.add_development_dependency g.to_s, AlsoMigrate::Gems.versions[g]
26
+ end
27
+
28
+ s.executables = `cd #{root} && git ls-files -- {bin}/*`.split("\n").collect { |f| File.basename(f) }
29
+ s.files = `cd #{root} && git ls-files`.split("\n")
30
+ s.require_paths = %w(lib)
31
+ s.test_files = `cd #{root} && git ls-files -- {features,test,spec}/*`.split("\n")
32
+ end
@@ -0,0 +1,4 @@
1
+ also_migrate:
2
+ active_wrapper: =0.4.2
3
+ rake: >=0.8.7
4
+ rspec: ~>1.0
@@ -0,0 +1,12 @@
1
+ name: also_migrate
2
+ version: 0.3.2
3
+ authors:
4
+ - Winton Welsh
5
+ email: mail@wintoni.us
6
+ homepage: http://github.com/winton/also_migrate
7
+ summary: Migrate multiple tables with similar schema at once.
8
+ description: Migrate multiple tables with similar schema at once.
9
+ dependencies: null
10
+ development_dependencies:
11
+ - rake
12
+ - rspec
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + "/rails/init"
@@ -0,0 +1,35 @@
1
+ module AlsoMigrate
2
+ module Base
3
+
4
+ def self.included(base)
5
+ unless base.respond_to?(:also_migrate)
6
+ base.extend ClassMethods
7
+ end
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ def also_migrate(*args)
13
+ options = args.extract_options!
14
+ @also_migrate_config ||= []
15
+ @also_migrate_config << {
16
+ :table_name => self.table_name,
17
+ :tables => args.collect(&:to_s),
18
+ :options => {
19
+ :add => options[:add] ? options[:add] : [],
20
+ :subtract => [ options[:subtract] ].flatten.compact,
21
+ :ignore => [ options[:ignore] ].flatten.compact,
22
+ :indexes => options[:indexes] ? [ options[:indexes] ].flatten : nil
23
+ }
24
+ }
25
+ self.class_eval do
26
+ class <<self
27
+ attr_accessor :also_migrate_config
28
+ end
29
+ end
30
+ ::AlsoMigrate.classes ||= []
31
+ ::AlsoMigrate.classes << self
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,154 @@
1
+ unless defined?(AlsoMigrate::Gems)
2
+
3
+ require 'yaml'
4
+
5
+ module AlsoMigrate
6
+ module Gems
7
+ class <<self
8
+
9
+ attr_accessor :config
10
+ attr_reader :gemset, :gemsets, :versions
11
+
12
+ class SimpleStruct
13
+ attr_reader :hash
14
+
15
+ def initialize(hash)
16
+ @hash = hash
17
+ @hash.each do |key, value|
18
+ self.class.send(:define_method, key) { @hash[key] }
19
+ self.class.send(:define_method, "#{key}=") { |v| @hash[key] = v }
20
+ end
21
+ end
22
+ end
23
+
24
+ Gems.config = SimpleStruct.new(
25
+ :gemsets => [ "#{File.expand_path('../../../', __FILE__)}/config/gemsets.yml" ],
26
+ :gemspec => "#{File.expand_path('../../../', __FILE__)}/config/gemspec.yml",
27
+ :warn => true
28
+ )
29
+
30
+ def activate(*gems)
31
+ begin
32
+ require 'rubygems' unless defined?(::Gem)
33
+ rescue LoadError
34
+ puts "rubygems library could not be required" if @config.warn
35
+ end
36
+
37
+ self.gemset ||= gemset_from_loaded_specs
38
+
39
+ gems.flatten.collect(&:to_sym).each do |name|
40
+ version = @versions[name]
41
+ vendor = File.expand_path("../../../vendor/#{name}/lib", __FILE__)
42
+ if File.exists?(vendor)
43
+ $:.unshift vendor
44
+ elsif defined?(gem)
45
+ gem name.to_s, version
46
+ else
47
+ puts "#{name} #{"(#{version})" if version} failed to activate" if @config.warn
48
+ end
49
+ end
50
+ end
51
+
52
+ def dependencies
53
+ dependency_filter(@gemspec.dependencies, @gemset)
54
+ end
55
+
56
+ def development_dependencies
57
+ dependency_filter(@gemspec.development_dependencies, @gemset)
58
+ end
59
+
60
+ def gemset=(gemset)
61
+ if gemset
62
+ @gemset = gemset.to_sym
63
+
64
+ @gemsets = @config.gemsets.reverse.collect { |config|
65
+ if config.is_a?(::String)
66
+ YAML::load(File.read(config)) rescue {}
67
+ elsif config.is_a?(::Hash)
68
+ config
69
+ end
70
+ }.inject({}) do |hash, config|
71
+ deep_merge(hash, symbolize_keys(config))
72
+ end
73
+
74
+ @versions = (@gemsets[gemspec.name.to_sym] || {}).inject({}) do |hash, (key, value)|
75
+ if !value.is_a?(::Hash) && value
76
+ hash[key] = value
77
+ elsif key == @gemset
78
+ (value || {}).each { |k, v| hash[k] = v }
79
+ end
80
+ hash
81
+ end
82
+ else
83
+ @gemset = nil
84
+ @gemsets = nil
85
+ @versions = nil
86
+ end
87
+ end
88
+
89
+ def gemset_names
90
+ (
91
+ [ :default ] +
92
+ @gemsets[gemspec.name.to_sym].inject([]) { |array, (key, value)|
93
+ array.push(key) if value.is_a?(::Hash) || value.nil?
94
+ array
95
+ }
96
+ ).uniq
97
+ end
98
+
99
+ def gemspec(reload=false)
100
+ if @gemspec && !reload
101
+ @gemspec
102
+ else
103
+ data = YAML::load(File.read(@config.gemspec)) rescue {}
104
+ @gemspec = SimpleStruct.new(data)
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def deep_merge(first, second)
111
+ merger = lambda do |key, v1, v2|
112
+ Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2
113
+ end
114
+ first.merge(second, &merger)
115
+ end
116
+
117
+ def dependency_filter(dependencies, match)
118
+ (dependencies || []).inject([]) { |array, value|
119
+ if value.is_a?(::Hash)
120
+ array += value[match.to_s] if value[match.to_s]
121
+ else
122
+ array << value
123
+ end
124
+ array
125
+ }.uniq.collect(&:to_sym)
126
+ end
127
+
128
+ def gemset_from_loaded_specs
129
+ if defined?(Gem)
130
+ Gem.loaded_specs.each do |name, spec|
131
+ if name == gemspec.name
132
+ return :default
133
+ elsif name[0..gemspec.name.length] == "#{gemspec.name}-"
134
+ return name[gemspec.name.length+1..-1].to_sym
135
+ end
136
+ end
137
+ :default
138
+ else
139
+ :none
140
+ end
141
+ end
142
+
143
+ def symbolize_keys(hash)
144
+ return {} unless hash.is_a?(::Hash)
145
+ hash.inject({}) do |options, (key, value)|
146
+ value = symbolize_keys(value) if value.is_a?(::Hash)
147
+ options[(key.to_sym rescue key) || key] = value
148
+ options
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,72 @@
1
+ module AlsoMigrate
2
+ module Migration
3
+
4
+ def self.included(base)
5
+ unless base.respond_to?(:method_missing_with_also_migrate)
6
+ base.extend ClassMethods
7
+ base.class_eval do
8
+ class <<self
9
+ alias_method :method_missing_without_also_migrate, :method_missing
10
+ alias_method :method_missing, :method_missing_with_also_migrate
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ module ClassMethods
17
+
18
+ def method_missing_with_also_migrate(method, *arguments, &block)
19
+ args = Marshal.load(Marshal.dump(arguments))
20
+ method_missing_without_also_migrate(method, *arguments, &block)
21
+
22
+ supported = [
23
+ :add_column, :add_index, :add_timestamps, :change_column,
24
+ :change_column_default, :change_table, :create_table,
25
+ :drop_table, :remove_column, :remove_columns,
26
+ :remove_timestamps, :rename_column, :rename_table
27
+ ]
28
+
29
+ if !args.empty? && supported.include?(method)
30
+ connection = ActiveRecord::Base.connection
31
+ table_name = ActiveRecord::Migrator.proper_table_name(args[0])
32
+
33
+ # Find models
34
+ if ::AlsoMigrate.classes
35
+ ::AlsoMigrate.classes.uniq.each do |klass|
36
+ if klass.also_migrate_config
37
+ klass.also_migrate_config.each do |config|
38
+ options = config[:options]
39
+ tables = config[:tables]
40
+
41
+ next unless config[:table_name] == table_name
42
+
43
+ # Don't change ignored columns
44
+ options[:ignore].each do |column|
45
+ next if args.include?(column) || args.include?(column.intern)
46
+ end
47
+
48
+ # Run migration
49
+ config[:tables].each do |table|
50
+ if method == :create_table
51
+ ActiveRecord::Migrator::AlsoMigrate.create_tables(klass)
52
+ elsif method == :add_index && !options[:indexes].nil?
53
+ next
54
+ elsif connection.table_exists?(table)
55
+ args[0] = table
56
+ args[1] = table if method == :rename_table
57
+ begin
58
+ connection.send(method, *args, &block)
59
+ rescue Exception => e
60
+ puts "(also_migrate warning) #{e.message}"
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,100 @@
1
+ module AlsoMigrate
2
+ module Migrator
3
+
4
+ def self.included(base)
5
+ unless base.respond_to?(:migrate_with_also_migrate)
6
+ base.send :include, InstanceMethods
7
+ base.class_eval do
8
+ alias_method :migrate_without_also_migrate, :migrate
9
+ alias_method :migrate, :migrate_with_also_migrate
10
+ end
11
+ end
12
+ end
13
+
14
+ module InstanceMethods
15
+
16
+ def migrate_with_also_migrate
17
+ if ::AlsoMigrate.classes
18
+ ::AlsoMigrate.classes.uniq.each do |klass|
19
+ if klass.respond_to?(:also_migrate_config)
20
+ AlsoMigrate.create_tables(klass)
21
+ end
22
+ end
23
+ end
24
+ rescue Exception => e
25
+ puts "AlsoMigrate error: #{e.message}"
26
+ puts e.backtrace.join("\n")
27
+ ensure
28
+ migrate_without_also_migrate
29
+ end
30
+
31
+ module AlsoMigrate
32
+ class <<self
33
+
34
+ def connection
35
+ ActiveRecord::Base.connection
36
+ end
37
+
38
+ def create_tables(klass)
39
+ config = klass.also_migrate_config
40
+ return unless config
41
+ old_table = klass.table_name
42
+ config.each do |config|
43
+ options = config[:options]
44
+ config[:tables].each do |new_table|
45
+ if !connection.table_exists?(new_table) && connection.table_exists?(old_table)
46
+ columns = connection.columns(old_table).collect(&:name)
47
+ columns -= options[:subtract].collect(&:to_s)
48
+ columns.collect! { |col| connection.quote_column_name(col) }
49
+ indexes = options[:indexes]
50
+ if indexes
51
+ engine =
52
+ if connection.class.to_s.include?('Mysql')
53
+ 'ENGINE=' + connection.select_one(<<-SQL)['Engine']
54
+ SHOW TABLE STATUS
55
+ WHERE Name = '#{old_table}'
56
+ SQL
57
+ end
58
+ connection.execute(<<-SQL)
59
+ CREATE TABLE #{new_table} #{engine}
60
+ AS SELECT #{columns.join(',')}
61
+ FROM #{old_table}
62
+ WHERE false;
63
+ SQL
64
+ indexes.each do |column|
65
+ connection.add_index(new_table, column)
66
+ end
67
+ else
68
+ connection.execute(<<-SQL)
69
+ CREATE TABLE #{new_table}
70
+ LIKE #{old_table};
71
+ SQL
72
+ end
73
+ end
74
+ if connection.table_exists?(new_table)
75
+ if options[:add] || options[:subtract]
76
+ columns = connection.columns(new_table).collect(&:name)
77
+ end
78
+ if options[:add]
79
+ options[:add].each do |column|
80
+ unless columns.include?(column[0])
81
+ connection.add_column(*([ new_table ] + column))
82
+ end
83
+ end
84
+ end
85
+ if options[:subtract]
86
+ options[:subtract].each do |column|
87
+ if columns.include?(column)
88
+ connection.remove_column(new_table, column)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/also_migrate/gems'
2
+
3
+ $:.unshift File.dirname(__FILE__)
4
+
5
+ require 'also_migrate/base'
6
+ require 'also_migrate/migration'
7
+ require 'also_migrate/migrator'
8
+
9
+ module AlsoMigrate
10
+ class <<self
11
+ attr_accessor :classes
12
+ end
13
+ end
14
+
15
+ ActiveRecord::Base.send(:include, AlsoMigrate::Base)
16
+ ActiveRecord::Migrator.send(:include, AlsoMigrate::Migrator)
17
+ ActiveRecord::Migration.send(:include, AlsoMigrate::Migration)
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/also_migrate')
data/spec/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/also_migrate/gems')
2
+
3
+ AlsoMigrate::Gems.require(:spec_rake)
4
+
5
+ require 'active_wrapper/tasks'
6
+
7
+ #begin
8
+ ActiveWrapper::Tasks.new(
9
+ :base => File.dirname(__FILE__),
10
+ :env => 'test'
11
+ )
12
+ # rescue Exception
13
+ # end
@@ -0,0 +1,249 @@
1
+ require 'spec_helper'
2
+
3
+ describe AlsoMigrate::Gems do
4
+
5
+ before(:each) do
6
+ @old_config = AlsoMigrate::Gems.config
7
+
8
+ AlsoMigrate::Gems.config.gemspec = "#{$root}/spec/fixtures/gemspec.yml"
9
+ AlsoMigrate::Gems.config.gemsets = [
10
+ "#{$root}/spec/fixtures/gemsets.yml"
11
+ ]
12
+ AlsoMigrate::Gems.config.warn = true
13
+
14
+ AlsoMigrate::Gems.gemspec true
15
+ AlsoMigrate::Gems.gemset = nil
16
+ end
17
+
18
+ after(:each) do
19
+ AlsoMigrate::Gems.config = @old_config
20
+ end
21
+
22
+ describe :activate do
23
+ it "should activate gems" do
24
+ AlsoMigrate::Gems.stub!(:gem)
25
+ AlsoMigrate::Gems.should_receive(:gem).with('rspec', '=1.3.1')
26
+ AlsoMigrate::Gems.should_receive(:gem).with('rake', '=0.8.7')
27
+ AlsoMigrate::Gems.activate :rspec, 'rake'
28
+ end
29
+ end
30
+
31
+ describe :gemset= do
32
+ before(:each) do
33
+ AlsoMigrate::Gems.config.gemsets = [
34
+ {
35
+ :name => {
36
+ :rake => '>0.8.6',
37
+ :default => {
38
+ :externals => '=1.0.2'
39
+ }
40
+ }
41
+ },
42
+ "#{$root}/spec/fixtures/gemsets.yml"
43
+ ]
44
+ end
45
+
46
+ describe :default do
47
+ before(:each) do
48
+ AlsoMigrate::Gems.gemset = :default
49
+ end
50
+
51
+ it "should set @gemset" do
52
+ AlsoMigrate::Gems.gemset.should == :default
53
+ end
54
+
55
+ it "should set @gemsets" do
56
+ AlsoMigrate::Gems.gemsets.should == {
57
+ :name => {
58
+ :rake => ">0.8.6",
59
+ :default => {
60
+ :externals => '=1.0.2',
61
+ :mysql => "=2.8.1",
62
+ :rspec => "=1.3.1"
63
+ },
64
+ :rspec2 => {
65
+ :mysql2 => "=0.2.6",
66
+ :rspec => "=2.3.0"
67
+ },
68
+ :solo => nil
69
+ }
70
+ }
71
+ end
72
+
73
+ it "should set Gems.versions" do
74
+ AlsoMigrate::Gems.versions.should == {
75
+ :externals => "=1.0.2",
76
+ :mysql => "=2.8.1",
77
+ :rake => ">0.8.6",
78
+ :rspec => "=1.3.1"
79
+ }
80
+ end
81
+
82
+ it "should return proper values for Gems.dependencies" do
83
+ AlsoMigrate::Gems.dependencies.should == [ :rake, :mysql ]
84
+ AlsoMigrate::Gems.development_dependencies.should == []
85
+ end
86
+
87
+ it "should return proper values for Gems.gemset_names" do
88
+ AlsoMigrate::Gems.gemset_names.should == [ :default, :rspec2, :solo ]
89
+ end
90
+ end
91
+
92
+ describe :rspec2 do
93
+ before(:each) do
94
+ AlsoMigrate::Gems.gemset = "rspec2"
95
+ end
96
+
97
+ it "should set @gemset" do
98
+ AlsoMigrate::Gems.gemset.should == :rspec2
99
+ end
100
+
101
+ it "should set @gemsets" do
102
+ AlsoMigrate::Gems.gemsets.should == {
103
+ :name => {
104
+ :rake => ">0.8.6",
105
+ :default => {
106
+ :externals => '=1.0.2',
107
+ :mysql => "=2.8.1",
108
+ :rspec => "=1.3.1"
109
+ },
110
+ :rspec2 => {
111
+ :mysql2=>"=0.2.6",
112
+ :rspec => "=2.3.0"
113
+ },
114
+ :solo => nil
115
+ }
116
+ }
117
+ end
118
+
119
+ it "should set Gems.versions" do
120
+ AlsoMigrate::Gems.versions.should == {
121
+ :mysql2 => "=0.2.6",
122
+ :rake => ">0.8.6",
123
+ :rspec => "=2.3.0"
124
+ }
125
+ end
126
+
127
+ it "should return proper values for Gems.dependencies" do
128
+ AlsoMigrate::Gems.dependencies.should == [ :rake, :mysql2 ]
129
+ AlsoMigrate::Gems.development_dependencies.should == []
130
+ end
131
+
132
+ it "should return proper values for Gems.gemset_names" do
133
+ AlsoMigrate::Gems.gemset_names.should == [ :default, :rspec2, :solo ]
134
+ end
135
+ end
136
+
137
+ describe :solo do
138
+ before(:each) do
139
+ AlsoMigrate::Gems.gemset = :solo
140
+ end
141
+
142
+ it "should set @gemset" do
143
+ AlsoMigrate::Gems.gemset.should == :solo
144
+ end
145
+
146
+ it "should set @gemsets" do
147
+ AlsoMigrate::Gems.gemsets.should == {
148
+ :name => {
149
+ :rake => ">0.8.6",
150
+ :default => {
151
+ :externals => '=1.0.2',
152
+ :mysql => "=2.8.1",
153
+ :rspec => "=1.3.1"
154
+ },
155
+ :rspec2 => {
156
+ :mysql2=>"=0.2.6",
157
+ :rspec => "=2.3.0"
158
+ },
159
+ :solo => nil
160
+ }
161
+ }
162
+ end
163
+
164
+ it "should set Gems.versions" do
165
+ AlsoMigrate::Gems.versions.should == {:rake=>">0.8.6"}
166
+ end
167
+
168
+ it "should return proper values for Gems.dependencies" do
169
+ AlsoMigrate::Gems.dependencies.should == [:rake]
170
+ AlsoMigrate::Gems.development_dependencies.should == []
171
+ end
172
+
173
+ it "should return proper values for Gems.gemset_names" do
174
+ AlsoMigrate::Gems.gemset_names.should == [ :default, :rspec2, :solo ]
175
+ end
176
+ end
177
+
178
+ describe :nil do
179
+ before(:each) do
180
+ AlsoMigrate::Gems.gemset = nil
181
+ end
182
+
183
+ it "should set everything to nil" do
184
+ AlsoMigrate::Gems.gemset.should == nil
185
+ AlsoMigrate::Gems.gemsets.should == nil
186
+ AlsoMigrate::Gems.versions.should == nil
187
+ end
188
+ end
189
+ end
190
+
191
+ describe :gemset_from_loaded_specs do
192
+ before(:each) do
193
+ Gem.stub!(:loaded_specs)
194
+ end
195
+
196
+ it "should return the correct gemset for name gem" do
197
+ Gem.should_receive(:loaded_specs).and_return({ "name" => nil })
198
+ AlsoMigrate::Gems.send(:gemset_from_loaded_specs).should == :default
199
+ end
200
+
201
+ it "should return the correct gemset for name-rspec gem" do
202
+ Gem.should_receive(:loaded_specs).and_return({ "name-rspec2" => nil })
203
+ AlsoMigrate::Gems.send(:gemset_from_loaded_specs).should == :rspec2
204
+ end
205
+ end
206
+
207
+ describe :reload_gemspec do
208
+ it "should populate @gemspec" do
209
+ AlsoMigrate::Gems.gemspec.hash.should == {
210
+ "name" => "name",
211
+ "version" => "0.1.0",
212
+ "authors" => ["Author"],
213
+ "email" => "email@email.com",
214
+ "homepage" => "http://github.com/author/name",
215
+ "summary" => "Summary",
216
+ "description" => "Description",
217
+ "dependencies" => [
218
+ "rake",
219
+ { "default" => [ "mysql" ] },
220
+ { "rspec2" => [ "mysql2" ] }
221
+ ],
222
+ "development_dependencies" => nil
223
+ }
224
+ end
225
+
226
+ it "should create methods from keys of @gemspec" do
227
+ AlsoMigrate::Gems.gemspec.name.should == "name"
228
+ AlsoMigrate::Gems.gemspec.version.should == "0.1.0"
229
+ AlsoMigrate::Gems.gemspec.authors.should == ["Author"]
230
+ AlsoMigrate::Gems.gemspec.email.should == "email@email.com"
231
+ AlsoMigrate::Gems.gemspec.homepage.should == "http://github.com/author/name"
232
+ AlsoMigrate::Gems.gemspec.summary.should == "Summary"
233
+ AlsoMigrate::Gems.gemspec.description.should == "Description"
234
+ AlsoMigrate::Gems.gemspec.dependencies.should == [
235
+ "rake",
236
+ { "default" => ["mysql"] },
237
+ { "rspec2" => [ "mysql2" ] }
238
+ ]
239
+ AlsoMigrate::Gems.gemspec.development_dependencies.should == nil
240
+ end
241
+
242
+ it "should produce a valid gemspec" do
243
+ AlsoMigrate::Gems.gemset = :default
244
+ gemspec = File.expand_path("../../../also_migrate.gemspec", __FILE__)
245
+ gemspec = eval(File.read(gemspec), binding, gemspec)
246
+ gemspec.validate.should == true
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,159 @@
1
+ require "spec_helper"
2
+
3
+ describe AlsoMigrate do
4
+
5
+ [ "table doesn't exist yet", "table already exists" ].each do |description|
6
+ describe description do
7
+ describe 'with all options' do
8
+
9
+ before(:each) do
10
+ reset_fixture
11
+
12
+ if description == "table doesn't exist yet"
13
+ Article.also_migrate(
14
+ :article_archives,
15
+ :add => [
16
+ [ 'deleted_at', :datetime ]
17
+ ],
18
+ :subtract => 'restored_at',
19
+ :ignore => 'body',
20
+ :indexes => 'id'
21
+ )
22
+ end
23
+
24
+ $db.migrate(1)
25
+ $db.migrate(0)
26
+ $db.migrate(1)
27
+
28
+ if description == "table already exists"
29
+ Article.also_migrate(
30
+ :article_archives,
31
+ :add => [
32
+ [ 'deleted_at', :datetime ]
33
+ ],
34
+ :subtract => %w(restored_at),
35
+ :ignore => %w(body),
36
+ :indexes => %w(id)
37
+ )
38
+ $db.migrate(1)
39
+ end
40
+ end
41
+
42
+ it "should create the add column" do
43
+ (columns('article_archives') - columns('articles')).should == [ 'deleted_at' ]
44
+ end
45
+
46
+ it "should not create the subtract column" do
47
+ (columns('articles') - columns('article_archives')).should == [ 'restored_at' ]
48
+ end
49
+
50
+ it 'should migrate both tables up' do
51
+ migrate_with_state(2)
52
+ (@new_article_columns - @old_article_columns).should == [ 'permalink' ]
53
+ (@new_archive_columns - @old_archive_columns).should == [ 'permalink' ]
54
+ end
55
+
56
+ it 'should migrate both tables down' do
57
+ $db.migrate(2)
58
+ migrate_with_state(1)
59
+ (@old_article_columns - @new_article_columns).should == [ 'permalink' ]
60
+ (@old_archive_columns - @new_archive_columns).should == [ 'permalink' ]
61
+ end
62
+
63
+ it "should ignore the body column" do
64
+ (columns('article_archives') - columns('articles')).should == [ 'deleted_at' ]
65
+ connection.remove_column(:articles, :body)
66
+ (columns('article_archives') - columns('articles')).should == [ 'body', 'deleted_at' ]
67
+ end
68
+
69
+ it "should only add an index for id" do
70
+ indexed_columns('articles').should == [ 'id', 'read' ]
71
+ indexed_columns('article_archives').should == [ 'id' ]
72
+ end
73
+ end
74
+
75
+ describe 'with no index option' do
76
+
77
+ before(:each) do
78
+ reset_fixture
79
+
80
+ if description == "table doesn't exist yet"
81
+ Article.also_migrate :article_archives
82
+ end
83
+
84
+ $db.migrate(0)
85
+ $db.migrate(1)
86
+
87
+ if description == "table already exists"
88
+ Article.also_migrate :article_archives
89
+ $db.migrate(1)
90
+ end
91
+ end
92
+
93
+ it "should add all indexes" do
94
+ indexed_columns('articles').should == [ 'id', 'read' ]
95
+ indexed_columns('article_archives').should == [ 'id', 'read' ]
96
+ end
97
+ end
98
+
99
+ describe "with other table" do
100
+
101
+ before(:each) do
102
+ reset_fixture
103
+
104
+ if description == "table doesn't exist yet"
105
+ Article.also_migrate :article_archives
106
+ Comment.also_migrate :comment_archives
107
+ end
108
+
109
+ $db.migrate(0)
110
+ $db.migrate(1)
111
+ $db.migrate(2)
112
+ $db.migrate(3)
113
+
114
+ if description == "table already exists"
115
+ Article.also_migrate :article_archives
116
+ Comment.also_migrate :comment_archives
117
+ $db.migrate(3)
118
+ end
119
+ end
120
+
121
+ it "should not affect other table" do
122
+ columns('articles').should == columns('article_archives')
123
+ columns('comments').should == columns('comment_archives')
124
+ columns('articles').should == ["id", "title", "body", "read", "restored_at", "permalink"]
125
+ columns('comments').should == ["id", "header", "description"]
126
+ end
127
+ end
128
+
129
+ if description == "table already exists"
130
+ describe 'with add and subtract option' do
131
+
132
+ before(:each) do
133
+ reset_fixture
134
+
135
+ Article.also_migrate :article_archives
136
+
137
+ $db.migrate(0)
138
+ $db.migrate(1)
139
+
140
+ Article.also_migrate_config = nil
141
+ Article.also_migrate(
142
+ :article_archives,
143
+ :add => [
144
+ [ 'deleted_at', :datetime ]
145
+ ],
146
+ :subtract => 'restored_at'
147
+ )
148
+ end
149
+
150
+ it "should add and remove fields" do
151
+ columns('article_archives').should == %w(id title body read restored_at)
152
+ $db.migrate(1)
153
+ columns('article_archives').should == %w(id title body read deleted_at)
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,6 @@
1
+ test:
2
+ adapter: mysql
3
+ database: also_migrate
4
+ username: root
5
+ password:
6
+ host: localhost
@@ -0,0 +1,15 @@
1
+ class CreateArticles < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :articles do |t|
4
+ t.string :title
5
+ t.string :body
6
+ t.boolean :read
7
+ t.datetime :restored_at
8
+ end
9
+ add_index :articles, :read
10
+ end
11
+
12
+ def self.down
13
+ drop_table :articles
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ class AddPermalink < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :articles, :permalink, :string
4
+ end
5
+
6
+ def self.down
7
+ remove_column :articles, :permalink
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ class CreateComments < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :comments do |t|
4
+ t.string :header
5
+ t.string :description
6
+ end
7
+ end
8
+
9
+ def self.down
10
+ drop_table :comments
11
+ end
12
+ end
@@ -0,0 +1,2 @@
1
+ class Article < ActiveRecord::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ class Comment < ActiveRecord::Base
2
+ end
@@ -0,0 +1,9 @@
1
+ name:
2
+ rake: =0.8.7
3
+ default:
4
+ mysql: =2.8.1
5
+ rspec: =1.3.1
6
+ rspec2:
7
+ mysql2: =0.2.6
8
+ rspec: =2.3.0
9
+ solo: null
@@ -0,0 +1,15 @@
1
+ name: name
2
+ version: 0.1.0
3
+ authors:
4
+ - Author
5
+ email: email@email.com
6
+ homepage: http://github.com/author/name
7
+ summary: Summary
8
+ description: Description
9
+ dependencies:
10
+ - rake
11
+ - default:
12
+ - mysql
13
+ - rspec2:
14
+ - mysql2
15
+ development_dependencies: null
@@ -0,0 +1,102 @@
1
+ require 'pp'
2
+
3
+ $root = File.expand_path('../../', __FILE__)
4
+ require "#{$root}/lib/also_migrate/gems"
5
+
6
+ AlsoMigrate::Gems.activate :active_wrapper, :rspec
7
+
8
+ require 'active_wrapper'
9
+
10
+ require "#{$root}/lib/also_migrate"
11
+ require "#{$root}/spec/fixtures/article"
12
+ require "#{$root}/spec/fixtures/comment"
13
+ require 'pp'
14
+
15
+ Spec::Runner.configure do |config|
16
+ end
17
+
18
+ $db, $log, $mail = ActiveWrapper.setup(
19
+ :base => File.dirname(__FILE__),
20
+ :env => 'test'
21
+ )
22
+ $db.establish_connection
23
+
24
+ def columns(table)
25
+ connection.columns(table).collect(&:name)
26
+ end
27
+
28
+ def connection
29
+ ActiveRecord::Base.connection
30
+ end
31
+
32
+ # For use with rspec textmate bundle
33
+ def debug(object)
34
+ puts "<pre>"
35
+ puts object.pretty_inspect.gsub('<', '&lt;').gsub('>', '&gt;')
36
+ puts "</pre>"
37
+ end
38
+
39
+ def indexed_columns(table_name)
40
+ # MySQL
41
+ if connection.class.to_s.include?('Mysql')
42
+ index_query = "SHOW INDEX FROM #{table_name}"
43
+ connection.select_all(index_query).collect do |r|
44
+ r["Column_name"]
45
+ end
46
+ # PostgreSQL
47
+ # http://stackoverflow.com/questions/2204058/show-which-columns-an-index-is-on-in-postgresql/2213199
48
+ elsif connection.class.to_s.include?('PostgreSQL')
49
+ index_query = <<-SQL
50
+ select
51
+ t.relname as table_name,
52
+ i.relname as index_name,
53
+ a.attname as column_name
54
+ from
55
+ pg_class t,
56
+ pg_class i,
57
+ pg_index ix,
58
+ pg_attribute a
59
+ where
60
+ t.oid = ix.indrelid
61
+ and i.oid = ix.indexrelid
62
+ and a.attrelid = t.oid
63
+ and a.attnum = ANY(ix.indkey)
64
+ and t.relkind = 'r'
65
+ and t.relname = '#{table_name}'
66
+ order by
67
+ t.relname,
68
+ i.relname
69
+ SQL
70
+ connection.select_all(index_query).collect do |r|
71
+ r["column_name"]
72
+ end
73
+ else
74
+ raise 'AlsoMigrate does not support this database adapter'
75
+ end
76
+ end
77
+
78
+ def migrate_with_state(version)
79
+ @old_article_columns = columns("articles")
80
+ @old_archive_columns = columns("article_archives")
81
+ $db.migrate(version)
82
+ @new_article_columns = columns("articles")
83
+ @new_archive_columns = columns("article_archives")
84
+ end
85
+
86
+ def reset_fixture
87
+ if Article.respond_to?(:also_migrate_config)
88
+ Article.also_migrate_config = nil
89
+ end
90
+
91
+ if Comment.respond_to?(:also_migrate_config)
92
+ Comment.also_migrate_config = nil
93
+ end
94
+
95
+ if connection.table_exists?('article_archives')
96
+ connection.execute('DROP TABLE article_archives')
97
+ end
98
+
99
+ if connection.table_exists?('comment_archives')
100
+ connection.execute('DROP TABLE comment_archives')
101
+ end
102
+ end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 3
8
- - 0
9
- version: 0.3.0
8
+ - 2
9
+ version: 0.3.2
10
10
  platform: ruby
11
11
  authors:
12
12
  - Winton Welsh
@@ -54,8 +54,33 @@ extensions: []
54
54
 
55
55
  extra_rdoc_files: []
56
56
 
57
- files: []
58
-
57
+ files:
58
+ - .gitignore
59
+ - LICENSE
60
+ - README.md
61
+ - Rakefile
62
+ - also_migrate.gemspec
63
+ - config/gemsets.yml
64
+ - config/gemspec.yml
65
+ - init.rb
66
+ - lib/also_migrate.rb
67
+ - lib/also_migrate/base.rb
68
+ - lib/also_migrate/gems.rb
69
+ - lib/also_migrate/migration.rb
70
+ - lib/also_migrate/migrator.rb
71
+ - rails/init.rb
72
+ - spec/Rakefile
73
+ - spec/also_migrate/gems_spec.rb
74
+ - spec/also_migrate_spec.rb
75
+ - spec/config/database.yml.example
76
+ - spec/db/migrate/001_create_articles.rb
77
+ - spec/db/migrate/002_add_permalink.rb
78
+ - spec/db/migrate/003_create_comments.rb
79
+ - spec/fixtures/article.rb
80
+ - spec/fixtures/comment.rb
81
+ - spec/fixtures/gemsets.yml
82
+ - spec/fixtures/gemspec.yml
83
+ - spec/spec_helper.rb
59
84
  has_rdoc: true
60
85
  homepage: http://github.com/winton/also_migrate
61
86
  licenses: []
@@ -88,5 +113,16 @@ rubygems_version: 1.3.7
88
113
  signing_key:
89
114
  specification_version: 3
90
115
  summary: Migrate multiple tables with similar schema at once.
91
- test_files: []
92
-
116
+ test_files:
117
+ - spec/Rakefile
118
+ - spec/also_migrate/gems_spec.rb
119
+ - spec/also_migrate_spec.rb
120
+ - spec/config/database.yml.example
121
+ - spec/db/migrate/001_create_articles.rb
122
+ - spec/db/migrate/002_add_permalink.rb
123
+ - spec/db/migrate/003_create_comments.rb
124
+ - spec/fixtures/article.rb
125
+ - spec/fixtures/comment.rb
126
+ - spec/fixtures/gemsets.yml
127
+ - spec/fixtures/gemspec.yml
128
+ - spec/spec_helper.rb