Download - The Rails Way
![Page 1: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/1.jpg)
The Rails WayApproach to modern web applications with
Rails 3.2
![Page 2: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/2.jpg)
80% of end-users response time is downloading all the assets.
The Performance Golden Rule
![Page 3: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/3.jpg)
The Rails Way to managing assets:
HTTP StreamingThe Assets Pipeline
![Page 4: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/4.jpg)
Assets Pipeline
● Assets concatenation● Assets minification● Support for high-level languages
○ CoffeeScript○ SASS
● Assets fingerprinting
![Page 5: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/5.jpg)
Syntactically Awesome Stylesheets$blue: #3bbfce;$margin: 16px;
.content-navigation { border-color: $blue; color: darken($blue, 9%);}
.border { padding: $margin / 2; margin: $margin / 2; border-color: $blue;}
![Page 6: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/6.jpg)
CoffeeScriptclass ProfileCompetences extends Backbone.View
tagName: 'ul'
className: 'inputs-select'
initialize: ->
@collection.on('reset', @render, this)
render: ->
competences = @collection.competences()
_.each competences, (competence) =>
view = new AutosaveSelectOption(model: @model, dict: competence)
$(@el).append(view.render().el)
this
![Page 7: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/7.jpg)
Serving Static Assets
Rails by default doesn't serve static assets in production environment.
![Page 8: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/8.jpg)
Deploymentict-ref-cloud/
current -> ~/ict-ref-cloud/releases/20120904134910
releases/
20120904134910/
app/
config/
database.yml -> ~/ict-ref-cloud/shared/config/database.yml
public/
assets -> ~/ict-ref-cloud/shared/assets
shared/
assets/
application-8e3bd046319a574dc48990673b1a4dd9.js
application-8e3bd046319a574dc48990673b1a4dd9.js.gz
application.css
application.css.gz
config/
database.yml
![Page 9: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/9.jpg)
Deployment
nginx ~/ict-ref-cloud/current/public
/tmp/unicorn.ict-ref-cloud.sock
unicorn ~/ict-ref-cloud/current
![Page 10: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/10.jpg)
OWASP Top Ten Security Risk
1. Injection2. Cross Site Scripting3. Broken Authentication and Session Management4. Insecure Direct Object Reference5. Cross Site Request Forgery6. Security Misconfiguration7. Insecure Cryptographic Storage8. Failure To Restrict URL Access9. Insufficient Transport Layer Protection
10. Unvalidated Redirects and Forwards
![Page 11: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/11.jpg)
OWASP Top Ten Security Risk
1. Injection2. Cross Site Scripting3. Broken Authentication and Session Management4. Insecure Direct Object Reference5. Cross Site Request Forgery6. Security Misconfiguration7. Insecure Cryptographic Storage8. Failure To Restrict URL Access9. Insufficient Transport Layer Protection
10. Unvalidated Redirects and Forwards
Problems related specifically to view layer.
![Page 12: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/12.jpg)
The Rails Way to security:
CSRF protectionXSS protection
![Page 13: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/13.jpg)
Cross Site Request Forgery
/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
end
/app/views/layouts/application.html.erb
<head>
<%= csrf_meta_tags %>
</head>
<meta content="authenticity_token" name="csrf-param">
<meta content="KklMulGyhEfVztqfpMn5nRYc7zv+tNYb3YovBwOhTic="
name="csrf-token">
![Page 14: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/14.jpg)
Cross Site Scripting
<div id="comments">
<% @post.comments.each do |comment| %>
<div class="comment">
<h4><%= comment.author %> say's:</h4>
<p><%= comment.content %></p>
</div>
<% end %>
</div>
<%# Insecure! %>
<%= raw product.description %>
![Page 15: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/15.jpg)
The Rails Way to routing:
Non-Resourceful routesResourceful routesSEO friendly URL's
![Page 16: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/16.jpg)
match 'products/:id' => 'products#show'
GET /products/10
post 'products' => 'products#create'
POST /products
namespace :api do
put 'products/:id' => 'api/products#update'
end
PUT /api/products/10
Non-Resourceful Routes
![Page 17: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/17.jpg)
Non-Resourceful Routes
match 'photos/show' => 'photos#show', :via => [:get, :post]
match 'photos/:id' => 'photos#show', :constraints => { :id => /[A-Z]\d{5}/ }
match "photos", :constraints => { :subdomain => "admin" }
match "/stories/:name" => redirect("/posts/%{name}")
match 'books/*section/:title' => 'books#show'
root :to => 'pages#main'
![Page 18: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/18.jpg)
Resourceful Routes
resources :photos
get '/photos' => 'photos#index'
get '/photos/new' => 'photos#new'
post '/photos' => 'photos#create'
get '/photos/:id' => 'photos#show'
get '/photos/:id/edit' => 'photos#edit'
put '/photos/:id' => 'photo#update'
delete '/photos/:id' => 'photo#destroy'
![Page 19: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/19.jpg)
Resourceful Routes
resource :profile
get '/profile/new' => 'profiles#new'
post '/profile' => 'profiles#create'
get '/profile' => 'profiles#show'
get '/profile/edit' => 'profiles#edit'
put '/profile' => 'profile#update'
delete '/profile' => 'profile#destroy'
![Page 20: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/20.jpg)
Named Routes
<%= link_to 'Profile', profile_path %>
=> <a href="/profile">Profile</a>
<%= link_to 'Preview', @photo %>
=> <a href="/photo/10">Preview</a>
![Page 21: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/21.jpg)
SEO Friendy URL's
/products/14-foo-bar
class Product < ActiveRecord::Base
def to_param
"#{id}-#{name.parametrize}"
end
end
<%= link_to product.name, product %>
/products/14-foo-bar
"/products/:id" => "products#show"
{ :id => "14-foo-bar" }
Product.find(params[:id])
Product.find(14)
Will generate
Will call to_i
Example from: http://www.codeschool.com/courses/rails-best-practices
![Page 22: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/22.jpg)
The Rails Way to view rendering:
Response renderingStructuring Layouts
AJAX
![Page 23: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/23.jpg)
/app
/controllers
products_controller.rb
users_controller.rb
Response Rendering
/app
/views
/products
index.html.erb
show.html.erb
/users
index.html.erb
new.html.erb
![Page 24: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/24.jpg)
class UsersController < ApplicationController
def new
@user = User.new
end
def create
@user = User.new(params[:user])
if @user.save
redirect_to :action => :show
else
render :new
end
end
end
Response Rendering
new.html.erb
show.html.erb
new.html.erb
![Page 25: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/25.jpg)
Rendering Response
render :edit
render :action => :edit
render 'edit'
render 'edit.html.erb'
render :template => 'products/edit'
render 'products/edit'
render :file => '/path/to/file'
render '/path/to/file'
![Page 26: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/26.jpg)
Rendering Response
render :inline => '<p><%= @comment.content %></p>'
render :text => 'OK'
render :json => @product
render :xml => @product
render :js => "alert('Hello Rails');"
render :status => 500
render :status => :forbidden
render :nothing => true, :status => :created
![Page 27: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/27.jpg)
Content Negotiation
respond_to do |format|
format.html
format.json { render :json => @product }
format.js
end
![Page 28: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/28.jpg)
Content Negotiation
class ProductsController < ApplicationController
respond_to :html, :json, :js
def edit
respond_with Product.find(params[:id])
end
def update
respond_with Product.update(params[:id], params[:product])
end
end
![Page 29: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/29.jpg)
Structuring Layout
/app
/views
/layouts
application.html.erb (default)
users.html.erb (UsersController)
public.html.erb (layout 'public')
![Page 30: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/30.jpg)
Structuring Layout
<!DOCTYPE html>
<head>
<title>User <%= yield :title %></title>
</head>
<html>
<body>
<%= yield %>
</body>
</html>
![Page 31: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/31.jpg)
Structuring Layout
<% content_for :title, @post.title %>
<div id="post">
<h2><%= @post.title %></h2>
<div><%= @post.content %></div>
</div>
![Page 32: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/32.jpg)
View Helpers
module ApplicationHelper
def title(title)
content_for :title, title
end
end
![Page 33: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/33.jpg)
View Helpers
<% title @post.title %>
<div id="post">
<h2><%= @post.title %></h2>
<div><%= @post.content %></div>
</div>
![Page 34: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/34.jpg)
Partials
/app
/views
/products
_form.html.erb
_product.html.erb
index.html.erb
edit.html.erb
new.html.erb
![Page 35: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/35.jpg)
Partials
/app/views/products/_product.html.erb
<div class="product">
<h2><%= product.name %></h2>
<p><%= product.description %></p>
</div>
Partial parameter
![Page 36: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/36.jpg)
Partials
<h1>Products</h1>
<% @products.each do |product| %>
<% render 'products/product',
:product => product %>
<% end %>
![Page 37: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/37.jpg)
Partials
<h1>Products</h1>
<% @products.each do |product| %>
<% render product %>
<% end %>
![Page 38: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/38.jpg)
Partials
<h1>Products</h1>
<% render @products %>
![Page 39: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/39.jpg)
Partials/app/views/products/_form.html.erb
<%= form_for @user do |f| %>
<div class="input">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="input">
<%= f.label :description %>
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
![Page 40: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/40.jpg)
Partials
../products/new.html.erb
<h1>New Product</h1>
<div id="form">
<% render 'form' %>
</div>
../products/edit.html.erb
<h1>Edit Product</h1>
<div id="form">
<% render 'form' %>
</div>
![Page 41: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/41.jpg)
AJAX/app/views/products/_form.html.erb
<%= form_for @user, :remote => true do |f| %>
<div class="input">
<%= f.label :name %>
<%= f.text_field :name %>
</div>
<div class="input">
<%= f.label :description %>
<%= f.text_area :description %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
![Page 42: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/42.jpg)
AJAX/app/controllers/products_controller.rb
class ProductsController < ApplicationController
def create
@product = Product.new(params[:product])
respond_to do |format|
if @product.save
format.html { redirect_to @product }
else
format.html { render :action => 'new' }
format.js
end
end
end
end
![Page 43: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/43.jpg)
AJAX
/app/views/products/create.js.erb
$('#form').html("<%= escape_javascript(render 'form') %>");
![Page 44: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/44.jpg)
AJAX
/app/views/products/index.html.erb
<%= link_to "Delete", product, method: :delete, remote: true %>
<a href="/products/1" data-method="delete"
data-remote="true" rel="nofollow">Delete</a>
(Rails is using unobtrusive javascript technique)
/app/views/products/destroy.js.erb
$("#<%= dom_id @product %>").fadeOut();
![Page 45: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/45.jpg)
The Rails Way to caching:
Basic CachingMemoization
![Page 46: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/46.jpg)
class ProductsController < ActionController
caches_page :index
def index
@products = Product.all
end
def create
expire_page :action => :index
end
end
Page caching won't work with filters.
Page Caching
![Page 47: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/47.jpg)
Action Caching
class ProductsController < ActionController
before_filter :authenticate_user!
caches_action :index
def index
@products = Product.all
end
def create
expire_action :action => :index
end
end
![Page 48: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/48.jpg)
Fragment Caching<% cache do %>
All available products:
<% @products.each do |p| %>
<%= link_to p.name, product_url(p) %>
<% end %>
<% end %>
expire_fragment(
:controller => 'products',
:action => 'recent',
:action_suffix => 'all_products'
)
![Page 49: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/49.jpg)
Sweepers
class ProductSweeper < ActionController::Caching::Sweeper
observe Product
def after_create(product)
# Expire the index page now that we added a new product
expire_page(:controller => 'products', :action => 'index')
# Expire a fragment
expire_fragment('all_available_products')
end
end
![Page 50: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/50.jpg)
Conditional GET support
class ProductsController < ApplicationController
def show
@product = Product.find(params[:id])
if stale?(:last_modified => @product.updated_at.utc, :etag => @product)
respond_to do |format|
# ... normal response processing
end
end
end
end
![Page 51: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/51.jpg)
Memoization
class City < ActiveRecord::Base
attr_accesible :name, :zip, :lat, :lon
def display_name
@display_name ||= "#@zip #@name"
end
end
![Page 52: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/52.jpg)
The Rails Way to solve typical problems:
N+1 ProblemFetching object in batches
![Page 53: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/53.jpg)
class User
def recent_followers
self.followers.recent.collect do |f|
f.user.name
end
end
end
Select followers where user_id=1
Select user where id=2
Select user where id=3
Select user where id=4
Select user where id=5
Source: http://www.codeschool.com/courses/rails-best-practices
N+1 Problem
![Page 54: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/54.jpg)
class User
def recent_followers
self.followers.recent.includes(:user).collect do |f|
f.user.name
end
end
end
Select followers where user_id=1
Select users where user_id in (2,3,4,5)
Bullet Gem:
https://github.com/flyerhzm/bulletSource: http://www.codeschool.com/courses/rails-best-practices
N+1 Problem
![Page 55: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/55.jpg)
Fetching objects in Java
List<Tweet> tweets = tweetDao.findAllForUser(user);
for (Tweet tweet : tweets) {
// ...
}
for (Tweet tweet : user.getTweets()) {
// ...
}
![Page 56: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/56.jpg)
Fetching objects in Rails
Tweet.where(user: user).find_each do |tweet|
# ...
end
user.tweets.find_each(batch_size: 5000) do |tweet|
# ...
end
By default pulls batches of 1,000 at a time
![Page 57: The Rails Way](https://reader031.vdocuments.mx/reader031/viewer/2022021113/5550fad1b4c90501448b4b89/html5/thumbnails/57.jpg)
Try Rails!