Railscast 035 Custom REST Actions

The GitHub Repo

The Heroku App

This is one of those episodes I needed to see. I haven’t had to use the collection or member blocks in the routes file before, but I’m glad to know what they mean now. member and collection allows the developer to create custom RESTful routes that extends the rails conventions. A member route appends after the /:model/:id, so the controller action has the params[:id]. A collection route appends after the /:model route.

Examples of these two were the routes created in this episode.

resources :tasks do
  get 'completed', on: :collection
  put 'complete', on: :member
end

This creates the normal RESTful Rails routes, but it also create two custom routes. /tasks/completed/ and /tasks/:id/complete. The Rails Guides for Routing was useful for further reading.

TasksController

def completed
  @tasks = Task.where(completed: true)
end

def complete
  @task.update_attribute :completed, true
  flash[:notice] = 'Task Completed'
  redirect_to completed_tasks_path
end

The two custom actions were completed and complete The completed action returns a tasks instance variable where completed is true. The complete action updates an attribute to true, then redirects to the completed tasks view with a flash notice.

To complete a task, the episode shows creating a link with the put method being sent the complete_task_path while sending the task’s id.

Heroku Issue

For some reason when I tried to click on this ‘complete task’ link, heroku is giving me a ‘page doesn’t exist’ error. Locally the link works however. I have opened a StackOverflow question about the issue. I don’t think you need a view for that action because it redirects to the completed_tasks_path. Even the rake routes show a PUT for /tasks/:id/complete. I did discover that the Rails core team has been switching over to the Patch verb over the put http verb. This adheres to RFC specification for partial updates. Even though I tried to change out put for patch, I was unable to get this link working.

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.

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.

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

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.

Make all Railscast Projects

I came across a post about someone asking what was the best way to learn Ruby on Rails via Quora. One answer suggested one of the best ways was to follow along with every Railscast project then upload the repo to both GitHub and Heroku. I liked the concept, so I have bought a membership of Railscast and have downloaded all the episodes.

While the goal is to create every project, I will watch every episode and decide if it would be useful to make the project or not. Some episodes are just small tips, so creating a new project just to use that one tip seems inefficient.

I might be repeating what episodes showed, but I found that writing about what I learnt from the episode solidifies what I have learnt. Similar to language learning, you need a balance of input and output to get good at any skill. The input here are the Railscast episodes and the things I look up to finish the apps, and the output are the working app and the blog post.

The first few days of trying this method, I created an app then wrote up the blog post. This takes a lot of context switching between tasks. I am currently trying to go through as many Railscasts in one day, then writing about each the following day.