Code Academy Week 7 Notes


3 more weeks of core material
The building weeks there might be breakout topics
This week:
Model callbacks
User auth
Nest resources
Environmental variables

Weeks 8-10
Twitter/Facebook auth
File uploads
Credit cards/PCI
Asset pipeline
Javascript and JQuery
Database Scaling

Model Callbacks:
For a user story:
As a user, I want to see the total price in the shopping cart before I check out.
When jeff did it, he put it in the view. This is generally considered bad. Need to calculate outside the view.
How about showing total of the Cart?
So we need to add that column, since we don’t have one now.
Or add a method to the cart

def total
    sum = 0
    cart_items.each do |item|
        sum = sum + item.product.price

Why not do this in the controller
Views cannot call controller methods
You also might need to know the total in another page
But do not call number_to_currency on sum in the model. Do not do any formatting in the model, do not calculate in the view.

Now he is doing total with inject method

cart_items.inject(0) do |total, item|
     total + item.product.price

You give it 0 arg because that is where you will start. It is another way of doing +=
It returns the final total at the end
Or on one line:
cart_items.inject(0) { |total, item| total + item.product.price }

@fredlee from ENova is here.
How to succeed at Enova:
– Skillnacity: skill + tenacity. He thinks tenacity is important. Skill can be learned.
– Takes initiative: it is better to ask for forgiveness than permission.
– Selfish giver: willing to give and take
– Networking: Bell Labs: star engineers were good at networking

His thoughts on Design
– Every design decision trends towards “wrong”
– or, every design get a little more wrong each second. The world changes
– design does not matter. Kind of.
So what does matter?
1. Tests. Preferably automated tests that validate system behavior.
2. Short iterations
3. Courage to throw it all away.

Too much technology!
xml, html, css, js, json, erb, yaml
This too shall pass
So what stays the same?
– Users first!
– Speed/scale/performance
– Money (your ability to generate revenue validates your purpose)

Thoughts On Code Quality:
– It does not matter. As long as it works. But higher quality is better
– Lean Startup: If you do not know who the customer is, you do not know what quality is
– Quality is defined by user
– Or, there is no universal objective definition of quality


Back to Jeff
CartItem model. Create one in memory, add/edit attritbutes, save it. You might later change it, later delete it.

Model validations can affect some of those methods. You cannot save an invalid object.
So first some validation runs. 1 of 2 things will happen.
The Creation case or the update case.
The create path is the first time this object goes into database. It will assign an ID to that object.
If it has already been saved, it will do the update case.
Which case is run will determine the callbacks you can run.
After creation, we can update a total column in the cart.
We write a hook method for callback. Callback meaning we write it but we do not explicitly call it. Rails will call it.
So we run a migration to add a total to the cart.

rails g migration


def change
    add_column :carts, :total, :integer, :default => 0

That could also go in an “up” method
If I want to undo it, make a “down” method

def down
    remove_column :carts, :total

# I missed a bit in here due to wireless issues
So now we have a total method, but Jeff already defined a total method
His version will take priority. You can redefine methods.
But it is not a good idea

So in CartItem model:
Add a callback:

after_create :increase_cart_total

Look at the guides to find the callbacks. The symbol is the name of a method that Rails will call.

def increase_cart_total += product.price

We could do this in the cart_items_controller
But what if I manipulate my models some other way
Callback: When one model changes another model
This after_create is creation in the database, no necessarily the controller “create” method.
Model callbacks: business process events.
Controllers generate the response. If you can have models talk to each other, that is better.
Javascript and jquery use a LOT of callbacks.
1. Add a total column to the cart model
2. Show the total inside the cart in the view
3. Add an after_create callback to update the total whenever a new cart item is created
rails g migration AddTotalToCart total:integer

The user model:
1. Add “bcrypt-ruby” to your Gemfil
2. Create a User model that includes a string column named “password_digest”
3. Use “has_secure_password” in the model

The user form will have attributes password and password_confirmation, even though they are not in the database. Only password_digest is a column. has_secure_password adds those memory-only attributes.
has_secure_password will also validate that the two password fields are the same
Add a login field.
He did not get in, so he used the logger method
logger.debug “Started sign in action”
Make the link to sign out go to /logout
In routes:

get '/logout' => 'sessions#destroy', :as => :logout

Session is not a regular ActiveRecord class
In the destroy method:


Authentication (who are you) versus authorization (what can you do)
Authentication: indentifying a user, making them identify themselves – usually username and password
Authorization: Permissions

Only let people check out if they are checked in. You could put this inthe shared/cart partial:
button_to “check out”, orders_url if session[:user_id].present?

Or you could do it in the checkout action
In OrdersController.create:

if session[:user_id].nil?
    redirect_to root_url, :notice => "Please check in"

This could give the double render error
the redirect_to has not effect on control flow. You should end the if block with “return”

if session[:user_id].nil?
    redirect_to root_url, :notice => "Please check in"

But we would need that in the index method as well. That gets messy.
You can also call show with /orders/6
We don’t want this code in three places. How to DRY up controller code? Before filters.

before_filter :require_login

def require_login
    if session[:user_id].nil?
        redirect_to root_url, :notice => "Please check in"
    @user = User.find(session[:user_id])

Since we get @user in the filter, we ccan remove all the User.find method calls
If the filter renders or redirects, the action will not be run.

To list all the tables from within the console:


In shopping app, we want to remove the item from the cart.
So in CartItem we put in another callback: after_destroy

def decrease_cart_total -= product.price

We can just call product since we have belongs_to :product in the model
after_destroy is better. It will keep it in memory after it deletes the row in the database. It is better to do after_destroy instead of before_destroy since something may go wrong between before_destroy and the actual destruction

Now we want to add some administrators to manage product catalog.

rails g migration AddAdministratorToUser administrator:boolean

Let’s change the migration file:
add_column :users, :administrator, :boolean, :default => false

you can use conditional logic in the view:
Instead of class.boolean == true
you can say

<% user = User.find_by_id(session[:user_id]) %>
<% if user.present? && user.administrator? %>

In all those pages. There has to be an easier way.
Put this is a helper. Partial is good for markup. Helper is good for logic.

def administrator?
    user = User.find_by_id(session[:user_id])
    return user.present? && user.administrator?

In the pages, do this:

<% if administrator? %>

Now we want to sort the data. Use the order method. We have done Product.all, Product.first, etc, now we have

Product.order("$COLUMN_NAME, DIRECTION")

You can also call it on the association proxies

@products = Product.order("name asc")
Product.order("LOWER(name) asc")

To limit how much you get back:

Product.order("price desc").limit(3)

Most recently added item:
application layout file:
Above the cart:

<div id="news">

<%= Product.recent_items.each do |product| %>
    link_to, product_url(product)
<% end %>

We don’t have Product.recent_items,
But we have columns in database: created_at and updated_at

Product.order("updated_at desc").limit(3)

But let’s not put that in the view. That could be in the controller instead of the view. Or in the model.

def most_recent
    Product.order("updated_at desc").limit(3)

So in the view you can say
But this is not an instance method. We want this to be a class method.
So you can do it this way:

def Product.most_recent
    Product.order("updated_at desc").limit(3)


def self.most_recent
    Product.order("updated_at desc").limit(3)

So you could even do this:

def self.most_recent
    order("updated_at desc").limit(3)

Or self.order. But if you did that, you would still need “self” in the method signature to keep it a class method.

Image from Wikimedia, assumed allowed under Fair Use. Image from the Ambrosian Iliad, a 5th century manuscript of the Iliad.