Code Academy Week 4 Notes

I only seem to have notes for the second day of week 4.

2012-02-02 Notes

Scaffolding notes:
respond_to
partials: Like a server side include

Look up respond_to method

respond_to do |format|
     format.html
     format.json { render json: @runners }
 end

JSON only:

respond_to do |format|
     format.json { render json: @runners }
 end

You could use curl as well.

514  curl -v "http://localhost:3000/runners"
 515  curl -v "http://localhost:3000/runners.json"
 516  curl -v "http://localhost:3000/runners" -H "Accept: text/html"
 518  curl -v "http://localhost:3000/runners" -H "Accept: application/json"

Try getting a png:

ca-imac1-2: ~/dev/tth/MarathonApp$ curl -v "http://localhost:3000/runners" -H "Accept: image/png"
 * About to connect() to localhost port 3000 (#0)
 *   Trying 127.0.0.1... connected
 * Connected to localhost (127.0.0.1) port 3000 (#0)
 > GET /runners HTTP/1.1
 > User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5
 > Host: localhost:3000
 > Accept: image/png
 >
 < HTTP/1.1 406 Not Acceptable
 < Content-Type: image/png; charset=utf-8
 < X-Ua-Compatible: IE=Edge
 < Cache-Control: no-cache
 < X-Request-Id: 0e74fdad7575b392561230a70fb0cadd
 < X-Runtime: 0.001522
 < Content-Length: 1
 < Server: WEBrick/1.3.1 (Ruby/1.9.3/2011-10-30)
 < Date: Thu, 02 Feb 2012 14:42:53 GMT
 < Connection: Keep-Alive
 <
 * Connection #0 to host localhost left intact
 * Closing connection #0

I did not know there was a code 406.

There is a method called responds_with :html, :json, :xml
Not used too often.  That was intended for all actions, but people did not want universal responders.

Scaffold does not provide any security out of the box.

New action also has a block to send json back
526  curl -v “http://localhost:3000/runners/new.json” -H “Accept: application/json”
returns

{"created_at":null,"id":null,"name":null,"updated_at":null}

This could help other developers know what data fields your classes have

The create action has an if block in the respond_to call to handle both a successful and unsuccessful creation.

In Ruby 1.9, you can use a new syntax for hashes

h = {:color => “Blue”, “fruit” => “apple”}

You could also do it like JSON

i = {color: “Blue”, “fruit” => “apple”}

You can only use the colon for keys if your key is a symbol.
So if you mix key types like in hash i, things get a bit ocnfusing. Your value can be a symbol, but that colon must still be on the left.

Assigment: look at the destroy. What is head :no_content about?

Memorize the RESTful Design chart with the routes, URLs, actions, etc

Let’s look at new and edit
TextMate tip: could go to the drawer. There is another way to get there. Command-T pops up a window, type in the name of your file. You can type something in the middle.
Look at the “new.html.erb” page. Where is the form?
Look at the “edit.html.erb” page. Where is the form?
We see this in each file:

<%= render 'form' %>

There is a file app/views/runners/_form.html.erb
Partials begin with underscores.
You just use the render method.
You can use instance variables in the partial.

From index.html, you could take this and put it in a partial:

<% @runners.each do |runner| %>
     <tr>
     <td><%= runner.name %></td>
     <td><%= link_to 'Show', runner %></td>
     <td><%= link_to 'Edit', edit_runner_path(runner) %></td>
     <td><%= link_to 'Destroy', runner, confirm: 'Are you sure?', method: :delete %></td>
     </tr>
 <% end %>

into a file called _list_of_runners.html.erb
and replace it with

<% render ‘list_of_runners’ %>

It makes the index.html clearer, but clutters up the directory.

Back to associations:
One-to-Many
One movie has many actors:

class Movie < ActiveRecord::Base
     has_many :actors
 end
class Actor < ActiveRecord::Base
     belongs_to :movie
 end

User stories:
As a user, I want to see the list of teams in our softball league.
A team has a company name and a nickname.
As a user, I want to select a team, and see the list of players on the team.
A player has a name and a jersey number.

What about many-to-many?
Actors are in many movies, and movies have many actors.
There is the notion of a “Role” in between Movie and Actor. That joins them together.
Movie <-> Role <-> Actor
Movie has many roles, and an Actor can have many roles
Roles would have id, movie_id and actor_id

class Actor < ActiveRecord::Base
     has_many :roles
 end
class Role < ActiveRecord::Base

    belongs_to :movie

    belongs_to :actor
end

 

 

User <-> Review <-> Landmark

You just generate a model for a role
rails g scaffold Movie title:string year:integer
rails g scaffold Actor name:string
rails g model role movie_id: integer actor_id:integer character:string
Add the has_many and belongs_to statements

IN the console:
the hard way

Role.create :movie_id => 1, :actor_id => 1, :character_name =>’Lucky Day’
 amigos.roles
 r = amigos.roles.first
 r.movie
 r.actor
 r.actor.name

Another way to create a role

amigos.roles
 amigos.roles.create :actor_id => 2, :character => ‘Dusty Bottoms’
 mark = Actor.create :name => ‘Mark Hamill’
 star_wars.roles.create :actor => mark
 starwars.roles.create :actor => Actor.find_by_id(3), :name => “Leia”

Some stuff on migrations and controller filters
What about the salary for the role?
You cannot put it in the old migration file and re-run that
The timestamps are on there for a reason

You need to create a new migration for the new field
rails generate migration $NAME $FIELD:$TYPE
rails generate migration AddSalaryToRole salary:integer
It might generate the right thing. You would need this:

def change
     add_column :roles, :salary, :integer
 end

Then type rake db:migrate to make changes
rake db:version

Controller filters:
In the controller, we do

@movie Movie.find(params[:id])

quite a few times. (This would be better for something that is multiple lines.)
Make a method
def find_the_movie
It might generate the right thing.
end
You can call that when you need to
But there is still some repetition
If you want to call a method at the beginning of an action or method, that is a before filter
Put this at the top:

before_filter :find_the_movie

So even though destroy action has @movie in the first line, you won’t get an error since you get @movie from the filter
But index method does not get params[:id]. You will get an error
So how to run it only for certain actions

before_filter :find_the_movie, :only => [:show, :edit, :update, :destroy]

or to exclude some methods

before_filter :find_the_movie, :except => [:index, :new, :create]

DRY-ing code:
partials in views, pull them in with renders
controllers: use filters to extract common code

You can make your db lookups faster with indexes:

add_index :roles, :movie_id

You can put these in a migration
You probably want this for foreign keys

Image from Wikimedia, assumed allowed under Fair Use. Image from the Vatican Virgil, a 5th century manuscript of poems by Virgil.