Chris@909: # Generates a migration which migrates all plugins to their latest versions Chris@909: # within the database. Chris@909: class PluginMigrationGenerator < Rails::Generator::Base Chris@909: Chris@909: # 255 characters max for Windows NTFS (http://en.wikipedia.org/wiki/Filename) Chris@909: # minus 14 for timestamp, minus some extra chars for dot, underscore, file Chris@909: # extension. So let's have 230. Chris@909: MAX_FILENAME_LENGTH = 230 Chris@909: Chris@909: def initialize(runtime_args, runtime_options={}) Chris@909: super Chris@909: @options = {:assigns => {}} Chris@909: ensure_schema_table_exists Chris@909: get_plugins_to_migrate(runtime_args) Chris@909: Chris@909: if @plugins_to_migrate.empty? Chris@909: puts "All plugins are migrated to their latest versions" Chris@909: exit(0) Chris@909: end Chris@909: Chris@909: @options[:migration_file_name] = build_migration_name Chris@909: @options[:assigns][:class_name] = build_migration_name.classify Chris@909: end Chris@909: Chris@909: def manifest Chris@909: record do |m| Chris@909: m.migration_template 'plugin_migration.erb', 'db/migrate', @options Chris@909: end Chris@909: end Chris@909: Chris@909: protected Chris@909: Chris@909: # Create the schema table if it doesn't already exist. Chris@909: def ensure_schema_table_exists Chris@909: ActiveRecord::Base.connection.initialize_schema_migrations_table Chris@909: end Chris@909: Chris@909: # Determine all the plugins which have migrations that aren't present Chris@909: # according to the plugin schema information from the database. Chris@909: def get_plugins_to_migrate(plugin_names) Chris@909: Chris@909: # First, grab all the plugins which exist and have migrations Chris@909: @plugins_to_migrate = if plugin_names.empty? Chris@909: Engines.plugins Chris@909: else Chris@909: plugin_names.map do |name| Chris@909: Engines.plugins[name] ? Engines.plugins[name] : raise("Cannot find the plugin '#{name}'") Chris@909: end Chris@909: end Chris@909: Chris@909: @plugins_to_migrate.reject! { |p| !p.respond_to?(:latest_migration) || p.latest_migration.nil? } Chris@909: Chris@909: # Then find the current versions from the database Chris@909: @current_versions = {} Chris@909: @plugins_to_migrate.each do |plugin| Chris@909: @current_versions[plugin.name] = Engines::Plugin::Migrator.current_version(plugin) Chris@909: end Chris@909: Chris@909: # Then find the latest versions from their migration directories Chris@909: @new_versions = {} Chris@909: @plugins_to_migrate.each do |plugin| Chris@909: @new_versions[plugin.name] = plugin.latest_migration Chris@909: end Chris@909: Chris@909: # Remove any plugins that don't need migration Chris@909: @plugins_to_migrate.map { |p| p.name }.each do |name| Chris@909: @plugins_to_migrate.delete(Engines.plugins[name]) if @current_versions[name] == @new_versions[name] Chris@909: end Chris@909: Chris@909: @options[:assigns][:plugins] = @plugins_to_migrate Chris@909: @options[:assigns][:new_versions] = @new_versions Chris@909: @options[:assigns][:current_versions] = @current_versions Chris@909: end Chris@909: Chris@909: # Returns a migration name. If the descriptive migration name based on the Chris@909: # plugin names involved is shorter than 230 characters that one will be Chris@909: # used. Otherwise a shorter name will be returned. Chris@909: def build_migration_name Chris@909: descriptive_migration_name.tap do |name| Chris@909: name.replace short_migration_name if name.length > MAX_FILENAME_LENGTH Chris@909: end Chris@909: end Chris@909: Chris@909: # Construct a unique migration name based on the plugins involved and the Chris@909: # versions they should reach after this migration is run. The name constructed Chris@909: # needs to be lowercase Chris@909: def descriptive_migration_name Chris@909: @plugins_to_migrate.map do |plugin| Chris@909: "#{plugin.name}_to_version_#{@new_versions[plugin.name]}" Chris@909: end.join("_and_").downcase Chris@909: end Chris@909: Chris@909: # Short migration name that will be used if the descriptive_migration_name Chris@909: # exceeds 230 characters Chris@909: def short_migration_name Chris@909: 'plugin_migrations' Chris@909: end Chris@909: end