Railscast 032 Time in Text Field

Project GitHub Repo

StringifyTime Gem GitHub Repo

The Heroku App

This episode was essentially repeated in Railscast 016 Virtual Attributes. Despite it being a repeat, I decided to create a project for this Railscast because the following episode, 033, used it to make a Rails Plugin. Rather than make the plugin, I wanted to try creating my second RubyGem based on this plugin.

Although I was not aware of this while making the gem, there was another person who had the same idea. His gem is called jakewendt-stringify_time, but it hasn’t been updated four years. The gem I created integrates with the changes made to the Ruby on Rails framework since that gem’s creation.

Because this would be my second gem I have created, I thought I could just model this gem similarly to my first gem body_id. I created the basic gem files using bundle gem stringify_time command. Then I edited the .gemspec file to put in the basic information needed. The part I got confused about was at the part I was going to use railtie. The Railscast simply extended ActiveRecord using the module created in the plugin. Instead of following my previous gem, I decided to try how it was made in the video. I copied the StingifyTime module from the episode and extended ActiveRecord just like it was shown in the episode. I ran rake release and had version 0.0.1 on RubyGems.org. That’s when I created the previous project from Episode 032 to try the gem out. The bad part was, it didn’t work.

After some searching around I noticed other gems did not use extend method on ActiveRecord directly, but they used the .send :include, StringifyTime method to call the include method on a module. So I tried to exchange

ActiveRecord::Base.extend StringifyTime

with

ActiveRecord::Base.send :include, StringifyTime

The problem was I blindly tried to copy and try this method without fully understanding why I was using include instead of extend. Include is used on an instance of a class to add methods, while extend is used to add methods to the class itself. At the same time, I searched my gem on RubyGems.org and discovered the Jake Wendt’s gem based on the same episode. I looked at his GitHub Repo and saw he had an init.rb and rails/init.rb. “Could this solve it?”, I thought. I applied the changes, and the stringify_time method was being include in my rails app.

Metaprogramming

What is so interesting about creating this stringify_time method is that you are writing a program in order to have it write another program. That’s the concept of metaprogramming at least. You could have defined each getter and setter method manually, but that isn’t practical when you have numerous attributes you need to do the same thing on. I have used metaprogramming when I made a seed.rb file to fill up the database with records. These methods saves a lot of time if made correctly.

Unresolved

When I went to edit the date on the form, the date was not updating. This is still a problem I need to solve, and I have opened a question on StackOverflow to try to resolve this issue. While I wait for an answer I’ll keep moving to another Railscast.

Update

Someone on StackOverflow suggested I try using the generated method via the Rails console. I tried to use the rails console, and the due_at attribute was updated. I thought why was I able to update the attribute in the console, but not via the form. The answer was because I was not whitelisting the :due_at_string param in the task_params definition. This is to protect from mass assignment. So the data from the form was never reaching the model because it was being prevented by the controller. A silly mistake to overlook.

Stringify Time Gem

This gem was created following the Rails plugin made in Railscast 033 Making a Plugin.

Installation

Add this line to your application’s Gemfile:

gem 'stringify_time'                                                   

And then execute:

$ bundle                                                               

Or install it yourself as:

$ gem install stringify_time                                           

Usage

Within an the model add stringify_time passing in a symbol that you want to set as the attribute you want aliased as a virtual attribute string.

class Task < ActiveRecord::Base
    stringify_time :due_at
end

This will create the due_at_string getter and setter methods within the Task model. You can then access this attribute in the views like so:

_form.html.erb

<%= form_for @task do |f| %>
  ...
  <div class="field">
    <%= f.label :due_at %><br>
    <%= f.text_field :due_at_string %>
  </div>
  ...
<% end %>

Your users are now able to type in the date into a text field instead of using the Rails’ default datetime_select dropdowns.

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

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.

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 – 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')

Change the data type of a column in the database

I accidentally created a column in the database with the datatype of string when it needed to be a integer. I’m on the BART, so I didn’t have internet. Fortunately I have a copy of the Rails repo and its guides. I opened up migrations.md and read to the changing tables section. Saw the following:

change_table :products do |t|
  t.remove :description, :name
  t.string :part_number
  t.index :part_number
  t.rename :upccode, :upc_code
end

I followed this and created the following migration:

change_tabe :products do |t|
  t.remove :price_in_cents
  t.integer :price_in_cents
end