Easily create database records with db:seed

I have a project and a task model. Projects has many tasks. I can easily create many records of these in the database using the db/seed.rb file. In this file you can write many ActiveRecord commands to be executed when you run rake db:seed Example where the seed.rb contains

Project.create!(name: 'Project')
Project.create!(name: 'Project1')

This is will create and save two records into the database when you run rake db:seed

If you wanted to create many projects and tasks, then you would not want to be defining each record like what I had above. Instead you would want to dynamically generate as many projects and tasks as you want. The way I did it was through looping through a range in order to assign a number to the project and task attributes.

project_array = []                                                         
(1..4).each do |num|                                                       
  project_array <<  Project.create!(name: "Project#{num}")                 
end                                                                        

project_array.each do |project|                                            
  (1..20).each do |num|                                                    
    Task.create!(description: "Task#{num}", project_id: project.id)        
  end                                                                      
end      

We first define the project_array to store all the created projects. We have a range passed into a block in order to generate the dynamically named project name. The project_array is then appended with Project objects.

The second loop first loops through all the projects then a range. The project loop comes first because you want to have many tasks under a few projects. In this case we are creating 20 tasks with the project_id attribute set to the project.id

This can be refactored and extended further, but for such a simple goal, no need to give it too much thought.

Rake db:reset

If you happen need to reset the database, then you do not need to run rake db:seed afterwards. rake db:reset performs the dropping, migrating, and seeding of the tables for you.

Try a different version of Rails with RVM

I wanted to try the new release candidate for Ruby on Rails 4.1.0. I have RVM installed, but didn’t know how to approach installing a different version of Rails. Once I looked online, I was reminded of something I forgot. Gemsets. So I did the following:

rvm gemset create 4.1.0rc1
rvm gemset use 4.1.0rc1
gem install rails -v 4.1.0rc1

I first create a gemset called 4.1.0rc1 so I can install another version of the Rails gem without interfering with the stable version of my already install Rails 4.0.3. Switch over to that gemset using gemset use, then install the desired version of Rails. A gemset is similar to how git has branches is many ways. You can create a new branch and make changes without messing up your master branch.

Heroku app and column data type

I ran into an error when I was pushing the last eager-loading app onto Heroku. I had the following error

2014-03-13T07:45:24.207959+00:00 app[web.1]: : SELECT products.*, categories.name as category_name FROM "products" INNER JOIN "categories" ON "categories"."id" = "products"."category_id"  ORDER BY categories.name):
2014-03-13T07:45:24.207959+00:00 app[web.1]:     13:     <% @products.each do |product| %>
2014-03-13T07:45:24.207959+00:00 app[web.1]:     14:       <tr>
2014-03-13T07:45:24.208979+00:00 app[web.1]:     15:         <td><%= link_to product.name, product %></td>
2014-03-13T07:45:24.208979+00:00 app[web.1]:     16:         <td><%= number_to_currency product.price %></td>
2014-03-13T07:45:24.208979+00:00 app[web.1]:   app/views/products/index.html.erb:13:in `_app_views_products_index_html_erb___1529240047445058566_70114821600640'
2014-03-13T07:45:24.208979+00:00 app[web.1]: 
2014-03-13T07:45:24.208979+00:00 app[web.1]: 
2014-03-13T07:45:24.208979+00:00 app[web.1]: I, [2014-03-13T07:45:24.200411 #2]  INFO -- : Started GET "/" for 98.207.180.92 at 2014-03-13 07:45:24 +0000
2014-03-13T07:45:24.208979+00:00 app[web.1]: I, [2014-03-13T07:45:24.201343 #2]  INFO -- : Processing by ProductsController#index as HTML
2014-03-13T07:45:24.208979+00:00 app[web.1]: E, [2014-03-13T07:45:24.204799 #2] ERROR -- : PG::UndefinedFunction: ERROR:  operator does not exist: integer = character varying

After some searching around I found that my category_id on my products table was set to the datatype of string. My first attempt to fix this was to change the datatype of the category_id directly.

 class ChangeCategoryIdInProducts < ActiveRecord::Migration
   def self.up
     change_column :products, :category_id, :integer
   end

   def self.down
     change_column :products, :category_id, :string
   end
 end

but this gave the error of

 PG::DatatypeMismatch: ERROR:  column "category_id" cannot be cast automatically to type integer

So directly changing the column does work, so I went with a stronger method. I removed the column category_id that was a string, and I added another column category_id that was an integer.

class ChangeCategoryIdForProducts < ActiveRecord::Migration
  def self.up
    remove_column :products, :category_id
    add_column :products, :category_id, :integer
  end

  def self.down
    remove_column :products, :category_id
    add_column :products, :category_id, :string
  end
end

This solved the problem I was having, but when I wanted to run rake db:reset Heroku gave me

FATAL:  permission denied for database "postgres"
DETAIL:  User does not have CONNECT privilege.

This is because you cannot drop a PG database on Heroku. The documentation mentions that you could run heroku pg:reset, but I was unable to get that working. I instead manually deleted the herokuapp, then re-uploaded the app onto heroku. When doing this, you have to remember to also remove the git remote the heroku toolbelt generated.

Railscast 022 Eager Loading and Joins

The GitHub Repo

The Heroku App

Setup

Although it might not be necessary to rebuild the starting app at the start of each episode, I find it useful. I just saw that the each episode’s repo has a before and after app. While I may use this in the future, I want to have muscle memory in creating a basic app up and running. I also just noticed that the before app included seeds.rb to populate the database. I may not want to copy the whole app, I am not above saving myself time tediously creating custom records.

ProductsController

The whole episode put a large focus on the ProductsController. The original index action created the @products instance variable by:

@products = Product.order('name')

getting all the product records and ordering them by area. The problem Eager Loading tries to solve is the following request made in the view.

<%= product.category.name %>

This is commonly known as the O(n + 1) problem. Where the query to get the the product is accompanied by another query to get the product’s category name. I’ve been interviewed about Big O Notation before, so I believe that its a topic worthy of its own post.

Joins

The way I learned about both Inner Join and Outer join was watching schneem’s video about ActiveRecord Joins as part of his open Ruby on Rails course videos. So when we call

`Product.order('category.name').join(:categories)

what is happening is that ActiveRecord is creating a virtual Products_Categories table. Because Product is the model which the methods are being called on, then an array of products are returned. The virtual table joins the product’s category_id with the corresponding category with the matching id. It would look something like this

+-----------------------------------------------+
| Products_Categories (virtual) Table           |
+----------------------------------+------------+
|Products                          | Categories |
+----+------+-------+--------------+----+-------+
| ID | Name | Price | Category_id  | ID | Name  |
+----+------+-------+--------------+----+-------+
| 12 |  Toy | 12.43 |      2       |  2 | Toys  |
+----+------+-------+--------------+----+-------+

This is an inner join. The inner join is includes all the records that has a matching category_id. If we created an outer join like this:

'Product.join('LEFT OUTER JOIN categories on products.category_id = categories.id')`

We could select all the records even those that did not have the three requirements needed to be included in an inner join.

  1. Product must have a category_id,
  2. Category must have a ID, and
  3. they must match.

In the case where a product does not have any categories and you wanted to find all those records, you would use a Outer Join. The join method above accepts custom SQL commands. So the SQL command above includes any matches that have the corresponding id and category_id and those that do not. It then takes the products from the left side of the column. To find records where there are no categories associated, you would write the following:

'Product.join('LEFT OUTER JOIN categories on products.category_id = categories.id').where('products: { category_id: nil }')`

The thing I’ve messed up on has been the pluralization of the table names while writing a custom SQL command. I have had times where I left out the s in products and used the singular category instead of the plural categories. The way I need to remember this is by remembering that the tables holds multiple records, thus the table names are always plural.

Select

To continue on with the episode’s lesson, select as used to select what records were to be queried from the database from the ProductsController

    @products = Product.order('categories.name').joins(:category).select('products.*, categories.name as category_name')

The select method used here has two arguments being passed in to limit the data returned. The first products.* is a SQL command to return all the products from the database. The second categories.name as category_name returns only the categories’ name. The as category_name aliases the data dynamically within the select method, so the attribute can be referenced from the model.

In the views you can replace product.category.name to product.category_name. In order to be sure that the category_name attribute is always available you have to create a custom getter method within the product model. This way you are not relying on the select method solely.

class Product < ActiveRecord::Base
  belongs_to :category
  def category_name
    read_attribute('category_name') || category.name
  end
end

The database is first checked if there is a category_name alias in the database. If there isn’t an aliased category_name, then it fetches the name through the ActiveRecord association.

Always save after updating a record within Rails console

You should always remember to do one of two things. save or use a bang method when working with records inside of Rails Console.

I had the following:

p = Product.first
p.category_id = 1

This is going to change the record within the console session, but this will not actually save the changes. You must run,

p.save

to save the changes.

Bang method

A common way to create a record from within the rails console is to do the following

p = Product.new(name: 'Settlers of Catan', price: '14.99', category_id: 3)
p.save

If you wanted to create the record within one line, then you should use create!

p = Product.create!(name: 'Settlers of Catan', price: '14.99', category_id: 3)

That’s one less line to type.

What arguments does collection_select accept?

What arguments does collection_select accept? That what I wondered when I saw this line in the views.

<%= f.collection_select :category_id, Category.order(:name), :id, :name %>

The answer was in the Ruby on Rails API.

collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})

So the first line of code passed in an object, then a method to order the categories. :id is the value that is submitted with the form. :name is what is displayed in the select dropdown.

Railscast: 017-habtm-checkboxes

The GitHub Repo The Heroku App

Setup
The setup for this episode because it had a has_many :through association. This is useful to find a record from an instance of the first model via a third model. The way this association was done in this episode was by creating three models, product, category, and categorization. These are the relevant lines of code:

class Product < ActiveRecord::Base
  has_many :categorizations
  has_many :categories, through: :categorization
end

class Category < ActiveRecord::Base
  has_many :categorizations
  has_many :products, through: categorization
end

class Categorization < ActiveRecord::Base
  belongs_to :product
  belongs_to :category
end

Just like when you have a has_many and belongs_to relationship, you must define the foreign key within the table that has the belongs_to method. In this case it is the Categorization model. The schema would look like this.

create_table "categorizations", force: true do |t|
  t.integer 'category_id'
  t.integer 'product_id'
  t.datetime 'created_at'
  t.datetime 'updated_at'
end

This setup allows us to call @product.categories. @product.categories returns an array of category objects associated through the categorization table. This is similar to how you would create followers and followed_users for a user.

Form
The main part of the episode was about how you can create checkboxes to select the categories. First obvious things to do are to loop through all the categories and display them in the _form partial. The tricky part comes with choosing a check_box or a check_box_tag. You want to choose a check_box_tag so you can display each category, not just one.

Next you want to fill in the value of checked or not checked. The second argument passed into the check_box_tag is the category.id. The third is the value of checked or not checked. The way you designate the value is by passing in true or false. The way it is done in the episode is by finding the @product.category_ids and checking if the category.id from the block is included within the @product‘s category_ids.

Now if you refreshed the page, the value is correctly displayed. A hidden_field_tag is also created in order to make sure that the form is submitted with a default value of nil. The reason you want to include this nil category_id field is to make sure that if no checkboxes are checked, then an empty array is passed as a parameter.

Ryan adds a usability enhancement by allowing users to click on the text to check and uncheck boxes. He first creates a unique id for each checkbox with dom_id. Next he adds a label_tag to the text. Oddly enough the dom_id method has no description within the Ruby on Rails API. It just has the source code. dom_id seems to be creating a unique string from the object’s model and id values. In the categories example, the id was defined as category_1 and so on.

The pertinent code:

<%= hidden_field_tag "product[category_ids][]", nil %>
<% Category.all.each do |category| %>
  <%= check_box_tag "product[category_ids][]", category.id, @product.category_ids.include?(category.id), id: dom_id(category) %>
  <%= label_tag dom_id(category), category.name %><br>
<% end %> 

Gotcha
One gotcha you have to watch out for when following this episode and creating a Rails 4 app is Strong Parameters. I was able to find a post on CoderWall that almost translated one-to-one to what I needed to do. You have to define the category_id param as an array. The product_params method definition in ProductsController:

def product_params
  params[:product].permit(:name, :price, category_id: [])
end

Rails: Remove a table

I created a table that I did not need. I needed to remove the table. The actual terminology is to Drop a table. The way you do this is to create a new migration.

rails g migration DropProducts

In my case, I created a products table when I didn’t needed it. Inside the migration, I do the following.

class DropProducts < ActiveRecord::Migration
  def change
    drop_table :products
  end
end

fin

Terminal: Copy a folder

Similar to the way you would delete a folder with:

rm -rf folder_name

You must include -rf when doing a copy or cp

cp -r folder_name new_folder_name

-rf stands for recursive force. This will include the directory as well as all the files and subdirectories within.

Railscast – 002 Dynamic find_by Method

The GitHub Repo

I was arguing if this episode was even worth creating a project for. Decided to create since it would be so quick to make.

The episode talks about how you can replace:

class TaskController < ApplicationController
  def incomplete
    @tasks = Task.find(:all, :conditions => ['complete = ?', false])
  end

  def last_incomplete 
    @task = Task.find(:first, :conditions => ['complete =?', false], :order => 'created_at DESC')
  end
end

With:

class TaskController < ApplicationController
  def incomplete
    @tasks = Task.find_all_by_complete(false)
  end

  def last_incomplete
    @task = Task.find_by_complete(false, :order => 'created_at DESC')
  end
end

Attention should be drawn to the find_all_by_complete method used. This is a shortcut for the first find methods with all of the options passed in. The word that follows the by is the column that is in the Tasks table. You can then pass in the value of the records for that column you want returned. If you want only a single record that matched the conditions, then you would omit the all from find_all_by_complete making it find_by_complete. If you were finding a record by its name, then you doing this:

Task.find_by_name('bob')