Managing Rails Migrations for different branches on the same machine - ruby-on-rails

I'm a one-man-band at the company I work for. I develop a Rails application for internal use within the company. Since the beginning of the project I have used SVN for source control and done most, but not all, development in trunk. Occasionally, when I have had very significant changes to make, I have branched and made the changes merging back in when done. All very typical.
However, none of those "significant changes" that I have had to make have ever touched the database migrations. They have always been view/controller stuff.
In this situation, with one development box, how do I play around with migrations and various database changes that I may or may not keep? I don't want to have to remember to revert all the migrations back to the beginning of the branch before I throw the branch out if it doesn't work.
I have considered setting up special development environments and databases (app_branch instead of app_development) but that seems to work strongly against the notion of "easy branching" that experimental development tends to rely on.
Are there best practices for this situation? What are others out there doing in this situation?

I try hard to keep my development database "droppable." If I lose it all - no big deal. My migrations are ready to build it up again from scratch and there's always a script with seed / test data in it somewhere. I guess it's not especially clever.
If I needed a new branch for database work, I would just check it out, drop, create, rake, and then seed. I guess I'd write a script to get it done because when I go to adandon the branch, I'm going to have to go through the same process again from the trunk.

Make sure your schema.rb file is in version control. That way, as you switch branches, you can drop your DB and then do rake db:schema:load as necessary.
Also, you really should switch to Git. It will make branch management a lot easier than SVN. (I speak from lots of experience with both programs.)

Well, if you want to have different schemas, you'll need multiple databases. "Easy branching" refers to source control, typically, and not databases. As far as I know there's no easy way to branch databases like you would branch in, say, git.
One thing we do to manage our dev/production branches is we check our current git branch in our database.yml file. If the current branch is production, we use one database, otherwise we use our dev database. something along the lines of this:
<% if 'git branch' =~ /^\* production/
db = 'production_database'
else
db = 'development_database
end %>
development:
database: <% db %>
Note, the 'production_database' refers to a local version of the production schema, not the live production database.

I wrote a script for dealing with this exact problem. It is based around git, but you could easily change it to work for svn:
https://gist.github.com/4076864
Given a branch name it will:
Roll back any migrations on your current branch which do not exist on the given branch
Discard any changes to the db/schema.rb file
Check out the given branch
Run any new migrations existing in the given branch
Update your test database
I find myself manually doing this all the time on our project, so I thought it'd be nice to automate the process.

If I am creating a branch where you are making siginificant changes, you can create a copy of the database before creating your migrations then change the development section of database.yml inside the branch. Leave your :production section alone and then decide which version of the database you want to keep for future development when you merge the branch back into the trunk.
We do this with feature releases. I'll have local DBs for version 1, 2, 3 like "db_v1", db_v2", etc. As we roll through the versions, each subsequent development branch gets an edit in database.yml while the trunk stays on the last version for bug fixing.

Related

git branch with migrations run

I checked out a branch in git to experiment with developing a feature that involved making three migrations, which I have run. Preserving the very small amount of data in the db is not important. How do I handle this when merging the branch back into master? Should I rollback the migrations before merging, and then run them again after merged (as seemed to be suggested by one SO answer), or do I leave it as is and just commit everything to the branch and then merge it without rolling anything back. Another SO answer suggested removing the db from the gitignore file, but it wasn't clear if that was only necessary in situations where preserving data might be important.
# Ignore the default SQLite database.
/db/*.sqlite3
You should not track your *.sqlite3 development files in git.
You should
Merge master into your branch
Make sure all is well
Checkout master
Merge the branch back into master
Continue development
The merge will pull in your migrations from the branch. You could rollback before the merge, do the merge, and then migrate, but there's no need; the resulting schema at the end will be identical.
In situations where a rollback/migration would be necessary, it's likely your migrations between master and the branch conflict with one another somehow. This is something you'd fix in step #2 above when you're "Making sure all is well".
As a general rule, you should be able to take a completely blank database, run rake db:migrate and end up with an up-to-date database structure without anything failing. This is why step #2 above is important, to ensure you're not merging a breaking/conflicting migration back into master.
As for situations where you are risking losing data in development, this is what fixtures are for. You can use Rails seeding functionality, or a gem like factory_girl.

Ignoring .gitignore

My brother and I are collaborating on an app from two different computers -- one mac and one pc. I can't for the life of me get Postgres to work on his computer, and after a whole bunch of hours, I decided to just have his computer run sqlite3 for development (which is easy as pie), and basically have all the production stuff happen on my mac, while still allowing him to make functional changes from his pc. And merge them to github.
The trouble is, this involves having two different database.yml files, two different db/schema.rb files (I think), and different gemfiles, one with sqlite and the other with pg.
My thought was just to do all that on his computer and add those files to the gitignore file. But if THAT isn't ignored, then when I pull back to my mac, won't I be merging his incorrect configurations to my machine?
At any rate, that's why I was thinking of adding .gitignore to .gitignore. Will this work? Will it create universe-bending paradoxes? Is there a better way to do this that I don't know about?
Are those two schemas really different? They usually aren't.
If they aren't then just ignore config/database.yml and create contig/database_sqlite_example.yml and contig/database_ppostgresql_example.yml. That way, when someone clones repo, he can use SQLite or PostgreSQL by simply copying example file to database.yml (which will be ignored)
No, don't ignore .gitignore
I've always liked the idea of creating local branches for this. I actually go a little local branch crazy... but that's a different issue all together.
If you want to have a private little work bubble then keep your branch local only. You control what gets merged into master (or whatever your development branch is) and you can commit everything in your local branch to git for history sake.
If you want to share what you are working on then just share your branch out. But this way you can keep your environment setups isolated while sharing the local branch so that it is visible for collaboration.
There's plenty of good documentation on Git Branching and Sharing so I'll leave that to you instead of clouding the post with links that surely will get broken.
I'm not sure any of us "really want to" be working directly in the master anyway, especially in collaboration efforts such as yours.

Is there a good migration management/clean-up strategy after completing a Rails project?

As I go through the development of an application, in my development environment, I make mistakes and create tables that need to be modified later - as I use the app and see the folly of earlier design decisions.
But when I push to production, I don't want to replicate all those changes when I can just create the 'perfect' table in one migration.
Is there a strategy or something I can use that can help me do that?
If this is the initial push to production, you should be able to copy your schema.rb contents (which should reflect the schema after all migrations were applied) into one giant migration file, and remove all previous migration files.
If your production environment has already been created and the database has been migrated up to a point, then you'll need to use something more intelligent that can bridge the gap between the current state and the latest schema. Unfortunately, I'm not aware of any such project at this time.

rake db migration issues

Some questions on db migration tasks (rake db:migrate)
Does it make sense to rename the file names, if there is a spelling mistake.
(e.g. CreaetFoos.rb to CreateFoos.rb)
I created a migration script (say version '3') by mistake during the dev process and I would like it to be removed from git. What if I had already migrated to be at the current level of '6', should I just rollback till '2', remove migration script corresponding to '3' from git and re-run migration scripts. Will schema_migrations hold the right data in this case?
I would like to create a migration script during the dev process, but I don't want this to be considered as part of the migration script unless I call it complete (i.e. I don't want other developers to use a incomplete migration script which is checked into git). How do I handle this?
A multi-part question! Let me answer them in the proper parts.
[Question 1] Does it make sense to rename the file names, if there is a spelling mistake.
If it bothers you that much, yes. It would also bother me too.
[Question 2] [Wall of text about removing a migration]
Once a migration has been committed to your version control system, it should remain untouched. If it's modified, then you and other developers would need to roll it back and forward in order to get its changes again. It would be much better if you were to never touch old migrations and to fix any issues in new migrations. There are exceptions to this rule, which will be obvious when you encounter them.
Such as migrations that drop entire tables by accident.
[Question 3] Handling of migrations committed to version control
It's best practice to work in your own branch if you're going to be committing work that is incomplete. By doing this, you will leave the main branch ("master", probably) pristine and complete, allowing for other developers to continue on their own work.
Once you've got that migration sorted, then you will merge that branch back into master.

Rolling up Migrations?

As I understand it the point of migrations is so you can revert the database back to a known state during the last stages of development.
Right now I'm still "fleshing" out my first Rails app and I'm wondering if its ok to roll up my migrations into bigger ones rather than dozens of changes.
The point of migrations is that you basically have a log of database changes, so then other developers can know what changes have been made, or to make sure your production environment gets the same changes you made during development.
As for your question: sure. If you create a new model, and then after a few minutes decide "this column could just be a string instead of text", roll back your migration, and change the column and then migrate again. No need to create a new migration.
Unless you've already committed the previous migration to source control that may have been fetched by other developers, or you've already applied the migration on the production server. Then you should use a new migration.
As an addendum to rspeicher, I limit the constraint to whether a migration has been released, not to whether it has been made available to other developers. If it's still pre-release, then the development team can be informed of any need to run migrations for any updates of the master code repository by using post-fetch hooks for the SCM being used. This is true of any configuration management change, not just migrations. For example, changing an implementation of something in the initializers folder may have no effect on a running instance of script/server in development mode. This is a ultimately a necessary mechanism for most teams in most technologies as well as for some configurations of continuous integration. Or, you need excellent communications channels in the team to make sure that everyone knows that a configuration change and restart is necessary.

Resources