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.

Use `gits` instead of `git status`

After using git version control for sometime, you may notice yourself running git status multiple times right before you commit. Like the programmers we are, we are inherently lazy. in a good way. Instead of typing git status numerous times a day, type gits.

Here’s line you’ll need to alias it in the bash terminal

alias gits='git status'

Add this line to either you ~/.bashrc or ~/.bash_profile. Remember to reload bash again by opening a new terminal window so bash can import this alias.

Bonus

alias gita='git add'

Railscast – 028 In Groups Of

The GitHub Repo
The Heroku App

Faster

I’m noticing I am getting faster. I’m attributing this to making essentially the same application numerous times with this study method. What I am learning that was stored in short-term memory, is slowly moving into the long-term memory part of brain. I also thinking about creating a bash script to help with creating a new GitHub repo for each new project.

In Groups Of

Using the in_groups_of method was fairly straight-forward. Create an instance variable of an array of object records, then call in_groups_of on it within the view. The first argument is how many objects are within one group, and the second argument is the object to pass in for padding. Padding meaning if you had 6 records with in_groups_of(5, false), the it would create the array [[1,2,3,4,5],[6,false,false,false,false]]. You can use this newly generated 2d array in a loop like so.

<table>
  <%= @tasks.in_groups_of(3, false) do |row_tasks| %>
    <tr>
      <% row_tasks.each do |task| %>
        <td><%= task.name %></td>
      <% end %>
    </tr>
  <% end %>
</table>

The first loop is to create an array called row_tasks. You then iterate through that row_tasks array to get at each individual task record.

Heroku Could not find * in any of the sources

When I was uploading 023-counter-cache-column, I ran into the following error

-----> Ruby app detected
-----> Compiling Ruby/Rails
-----> Using Ruby version: ruby-2.0.0
-----> Installing dependencies using 1.5.2
       New app detected loading default bundler cache
       Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment
       Fetching gem metadata from https://rubygems.org/..........
       Fetching additional metadata from https://rubygems.org/..
       Could not find sprockets-2.11.1 in any of the sources
       Bundler Output: Fetching gem metadata from https://rubygems.org/..........
       Fetching additional metadata from https://rubygems.org/..
       Could not find sprockets-2.11.1 in any of the sources
 !
 !     Failed to install gems via Bundler.
 !

 !     Push rejected, failed to compile Ruby app

The issue was with my Gemfile.lock. I needed to run bundle update to make sure this file was updated. After I ran bundle update sprockets changed to version 2.11.0. This makes the error understandable because 2.11.1 would be unavailable through rubygems.org unless specified.

Referenced StackOverflow

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.

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.