class XMigra::ImpdeclMigrationAdder
Attributes
strict[RW]
Public Class Methods
each_support_type(&blk)
click to toggle source
# File lib/xmigra/impdecl_migration_adder.rb, line 27 def self.each_support_type(&blk) @support_types.each_pair(&blk) end
new(path)
click to toggle source
Calls superclass method
# File lib/xmigra/impdecl_migration_adder.rb, line 52 def initialize(path) super(path) @migrations = MigrationChain.new( self.path.join(STRUCTURE_SUBDIR), :db_specifics=>@db_specifics, :vcs_specifics=>@vcs_specifics, ) end
register_support_type(tag, klass) { || ... }
click to toggle source
# File lib/xmigra/impdecl_migration_adder.rb, line 9 def self.register_support_type(tag, klass) if @support_types.has_key? tag raise Error, "#{@support_types[tag]} already registered to handle #{tag}" end @support_types[tag] = klass if block_given? begin yield ensure @support_types.delete(tag) end end end
support_type(tag)
click to toggle source
# File lib/xmigra/impdecl_migration_adder.rb, line 23 def self.support_type(tag) @support_types[tag] end
Public Instance Methods
add_migration_implementing_changes(file_path, options={})
click to toggle source
# File lib/xmigra/impdecl_migration_adder.rb, line 63 def add_migration_implementing_changes(file_path, options={}) file_path = Pathname(file_path) prev_impl = @migrations.latest_declarative_implementations[file_path] decl_stat = prev_impl.declarative_status # Declarative doesn't make any sense without version control unless VersionControlSupportModules.find {|m| self.kind_of? m} raise Error, "#{self.path} is not under version control (required for declarative)" end # Check if an implementation is needed/allowed if bad_rel = { :equal=>"the same revision as", :older=>"an older revision than", }[decl_stat] raise NoChangesError, "#{file_path} changed in #{bad_rel} the latest implementing migration #{prev_impl.file_path}" end # This should require the same user to generate a migration on the same # day starting from the same committed version working on the same # branch to cause a collision of migration file names: file_hash = begin file_base = begin [ SchemaUpdater.new(path).branch_identifier, vcs_latest_revision(file_path), ].join("\x00") rescue VersionControlError '' end XMigra.secure_digest( [(ENV['USER'] || ENV['USERNAME']).to_s, file_base.to_s].join("\x00"), :encoding=>:base32 )[0,12] end summary = "#{file_path.basename('.yaml')}-#{file_hash}.decl" add_migration_options = { :file_path=>file_path, } # Figure out the goal of the change to the declarative fail_options = [] case decl_stat when :unimplemented fail_options << :renounce add_migration_options[:goal] = options[:adopt] ? 'adoption' : 'creation' when :newer fail_options.concat [:adopt, :renounce] add_migration_options[:goal] = 'revision' when :missing fail_options << :adopt add_migration_options[:goal] = options[:renounce] ? 'renunciation' : 'destruction' end if opt = fail_options.find {|o| options[o]} raise Program::ArgumentError, "--#{opt} flag is invalid when declarative file is #{decl_stat}" end # gsub gets rid of trailing whitespace on a line (which would force double-quote syntax) add_migration_options[:delta] = prev_impl.delta(file_path).gsub(/\s+$/, '').extend(LiteralYamlStyle) unless options[:adopt] || options[:renounce] begin if suggested_sql = build_suggested_sql(decl_stat, file_path, prev_impl) add_migration_options[:sql] = suggested_sql add_migration_options[:sql_suggested] = true end rescue DeclarativeSupport::SpecificationError add_migration_options[:spec_error] = $!.to_s end end add_migration(summary, add_migration_options) end
build_suggested_sql(decl_stat, file_path, prev_impl)
click to toggle source
# File lib/xmigra/impdecl_migration_adder.rb, line 180 def build_suggested_sql(decl_stat, file_path, prev_impl) d = SupportedObjectDeserializer.new( file_path.basename('.yaml').to_s, @db_specifics ) case decl_stat when :unimplemented initial_state = YAML.parse_file(file_path) initial_state = d.deserialize(initial_state.children[0]) if initial_state.kind_of?(SupportedDatabaseObject) initial_state.creation_sql end when :newer old_state = YAML.parse( vcs_contents(file_path, :revision=>prev_impl.vcs_latest_revision), file_path ) old_state = d.deserialize(old_state.children[0]) new_state = YAML.parse_file(file_path) new_state = d.deserialize(new_state.children[0]) if new_state.kind_of?(SupportedDatabaseObject) && old_state.class == new_state.class new_state.sql_to_effect_from old_state end when :missing penultimate_state = YAML.parse( vcs_contents(file_path, :revision=>prev_impl.vcs_latest_revision), file_path ) penultimate_state = d.deserialize(penultimate_state.children[0]) if penultimate_state.kind_of?(SupportedDatabaseObject) penultimate_state.destruction_sql end end rescue DeclarativeSupport::SpecificationError raise rescue StandardError => e XMigra.log_error(e) raise if strict nil end
migration_data(head_info, options)
click to toggle source
Calls superclass method
# File lib/xmigra/impdecl_migration_adder.rb, line 138 def migration_data(head_info, options) target_object = options[:file_path].basename('.yaml') goal = options[:goal].to_sym super(head_info, options).tap do |data| # The "changes" key is not used by declarative implementation #migrations -- the "of object" (TARGET_KEY) is used instead data.delete(Migration::CHANGES) data[DeclarativeMigration::GOAL_KEY] = options[:goal].to_s data[DeclarativeMigration::TARGET_KEY] = target_object.to_s data[DeclarativeMigration::DECLARATION_VERSION_KEY] = begin if [:renunciation, :destruction].include?(goal) 'DELETED' else XMigra.secure_digest(options[:file_path].read) end end data['delta'] = options[:delta] options[:spec_error].tap do |message| data['specification error'] = message if message end # Reorder "sql" key to here (unless adopting or renouncing, then # remove "sql" completely) provided_sql = data.delete('sql') unless [:adoption, :renunciation].include? goal data['sql'] = provided_sql data[DeclarativeMigration::QUALIFICATION_KEY] = begin if options[:sql_suggested] 'suggested command sequence' else 'unimplemented' end end end # Reorder "description" key to here with data.delete('description') data['description'] = "Declarative #{goal} of #{target_object}" end end