57
The Rails Way Approach to modern web applications with Rails 3.2

The Rails Way

Embed Size (px)

Citation preview

Page 1: The Rails Way

The Rails WayApproach to modern web applications with

Rails 3.2

Page 2: The Rails Way

80% of end-users response time is downloading all the assets.

The Performance Golden Rule

Page 3: The Rails Way

The Rails Way to managing assets:

HTTP StreamingThe Assets Pipeline

Page 4: The Rails Way

Assets Pipeline

● Assets concatenation● Assets minification● Support for high-level languages

○ CoffeeScript○ SASS

● Assets fingerprinting

Page 5: The Rails Way

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

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

Serving Static Assets

Rails by default doesn't serve static assets in production environment.

Page 8: The Rails Way

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

Deployment

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

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

unicorn ~/ict-ref-cloud/current

Page 10: The Rails Way

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

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

The Rails Way to security:

CSRF protectionXSS protection

Page 13: The Rails Way

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

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

The Rails Way to routing:

Non-Resourceful routesResourceful routesSEO friendly URL's

Page 16: The Rails Way

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

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

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

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

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

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

The Rails Way to view rendering:

Response renderingStructuring Layouts

AJAX

Page 23: The Rails Way

/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

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

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

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

Content Negotiation

respond_to do |format|

format.html

format.json { render :json => @product }

format.js

end

Page 28: The Rails Way

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

Structuring Layout

/app

/views

/layouts

application.html.erb (default)

users.html.erb (UsersController)

public.html.erb (layout 'public')

Page 30: The Rails Way

Structuring Layout

<!DOCTYPE html>

<head>

<title>User <%= yield :title %></title>

</head>

<html>

<body>

<%= yield %>

</body>

</html>

Page 31: The Rails Way

Structuring Layout

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

<div id="post">

<h2><%= @post.title %></h2>

<div><%= @post.content %></div>

</div>

Page 32: The Rails Way

View Helpers

module ApplicationHelper

def title(title)

content_for :title, title

end

end

Page 33: The Rails Way

View Helpers

<% title @post.title %>

<div id="post">

<h2><%= @post.title %></h2>

<div><%= @post.content %></div>

</div>

Page 34: The Rails Way

Partials

/app

/views

/products

_form.html.erb

_product.html.erb

index.html.erb

edit.html.erb

new.html.erb

Page 35: The Rails Way

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

Partials

<h1>Products</h1>

<% @products.each do |product| %>

<% render 'products/product',

:product => product %>

<% end %>

Page 37: The Rails Way

Partials

<h1>Products</h1>

<% @products.each do |product| %>

<% render product %>

<% end %>

Page 38: The Rails Way

Partials

<h1>Products</h1>

<% render @products %>

Page 39: The Rails Way

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

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

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

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

AJAX

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

$('#form').html("<%= escape_javascript(render 'form') %>");

Page 44: The Rails Way

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

The Rails Way to caching:

Basic CachingMemoization

Page 46: The Rails Way

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

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

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

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

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

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

The Rails Way to solve typical problems:

N+1 ProblemFetching object in batches

Page 53: The Rails Way

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

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

Fetching objects in Java

List<Tweet> tweets = tweetDao.findAllForUser(user);

for (Tweet tweet : tweets) {

// ...

}

for (Tweet tweet : user.getTweets()) {

// ...

}

Page 56: The Rails Way

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

Try Rails!