Sunday, March 23, 2014

Migrations in Ruby on Rails

Last week, Natalie and I continued working on the progress bar. In order to keep track of the challenges that the user have succeed, we decided that we need to add a new field to the database. This new boolean field had to be added to the user challenges table, and was called "finished". We looked for some documentation, and we figured out that there was a kind of migration "convention", which is used each time you want to make changes in a database.

The first step was to create the new column in the database by typing in the RoR console (Rails c) a command that follows this pattern:

rails g migration add_<new_field_name>_to_<table_name> <new_field_name>:<type>

If we need to delete a column, we should use the same format, but switching "add" by "remove":

rails g migration remove_<field_name>_from_<table_name>

Returning to our particular case, the command used was:

rails g migration add_finished_to_user_challenges finished:boolean

This command creates a new .rb file in the migrations folder, located at your_project_folder/db/migrate/. It is labeled with the current date following by the same name as we wrote the previous command. For this reason, it is very important to use the convention that I commented on the first paragraph.

Thus, we edited the new migration file, by creating a new class that generates the new field:

class AddFinishedToUserChallenge < ActiveRecord::Migration
  def change
    add_column :user_challenges, :finished, :boolean, default: false
  end
end

Moreover, we also decided to edit an existing field in the same table (user_challenges) and the histories table. This time, we needed to change the data type of the "duration" field, because it was created as an integer. As it was calculated by multiplying the number of times per week that the user have to check in and the number of weeks of the challenge, and we divided that value by 100 to get a progress bar percentage, the value was eventually rounded causing accuracy issues. Therefore, Natalie and I changed the "duration" field type from integer to decimal, following this format:

rails g migration change_data_type_for_<field_name>_in_<table_name>

Applying the previous pattern to our case:

rails g migration change_data_type_for_progress_in_user_challenges

rails g migration change_data_type_for_progress_in_histories

After creating the migration files, the next step was to define the class in both files to define the migrations:

class ChangeDataTypeForProgressInUserChallenge < ActiveRecord::Migration
  def change
    change_table :user_challenges do |t|
      t.change :progress, :decimal
    end
  end
end

Finally, Natalie and I executed a rake db:migrate command to apply the changes specified in the migration files. Thanks to this approach, we committed all the new files and the rest of the members of the team were able to pull this files, getting the same changes immediately.

- More information about migrations on RoR

No comments:

Post a Comment