-1

Suppose there's a migration that creates some records and there are two commits that look like this

# commit 1 by Alice
# migration
def up
    create_table :cities do |t|
        t.string :name
    end
    City.create!(name: 'New York')
    City.create!(name: 'Moscow')
end

# commit 2 by Bob
# migration
def change
    change_table :cities do |t|
        t.boolean :capital
    end
end
# model
class City
    before_create :set_capital

    def set_capital
        capital = false
    end
end

Now, if the third person pulls the code migrations will fail because capital attribute won't be there. Is there a way to remedy the situation?

mu is too short
  • 426,620
  • 70
  • 833
  • 800
synapse
  • 5,588
  • 6
  • 35
  • 65
  • 1
    Well for starters don't use migrations to create records - just use them to alter the database schema. Thats what the seeds file is for. So fire Alice :) – max Feb 15 '16 at 02:11
  • If you remedy that there is no obvious reason why the migrations would fail when the third person clones the repo and runs `rake db:migrate` - migrations don't really give a hoot about you models or your `before_create` filter. They just alter the database schema. – max Feb 15 '16 at 02:16
  • If you had to fix this you would create a new commit which removes `City.create!(name: 'Moscow')` from Alice's bad migration file. – max Feb 15 '16 at 02:27

1 Answers1

-1

I would do two things here:

  1. Do not allow NULLs in the capital column. Allowing NULLs is the Rails default but it is almost always the wrong choice so make the column NOT NULL by hand. You might want to apply this to all the other columns in your tables too: if you can't justify allow NULLs in a column, make it NOT NULL.

  2. Include a default value – in the database, not just in your Rails code – for the capital column.

This would make migration 2 look like:

change_table :cities do |t|
    t.boolean :capital, :null => false, :default => false
end

If you do both of those then the database will fill in the defaults when it adds your new column and your problem goes away.

I tend to treat the database as an entirely separate application unto itself whose interface happens to be SQL (mostly) hidden behind ActiveRecord or some other ORM.

I'd also agree with the commenters that using models inside your migrations is a bad idea.


BTW, your set_capital method doesn't do what you think it does. You want to say:

def set_capital
    self.capital = false
    true
end

capital = false only creates a local variable and then set_capital will return false; so your set_capital won't initialize the capital attribute and a before_* callback return false will stop everything in its tracks:

Canceling callbacks

If a before_* callback returns false, all the later callbacks and the associated action are cancelled.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • The code is just a contrived example of the following situation - someone uses migration to create a table and populate it with data, then adds a `before_create` in such a way that the previous migration becomes usable only by people who got a very specific snapshot of the code base. – synapse Feb 15 '16 at 06:35
  • Then the real solution will depend on the specifics. Rules of thumb: (1) treat the database as a separate application rather than just a component of the web-app. (2) Don't use models in your migrations, do everything that needs to be done by directly talking to the database with no ORM or models in the way. – mu is too short Feb 15 '16 at 06:44