rake db:migrate doesn't detect new migration? - ruby-on-rails

Experienced with Rails / ActiveRecord 2.1.1
You create a first version with (for example) ruby script\generate scaffold product title:string description:text image_url:string
This create (for example) a migration file called 20080910122415_create_products.rb
You apply the migration with rake db:migrate
Now, you add a field to the product table with ruby script\generate migration add_price_to_product price:decimal
This create a migration file called 20080910125745_add_price_to_product.rb
If you try to run rake db:migrate, it will actually revert the first migration, not apply the next one! So your product table will get destroyed!
But if you ran rake alone, it would have told you that one migration was pending
Pls note that applying rake db:migrate (once the table has been destroyed) will apply all migrations in order.
The only workaround I found is to specify the version of the new migration as in:
rake db:migrate version=20080910125745
So I'm wondering: is this an expected new behavior?

You should be able to use
rake db:migrate:up
to force it to go forward, but then you risk missing interleaved migrations from other people on your team
if you run
rake db:migrate
twice, it will reapply all your migrations.
I encounter the same behavior on windows with SQLite, it might be a bug specific to such an environment.
Edit -- I found why. In the railstie database.rake task you have the following code :
desc "Migrate the database through scripts in db/migrate. Target specific version with VERSION=x. Turn off output with VERBOSE=false."
task :migrate => :environment do
ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
ActiveRecord::Migrator.migrate("db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
Rake::Task["db:schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby
end
Then in my environment variables I have
echo %Version% #=> V3.5.0f
in Ruby
ENV["VERSION"] # => V3.5.0f
ENV["VERSION"].to_i #=>0 not nil !
thus the rake task calls
ActiveRecord::Migrator.migrate("db/migrate/", 0)
and in ActiveRecord::Migrator we have :
class Migrator#:nodoc:
class << self
def migrate(migrations_path, target_version = nil)
case
when target_version.nil? then up(migrations_path, target_version)
when current_version > target_version then down(migrations_path, target_version)
else up(migrations_path, target_version)
end
end
Yes, rake db:migrate VERSION=0 is the long version for rake db:migrate:down
Edit - I would go update the lighthouse bug but I the super company proxy forbids that I connect there
In the meantime you may try to unset Version before you call migrate ...

This is not the expected behaviour. I was going to suggest reporting this as a bug on lighthouse, but I see you've already done so! If you provide some more information (including OS/database/ruby version) I will take a look at it.

I respectfully disagree Tom! this is a bug !! V3.5.0f is not a valid version for rake migrations. Rake should not use it to migrate:down just because ruby chose to consider that "V3.5.0f".to_i is 0 ...
Rake should loudly complain that VERSION is not valid so that users know what is up
(between you and me, checking that the version is a YYYYMMDD formated timestamp by converting to integer is a bit light)
[Damn IE6 that won't allow me to comment ! and no I can't change browser thanks corporate]

Jean,
Thanks a lot for your investigation. You're right, and actually I think you've uncovered a more severe bug, of species 'design bug'.
What's happening is that rake will grab whatever value you pass to the command line and store them as environment variables. The rake tasks that will eventually get called will just pull this values from the environment variable.
When db:migrate queries ENV["VERSION"], it actually requests the version parameter which you set calling rake. When you call rake db:migrate, you don't pass any version.
But we do have an environment variable called VERSION that has been set for other purposes by some other program (I don't which one yet). And the guys behind rake (or behind database.rake) haven't figured this would happen. That's a design bug. At least, they could have used more specific variable names like "RAKE_VERSION" or "RAKE_PARAM_VERSION" instead of just "VERSION".
Tom, I will definitely not close but edit my bug report on lighthouse to reflect these new findings.
And thanks again Jean for your help. I've posted this bug on lighthouse like 5 days agao and still got no answer!
Rollo

Related

Why is 'rake db:rollback' rolling back 2 steps instead of the default of 1

I am having some problems with rake db:rollback. Here's the setup:
One week ago I pulled some changes from the repository, which included a new migration. I ran rake db:migrate and all was well with the world. Fast-forward 7 days (and multiple commits and pushes), and I started working on a new feature. I created a new feature branch and then created a new migration. I ran that migration, wrote some codes, then realized I had made a small error in the migration that I had just created. I thought I could just run rake db:rollback, make a few changes to that one migration, and then rake db:migrate again.
But here's the rub. When I ran rake db:rollback it rolled back two migrations instead of just one. Running rake db:migrate then migrates both of those migrations. I can go back and forth all day long, but every time it always rolls back 2 migrations.
I did some searching around here on stackoverflow and found this question that is specifically referencing the STEP parameter that can be added to migrate and rollback, but going through everything in that question has not helped me. At first nothing was returned when I ran echo $STEP, so I specifically set it with export STEP=1, but that didn't help: rake db:rollback still rolls back 2 steps every time. And, even when I run rake db:rollback STEP=1 it still rolls back 2 steps.
Also, I checked the version number in the schema, and just after running rake db:migrate the version is consistent with the newest migration that I just created. After running rake db:rollback and it going back 2 steps, the version number is consistent with the 2nd most recent migration.
I want to solve this problem for 2 reasons:
I need to rollback only one migration so I can commit my changes and then switch back to the development branch and not pollute the developement schema.
I want to fully understand how rake db:rollback works. Maybe there is a way to hack this out and get things going, but I want to understand this at a deeper level.
Is there some sort of app config that determines this? Have some wires gotten crossed and I need to manually reset something? Thanks for your help!
UPDATE
As per some comments and responses:
I explicitly set export STEP=1 in my .bash_profile and that hasn't made a difference and it still rolls back 2 steps.
I was aware of migrating to a specific version, and yes, that is a way to get to specific place for me. I can rollback the 2 steps that it is forcing me and then migrate forward 1 step by specifying the version. It works, but it's not how this should work and I really want to get to the bottom of all this.
The output of rake db:migrate:status always returns this error:
rake aborted!
ArgumentError: invalid value for Integer(): ""
/Users/eliduke/.rvm/gems/ruby-2.1.0/gems/activerecord-4.0.5/lib/active_record/railties/databases.rake:98:in `%'
/Users/eliduke/.rvm/gems/ruby-2.1.0/gems/activerecord-4.0.5/lib/active_record/railties/databases.rake:98:in `block (4 levels) in <top (required)>'
/Users/eliduke/.rvm/gems/ruby-2.1.0/gems/activerecord-4.0.5/lib/active_record/railties/databases.rake:98:in `map!'
/Users/eliduke/.rvm/gems/ruby-2.1.0/gems/activerecord-4.0.5/lib/active_record/railties/databases.rake:98:in `block (3 levels) in <top (required)>'
/Users/eliduke/.rvm/gems/ruby-2.1.0/bin/ruby_executable_hooks:15:in `eval'
/Users/eliduke/.rvm/gems/ruby-2.1.0/bin/ruby_executable_hooks:15:in `<main>'
Tasks: TOP => db:migrate:status
And the output of rake db:version might be acting strange. If I have just recently migrated all the way forward, it is 20150309194758, which corresponds to my most recent migration. If I have just rolled back (the 2 steps it is forcing me), the version is 20150203171351, which corresponds to the migration that is 3 steps back. Is that normal? Shouldn't it be 20150303192838, which is 2 steps back?
Also, I now see a peculiarity in the output for rake db:rollback. It first shows the rolling back of the first migration like normal (1 step) and then it looks like maybe the Apartment gem is chiming in and rolling back the next migration (2 step).
== 20150309194758 CreateFeeds: reverting ======================================
-- drop_table(:feeds, {:id=>false})
-> 0.0291s
== 20150309194758 CreateFeeds: reverted (0.0293s) =============================
Rolling back development tenant
== 20150303192838 AddDisplayRankToEventPrizes: reverting ======================
-- remove_column(:event_prizes, :display_rank, :integer, {:index=>true, :after=>:winner_user_id})
-> 0.5963s
== 20150303192838 AddDisplayRankToEventPrizes: reverted (0.5964s) =============
See that Rolling back development tenant line? Could the apartment gem be causing the 2nd step rollback?
If you have a specific migration and don't want to touch any other migrations, you can run rake db:migrate:redo VERSION="XXXXXXXXXXX" to re-run just that specific migration.
The VERSION ID is the longer number that prefixes all of your migrations, which just connotes the date/time at which it was created. So if I had a migration file named: 20150114194155_add_foo_to_bar my command would be something like this:
rake db:migrate:redo VERSION="20150114194155"
If you truly need to leave a migration in its "down" state, it'll be a little more work. First, make sure your target migration has a down action. Then, simply run:
rake db:migrate:down VERSION="20150114194155"
This will run the "down" action on that single migration.
Here's the Rails documentation to learn more.
In the rail codebase you can find:
line 125
desc 'Rolls the schema back to the previous version (specify steps w/ STEP=n).'
task :rollback => [:environment, :load_config] do
step = ENV['STEP'] ? ENV['STEP'].to_i : 1
ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step)
db_namespace['_dump'].invoke
end
So if the environment variable has to be set somewhere. Non the less, it's a good practice to always specify the number of steps:
rake db:rollback STEP=1

Regenerate ERD after rake db:migrate

I am using http://rails-erd.rubyforge.org/ to generate an ERD - the output is a very nice diagram of my project's object model. There is also a rake task to generate the ERD, generate_erd, that I would like to have invoked automatically after I run rake db:migrate. How do I do that?
The given link by #MaxWilliams is a helpful but I don't think any of those answers quite do what you want. I found this article on Rake Task Overwriting. It's from 2008, but I tried this out and it worked.
I created another .rake file (for organization) and just happened to call mine migrate_and_generate_erb.rake but name it whatever you want.
Inside I just had this:
namespace :db do
task :migrate do
Rake::Task["erd"].invoke
end
end
Basically, according to the article, Rake just keeps appending code implementation to the task if it's already defined.
Now running rake db:migrate also generated me my ERD.
Careful: You'll also want to do the same for db:rollback so that rolling back a migration also updates your ERD.
One last note: consider also just aliasing this (shell command), just in case you'd ever want to run the migrate without generate the ERD, or use environment variables along with your new Rake task.

ActiveRecord how to ignore pending migrations

The problem is the following:
I have db/seed.rb full of initial data.
One of migrations depends on data this seed provides.
I'm trying to deploy my app from empty db.
Result is:
RAILS_ENV=production rake db:migrate - fails due to lack of initial data
RAILS_ENV=production rake db:seed - fails due to pending migrations
I wanted to somehow tell rake to ignore pending migrations, but unable to do it so far.
UPDATE (due to additional experience)
Sometimes migrations and model code goes out of sync, so migrations are not being run.
To avoid this problem recently used redefining of model in migrations:
# reset all callbacks, hooks, etc for this model
class MyAwesomeModel < ActiveRecord::Base
end
class DoSomethingCool < ActiveRecord::Migration
def change
...
end
end
I am not very sure if this will help you. But I was looking for something and found this question. So it looks like this might help:
In RAILS_ROOT/config/environments/development.rb
Set the following setting to false:
# NOTE: original version is `:page_load`
config.active_record.migration_error = false
In my situation it now does not show the pending migration error anymore. Should work for rake tasks and console for the same environment as well.
Source in rails/rails
Rename the migration dependent on the data from:
20140730091353_migration_name.rb
to
.20140730091353_migration_name.rb
(add a dot at the start of the filename)
Then run rake db:seed (it will no longer complain on the pending migrations) and then rename back the migration.
If you have more migrations following after, you have to rename all of them or just move it temporary away.
Rails stores migration information in a table called schema_migrations.
You can add the version from your migration into that table to skip a specific migration.
The version is the number string which comes before the description in the file name.
[version]_Create_Awesome.rb
I had a similar issue. I commented out the add_column lines and ran the rake db:migrate commands and then removed the comment when I will need it for the testing or production environment.
There is no way unless you monkey patch the Rails code. I strongly advise you to fix your migrations instead.
A migration should not depend on the existence of some data in the database. It can depend on a previous migration, but of course absolutely not on the data on the db.
If you came across the "pending migrations" issue when trying to seed your data from within a running Rails application, you can simply call this directly which avoids the abort_if_pending_migrations check:
ActiveRecord::Tasks::DatabaseTasks.load_seed
See where seeds are actually called from within ActiveRecord:
https://github.com/rails/rails/blob/v6.0.3.2/activerecord/lib/active_record/railties/databases.rake#L331
and see the DatabaseTasks docs:
https://apidock.com/rails/v6.0.0/ActiveRecord/Tasks/DatabaseTasks
https://apidock.com/rails/v6.0.0/ActiveRecord/Tasks/DatabaseTasks/load_seed

How can I tell what changes 'rake db:migrate' will apply before applying them?

The command rake db:migrate will apply all relevant new migrations to a database and will list in the output the migrations that were applied.
I'd like to know in advance what migrations will be applied so as to note down a list of the changes that are expected in a production environment.
Is there an option I can use in conjunction to rake db:migrate that will show me what changes will be applied without actually applying them?
Does rake db:migrate:status (rails 3+ only) fit the bill?
I guess here's the answer: Show pending migrations in rails .
If you are deploying with capistarno, you could add a task similar to this: http://gem-session.com/2010/08/warn-of-pending-migrations-when-deploying-with-capistrano .

rake db:reset does not populated data

My Environment -> Ruby 1.9.2 and Rails v3.0.5
I noted a strange pattern in rake db:reset. According to rails source code, rake db:reset will => db:drop, db:create and db:migrate. https://github.com/rails/rails/blob/v3.0.5/activerecord/lib/active_record/railties/databases.rake#L159
Setup: One of my migration files have Model.create statements to populate some data (Forgive me, I'm not the one who had put data-filling-code in those migrations :) ..)
Case 1: When I do the steps manually, i mean drop, create, and migrate, one by one - those statements fill data in the table.
Case 2: When I do just rake db:reset, schema is set properly. but the data is not entering the db. Does db:reset skip create/update statements.. I have tried this several times to make sure that I have no faults in the steps I do. I still get this behavior.
what is going wrong here... ?
I think you're reading the wrong line in the source. As I read it:
db:migrate:reset # => [:drop, :create, :migrate]
db:reset # => [:drop, :setup]
So db:reset just create the tables and sets the migrations as if they had been run, without actually running them. db:migrate:reset actually runs each migration.
I had the same problem before but I was running 3.0.3, and it turns out, somehow I manage to mess up the migrations by change the migrations files and not running the migrations(forgot about it or something)...I'll start by checking those files

Resources