How to recreate test fixtures based on development database schema - ruby-on-rails

The situation: I used generate scaffold to set up my objects in a new Rails project. Besides doing some migrations, I also directly edited the MySQL tables by adding and renaming columns etc. Now I can't get tests to run because the automatically-generated fixtures do not correspond to the database schema. Perhaps they're based on the original definitions plus migrations, and my migrations to not completely describe the new structure.
I discovered I could use "rake db:test:clone_structure" to duplicate the tables in the test database (db:test:prepare apparently creates them from the migrations?), but I can't figure out how to get the fixtures created from the development schema rather than from the migrations (or whatever is happening).
I got as far as seeing that there is a Fixtures.create_fixtures method, but where would I put it and how would I use it to regenerate all my fixtures?

When working with rails you never do this - "directly edited the MySQL tables by adding and renaming columns"
You create migrations
http://guides.rubyonrails.org/migrations.html

I completely agree that you should not change the database without using migrations, but recreating the fixtures is still a problem that people bump into from time to time. The best solution I have found is here:
http://snippets.dzone.com/posts/show/2525

Related

Rails - Generating migration script from model

I am learning rails, and I came across Migrations. It seems that everytime I want to edit a model, I would need to add a migration script, even though I am not yet in production.
Can you edit your model, add all your attributes you need to it, and before releasing it, have the migration script autogenerated?
Thanks!
If your using rails 3+ you might want to consider DataMapper instead of ActiveRecord. It lets you define the data model in the model instead of multiple migration files. As I understand DataMapper lets you generate migrations from changes.
This is a tried and trusted pattern often used in the wider ORM community.
I agree with the comments so far. The idea of migrations is to make it simple to fluidly adapt your data schema to fit your application as you want to add new fields. It's a simple and beautiful system.
So yes, you can (and should) use rails generate migration... as not only does this generate the proper code in many common cases, it also keeps track of which migrations have been run in different versions of the database. See http://guides.rubyonrails.org/migrations.html#creating-a-migration
A common workflow might be something like this:
create a new model, for example User with fields like first_name, last_name, user_name
this will create an associated migration, which you can run using bundle exec rake db:migrate -- your database schema will be updated
you decide you want additional information, such as birthdate, so run rails generate migration AddBirthdateToUser birthdate:date. For some simple operations like adding a column, index, etc., the full migration code will be generated; in other cases you'll need to write the migration. When done, run the migration.
If you find a problem in development, for example a field type should be float, not integer, or you forgot to add an index, you can roll back the migration (bundle exec rake db:rollback), fix the migration and re-run it.
run your tests (which will run the migrations), and when it all works for you locally, check in the files (including the migrations) and deploy to a QA or staging server, which has its own copy of the database.
run rake db:migrate on the staging server. If you're on a team and other developers have checked in migrations, their will run, too. Now your code and data schema are in sync.
repeat :-)
There's no harm whatsoever running migrations during a production deployment (I respectfully disagree with a comment above) -- you should embrace the idea that change, even changes like this (which can be incredibly difficult in other environments) are a normal part of everyday Rails life!

Why is db:reset different from running all migrations?

In section Rails Database Migrations of Ruby on Rails Guides, there is one line saying that
The db:reset task will drop the database, recreate it and load the current
schema into it. This is not the same as running all the migrations.
Can anyone tell me where exactly they are different and why it is more error prone to replay the migration history?
I'm fairly new to Ruby on Rails. Thanks in advance.
The schema file contains the current structure of your database. When you load it, you are guaranteed to have the exact schema in your db that is in the file. Migrations were designed to make incremental changes in the database. You may add a table, then some columns, and then remove the table in three separate migrations. There's no need to go through all this when the schema already knows that the table no longer exists.
On why they are error prone, I'm not totally sure. The one thing I can think of is that migrations can be used to make changes to data and not just the structure.
Running rake db:reset will rebuild the structure of your database from schema.db, which essentially works as a cached version of your migrated database structure. Running all your migrations, on the other hand, applies the migrations one by one, which may include arbitrary code to accomodate for changes to the database (e.g. prepopulate an added counter cache column).
It can be more error prone to replay the migration history, since it is the product of changes to both the structure and data of the database. If the developers haven't been careful, it might not apply cleanly to a fresh environment (e.g. the migration assumes an old version of a model). On the other hand, schema.db can get out of sync if you edit a migration once you've migrated (a useful trick to avoid migration explosion during development). In that case, you need to run rake db:migrate:reset.

Aggregate migrations in Rails

I have several dozens Rails DB migrations which were written over a year. Is there a way to aggregate them to one migration so that I will just see a full DDL statement for the database as it exists now? I just need the current snaphot without all the history of how we got to it.
It is possible, but probably not a good idea to aggregate the migrations!
Maybe ask:
Why do you want to do this?
How often do you really need to migrate all the way to VERSION=0 and then back up again?
Is something really broken? (if not, then don't fix it)
I've had the same problem once.. I ended up just re-ordering my migrations, because changes in the schema caused it to not correctly migrate up/down anymore. I would be hesitant to do that again.
If you have migrations which just add fields or indexes, then maybe you can combine them with the main migration for the model -- but beware that you can't reproduce old situations anymore, e.g. older DB-dumps may not be compatible with what migration number they should be compatible with -- that is probably the biggest argument against aggregating...
Technically, you can dump the schema and then load it directly - that is one way:
rake db:schema:dump
then create a single new migration with the contents of the schema dump file db/schema.rb
Here are some similar questions:
Rebase Rails migrations in a long running project
Deleting/"Rebasing" rails migrations
Way to "flatten" Rails migrations?
Should I flatten Rails migrations?
P.S.: I found it useful to stick with the old migration numbering scheme, where the migrations do not use timestamps - for me this works better (is easier to see in which order they are).
e.g. in your config/application.rb file:
config.active_record.timestamped_migrations = false
You should never be using all the migrations to get a database up and running. The current schema.rb is always what the DB looks like 'presently'.
It's good practice to periodically just truncate your migrations if you have a ton of them in there. We finally did that with one of our larger applications, removing a good 50 migrations from the folder because the only thing that matters is schema.rb. Migrations are just that, a way to migrate and make changes to an existing state of the database. They should only ever have to be run once.
You can simply load the current schema into the DB.
rake db:schema:load RAILS_ENV=[production, test, etc.]
This will take the schema.rb file's version of the schema, and load it into the DB without running individual migrations.
NOTE: if you have migrations that put data into the DB (e.g. default values, for example), that data will not be added to the DB.
If you need to load default values into your DB, that might be better done via a custom rake task, independent of migrations.

What is the use of the Schema.rb file?

What exactly is the use of the Schema.rb file?
It is a generated file when you run your migrations. To the best of my knowledge it has two uses:
Your database schema is in one place and can be viewed and reviewed.
You can load your database in one shot by use rake db:schema:load instead of having to walk through your migrations
This file is reproduced every time you execute a migration. It reproduces the entire schema of your database. It is also used by certain commands in migration in Rails to avoid redoing all the migrations one by one (in the case of a migration reset for instance).
It is also the only place when you can see all your tables at once for the reference of columns. But I suggest you to use the gem annotate in order to document your models with all column names. It's easier to work like this than having to go to schema.rb everytime you want details about the DB structure of your model.
One good usage of schema.rb is whenever we run rake test, it prepares the test database instantly using schema.rb(without running the migrations).

Ruby on Rails and db:fixtures:load - can it ignore some models?

I have two databases in use in a Ruby on Rails application; one is the database for the application while the second is an independent database over which Rails is not given control.
Problem is when loading fixtures into the dev, it tries to run DELETE statements on the tables in the independent database from the connection to the dev database, which obviously errors out.
I don't want Rails to try to do ANYTHING but read the independent database - I especially don't want it trying to delete tables.
Is there a simple way to tell Rails to ignore the models for the second database when loading fixtures?
UPDATE: To clarify, Rails seems to think the tables from the independent database are part of the development connection, though I have specified the correct connection in the model class using establish_connection. As another note, all model classes work precisely as desired from script/console.
rake db:fixtures:load RAILS_ENV=testing
will do the job for database configured as testing in your database.yml
I think you might also be able to accomplish this by adding all the tables in the independent database to ActiveRecord::SchemaDumper.ignore_tables in environment.rb, like so:
ActiveRecord::SchemaDumper.ignore_tables = ['independent_db_table1', 'independent_db_table2']
Okay...my issue was that I used the script/generate to create the models from the secondary database, which also created fixture, schema, test, and migrations files. I had removed the schema, test, and migrations files, but did not remove the generated fixtures (they were empty files) as I did not think it had created any.
After removing all files (including the models) from the secondary database and re-running the migrations for the dev db, I added back only the model files and the database in databases.yml from the secondary db, which solved the issue.
I still can't explain why Rails' rake tasks were looking in the wrong database and I am a little disappointed with rails' addition of the schema_migrations table in the secondary database, which it obviously does not need.
However, it works now.
Delete the model_name.yml file in your test/fixtures directory and Rails will not try to delete these tables.
Better yet, delete all your *.yml files and stop using fixtures entirely.

Resources