rails generate migration issues using Rails 4

51 views
Skip to first unread message

RoR

unread,
Oct 27, 2014, 5:42:54 PM10/27/14
to pdxruby-...@googlegroups.com
Hey folks, I made a lot of progress in the first month of teaching myself Ruby and Rails including finishing a couple really cool tools.  However I've simply hit a wall using the "rails g migration" commands.  It seems between Rails 3 and Rails 4 these commands have changed.  My google searches are turning up tutorials and examples that just don't work for Rails 4.  I have wasted a lot of hours on two commands that should have taken about 10 minutes! Hopefully somebody experienced can help me cut through the noise, the searches I'm doing are turning up old unusable rails 3 commands.

Here is an example of a rails 3 vs rails 4 issue. This is a problem I was able to solve but I think it will help you understand the type of problem i am now experiencing.

I found this incorrect solution in at least three places online for adding in 'email' column/variable to an ActiveRecord for table/model 'contacts'

>>> rails generate migration AddEmailToContacts
>>> rake db:migrate

I tried everything I could think of in the migration file this generated, but it just would not work .  No errors of any type were ever thrown to suggest what the problem was, The command created a file, but otherwise did nothing. After trying everything i could think of over 8 hours and several days I finally decided the command must be wrong and I found this correct command

>>> rails generate migration AddEmailColumnToContacts email:string
>>> rake db:migrate
After i updated the change method in the migration file this created it worked like a charm.  So, apparently there is a new Rails 4 command and I assume the >>> rails generate migration AddEmailToContacts is the deprecated Rails 3 command.
Now, I have run into a similar problem trying to use migrations to change the column type of a table's field.  The rails 3 command I found online do not work and I can't seem to find a new rails 4 command. Here are the two answers i found online to change column price of table products from integer to decimal:
>>> change_column :products, :price, :decimal  
or
>>> change_column :products, :price, :decimal, :precision => 20, :scale => 2   #20 max digits including 2 decimal places
My understanding the "Rails Way" is to generate the migration file on command line and then add the line ( change_column :products, :price, :decimal ) to the change method.  However, this isn't clear from the answers I found which simply says the solution is to use 'change_column :products, :price, :decimal'  In the solutions I found there is still no explanation where the  'change_column :products, :price, :decimal' command should go or what the associated 'generate migration' command is to generate the migration file. 
So, I tried to research/guess what the rails generate migration command should look like. I tried all the following, none worked for me (rails 4)

A) >>> rails generate migration change_column_products_price  # generates a migration file but nothing happens when i run >>> rake db:migrate
B) >>> rails generate migration change_price_type_in_products  # again generates a migration file but nothing happens when i run >>> rake db:migrate
C) >>> rails generate migration alter_column_products_price  # again generates a migration file but nothing happens when i run >>> rake db:migrate
D) >>> rails generate migration change_price_column_in_products  # again generates a migration file but nothing happens when i run >>> rake db:migrate
Each of these four migration commands generates a migration file.  None of these four migrations generate any errors when run or on rake db:migrate.  Yet, as far as i can tell none of them actually do anything except generate the file.  This is another 5 minute update and I'm at least 4 hours into it.
Thanks for reading, here is my question:
 How do I change a column type of an ActiveRecord in Rails 4? Using the example of changing the column 'price' from integer to decimal for table 'products'

1a) What is the migration command to run from the command line?  
>>> rails generate migration ??????

1b) what should the migration file look like for changing a column from integer to decimal
Class ?????????????????  << ActiveRecord::Migrations
   def change
#  ?????????????
   end
# anything else like def up, def down?
end
2) I have read a dozen rails tutorials and examples on migrations that simply do not work. Is there a dedicated Rails 4 resource online with lots of good examples that you can recommend?

thank you!






Rico Jones

unread,
Oct 28, 2014, 12:42:10 PM10/28/14
to pdxruby-...@googlegroups.com
I think the crucial piece of information that you are missing is this: there is some magic going on with the command you use from the terminal, `rake generate migration add_something_to_something_else`. That command does several things;

1) Creates a migration file
2) Makes sure the migration file has a timestamp in the filename
3) Makes sure the filename matches the name of the class that's defined inside of it (all migrations are really just Ruby classes with either a) both an `up` and a `down` method, or b) a single `change` method).

Additionally, it can do some magic stuff based on the name of the migration or params you pass when you run the command, like:

4) Create either the `change` method or the `up` and `down` methods.
5) Add the code that actually does the migration (eg. `change_column :table_name, :column_name, :new_type`)

The problem you're running into is that you have been following tutorials/examples that are taking advantage of the magic stuff and now the magic isn't working, so you're confused. If you've seen The Wizard of Oz, it's time to pay attention to the man behind the curtain (http://www.youtube.com/watch?v=NZR64EF3OpA)

The command you use in your terminal is the **least important** part of creating a migration for your Rails app. It's main purpose is to simply create a migration file whose filename starts with a timestamps (so Rails knows which order to run the migrations in) and also contains the name of the migration class contained in the file. So, your migration file might be named something like: `20141028090567_add_email_to_contacts.rb`. The numbers are the timestamp, in this case October 28, 2014 09:05:67. The rest is a snake cased version of the class defined inside the migration file, `AddEmailToContacts`, which is in camel case (same as the scheme for naming the files that contain your models and controllers).

THE IMPORTANT PART OF THE MIGRATION IS WHAT HAPPENS INSIDE THE CLASS DEFINED IN THE MIGRATION FILE (sorry for yelling, but that's the key). The reason why your migrations are not doing anything is because they are empty. You are running your migrations with `rake db:migrate`, but nothing changes in your database because nothing is defined inside your migration class.

There are two methods that you put inside of a migration class, `up` and `down`. `up` specifies what should happen when you run the migration with `rake db:migrate` and `down` specifies what should happen when you reverse the migration with `rake db:rollback`. So, in the case of adding an email field to the contacts table, your migration would look like this:

def AddEmailToContacts < ActiveRecord::Migration
  def up
    add_column :contacts, :email, :text
  end

  def down
    remove_column :contacts, :email
  end
end

The Rails documentation has a list of all the transformations you can do inside of a migration here: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html#class-ActiveRecord::Migration-label-Available+transformations

Now, in this case, we have a pretty simple transformation and, crucially, defining separate up and down methods is redundant. Rails is smart (or crazy magical, take your pick) and knows that the opposite of adding a column is removing a column. So, it's redundant to specify that we want to do `add_column :contacts, :email, :text` in the `up` method and then also specify that we want to do `remove_column :contacts, :email` in the `down` method. In Rails, this is called a reversible migration (http://api.rubyonrails.org/classes/ActiveRecord/Migration.html#class-ActiveRecord::Migration-label-Reversible+Migrations).

Rails knows what needs to happen to undo the effects of `add_column`. If the only transformations inside of your migration are reversible ones, you can use a single `change` method, instead of both the `up` and the `down` method. So, we could rewrite our migration from above like this:

def AddEmailToContacts < ActiveRecord::Migration
  def change
    add_column :contacts, :email, :text
  end
end

The Rails docs have a list of all the reversible transformations here: http://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html

So, what you need to do from here (if I am not mistaken), is do `rake db:rollback` enough times to undo your blank migration (pay attention to the output to tell when you have reversed the migration in question). Then, put some transformations inside your migration and do `rake db:migrate` again.

Hope that helps,

Rico

Michael Kaiser-Nyman

unread,
Oct 28, 2014, 1:02:01 PM10/28/14
to pdxruby-...@googlegroups.com
Awesome explanation, Rico!

I'll add that when you're just starting to learn Rails, the temptation
is try to get to building a web app as fast as possible, using
generators and running commands whose implications you don't really
understand. I'd strongly encourage you to take your time and instead
learn more about what's going on under the hood with Rails, starting
out by just learning pure Ruby.

If you're game to try that approach, the curriculum I wrote for
Epicodus is online for free and will guide you through Ruby,
databases, how the web works, and Rails, as well as a bunch of other
interesting and useful stuff along the way:
learnhowtoprogram.com/table-of-contents

Good luck and have fun!
> --
> You received this message because you are subscribed to the Google Groups
> "pdxruby-beginners" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to pdxruby-beginn...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Rico Jones

unread,
Oct 28, 2014, 1:13:44 PM10/28/14
to pdxruby-...@googlegroups.com
When I was getting started with Rails a few years ago, I got bitten by exactly this kind of thing a bunch of times. The solution that I settled on was to stop using the generators and just make every file I needed by hand. This helped me get a concrete grasp of where all the files are located in a Rails app and, more importantly, how all the pieces fit together.

I think generators are better for experienced dev who already know Rails inside and out and just need to get things done quickly. For somebody still learning, speed isn’t the thing desired – learning is. And there’s no better way to learn how to do something than to do it a bunch of times.

Maybe something to consider...

- Rico

ri...@toasterlovin.com
> You received this message because you are subscribed to a topic in the Google Groups "pdxruby-beginners" group.
> To unsubscribe from this topic, visit https://groups.google.com/d/topic/pdxruby-beginners/gPcKwwnzjHA/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to pdxruby-beginn...@googlegroups.com.

RoR

unread,
Oct 28, 2014, 3:59:10 PM10/28/14
to pdxruby-...@googlegroups.com
I really appreciate the response Rico it was insightful but I was obviously not clear enough because I was successful in adding the email column to the contacts table.  I included this problem/resolution because it was a similar issue to the one I am now facing which is how to change a column type from :integer to :decimal. 

I didn't solve the 'add email column to contacts'  issue by updating the change method in the migration file.  The  change method I had correct all along (which is why i glossed over it in my first post, I see this was confusing).  But the change method code only worked after I found an alternative  'rails generate migration command,' to the one I found in multiple places online.  By putting the same change method in this newly generated file I was able to add email column to contacts table. Again here is what worked and what didn't work.

To add email column to contacts here are both migration commands I tried. In both cases the 'change' method of the generated migration file was the same, correct code.

DID NOT WORK!
>>> rails generate migration AddEmailToContacts  # found this command in at least three different places online, but it didn't work
>>> #add correct code to change method
>>> rake db:migrate
DID NOT WORK!

THIS DID WORK
>>> rails generate migration AddEmailColumnToContacts email:string # found this in one place online, it works >>> #add correct code to change method
>>> rake db:migrate THIS DID WORK
So, my problem in this case was not with the body change method, the problem seems to me to have been the file/ClassName of the migration file.  It was only when I generated a new file with the "rails generate migration AddEmailColumnToContacts email:string" command that Rails processed the migration file correctly.
After I solve this I ran into a similar issue attempting to change  'price' column in 'products' database from :integer to :decimal. I believe my issue is again the file name, but i could be wrong.  I suspect I again need a different 'rails generate migration' than i am finding online for rails 4.
I tried all the migration commands A,B,C,D below to generate the correct file name. And, I updated the change method manually each time using the code      change_column :products, :price, :decimal  and for each I also tried change_column :products, :price, :decimal, :precision => 10, :scale => 2 (see below).  However, this time none of these attempts were successful
A) >>> rails generate migration change_column_products_price  #  i updated change method (see code below) in the migration file this created, nothing happens when i run >>> rake db:migrate
B) >>> rails generate migration change_price_type_in_products   #  i updated change method (see code below) in the migration file this created, nothing happens when i run >>> rake db:migrate
C) >>> rails generate migration alter_column_products_price   #  i updated change method (see code below) in the migration file this created, nothing happens when i run >>> rake db:migrate
D) >>> rails generate migration change_price_column_in_products   #  i updated change method (see code below) in the migration file this created, nothing happens when i run >>> rake db:migrate
Class ?????????????????  << ActiveRecord::Migrations
 def change
     change_column :products, :price, :decimal  

     # or, alternatively, 
     # change_column :products, :price, :decimal, :precision => 10, :scale => 2   #12 max digits including 2 decimal places
  end
end
So, three questions: 
1) What is the rails 4 generate command to generate a migration file to change a column type? I want to change column 'price' from :integer to :decimal in table 'products' 

2) Is this the correct change method to use in the migration file? 
def change
    change_column :products, :price, :decimal #  I have also found and tried this command .... change_column :products, :price, :decimal, :precision => 10, :scale => 2 
end
3) Is it necessary to use up/down/reversible here?  My understanding is Rails 4 requires only change method and that up/down are optional, but maybe that isn't the case for change_column I don't know.
Hopefully my problem is more clear now.  Your help is greatly appreciated!

Dustin Brown

unread,
Oct 28, 2014, 4:38:12 PM10/28/14
to pdxruby-...@googlegroups.com
Hey RoR, sounds like this is a pretty hairy issue. It's one of those situations where it's hard to know without seeing the errors yourself. So here's my offer: I work in downtown. If you're willing to come down around noon and buy coffee, I'd be more than happy to walk you through what's might be going on. Just let me know. Either way, good luck to you. It's an exciting fields you're stepping into.

RoR

unread,
Oct 28, 2014, 9:16:58 PM10/28/14
to pdxruby-...@googlegroups.com
Thank you Dustin that would be great. Does Thursday work for you? What location works best for you?

I appreciate the tips previously to learn all i can about ruby and learn rails under the hood. I would love to know everything about both.  I programmed in Java using Struts for years so I understood MVC and OO going in and also have a BIG appreciation for the simple elegance of  RoR.  A major improvement that is definitely a motivation to learn everything I can.  

Shawna Scott

unread,
Oct 30, 2014, 3:24:28 AM10/30/14
to pdxruby-...@googlegroups.com
One more resource for good measure: Rails Guides

I find the Rails Guides to be much more understandable than reading the source code documentation, and it's generally my first stop (yes, even before Stack Overflow) when I have something I want clarified. Their discussion of migrations might be helpful here.
Reply all
Reply to author
Forward
0 new messages