The Rails Way
Approach to modern web applications with
               Rails 3.2
The Performance Golden Rule
80% of end-users response time is
downloading all the assets.
The Rails Way to managing
assets:
    HTTP Streaming
   The Assets Pipeline
Assets Pipeline



● Assets concatenation
● Assets minification
● Support for high-level languages
  ○ CoffeeScript
  ○ SASS
● Assets fingerprinting
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;
}
CoffeeScript
class 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
Serving Static Assets




 Rails by default doesn't serve static assets in
           production environment.
Deployment
ict-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
Deployment

                        nginx     ~/ict-ref-cloud/current/public




/tmp/unicorn.ict-ref-cloud.sock




                       unicorn    ~/ict-ref-cloud/current
OWASP Top Ten Security Risk
 1.   Injection
 2.   Cross Site Scripting
 3.   Broken Authentication and Session Management
 4.   Insecure Direct Object Reference
 5.   Cross Site Request Forgery
 6.   Security Misconfiguration
 7.   Insecure Cryptographic Storage
 8.   Failure To Restrict URL Access
 9.   Insufficient Transport Layer Protection
10.   Unvalidated Redirects and Forwards
OWASP Top Ten Security Risk
 1.   Injection
 2.   Cross Site Scripting
 3.   Broken Authentication and Session Management
 4.   Insecure Direct Object Reference
 5.   Cross Site Request Forgery
 6.   Security Misconfiguration
 7.   Insecure Cryptographic Storage
 8.   Failure To Restrict URL Access
 9.   Insufficient Transport Layer Protection
10.   Unvalidated Redirects and Forwards


 Problems related specifically to view layer.
The Rails Way to security:
     CSRF protection
     XSS protection
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">
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 %>
The Rails Way to routing:
  Non-Resourceful routes
    Resourceful routes
   SEO friendly URL's
Non-Resourceful Routes
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

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'
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'
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'
Named Routes



<%= link_to 'Profile', profile_path %>
=> <a href="/profile">Profile</a>

<%= link_to 'Preview', @photo %>
=> <a href="/photo/10">Preview</a>
SEO Friendy URL's
                                                                 <%= link_to product.name, product %>


                                                                       Will generate

                                                                 /products/14-foo-bar
  /products/14-foo-bar                                           "/products/:id" => "products#show"
                                                                 { :id => "14-foo-bar" }
  class Product < ActiveRecord::Base
    def to_param
      "#{id}-#{name.parametrize}"
    end                                                          Product.find(params[:id])

  end
                                                                       Will call to_i
                                                                 Product.find(14)




Example from: http://www.codeschool.com/courses/rails-best-practices
The Rails Way to view
rendering:
   Response rendering
   Structuring Layouts
          AJAX
Response Rendering


/app
                                 /app
    /controllers
                                     /views
        products_controller.rb
                                         /products
        users_controller.rb
                                             index.html.erb
                                             show.html.erb
                                         /users
                                             index.html.erb
                                             new.html.erb
Response Rendering
class UsersController < ApplicationController
    def new
        @user = User.new                        new.html.erb
    end

      def create
          @user = User.new(params[:user])
          if @user.save
              redirect_to :action => :show      show.html.erb
          else
              render :new                       new.html.erb
          end
      end
end
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'
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
Content Negotiation



respond_to do |format|
   format.html
   format.json { render :json => @product }
   format.js
end
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
Structuring Layout


/app
   /views
      /layouts
         application.html.erb (default)
         users.html.erb (UsersController)
         public.html.erb (layout 'public')
Structuring Layout

<!DOCTYPE html>
<head>
   <title>User <%= yield :title %></title>
</head>
<html>
   <body>
      <%= yield %>
   </body>
</html>
Structuring Layout



<% content_for :title, @post.title %>

<div id="post">
   <h2><%= @post.title %></h2>
   <div><%= @post.content %></div>
</div>
View Helpers



module ApplicationHelper
   def title(title)
      content_for :title, title
   end
end
View Helpers



<% title @post.title %>

<div id="post">
   <h2><%= @post.title %></h2>
   <div><%= @post.content %></div>
</div>
Partials


/app
   /views
      /products
         _form.html.erb
         _product.html.erb
         index.html.erb
         edit.html.erb
         new.html.erb
Partials


/app/views/products/_product.html.erb

<div class="product">
   <h2><%= product.name %></h2>
   <p><%= product.description %></p>
</div>

                     Partial parameter
Partials



<h1>Products</h1>

<% @products.each do |product| %>
   <% render 'products/product',
      :product => product %>
<% end %>
Partials



<h1>Products</h1>

<% @products.each do |product| %>
   <% render product %>
<% end %>
Partials




<h1>Products</h1>

<% render @products %>
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 %>
Partials



../products/new.html.erb   ../products/edit.html.erb

<h1>New Product</h1>       <h1>Edit Product</h1>
<div id="form">            <div id="form">
   <% render 'form' %>        <% render 'form' %>
</div>                     </div>
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 %>
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
AJAX




/app/views/products/create.js.erb

$('#form').html("<%= escape_javascript(render 'form') %>");
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();
The Rails Way to caching:
     Basic Caching
      Memoization
Page Caching

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.
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
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'
)
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
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
Memoization


class City < ActiveRecord::Base
   attr_accesible :name, :zip, :lat, :lon

   def display_name
      @display_name ||= "#@zip #@name"
   end
end
The Rails Way to solve
typical problems:
       N+1 Problem
 Fetching object in batches
N+1 Problem
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
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/bullet
Source: http://www.codeschool.com/courses/rails-best-practices
Fetching objects in Java


List<Tweet> tweets = tweetDao.findAllForUser(user);
for (Tweet tweet : tweets) {
    // ...
}

for (Tweet tweet : user.getTweets()) {
   // ...
}
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
Try Rails!

The Rails Way

  • 1.
    The Rails Way Approachto modern web applications with Rails 3.2
  • 2.
    The Performance GoldenRule 80% of end-users response time is downloading all the assets.
  • 3.
    The Rails Wayto managing assets: HTTP Streaming The Assets Pipeline
  • 4.
    Assets Pipeline ● Assetsconcatenation ● Assets minification ● Support for high-level languages ○ CoffeeScript ○ SASS ● Assets fingerprinting
  • 5.
    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; }
  • 6.
    CoffeeScript class ProfileCompetences extendsBackbone.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
  • 7.
    Serving Static Assets Rails by default doesn't serve static assets in production environment.
  • 8.
    Deployment ict-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
  • 9.
    Deployment nginx ~/ict-ref-cloud/current/public /tmp/unicorn.ict-ref-cloud.sock unicorn ~/ict-ref-cloud/current
  • 10.
    OWASP Top TenSecurity Risk 1. Injection 2. Cross Site Scripting 3. Broken Authentication and Session Management 4. Insecure Direct Object Reference 5. Cross Site Request Forgery 6. Security Misconfiguration 7. Insecure Cryptographic Storage 8. Failure To Restrict URL Access 9. Insufficient Transport Layer Protection 10. Unvalidated Redirects and Forwards
  • 11.
    OWASP Top TenSecurity Risk 1. Injection 2. Cross Site Scripting 3. Broken Authentication and Session Management 4. Insecure Direct Object Reference 5. Cross Site Request Forgery 6. Security Misconfiguration 7. Insecure Cryptographic Storage 8. Failure To Restrict URL Access 9. Insufficient Transport Layer Protection 10. Unvalidated Redirects and Forwards Problems related specifically to view layer.
  • 12.
    The Rails Wayto security: CSRF protection XSS protection
  • 13.
    Cross Site RequestForgery /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">
  • 14.
    Cross Site Scripting <divid="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 %>
  • 15.
    The Rails Wayto routing: Non-Resourceful routes Resourceful routes SEO friendly URL's
  • 16.
    Non-Resourceful Routes 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
  • 17.
    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'
  • 18.
    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'
  • 19.
    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'
  • 20.
    Named Routes <%= link_to'Profile', profile_path %> => <a href="/profile">Profile</a> <%= link_to 'Preview', @photo %> => <a href="/photo/10">Preview</a>
  • 21.
    SEO Friendy URL's <%= link_to product.name, product %> Will generate /products/14-foo-bar /products/14-foo-bar "/products/:id" => "products#show" { :id => "14-foo-bar" } class Product < ActiveRecord::Base def to_param "#{id}-#{name.parametrize}" end Product.find(params[:id]) end Will call to_i Product.find(14) Example from: http://www.codeschool.com/courses/rails-best-practices
  • 22.
    The Rails Wayto view rendering: Response rendering Structuring Layouts AJAX
  • 23.
    Response Rendering /app /app /controllers /views products_controller.rb /products users_controller.rb index.html.erb show.html.erb /users index.html.erb new.html.erb
  • 24.
    Response Rendering class UsersController< ApplicationController def new @user = User.new new.html.erb end def create @user = User.new(params[:user]) if @user.save redirect_to :action => :show show.html.erb else render :new new.html.erb end end end
  • 25.
    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'
  • 26.
    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
  • 27.
    Content Negotiation respond_to do|format| format.html format.json { render :json => @product } format.js end
  • 28.
    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
  • 29.
    Structuring Layout /app /views /layouts application.html.erb (default) users.html.erb (UsersController) public.html.erb (layout 'public')
  • 30.
    Structuring Layout <!DOCTYPE html> <head> <title>User <%= yield :title %></title> </head> <html> <body> <%= yield %> </body> </html>
  • 31.
    Structuring Layout <% content_for:title, @post.title %> <div id="post"> <h2><%= @post.title %></h2> <div><%= @post.content %></div> </div>
  • 32.
    View Helpers module ApplicationHelper def title(title) content_for :title, title end end
  • 33.
    View Helpers <% title@post.title %> <div id="post"> <h2><%= @post.title %></h2> <div><%= @post.content %></div> </div>
  • 34.
    Partials /app /views /products _form.html.erb _product.html.erb index.html.erb edit.html.erb new.html.erb
  • 35.
    Partials /app/views/products/_product.html.erb <div class="product"> <h2><%= product.name %></h2> <p><%= product.description %></p> </div> Partial parameter
  • 36.
    Partials <h1>Products</h1> <% @products.each do|product| %> <% render 'products/product', :product => product %> <% end %>
  • 37.
    Partials <h1>Products</h1> <% @products.each do|product| %> <% render product %> <% end %>
  • 38.
  • 39.
    Partials /app/views/products/_form.html.erb <%= form_for @userdo |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 %>
  • 40.
    Partials ../products/new.html.erb ../products/edit.html.erb <h1>New Product</h1> <h1>Edit Product</h1> <div id="form"> <div id="form"> <% render 'form' %> <% render 'form' %> </div> </div>
  • 41.
    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 %>
  • 42.
    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
  • 43.
  • 44.
    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();
  • 45.
    The Rails Wayto caching: Basic Caching Memoization
  • 46.
    Page Caching 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.
  • 47.
    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
  • 48.
    Fragment Caching <% cachedo %> 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' )
  • 49.
    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
  • 50.
    Conditional GET support classProductsController < 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
  • 51.
    Memoization class City <ActiveRecord::Base attr_accesible :name, :zip, :lat, :lon def display_name @display_name ||= "#@zip #@name" end end
  • 52.
    The Rails Wayto solve typical problems: N+1 Problem Fetching object in batches
  • 53.
    N+1 Problem 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
  • 54.
    N+1 Problem 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/bullet Source: http://www.codeschool.com/courses/rails-best-practices
  • 55.
    Fetching objects inJava List<Tweet> tweets = tweetDao.findAllForUser(user); for (Tweet tweet : tweets) { // ... } for (Tweet tweet : user.getTweets()) { // ... }
  • 56.
    Fetching objects inRails 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
  • 57.