Head First Rails (Errata) - Updating for Rails 3.x
 Share
The version of the browser you are using is no longer supported. Please upgrade to a supported browser.Dismiss

 
$
%
123
 
 
 
 
 
 
 
 
 
ABDE
1
2
Rails 3.x (or now 4.x)
3
PageTo:Changes NeededNotes
4
7create new rails application> rails new tickets
5
Not in bookspecify rails version> rails _3.x.x_ new app_namerails 4.0 is out and may be your default install. You may want to change it to 3.x at the start. To check your default rails version >rails -v will >>version# you're using by default. Trying to change the version in the Gemfile after installing using rails4 will cause problems later.
6
8start built-in web server> rails sfd
7
8 > rails s -p 8000there is also a long form for this > rails server and >rails server -p 8000
8
Not in bookstop serverpress keys "ctrl" and "c" togetherin windows you need to press "ctrl" and "pause/break". Not sure if the book includes this but it could help
9
11generate scaffolding code> rails generate scaffold ...can also use the short form > rails g scaffold ...
10
34generate migration> rails generate migration ...can also use the short form > rails g migration ...
11
35the code of the migrationclass AddPhoneToTickets < ActiveRecord::Migration
def change
add_column :tickets, :phone, :string
end
end
the code generated by rails 3 is different, the method change replaces self.up and self.down
12
36-37add phone_noAdd phone field to 3 views files (index.html.erb, _form.html.erb, show.html.erb,) similar to what is indicated in pages 36-37.
Also add “:phone” to the list of attributes after attr_accessible in ticket.rb model file. if using Rails 3.2.8 or above

1 Add phone field to 3 views files (index.html.erb, _form.html.erb, show.html.erb,) similar to what is indicated in pages 36-37.
the only difference is that _form now is called inside edit and new so one less form to edit.
2 (if u use rails 3.x use this way)Must also add “:phone” to the list of attributes after attr_accessible in ticket.rb model file. (app/models/ticket.rb)
(but ,if use rauls 4.x) you should edit (app/controller/ticket_controller.rb) and then find
"def ticket_params
params.require(:ticket).permit(:name, :seat_id_seq, :address, :price_paid, :email_address)
end " and add ":phone" after :email_address
if not then it will throw error: "Can't mass-assign protected attributes: phone" when clicking on create or update button (Required from Rails 3.2.8)
13
QA comment on the need of =hescaped by default in rails 3In rails 3 you no longer need to call h(string) to escape HTML output, it is on by default in all view templates. If you want the unescaped string, call raw(string).
14
40excercise solution> rails generate scaffold ...
15
41excercise solutionneed to change only 3: index, show and _formIn rails 3 you no longer need to call h(string) to escape HTML output, it is on by default in all view templates. If you want the unescaped string, call raw(string).
16
48excercise solutionrails new mebay
17
50generate model > rails generate model ...can also use the short form > rails g model ...
18
52generate controller> rails generate controller ...can also use the short form > rails g controller ...
19
57create a route(rails 3)match '/ads/:id', to: 'ads#show'
(rails 4)get '/ads/:id' => 'ad#show'
(also rails 4)get '/ads/:id', to: 'ads#show'
this is the routing used in Rails 3.2.14
20
76create a routematch '/ads', to: 'ads#index'this is the routing used in Rails 3.2.14
21
116metoda Ad.find(:all)kod w notatce, najedź kursorem
22
95refer to a stylesheet 'default.css'not needed, but the convention is <%= stylesheet_link_tag "application", :media => "all" %>
23
95put images in /app/assets/images
24
97put default.css in /app/assets/stylesheets
25
97edit cssCory wrote: "When I worked on chapter two I had to play with the stylesheet to get the images to display. I had to remove the /images/ from the path."
26
108routes get '/ads/new', to: 'ads#new'
post '/ads/create', to: 'ads#create'
get '/ads', to: 'ads#index'
get '/ads/:id', to: 'ads#show'
this is the routing used in Rails 3.2.14
27
112 code line 2<%= form_for ...
(rails 4)<%= form_for @ad, url: {action: "create"} do |f| %>
Rails 3 needs the "="
28
122params[:ad]kod w notatce, najedź kursorem
29
138link to edit<%= link_to :Edit, action: :edit %>In this case both work but the second one is the convention
30
138routes get '/ads/:id/edit', to: 'ads#edit'
put'/ads/:id', to: 'ads#update' or patch'/ads/:id', to: 'ads#update'
this is the routing used in Rails 3.2.14
31
148routesget '/ads/:id/delete', to: 'ads#destroy'this is the routing used in Rails 3.2.14
32
164 code line 11<%= form_tag ...Rails 3 needs the "="
33
167routesNeed to specify the root, by default the form is associated with the 'post' method but the correct one should be 'get'Specify the route to 'find' and select that route in the form_tag as follows.

Edit the config/routes.rb file to look like this

resources :client_workouts do
collection do
get 'find'
end
end

And edit the first line of the form_tag in app/views/layouts/application.html.rb to look like this

<%= form_tag "/client_workouts/find", :method => :get do %>

34
210redirect syntax if @ad.save()
redirect_to "/ads/#{@ad.id}"
else
render action: "new"
end
No error here, the code in the book will work on rails 3 but the syntax has changed and could also be written as shown on the second column
35
212error messages<%= render partial: 'seats/new_seat'>f.error_message was deprecated for rails 3. You can see: http://stackoverflow.com/questions/3873190/f-error-messages-in-rails-3-0
36
224-231partial for new bookingsrails 3 creates a form that is used both for edit and new on seats, this form can be modified as needed and left with the other seat files instead of copying it. This requires less modifications to the partial as there is no links to remove.
The partial can be directly loaded into the show flight template from there.
Notice the syntax change, this is valid for ruby 1.9+.
If using ruby 1.8 then render should be:
<%= render :partial => 'seats/new_seat'>
37
235passing the seat to the partial<%= render partial: 'seats/new_seat', locals: {seat: Seat.new} %>Again, this is a syntax change if using ruby 1.9+. the book sample will work on both but the new syntax is recomended to get used working with ruby 1.9+
38
243-246partial for listing booked seats<%= auto_discovery_link_tag(:rss, {:action=>'news',:format=>:rss})%>In this case the book is right on suggesting building the partial from the index on seats, just do not copy the file into flights, leave it under seats and reference to it as shown
39
249-259custom validatorsclass Seat < ActiveRecord::Base
attr_accessible :baggage, :flight_id, :name
belongs_to :flight
validate :baggage_allowance_not_exceeded,
:flight_capacity_not_exceeded

def baggage_allowance_not_exceeded
if baggage > flight.baggage_allowance
errors.add(:baggage, "You have too much baggage")
end
end

def flight_capacity_not_exceeded
if flight.seats.size >= flight.capacity
errors[:base]<< "The flight is fully booked"
end
end
end
Custom validators are declared differently in rails 3

1. need to separate and name each validator
2. need to specify that the validation is needed
3. the first method will add a custom error message linked to the baggage field
4. the second method will add a generic message related to the capacity of the flight being exceeded. Instead of "erros.add_to_base" use "errors[:base]<<"

for more details see:
http://edgeguides.rubyonrails.org/active_record_validations.html#custom-validators




40
268route resources :seats do
collection do
get :flight_seats
end
end
after some tests this is the best way to specify this route routing in Rails 3.2.14
41
269partial for listing booked seats def flight_seats
@flight = Flight.find(params[:flight_id])
render :partial=>"seats/seat_list", :locals=>{:seats=>@flight.seats}
end
Just change so the seats partial stays with the rest of the seats templates and partials
42
272include AJAX LibrariesThis is not needed, by default the javascript libraries are included with: <%= javascript_include_tag "application" %>In rails 3, jquery replaced prototype as the javascript library
43
273-275placing the div idmodify views/flights/show.html.erb to include:

<%= render :partial=>"seats/seat_list", :locals=>{:seats=>@flight.seats}%>


write the div inside views/seats/_seat_list.html.erb
44
273-275put div inside _seat_list.html.erb<div id="seats">
<h1>Listing seats</h1>

<table>
<tr>
<th>Name</th>
<th>Baggage</th>
<th></th>
<th></th>
<th></th>
</tr>

<% seats.each do |seat| %>
<tr>
<td><%= seat.name %></td>
<td><%= seat.baggage %></td>
<td><%= link_to 'Show', seat %></td>
<td><%= link_to 'Edit', edit_seat_path(seat) %></td>
<td><%= link_to 'Destroy', seat, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
<br />
</div>
when replaceWith is called from javascript, the <div> would be lost after first call, so it needs to be in the partial
45
273-275link to refresh<%= link_to "refresh seats", :id => @flight.id, action: :flight_seats, controller: :seats, method: :get, :remote => true %>

link_to_remote is not used anymore, use link_to, with :remote => true, but the updater is not generated so more code is needed
46
273-275Edit controller method to return javascript def flight_seats
@flight = Flight.find(params[:id])
respond_to do |format|
format.html {render :partial=>"seats/seat_list", :locals=>{:seats=>@flight.seats}}
format.js
end
end
Add respond_to and format.js so the js.erb is rendered, generating javascript code that is returned for updating list of seats. Much of these was adapted from the response of EdJones on http://stackoverflow.com/questions/4016285/rails-3-simple-ajaxy-page-updates
47
273-275Create js template: flight_seats.js.erb<% updated_list = escape_javascript(render(:partial => 'seat_list', :locals => {:seats => @flight.seats}))%>
$("#seats").replaceWith("<%= updated_list%>");
This is the code that does the replacement. Use the code to create a file called flight_seats.js.erb and save it on the same path as other seat templates (app/views/seats), the controller will look for it in there.
48
280autorefresh <%= javascript_tag do -%>
$(document).ready(
function(){setInterval("refresh_seats()",5000)
});
function refresh_seats(){
$.ajax({type: "GET",
url: "/seats/flight_seats/",
data: "id=<%= @flight.id%>",
dataType: "script"
});
}
<% end -%>
link_call_remote was deprecated on rails 3, but the updater is not generated so more code is needed, add the code at the bottom of the /app/views/flights/show.html.erb. The first javascript sets the counter to 5000 milliseconds (= 5 seconds). The second function calls the flight_seats method on the seats controller. That is all that's required
49
285-287refresh list when adding a new recordin Rails 3 the change needed is from:
<%= form_for(seat) do |f| %>
to
<%= form_for(seat, :remote => true) do |f| %>
remote_form_for was deprecated for rails 3
50
288-292refresh list when adding a new record # Changes required on rails 3
def create
@seat = Seat.new(params[:seat])
@flight = @seat.flight #1 need to pass the flight to the js.erb
respond_to do |format|
if @seat.save
format.html { redirect_to @seat, notice: 'Seat was successfully booked.' }
format.json { render json: @seat, status: :created, location: @seat }
format.js {@seat} #2 this calls the js.erb if update was ok
else
format.html { render action: "new" }
format.json { render json: @seat.errors, status: :unprocessable_entity }
format.js {@seat} #3 this calls the js.erb if update was not ok
end
end
end
Lines #1,#2 and #3. Line #1 will get the flight which will be used for reloading the seats list. Lines #2 and #3 indicate what to do if the method is called with remote set to true (js call)
51
292-306New js template: create.js.erb # Changes required on rails 3
def create
@seat = Seat.new(params[:seat])
@flight = @seat.flight #1 need to pass the flight to the js.erb
respond_to do |format|
if @seat.save
format.html { redirect_to @seat, notice: 'Seat was successfully booked.' }
format.json { render json: @seat, status: :created, location: @seat }
format.js {@seat} #2 this calls the js.erb if update was ok
else
format.html { render action: "new" }
format.json { render json: @seat.errors, status: :unprocessable_entity }
format.js {@seat} #3 this calls the js.erb if update was not ok
end
end
end
this js template will update the list and clean the form if the creation succeeds or present the error if it does not. Use the code to create a file called create.js.erb and save it on the same path as other seat templates (app/views/seats), the controller will look for it in there. got some hints on how to do the required changes from: http://stjhimy.com/posts/07-creating-a-100-ajax-crud-using-rails-3-and-unobtrusive-javascript
52
312Route to show with map match 'incidents/map/:id'=> 'incidents#show_with_map'
53
330add .:format to path match 'incidents/map/:id'=> 'incidents#show_with_map'not necesary in rails 3. '.:format' is implicit in all routes.
54
345route to newsmatch 'incidents/news'=> 'incidents#news'
the order is important, this needs to go before the resources :incidents line in the routes.rb file. If it is placed afte it will not work, throwing an error saying something like "cannot find incident with id news"
55
349news method in incidents controller def news
@incidents = Incident.find(:all, :conditions=>['[when] >= ?', Time.now.yesterday])

respond_to do |format|
format.rss { render :layout => false } #news.rss.builder
format.xml {render :xml=> @incidents}
format.json { render json: @incidents }
end
end
this code will work and return three types of data: rss, xml, json. Just need to name the builder template as news.rss.builder
56
352builder fileThe builder works as expected but it can also be named news.rss.builder. this way the adress in the bar will be http://localhost:3000/incidents/news.rss which looks more like an actual feed
57
353autodicovery tagIn this case the format needs to be specified so the url in the link is build properly as localhost:3000/incidents/news.rss. If format is not specified the redirection when clicking the RSS feed buton will not work
NOTE: Browsers do not automathically display the rss button on the address bar(box) for explorer, firefox and chrome do:
CRHOME: the rss feed is a plugin that needs to be added, go to settings/extensions and click on get extensions then look for rss subscription extension and click on the add button (it is free)
FIREFOX: from the menu go to options/toolbar layout or right click on the toolbar and select customise, then drag the rss icon to the toolbar.
EXPLORER: the rss button is in the command bar, if the bar is hidden just rightclick on the top of the browser and select Command bar to see the icon

58
360include AJAX LibrariesThis is not needed, by default the javascript libraries are included with: <%= javascript_include_tag "application" %>In rails 3, jquery replaced prototype as the javascript library
59
362-366NOT IN THE BOOK. Edit file: _map.html.erb to show incident in the map. Lines 51-57 need to be changed $.ajax({type: "GET", url:link,
success: function(data){
var contentString = "<div align='left' id='map_info' style='min-width: 400px; min-height: 200px; width: 400px; height: 200px;'>" +
data + "</div>";
mark1.openInfoWindowHtml(contentString);
},
dataType: 'html'

The code in the book from pages 361-365 should work OK. However, when trying it, the info window with the content is not shown.
The javascript on the _map.html.erb file is incorrect, using firebug it appears that the problem is the call "new Ajax.request".
This is because the original code was made to work with the old JavaScript library for Rails (Prototype). The code must be edited to make it work with jquery, the javascript library for rails 3.2.14
Looking at the documentation of the GoogleMaps JavaScript API and examples on Stackoverflow helped in getting the code right.
References:
http://stackoverflow.com/questions/5544871/jquery-equivalent-to-prototype-ajax-request
https://google-developers.appspot.com/maps/documentation/javascript/examples/infowindow-simple
http://stackoverflow.com/questions/8142265/jquery-ajax-get-response
60
372second line on the _new.html.erb partial<%= form_for(incident, :update=>'map_info', :remote=>true) do |f| %>remote_form_for was deprecated for rails 3
61
372-373NOT IN THE BOOK. Edit file: _map.html.erb to show input form in the map. Lines 166-177 need to be changed $.ajax({type: "GET", url: new_path(),
data: {latitude: point.y, longitude: point.x},
success: function(data){
var contentString = "<div align='left' id='map_info' style='min-width: 400px; min-height: 300px; width: 400px; height: 300px;'>" +
data + "</div>";
marker.openInfoWindowHtml(contentString);
},
dataType: 'html'
});
The code was intended for Prototype (Rails 2 preferred javascript library) so it needs to be updated to use JQuery instead
62
377-378refresh popup after adding a new incidentFor Rails 3.2.14 this changes to:
<%= form_for(incident, :remote => true) do |f| %>
remote_form_for was deprecated for rails 3
63
377-378refresh popup after adding a new incident The create method response needs to specif a javascript response: format.js and a new template create.js.erb needs to be created
def create
@incident = Incident.new(params[:incident])
respond_to do |format|
if @incident.save
format.html { redirect_to @incident, notice: 'Incident was successfully created.' }
format.js
format.json { render json: @incident, status: :created, location: @incident }
else
format.html { render action: "new" }
format.json { render json: @incident.errors, status: :unprocessable_entity }
end
end
end
this is needed to produce the behavior that remote_form_for used to provide
64
377-378refresh popup after adding a new incident<% new_incident = escape_javascript(render(:partial => 'show', :locals => {:incident => @incident}))%>
$("#map_info").replaceWith("<%= new_incident%>");
This is the code that goes into the create.js.erb file
65
377-378refresh popup after adding a new incident<div align='left' id='map_info' style='min-width: 400px; min-height: 300px; width: 400px; height: 300px;'>
<p id="notice"><%= notice %></p>
<p>
<b>Mountain:</b>
<%= @incident.mountain %>
</p>
<p>
<b>When:</b>
<%= @incident.when %>
</p>
<p>
<b>Title:</b>
<%= @incident.title %>
</p>
<p>
<b>Description:</b>
<%= @incident.description %>
</p>
<%= link_to 'Edit', edit_incident_path(@incident), :remote => true %>
</div>
The map_info <div> needs to be added to the _show.html.erb partial, so that the html is rendered correctly in the popup box
66
379-396add link to edit in the _show.html.erb partial and replace with _edit.html.erb partial<%= link_to 'Edit', edit_incident_path(@incident), :remote => true %>link_to_remote is deprecated, we need to use link_to, with :remote => true, but the update action is not handled like in prototype so more code is needed
67
379-396add link to edit in the _show.html.erb partial and replace with _edit.html.erb partial # GET /incidents/1/edit
The edit method in the incidents controller needs to be modified to respond with javascript as follows:

# GET /incidents/1/edit
def edit
@incident = Incident.find(params[:id])
respond_to do |format|
format.html
format.js
format.json { render json: @incident, status: :created, location: @incident }
end
end
this is needed to produce the behavior that remote_form_for used to provide
68
379-396add link to edit in the _show.html.erb partial and replace with _edit.html.erb partial<% edit_incident = escape_javascript(render(:partial => 'edit', :locals => {:incident => @incident}))%>
$("#map_info").replaceWith("<%= edit_incident%>");
This is the code that goes into the edit.js.erb template file
69
379-396add link to edit in the _show.html.erb partial and replace with _edit.html.erb partial<% edit_incident = escape_javascript(render(:partial => 'edit', :locals => {:incident => @incident}))%>
$("#map_info").replaceWith("<%= edit_incident%>");
This is the code that goes into the edit.js.erb template file
70
379-396add link to edit in the _show.html.erb partial and replace with _edit.html.erb partial<div align='left' id='map_info' style='min-width: 400px; min-height: 300px; width: 400px; height: 300px;'>
<h1>Editing incident</h1>
<%= form_for(incident, :remote=>true) do |f| %>
<p>
<%= f.label :mountain %> <%= f.text_field :mountain %>
</p>
<%= f.hidden_field :latitude %>
<%= f.hidden_field :longitude %>
<p>
<%= f.label :when %> <%= f.datetime_select :when %>
</p>
<p>
<%= f.label :title %> <%= f.text_field :title %>
</p>
<div class="field">
<%= f.label :description %><br />
<%= f.text_area :description,:rows=>3 %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<%end%>
</div>
The map_info <div> needs to be added to the _edit.html.erb partial, so that the html is rendered correctly in the popup box
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
Loading...
Main menu