lib/migrant/migration_generator.rb in migrant-1.1.1 vs lib/migrant/migration_generator.rb in migrant-1.1.2
- old
+ new
@@ -1,5 +1,7 @@
+require 'erubis'
+
module Migrant
class MigrationGenerator
TABS = ' ' # Tabs to spaces * 2
NEWLINE = "\n "
def run
@@ -19,83 +21,56 @@
Dir["#{Rails.root.to_s}/app/models/**/*.rb"].each { |f| require(f) }
ActiveRecord::Base.descendants.each do |model|
next if model.schema.nil? || !model.schema.requires_migration? # Skips inherited schemas (such as models with STI)
model.reset_column_information # db:migrate doesn't do this
- model_schema = model.schema.column_migrations
+ @table_name = model.table_name
if model.table_exists?
# Structure ActiveRecord::Base's column information so we can compare it directly to the schema
db_schema = Hash[*model.columns.collect {|c| [c.name.to_sym, Hash[*[:type, :limit].map { |type| [type, c.send(type)] }.flatten] ] }.flatten]
- changes = model.schema.columns.collect do |name, data_type|
- begin
- [name, data_type.structure_changes_from(db_schema[name])]
- rescue DataType::DangerousMigration
- puts "Cannot generate migration automatically for #{model.table_name}, this would involve possible data loss on column: #{name}\nOld structure: #{db_schema[name].inspect}. New structure: #{data_type.column.inspect}\nPlease create and run this migration yourself (with the appropriate data integrity checks)"
- return false
+ @changed_columns, @added_columns = [], []
+ model.schema.columns.to_a.sort { |a,b| a.to_s <=> b.to_s }.each do |field_name, data_type|
+ begin
+ if (options = data_type.structure_changes_from(db_schema[field_name]))
+ if db_schema[field_name]
+ @changed_columns << [field_name, options, db_schema[field_name]]
+ else
+ @added_columns << [field_name, options]
+ end
end
- end.reject { |change| change[1].nil? }
- next if changes.blank?
- changed_fields, added_fields = [], []
+ rescue DataType::DangerousMigration
+ puts "Cannot generate migration automatically for #{model.table_name}, this would involve possible data loss on column: #{field_name}\nOld structure: #{db_schema[field_name].inspect}. New structure: #{data_type.column.inspect}\nPlease create and run this migration yourself (with the appropriate data integrity checks)"
+ return false
+ end
+ end
- up_code = changes.collect do |field, options|
- type = options.delete(:type)
- arguments = (options.blank?)? "" : ", #{options.inspect[1..-2]}"
+ # For adapters that can report indexes, add as necessary
+ if ActiveRecord::Base.connection.respond_to?(:indexes)
+ current_indexes = ActiveRecord::Base.connection.indexes(model.table_name).collect { |index| (index.columns.length == 1)? index.columns.first.to_sym : index.columns.collect(&:to_sym) }
+ @indexes = model.schema.indexes.uniq.reject { |index| current_indexes.include?(index) }.collect { |field_name| [field_name, {}] }
+ # Don't spam the user with indexes that columns are being created with
+ @new_indexes = @indexes.reject { |index, options| @changed_columns.detect { |c| c.first == index } || @added_columns.detect { |c| c.first == index } }
+ end
- if db_schema[field]
- changed_fields << field
- "change_column :#{model.table_name}, :#{field}, :#{type}#{arguments}"
- else
- added_fields << field
- "add_column :#{model.table_name}, :#{field}, :#{type}#{arguments}"
- end
- end.join(NEWLINE+TABS)
+ next if @changed_columns.empty? && @added_columns.empty? && @indexes.empty? # Nothing to do for this table
- activity = 'changed_'+model.table_name+[(added_fields.blank?)? nil : '_added_'+added_fields.join('_'), (changed_fields.blank?)? nil : '_modified_'+changed_fields.join('_')].compact.join('_and_')
-
- down_code = changes.collect do |field, options|
- if db_schema[field]
- type = db_schema[field].delete(:type)
- arguments = (db_schema[field].blank?)? "" : ", #{db_schema[field].inspect[1..-2]}"
- "change_column :#{model.table_name}, :#{field}, :#{type}#{arguments}"
- else
- "remove_column :#{model.table_name}, :#{field}"
- end
- end.join(NEWLINE+TABS)
-
- # For adapters that can report indexes, add as necessary
- if ActiveRecord::Base.connection.respond_to?(:indexes)
- current_indexes = ActiveRecord::Base.connection.indexes(model.table_name).collect { |index| (index.columns.length == 1)? index.columns.first.to_sym : index.columns.collect(&:to_sym) }
- up_code += model.schema.indexes.uniq.collect do |index|
- unless current_indexes.include?(index)
- NEWLINE+TABS+"add_index :#{model.table_name}, #{index.inspect}"
- end
- end.compact.join
- end
+ # Example: changed_table_added_something_and_modified_something
+ @activity = 'changed_'+model.table_name+[['added', @added_columns], ['modified', @changed_columns], ['indexed', @new_indexes]].reject { |v| v[1].empty? }.collect { |v| "_#{v[0]}_"+v[1].collect(&:first).join('_') }.join('_and')
+ @activity = @activity.split('_')[0..2].join('_') if @activity.length >= 240 # Most filesystems will raise Errno::ENAMETOOLONG otherwise
+
+ render('change_migration')
else
- activity = "create_#{model.table_name}"
- up_code = "create_table :#{model.table_name} do |t|"+NEWLINE+model_schema.collect do |field, options|
- type = options.delete(:type)
- options.delete(:was) # Aliases not relevant when creating a new table
- arguments = (options.blank?)? "" : ", #{options.inspect[1..-2]}"
- (TABS*2)+"t.#{type} :#{field}#{arguments}"
- end.join(NEWLINE)+NEWLINE+TABS+"end"
+ @activity = "create_#{model.table_name}"
+ @columns = model.schema.column_migrations
+ @indexes = model.schema.indexes
- down_code = "drop_table :#{model.table_name}"
- up_code += NEWLINE+TABS+model.schema.indexes.collect { |fields| "add_index :#{model.table_name}, #{fields.inspect}"}.join(NEWLINE+TABS)
+ render("create_migration")
end
-
- # Indexes
- # down_code += NEWLINE+TABS+model.schema.indexes.collect { |fields| "remove_index :#{model.table_name}, #{fields.inspect}"}.join(NEWLINE+TABS)
- begin
- filename = "#{migrations_path}/#{next_migration_number}_#{activity}.rb"
- File.open(filename, 'w') { |migration| migration.write(migration_template(activity, up_code, down_code)) }
- rescue Errno::ENAMETOOLONG
- activity = activity.split('_')[0..2].join('_')
- filename = "#{migrations_path}/#{next_migration_number}_#{activity}.rb"
- File.open(filename, 'w') { |migration| migration.write(migration_template(activity, up_code, down_code)) }
- end
+
+ filename = "#{migrations_path}/#{next_migration_number}_#{@activity}.rb"
+ File.open(filename, 'w') { |migration| migration.write(@output) }
puts "Wrote #{filename}..."
end
true
end
@@ -119,19 +94,11 @@
else
(highest.to_i + 1).to_s
end
end
- def migration_template(activity, up_code, down_code)
- "class #{activity.camelize.gsub(/\s/, '')} < ActiveRecord::Migration
- def self.up
- #{up_code}
- end
-
- def self.down
- #{down_code}
- end
-end"
+ def render(template_name)
+ @output = Erubis::Eruby.new(File.read(File.join(File.dirname(__FILE__), "../generators/templates/#{template_name}.erb"))).result(binding)
end
end
end