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.