Railscast 023 Counter Cache Column

The GitHub Repo
The Heroku App

Rails 4.1.0rc1

This episode I wanted to try out the new 4.1.0rc1 version of rails. I had a weird issue when I tried to enter the rails console when I had pg install and marked into the production group like so

gem 'sqlite3', group: :development
gem 'pg', group: :production

I entered rails c and got

Could not find pg-0.17.1 in any of the sources
Run `bundle install` to install missing gems.

Even after I ran bundle install and the output says I have it installed, I was unable to enter the console. I temporarily commented it out in order to work on the actual project. After I was finished with the project, I went to prepare the project to be hosted on Heroku. I uncommented the pg line, then tried bundle install again. This time I was able to enter rails console without any projects. I was in another tab this time. I’m not completely sure why this happened, but my best guess is that bash or rvm didn’t load completely in the tab I was attempting to run rails c on. Embarrassingly enough, I actually opened an issue on GitHub on the Rails repo.

Create records with seed.rb

I populated the database using the seed.rb. Instead of declaring each record one by one, I did it dynamically with ruby. More in the blog post.

Counter Cache

The concept this episode was adding a counter cache column to the the projects table. This allows us to call project.tasks_count instead of project.tasks.count. The latter is inefficient because the database not only returns the projects, but also all the tasks associated with that task. The former improves performance by allowing us to only send the projects’ object which contains the tasks_count attribute.

The Migration

The migration wasn’t too different from what I’ve seen before, but there were a few things to take note of.

class AddTasksCount < ActiveRecord::Migration
  def self.up
    add_column :projects, :tasks_count, :integer, default: 0

    Project.reset_column_information
    Project.all.each do |p|
      Project.update p.id, tasks_count: p.tasks.length
    end
  end

  def self.down
    remove_column :projects, :tasks_count
  end
end

There is a default value on the tasks_count column. This default value allows us to increment and decrement when new tasks are added or deleted for each given project.

reset_column_information

According to the Ruby on Rails API the reset_column_information method resets the cached information about a column. This is useful because if you didn’t run reset_column_information, then the update of the tasks_count could be incorrect.

Update()

The update() accepts two arguments an id and an attribute. The attribute could be a hash. So p.id designates the correct project while tasks_count: p.tasks.length updates the tasks_count column with the correct value.

Counter Cache: true

Finally you add counter_cache: true in the task.rb

class Task < ActiveRecord::Base 
  belongs_to :project, counter_cache: true 
end

Now counter caching is enabled.

Further reading on the Rails Guides

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

Railscast – 016-Virtual-Attributes

The GitHub Repo
The Heroku app

I found this episode useful. It showed me that a model’s attribute does not have to be directly on one of the table’s columns. Not only did I learn about virtual attributes, but I also improved on associations, validations, and callbacks.

The virtual attributes were easy enough to understand. You create a setter and getter method named with the desired virtual attribute name. In the episode’s case it was price_in_cents sort of aliased to price_in_dollars. Within the method you could change the value of the virtual attribute to match whatever is in the database.

def price_in_dollars
  price_in_cents.to_d / 100 if price_in_cents
end

def price_in_dollars=(dollars)
  self.price_in_cents = dollars.to_d * 100 if dollars.present?
end 

Strftime

This is the first time I used the :strftime method, but it is not too dissimilar to how I have formatted dates within the terminal.

Rails 3 to Rails 4

One thing to note, these videos date back sometime before the current Ruby on Rails 4.0 release, thus I must convert some of the techniques shown in the videos into whatever they translate to in the new version of the framework. An example of this is the use of attr_accessible. Attr_accessible has been incorporated in the the strong parameters update in Rails 4. So instead of

attr_accessible :name, :price_in_dollars, :released_at_text, :category_id, :new_category, :tag_names

You would do the following in the ProductsController

def product_params
  params.require(:product).permit(:name, :price_in_dollars, :released_at_text, :category_id, :new_category, :tag_names)
end

Associations

I knew I was a little weak with associations, so when I saw that this project had categories, products, tags, and taggings, I decided to make everything from scratch. I followed along with the schema to create the migrations needed. When creating a has_many and belongs_to relationship you always put the parent id onto the child’s table. Example:

class Product < ActiveRecord::Base
  belongs_to :category
end

class Category < ActiveRecord::Base
  has_many :products
end

The schema would look like this:

 create_table "categories", :force => true do |t|
    t.string   "name"
    t.datetime "created_at", :null => false
    t.datetime "updated_at", :null => false
  end

  create_table "products", :force => true do |t|
    t.string   "name"
    t.integer  "category_id"
    t.datetime "created_at",     :null => false
    t.datetime "updated_at",     :null => false
  end

This relationship is similar to the taggings, tags, and products relationship. The taggings has the foreign key for both the tags and products. The difference is that products has_many tags through the taggings model.

Validations

I liked how Ryan Bates created his own custom validation method with check_released_at_text The validation first check if the released_at_text instance variable is present and the time is nil. If it is, then add to the errors object with a custom error message. If there was an ArgumentError, then again the errors object is appended with the custom error message. Creating a custom validation adds more control than if some of the more generic validation methods like presence or length.

Callbacks

The callbacks created this episode used before_save. The reasoning for this is to actually save the instance variable’s values to the database in the correctly formatted form. In the create_category callback it actually creates a new category record.

  def create_category
    self.category = Category.create!(name: new_category) if new_category.present?
  end

Pluck

The first time I’ve seen the pluck method used. I looked it up and found that tags.pluck(:name) is a shortcut for tags.map(&:name). The goal is to get only a certain attribute from the model.

Those where the noteworthy parts of the episode.