Code Academy Week 2 Notes Post 02

2012-01-19_08.01.49
Methods always return something, even nil
Put an at sign in front of variable makes it an instance variable
Class names get upper case – but when you enter rails generate command you can make the model name lower case
model: class name is singular – this is a particular row in the database
The generators are pretty forgiving
But the pluralization is important – you need to get that right
Model names are singular, table name is plural
Model methods: new, save, all, first,
Landmark.all -> talks to the table
To populate database in the rails console:

a = Landmark.new
a.name = "Soldier Field"
a.cost = 50
a.save

Undocumented trick in rails console: type name of class and press enter. If there is a model class with that name, it will print info about class
Another way: pass a hash

x = Landmark.new(:name => "Art Institute", :cost => 18)

Hash keys: Could be strings, people prefer symbols
symbol: like a lightweight string, faster to process, fewer methods you could run on it, like a number that’s easy to read
You still call x.save with hash
He used “x” as variable name twice
The old value went away
Another way:

Landmark.create(:name =>"City Hall", :cost => 0 )

Parentheses are optional in Ruby
Using create will save it in one step
What if I have another landmark with the same name as an existing one?
Landmark.find(4) where the arg is the ID number
The IDs start at one
To list them:

y Landmark.all

To list one

y Landmark.find(3)
x = Landmark.find(2)
call x.name = "jdjd"
x.save

How to delete?

x = Landmark.find 4
x.destroy

But variable x still has data in it, even though that record is not in database
Or you could call Landmark.destory(id_name)
You could do Landmark.find_by_cost(15)
That would show anything with a cost of 15
Landmark.find_by_$FIELD($ARG)
The find_by methods will only give you the first that it finds
It dynamically makes methods via code generation = kind of metaprogramming

Some of the HTML is done by app/views/layouts/application.html.erb
The yield method puts our stuff in there
You can have different layouts for different controllers

How to select one and show just that one?
Go to controller file:

@landmark = Landmark.find(ID)

Make a new route:

get "landmarks/5/show", :controller => 'landmarks;, :action => 'show'

That’s hard-coded

get "landmarks/:id/show", :controller => 'landmarks;, :action => 'show'

What about the controller? How to put that :id in call to Landmark.find?
Now, 5 is hard-coded in the controller. Here is the log:

Started GET "/landmarks/3/show" for 127.0.0.1 at 2012-01-19 09:25:56 -0600
  Processing by LandmarksController#show as HTML
  Parameters: {"id"=>"3"}
  Landmark Load (36.0ms)  SELECT "landmarks".* FROM "landmarks" WHERE "landmarks"."id" = ? LIMIT 1  [["id", 5]]
Rendered landmarks/show.html.erb within layouts/application (8.6ms)
Completed 200 OK in 152ms (Views: 113.2ms | ActiveRecord: 36.3ms)

The second time is better:

Started GET "/landmarks/3/show" for 127.0.0.1 at 2012-01-19 09:27:06 -0600
  Processing by LandmarksController#show as HTML
  Parameters: {"id"=>"3"}
  Landmark Load (0.1ms)  SELECT "landmarks".* FROM "landmarks" WHERE "landmarks"."id" = ? LIMIT 1  [["id", 5]]
Rendered landmarks/show.html.erb within layouts/application (4.0ms)
Completed 200 OK in 10ms (Views: 8.8ms | ActiveRecord: 0.4ms)

If you changed :id in the route to :jeff, you would get the same thing in the log except for timestamps, and

Parameters: {"id"=>"3"}

would be

Parameters: {"jeff"=>"3"}

So to get the id we send, put this in controller:

@landmark = Landmark.find params[:id]

or

@landmark = Landmark.find( params[:id])

Model: singular, controller is plural
Model: name.rb, Controller: names_controller.rb

To catch a bad :id in controller show method:

if Landmark.exists?(param[:id])
    @landmark = Landmark.find(params[:id])
else
    @landmark = Landmark.first
end

That’s not the best way, but it works for now

Change routes.rb:

get "landmarks", :controller => 'landmarks', :action => 'index'

Or

get "landmarks" => 'landmarks#index'

And for the other one:

get "landmarks" => 'landmarks#show'

This is only for Rails 3 and beyond
Put a link that takes us back to the home page:
IN the show template for show.html.erb
You could remove /public/index.html
and put this in routes.rb:

root :to => 'landmarks#index'

rake has a lot of good stuff

rake routes

will print the routes

ericm@finance:~/ruby/codeAcademy/tth/second$ rake routes
     root     /                             {:controller=>"landmarks", :action=>"index"}
landmarks GET /landmarks(.:format)          {:action=>"index", :controller=>"landmarks"}
          GET /landmarks/:id/show(.:format) {:controller=>"landmarks", :action=>"show"}

so if the route has a name, you can use it in a link_to method call

<%= link_to 'Home', landmarks_url %>

You need to run rake routes to see which ones have names
You could also do landmarks_path
This will just create a relative URL

You can give a route a name:

get "landmarks/:id/show" => 'landmarks#show', :as => :landmark

In the index when we iterate, we could:

<li><%= link_to landmark.name, "/landmarks/#{landmark.id}/show" %></li>

or

<li><%= link_to landmark.name, landmark_url(landmark) %></li>

or

<li><%= link_to landmark.name, landmark_url(landmark.id) %></li>

Let’s add a form
What should the URL be?
/landmarks/new
So put it in the routes.rb

get "landmarks/new", :as => :new_landmark

So put an action “new” in the landmarks controller
and create a file app/views/landmarks/new.html.erb

When to add a controller instead of a new method?
Is the activity still related to the same type? Or is it a new thing?
We do not need the “form” tag

<%= form_for @landmark do |form| %>
    <%= form.label :name %>
    <%= form.text_field :name %>
    <%= form.submit %>
<% end %>

Go to routes:

post "landmarks" => 'landmarks#create'

put a create method in the controller
The new method was just for the form
In log:

Started POST "/landmarks" for 127.0.0.1 at 2012-01-19 11:14:23 -0600
  Processing by LandmarksController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"FO5+OlbNWa0g7xO3a/2hWtXnIPx7u5Ml0du2Oij74Eg=", "landmark"=>{"name"=>"fghd"}, "commit"=>"Create Landmark"}
Completed 500 Internal Server Error in 3ms

ActionView::MissingTemplate (Missing template landmarks/create, application/create with {:handlers=>[:erb, :builder, :coffee], :formats=>[:html], :locale=>[:en, :en]}. Searched in:
* “/home/ericm/ruby/codeAcademy/tth/second/app/views”
):
In parameters, there is

"landmark"=>{"name"=>"fghd"}

So our data is in the hash. The data is the value of a hash, which itself is another hash
Do this:

params[:landmark][:name]

You could do:

form_data = params[:landmark]
@landmark.name = form_data[:name]

At the end of the create method

redirect_to root_url

the URL must be a named route

Started POST "/landmarks" for 127.0.0.1 at 2012-01-19 11:22:44 -0600
  Processing by LandmarksController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"FO5+OlbNWa0g7xO3a/2hWtXnIPx7u5Ml0du2Oij74Eg=", "landmark"=>{"name"=>"fghd"}, "commit"=>"Create Landmark"}
  SQL (3.4ms)  INSERT INTO "landmarks" ("cost", "created_at", "name", "updated_at") VALUES (?, ?, ?, ?)  [["cost", nil], ["created_at", Thu, 19 Jan 2012 17:22:44 UTC +00:00], ["name", "fghd"], ["updated_at", Thu, 19 Jan 2012 17:22:44 UTC +00:00]]
Redirected to http://localhost:3000/
Completed 302 Found in 160ms

Images:
app/assets/images
The tag is

<%= image_tag 'tower.jpg' %>
<%= image_tag 'tower.jpg', :size  => '200x100' %>

How to get rid of a landmark:
Is the show.html.erb

<%= link_to "Toast", landmark_url(@landmark) %>

In routes:

delete "landmarks/:id", :as => :landmark

But there is a name conflict in the route – we will look at this next week

To update in console: could get it, change it, save
or: call method update_attribute

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