#1214 ✓resolved
Daniel Neighman

Migrations broken in rails 3

Reported by Daniel Neighman | March 6th, 2010 @ 05:45 PM | in 1.1

When trying to run a migration in rails 3, the migration breaks because it cannot find the Sql constnat in the adapter. The adapter used is the Benchmark adapter.

The code responsible is in the migration.rb file in the initialize method.

def initialize( position, name, opts = {}, &block )
  @position, @name = position, name
  @options = opts

  @database = DataMapper.repository(@options[:database] || :default)
  @adapter = @database.adapter
  if @adapter.respond_to?(:adapter)
    @adapter = @adapter.adapter
  end

  case @adapter.class.to_s
  when /Sqlite3/  then @adapter.extend(SQL::Sqlite3)
  when /Mysql/    then @adapter.extend(SQL::Mysql)
  when /Postgres/ then @adapter.extend(SQL::Postgresql)
  else
    raise "Unsupported Migration Adapter #{@adapter.class}"
  end

Here, to get around it I've added the:

  if @adapter.respond_to?(:adapter)
    @adapter = @adapter.adapter
  end

portion. This will use the adapter of the benchmark adapter.

Comments and changes to this ticket

  • Ashley Moran

    Ashley Moran March 7th, 2010 @ 06:03 AM

    • Tag changed from dm-migrations to dm-migrations, dm-rails

    Turns out we were debugging this at the exact same time. I put it in the wrong place - http://github.com/datamapper/dm-rails/issues/closed#issue/1 - but your fix is better than mine. Thanks!

  • Ashley Moran

    Ashley Moran March 7th, 2010 @ 06:14 AM

    That's not the only bug here though. For me to get it to work, I had to modify the rake tasks by creating lib/tasks/datamapper.rake:

    namespace :db do
      namespace :migrate do
        desc 'Migrate up using migrations'
        task :up, :version, :needs => :load do |t, args|
          migrate_up!(args[:version])
        end
    
        desc 'Migrate down using migrations'
        task :down, :version, :needs => :load do |t, args|
          migrate_down!(args[:version])
        end
      end
    end
    

    This is because the :load task runs migration files which call the migration method included in the global Object, whereas the current definition of :up and :down specifies ::DataMapper::MigrationRunner.migrate_up! (and down).

    If this works in 1.8.7, then it's a 1.9.1 bug, and they must have changed the way instance variables are located. The contents of @migrations "disappears" between the two locations of calling the methods (global Object vs named module).

  • Daniel Neighman

    Daniel Neighman March 7th, 2010 @ 06:18 AM

    Actually I found this also. I haven't pushed up a patch for this simply because I haven't written tests to make sure it's right. Here's the other piece of my puzzle.

    In the migration runner:

    def migrations
      @@migrations ||= []
    end
    

    Setting this to a class level variable means that it's stored with the runner class not the instance. This, for me at least, allowed it to run.

  • Ashley Moran

    Ashley Moran March 7th, 2010 @ 06:26 AM

    Once upon a time I wrote a server process that migrated its own database. I kept getting into duplicate migration conflicts until I realised MigrationRunner is effectively a singleton. The workaround was to hideously monkey-patch MigrationRunner so it could be included in another object.

    IMO the real issue here is that MigrationRunners need to be first class objects, so you can instantiate one, used it as the scope to load the migrations, run it, and discard it. This type of bug would not be possible under those circumstances.

  • Ashley Moran

    Ashley Moran March 7th, 2010 @ 06:31 AM

    Incidentally, this is the code I used in said server daemon:

    require 'rubygems'
    gem 'dm-migrations', '~> 0.9', '>= 0.9.10'
    require 'dm-migrations'
    
    module Application #:nodoc:
      module Database #:nodoc:
        
        # This class is a hack because the DataMapper file loads
        # include DataMapper::MigrationRunner into the main object
        class Migrator
          RSPEC_LOADED = Object.const_defined?(:Spec)
          
          # Default value of app_dir
          def initialize(app_dir = File.dirname(__FILE__) + "/../../..")
            @app_dir = app_dir
          end
          
          def reset_database!
            migrator = create_migrator
    
            migrator.migrate_down!
            migrator.migrate_up!
          end
    
          def migrate_up!
            app.logger.info("Migrating database up")
            create_migrator.migrate_up!
          end
    
          private
    
          def create_migrator
            migrator = Object.new
            class << migrator
              class_eval do
                require 'migration_runner'
                include DataMapper::Types
    
                # When running specs, we end up loading these multiple times,
                # which DataMapper (rightly) complains about - or at least it would be
                # rightly, but storing the migrations in a class variable is not ideal
                def verbose_migrations?
                  !RSPEC_LOADED
                end
                
                def reset_migrations!
                  migrations.delete_at(0) until migrations.empty?
                end 
              end
            end
    
            migrator.reset_migrations!
    
            Dir[File.join(@app_dir, '/lib/app/database/migrations/*.rb')].each do |migration_file|
              migrator.instance_eval(IO.read(migration_file), migration_file)
            end
    
            migrator
          end        
        end
        
      end
    end
    
  • Martin Gamsjaeger (snusnu)

    Martin Gamsjaeger (snusnu) March 25th, 2010 @ 09:16 AM

    • Milestone set to 1.0.0
    • State changed from “new” to “confirmed”
    • Assigned user set to “Martin Gamsjaeger (snusnu)”
  • Martin Gamsjaeger (snusnu)

    Martin Gamsjaeger (snusnu) March 30th, 2010 @ 09:14 AM

    • State changed from “confirmed” to “accepted”
  • Martin Gamsjaeger (snusnu)

    Martin Gamsjaeger (snusnu) March 30th, 2010 @ 09:56 AM

    • State changed from “accepted” to “resolved”

    This should be fixed in http://github.com/datamapper/dm-rails/commit/a0eb1fd1fbf09c222a7772...

    Marking this resolved for now, please reopen if there are still problems!

  • Xavier Shay

    Xavier Shay September 19th, 2010 @ 05:08 AM

    • State changed from “resolved” to “confirmed”
    • Milestone order changed from “0” to “0”

    I still needed to make the patch suggested by Dan above (change the instance var to a class var) to get these to work. I can't explain why though, I would have expected the instance var to work.

    Have re-opened this issue rather than create a new because I think the history in this ticket is relevant.

  • Xavier Shay

    Xavier Shay September 19th, 2010 @ 07:51 AM

    Here is a rake task I am using to monkey patch the gem to make the rake tasks work from my own code base:

    # Need to be able to monkey-patch MigrationRunner before all migrations
    # are loaded to work around a bug, so replace db:migrate:load with our own
    # version
    
    # From http://github.com/eugenebolshakov/override_rake_task
    Rake::TaskManager.class_eval do
      def alias_task(old_name, new_name)
        @tasks[new_name] = @tasks.delete(old_name)
      end 
    end
    
    def alias_task(old_name, new_name)
      Rake.application.alias_task(old_name, new_name)
    end
    
    def override_task(*args, &block)
      name, params, deps = Rake.application.resolve_args(args.dup)
      alias_task name.to_s, "#{name}:original"
      Rake::Task.define_task(*args, &block)
    end
    
    override_task :'db:migrate:load' => :environment do
      require 'dm-migrations/migration_runner'
    
      # Original version has an instance var, not a class, and doesn't work
      # for me
      module DataMapper
        module MigrationRunner
          def migrations
            @@migrations ||= []
          end
        end
      end
    
      # Code from original task
      FileList['db/migrate/*.rb'].each do |migration|
        load migration
      end
    
    end
    
  • Piotr Solnica (solnic)

    Piotr Solnica (solnic) October 15th, 2010 @ 05:43 AM

    • Milestone changed from 1.0.0 to 1.1
    • Milestone order changed from “13” to “0”
  • Dan Kubb (dkubb)

    Dan Kubb (dkubb) October 24th, 2010 @ 12:50 PM

    • State changed from “confirmed” to “resolved”

    (from [cd9d62ee08b6615a2d1ab2482b24d72232d867a3]) Store DataMapper::MigrationRunner#migrations in a class variable

    Fixes an issue where migrations don't get run at
    all in dm-rails rake tasks.

    Further explanation can be found in the related
    ticket on LH. This is weird. Maybe it has to do
    something with the way rails loads the rake tasks
    and invokes our stuff. No idea ...

    [#1214 state:resolved] http://github.com/datamapper/dm-migrations/commit/cd9d62ee08b6615a2...

Please Sign in or create a free account to add a new ticket.

With your very own profile, you can contribute to projects, track your activity, watch tickets, receive and update tickets through your email and much more.

New-ticket Create new ticket

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

Referenced by

Pages