0% found this document useful (0 votes)
35 views

Beautiful Code in Rails 3: Gregg Pollack

1. The document discusses new features in Rails 3 including starting a new app, the new router API, ActionController's respond_with method, updated ActionMailer syntax, and ActiveRelation which replaces ad-hoc queries with relational algebra. 2. The new router API uses a block syntax for defining routes rather than the old map method. It also simplifies member and collection routes. 3. ActionController's respond_with automatically formats the response based on requested format like HTML or XML. ActionMailer also received syntax updates like default from addresses and attachment support.

Uploaded by

Fernando Viadero
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
35 views

Beautiful Code in Rails 3: Gregg Pollack

1. The document discusses new features in Rails 3 including starting a new app, the new router API, ActionController's respond_with method, updated ActionMailer syntax, and ActiveRelation which replaces ad-hoc queries with relational algebra. 2. The new router API uses a block syntax for defining routes rather than the old map method. It also simplifies member and collection routes. 3. ActionController's respond_with automatically formats the response based on requested format like HTML or XML. ActionMailer also received syntax updates like default from addresses and attachment support.

Uploaded by

Fernando Viadero
Copyright
© Attribution Non-Commercial (BY-NC)
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 45

Beautiful Code in Rails 3

by
Gregg Pollack

Starting a new app


New Router API
ActionController - respond_with
ActionMailer Syntax
ActiveRelation (arel)
ERB Strings Escaped
Unobtrusive Javascript
Starting a New App
$ rails
Usage:
  rails APP_PATH [options]

Options:
‐O, [‐‐skip‐activerecord]  # Skip ActiveRecord files
  ‐r, [‐‐ruby=PATH]          # Path to the Ruby binary of your choice
                             # Default: /Users/greggpollack/.rvm/rubies/ruby‐
  ‐T, [‐‐skip‐testunit]      # Skip TestUnit files
      [‐‐dev]                # Setup the application with Gemfile pointing
                               to your rails checkout
‐J, [‐‐skip‐prototype]     # Skip Prototype files
  ‐G, [‐‐skip‐git]           # Skip Git ignores and keeps
  ‐m, [‐‐template=TEMPLATE]  # Path to an application template 
  ‐d, [‐‐database=DATABASE]  # Preconfigure for selected database 
      [‐‐edge]               # Setup the application with Gemfile
                             # pointing to Rails repository
 
$ rails test_app
      create   $ ls script/
      create  README
      create  .gitignore
  
      ...
$ cd test_app/
  rails
$ rails
Usage: rails COMMAND [ARGS]

The most common rails commands are:
 generate    Generate new code (short‐cut alias: "g")
c
console     Start the Rails console (short‐cut alias: " ")
s
server      Start the Rails server (short‐cut alias: " ")
dbconsole   Start a console for the database specified in config/database.yml
            (short‐cut alias: "db")
In addition to those, there are:
 application  Generate the Rails application code
 destroy      Undo code generated with "generate"
 benchmarker  See how fast a piece of code runs
 profiler     Get profile information from a piece of code
 plugin       Install a plugin
 runner       Run a piece of code in the application environment

All commands can be run with ‐h for more information.
old scripts new hotness

script/generate rails g

script/console rails c

script/server rails s

script/dbconsole rails db
old scripts new hotness

script/generate r g

script/console r c

script/server r s

script/dbconsole r db

alias r='rails'
New Router API
New Routing API

config/routes.rb
TestApp::Application.routes.draw do |map|

map.resources :posts

end
New Routing API

config/routes.rb
TestApp::Application.routes.draw do |map|

resources :posts

end
New Routing API
Rails 2
map.resources :posts do |post|
post.resources :comments
end

Rails 3
resources :posts do
resources :comments
end
New Routing API
Rails 2
map.resources :posts, :member => { :confirm => :post, :notify => :post } do |post|
post.resources :comments, :member => { :preview => :post }, :collection => { :archived => :get }
end

Rails 3
resources :posts do
member do Rails 3
post :confirm
resources :posts do
get :notify
member do
end
post :confirm
get :notify
resources :comments do
end
member do
post :preview
resources :comments do
end
post :preview, :on => :member
get :archived, :on => :collection
collection do
end
get :archived
end
end
end
end
Rails 2
map.connect 'login', :controller => 'session', :action => 'new'

Rails 3
match 'login' => 'session#new'

Named Route login_path


Rails 2
map.login 'login', :controller => 'session', :action => 'new'

Rails 3
match 'login' => 'session#new', :as => :login
New Routing API
Rails 2
map.root :controller => "users"

Rails 3
root :to => "users#index"

Legacy Route Rails 2


map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'

Rails 3
match ':controller(/:action(/:id(.:format)))'

(commented out by default)


For more information
http://guides.rails.info/routing.html
Beautiful Code in Rails 3

Starting a new app


New Router API
ActionController - respond_with
ActionMailer Syntax
ActiveRelation (arel)
ERB Strings Escaped
Unobtrusive Javascript
#Rails3OMGPonies!
New ActionController Syntax
Regular Syntax
class UsersController < ApplicationController

def index
@users = User.all

respond_to do |format|
format.html
format.xml { render :xml => @users.to_xml }
end
end

def show
@user = User.find(params[:id])

respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @user }
end
end
...
New ActionController Syntax
Improved Syntax
class UsersController < ApplicationController
respond_to :html, :xml, :json

def index
@users = User.all
respond_with(@users)
end

def show
@user = User.find(params[:id])
respond_with(@user)
end
...
Mikel L i n ds aa r
ActionMailer
New ActionMailer Syntax

Rails 2
$script/generate mailer UserMailer welcome forgot_password

create  app/models/user_mailer.rb

Rails 3
$r g mailer UserMailer welcome forgot_password

create  app/mailers/user_mailer.rb
New ActionMailer Syntax

Rails 2
def welcome(user, subdomain)
subject 'Welcome to TestApp'
recipients user.email
from '[email protected]'

body :user => user, :subdomain => subdomain


end

UserMailer.deliver_welcome(user, subdomain)

Rails 3
def welcome(user, subdomain)
@user = user
@subdomain = subdomain

mail(:from => "[email protected]",


:to => user.email,
:subject => "Welcome to TestApp")
end

UserMailer.welcome(user, subdomain).deliver
New ActionMailer Syntax

Rails 3
class UserMailer < ActionMailer::Base

default :from => "[email protected]"

def welcome(user, subdomain)


@user = user
@subdomain = subdomain

attachments['test.pdf'] = File.read("#{Rails.root}/public/test.pdf")

mail(:to => @user.email, :subject => "Welcome to TestApp") do |format|


format.html { render 'other_html_welcome' }
format.text { render 'other_text_welcome' }
end
end

end

welcome.text.erb
Defaults welcome.html.erb
Nic k K al l e n
ActiveRelation

replaces the internal ad-hoc query generation with


query generation based on relational algebra.
ActiveRelation
Rails 2
@posts = Post.find(:all, :conditions => {:published => true})

immediately queries the db


returns an Array of Posts

Rails 3
@posts = Post.where(:published => true)

doesn’t query the db


returns an ActiveRecord::Relation
ActiveRelation

@posts = Post.where(:published => true)

if params[:order]
@posts = @posts.order(params[:order])
end

@posts.each do |p|
...
Que
end ry r
uns
here
Lazy Loading
We can refactor!
@posts = Post.where(:published => true)

if params[:order]
@posts = @posts.order(params[:order])
end

@posts = Post.where(:published => true)

@posts = @posts.order(params[:order])

@posts = Post.where(:published => true).order(params[:order])


ActiveRelations can be Shared
@posts = Post.where(:published => true).order(params[:order])

posts = Post.order(params[:order])

@published = posts.where(:published => true)


@unpublished = posts.where(:published => false)

This is obviously a bad example (should be using named routes)

@published = Post.published
@unpublished = Post.unpublished
ActiveRelation
@published = Post.published
@unpublished = Post.unpublished

Rails 2
class Post < ActiveRecord::Base
default_scope :order => 'title'

named_scope :published, :conditions => {:published => true}


named_scope :unpublished, :conditions => {:published => false}
end

Rails 3
class Post < ActiveRecord::Base
default_scope order('title')

scope :published, where(:published => true)


scope :unpublished, where(:published => false)
end
ActiveRelation
New Finder Methods
where(:conditions)
having(:conditions)
select
group
order
limit
offset
joins
includes(:include)
lock
readonly
from
ActiveRelation

Rails 2
Post.find(:all, :conditions => {:author => "Joe"}, :includes => :comments,
:order => "title", :limit => 10)

Rails 3
Post.where(:author => "Joe").include(:comments).order(:title).limit(10)

Remember, this version doesn’t do the query immediately


Beautiful Code in Rails 3

Starting a new app


New Router API
ActionController - respond_with
ActionMailer Syntax
ActiveRelation (arel)
ERB Strings Escaped
Unobtrusive Javascript
#Rails3OMGPonies!
Use of external libraries

ActiveRecord ActiveRelation

ActionView Erubis

Erubis is a fast, secure, and very extensible implementation of ERB


Cross-Site Scripting (XSS)

Rails 2 Rails 2
<%= @post.body %> <%= h @post.body %>

(unsafe) (safe)

Rails 3 Rails 3
<%= raw @post.body %> <%= @post.body %>

(unsafe) (safe)
Adopting Unobtrusive Javascript

HTML 5 custom data attributes data-*


Custom data attributes are intended to store custom data private to the page or
application, for which there are no more appropriate attributes or elements

data-remote

data-method

data-confirm

data-disable-with
Adopting Unobtrusive Javascript

Rails 2
<%= link_to_remote 'Show', :url => post %>

<a href="#" onclick="new Ajax.Request('/posts/1', {asynchronous:true,


evalScripts:true, parameters:'authenticity_token=' +
encodeURIComponent('9sk..44d')}); return false;">Show</a>

Rails 3
<%= link_to 'Show', post, :remote => true %>

<a href="/posts/1" data-remote="true">Show</a>


Adopting Unobtrusive Javascript

Rails 2
<% remote_form_for(@post) do |f| %>

<form action="/posts" class="new_post" id="new_post" method="post"


onsubmit="new Ajax.Request('/posts', {asynchronous:true,
evalScripts:true, parameters:Form.serialize(this)}); return false;">

Rails 3
<% form_for(@post, :remote => true) do |f| %>

<form action="/posts" class="new_post" data-remote="true" id="new_post" method="post">


Adopting Unobtrusive Javascript

Rails 2 Rails 3
<%= link_to 'Destroy', post, :method => :delete %>

<a href="/posts/1" onclick="var f = document.createElement('form'); f.style.display = 'none';


this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m =
document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method');
m.setAttribute('value', 'delete'); f.appendChild(m);var s = document.createElement('input');
s.setAttribute('type', 'hidden'); s.setAttribute('name', 'authenticity_token'); s.setAttribute('value',
'9skdJ0k+l9/q3PWToz6MtfyiB2gcyhnKubeGV6WFL44='); f.appendChild(s);f.submit();return false;">Destroy</a>

<a href="/posts/1" data-method="delete" rel="nofollow">Destroy</a>


Adopting Unobtrusive Javascript

Rails 2 Rails 3
<%= link_to 'Destroy', post, :confirm => 'Are you sure?', :method => :delete %>

<a href="/posts/1" onclick="if (confirm('Are you sure?')) { var f = document.createElement('form');


f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'POST'; f.action = this.href;var m =
document.createElement('input'); m.setAttribute('type', 'hidden'); m.setAttribute('name', '_method');
m.setAttribute('value', 'delete'); f.appendChild(m);var s = document.createElement('input');
s.setAttribute('type', 'hidden'); s.setAttribute('name', 'authenticity_token'); s.setAttribute('value',
'9skdJ0k+l9/q3PWToz6MtfyiB2gcyhnKubeGV6WFL44='); f.appendChild(s);f.submit(); };return false;">Destroy</a>

<a href="/posts/1" data-confirm="Are you sure?" data-method="delete" rel="nofollow">Destroy</a>


Adopting Unobtrusive Javascript

Rails 2
<%= f.submit 'Create Post', :disable_with => "Please wait..." %>

<input id="post_submit" name="commit" onclick="if (window.hiddenCommit)


{ window.hiddenCommit.setAttribute('value', this.value); }else { hiddenCommit =
document.createElement('input');hiddenCommit.type = 'hidden';hiddenCommit.value =
this.value;hiddenCommit.name =
this.name;this.form.appendChild(hiddenCommit); }this.setAttribute('originalValue',
this.value);this.disabled = true;this.value='Please wait...';result =
(this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) :
this.form.submit());if (result == false) { this.value =
this.getAttribute('originalValue');this.disabled = false; }return result;" type="submit"
value="Create Post" />

Rails 3
<%= f.submit :disable_with => "Please wait..." %>

<input data-disable-with="Please wait..."


id="post_submit" name="commit" type="submit" value="Create Post" />

Notice, the button label


has an intellegent default
Adopting Unobtrusive Javascript

HTML 5 custom data attributes

data-remote

data-method

data-confirm

data-disable-with
/public/stylesheets/rails.js
document.observe("dom:loaded", function() {

$(document.body).observe("click", function(event) {

var message = event.element().readAttribute('data-confirm');


if (message) {
// ... Do a confirm box
}

var element = event.findElement("a[data-remote=true]");


if (element) {
// ... Do the AJAX call
}

var element = event.findElement("a[data-method]");


if (element) {
// ... Create a form
}
});
jQuery in Rails?
http://github.com/rails/jquery-ujs

$('a[data-confirm],input[data-confirm]').live('click', function () {
// ... Do a confirm box
});

$('form[data-remote="true"]').live('submit', function (e) {


// ... Do an AJAX call
});
Deprecated Methods
link_to_remote
remote_form_for
observe_field
observe_form
form_remote_tag
button_to_remote
submit_to_remote
link_to_function
periodically_call_remote

prototype_legacy_helper
http://github.com/rails/prototype_legacy_helper
Beautiful Code in Rails 3

Starting a new app


New Router API
ActionController - respond_with
ActionMailer Syntax
ActiveRelation (arel)
ERB Strings Escaped
Unobtrusive Javascript
#Rails3OMGPonies!
Missing (that
APIs I didn’t have time for)

Bundler
http://railscasts.com/episodes/201-bundler

Making Generators with Thor


http://bit.ly/rails3generators
Creative Commons
name author URL
rainbow of 80s toys merwing✿little dear http://www.flickr.com/photos/merwing/2152164258/

Old Loc Merlijn Hoek http://www.flickr.com/photos/merlijnhoek/1040997599/

Notting Hill Gate Eole http://www.flickr.com/photos/eole/942309733/

Britomart Train Station EssjayNZ http://www.flickr.com/photos/essjay/260511465/

Das Licht Small http://www.flickr.com/photos/small/62713023/

Metro Genova opti mystic http://www.flickr.com/photos/miiilio/2503634282/

Immobility Dilemna gilderic http://www.flickr.com/photos/gilderic/3528157964/

train station nolifebeforecoffee http://www.flickr.com/photos/nolifebeforecoffee/1803584805/

Mystical station Jsome1 http://www.flickr.com/photos/jsome1/2226394415/

Railswaystation Pieter Musterd http://www.flickr.com/photos/piet_musterd/2233025691/

The Handover MarkyBon http://www.flickr.com/photos/markybon/152769885/

EN57 magro_kr http://www.flickr.com/photos/iks_berto/1328682171/


Presentation by: If you need help with a Rails 3
project, feel free to give us a call

http://envylabs.com
Come to our Rails 3 training at
Railsconf 2010
Gregg Pollack
http://bit.ly/rails3ropes
407-754-5517
[email protected]
We’re also available to run Rails 3
Training at your company, email:
Ruby5 Podcast [email protected]
http://ruby5.envylabs.com

You might also like