The document discusses the architecture and advantages of Rails 3. It describes how Rails 3 applications are organized as Rack applications and are more modular than previous versions. Key advantages mentioned include improved performance, being more framework-agnostic, and increased modularity.
2. Rails 3
ID blog twitter
Hugo Baraúna blog.plataformatec.com @hugobarauna
3. Quem sou eu?
• Hugo Baraúna
• 24 anos
• Engenharia de Computação na Politécnica da USP
• Desenvolvedor Ruby e Rails há mais de 3 anos
• Co-fundador e engenheiro da Plataforma Tecnologia
Hugo Baraúna blog.plataformatec.com @hugobarauna
7. Arquitetura
Todo o resto são
Rails 3 Railties!
Rails
ActionDispatch
ActiveSupport
Hugo Baraúna blog.plataformatec.com @hugobarauna
8. Arquitetura
Todo o resto são
Rails 3 Railties!
ActiveRecord
Rails
ActionView
ActionDispatch
outros...
ActiveSupport
ActionController
ActionMailer
Hugo Baraúna blog.plataformatec.com @hugobarauna
15. blog/config.ru
require ::File.expand_path('../config/environment', __FILE__)
run Blog::Application
Hugo Baraúna blog.plataformatec.com @hugobarauna
16. blog/config.ru
require ::File.expand_path('../config/environment', __FILE__)
run Blog::Application
Hugo Baraúna blog.plataformatec.com @hugobarauna
17. blog/config/application.rb
require File.expand_path('../boot', __FILE__)
require 'rails/all'
Bundler.require(:default, Rails.env) if defined?(Bundler)
module Blog
class Application < Rails::Application
config.encoding = "utf-8"
config.filter_parameters += [:password]
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
18. blog/config/application.rb
require File.expand_path('../boot', __FILE__)
require 'rails/all'
Bundler.require(:default, Rails.env) if defined?(Bundler)
module Blog uma Rack App!
class Application < Rails::Application
config.encoding = "utf-8"
config.filter_parameters += [:password]
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
19. blog/config/application.rb
require File.expand_path('../boot', __FILE__)
require 'rails/all'
Bundler.require(:default, Rails.env) if defined?(Bundler)
module Blog
class Application < Rails::Application
config.encoding = "utf-8"
config.filter_parameters += [:password]
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
20. blog/config/application.rb
require File.expand_path('../boot', __FILE__)
require 'rails/all'
Bundler.require(:default, Rails.env) if defined?(Bundler)
module Blog
class Application < Rails::Application
config.encoding = "utf-8"
config.filter_parameters += [:password]
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
21. blog/config/boot.rb
require 'rubygems'
# Set up gems listed in the Gemfile.
gemfile = File.expand_path('../../Gemfile', __FILE__)
begin
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
Bundler.setup
rescue Bundler::GemNotFound => e
STDERR.puts e.message
STDERR.puts "Try running `bundle install`."
exit!
end if File.exist?(gemfile)
Hugo Baraúna blog.plataformatec.com @hugobarauna
22. blog/config/boot.rb
require 'rubygems'
# Set up gems listed in the Gemfile.
gemfile = File.expand_path('../../Gemfile', __FILE__)
begin
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
Bundler.setup
rescue Bundler::GemNotFound => e
STDERR.puts e.message
STDERR.puts "Try running `bundle install`."
exit!
end if File.exist?(gemfile)
Hugo Baraúna blog.plataformatec.com @hugobarauna
55. Named routes
Rails 2.3
map.purchase "products/:id/purchase", :controller => "catalog",
:action => "purchase"
Rails 3
match "products/:id/purchase" => 'catalog#purchase',
:as => :purchase
Hugo Baraúna blog.plataformatec.com @hugobarauna
56. Resources com member e
collection
Rails 2.3
map.resources :products, :member => { :short => :get,
:toggle => :post }, :collection => { :sold => :get }
resources :products do
Rails 3 member do
get :short
post :toggle
end
collection do
get :sold
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
57. Router and Rack FTW!
Hugo Baraúna blog.plataformatec.com @hugobarauna
59. Rack FTW!
match "posts/:echo" => "posts#show"
Hugo Baraúna blog.plataformatec.com @hugobarauna
60. Rack FTW!
match "posts/:echo" => "posts#show"
uma Rack App!
match "posts/:echo" => PostsController.action(:show)
Hugo Baraúna blog.plataformatec.com @hugobarauna
61. Rack FTW!
match "posts/:echo" => "posts#show"
match "posts/:echo" => PostsController.action(:show)
uma Rack App!
match "posts/:echo" => lambda { |env| [ 200, {“Content-Type” =>
“plain/text”}, ["Echo!"] ] }
Hugo Baraúna blog.plataformatec.com @hugobarauna
62. Rack FTW!
match "posts/:echo" => "posts#show"
match "posts/:echo" => PostsController.action(:show)
match "posts/:echo" => lambda { |env| [ 200, {“Content-Type” =>
“plain/text”}, ["Echo!"] ] }
uma Rack App!
match "posts/:echo" => MySinatraBlog
Hugo Baraúna blog.plataformatec.com @hugobarauna
65. ActionMailer new API
guru/code$ rails g mailer Notifier signup_notification
create app/mailers/notifier.rb
invoke erb
create app/views/notifier
create app/views/notifier/signup_notification.text.erb
invoke test_unit
create test/functional/notifier_test.rb
guru/code$ ls -lp app/
controllers/
helpers/
mailers/
models/
views/
Hugo Baraúna blog.plataformatec.com @hugobarauna
66. ActionMailer new API
guru/code$ rails g mailer Notifier signup_notification
create app/mailers/notifier.rb
invoke erb
create app/views/notifier
create app/views/notifier/signup_notification.text.erb
invoke test_unit
create test/functional/notifier_test.rb
guru/code$ ls -lp app/
controllers/
helpers/
mailers com diretório próprio
mailers/
models/
views/
Hugo Baraúna blog.plataformatec.com @hugobarauna
67. ActionMailer new API
class Notifier < ActionMailer::Base
default :from => "system@example.com"
def signup_notification(recipient)
@account = recipient
attachments['image.jpg'] = File.read
("image.jpg")
mail(:to => recipient.email) do |format|
format.html
format.text
end
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
68. ActionMailer new API
class Notifier < ActionMailer::Base
Variáveis default :from => "system@example.com"
de
instância def signup_notification(recipient)
@account = recipient
attachments['image.jpg'] = File.read
("image.jpg")
mail(:to => recipient.email) do |format|
format.html
format.text
end
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
69. ActionMailer new API
class Notifier < ActionMailer::Base
Variáveis default :from => "system@example.com"
de
instância def signup_notification(recipient)
@account = recipient Attachments tipo
attachments['image.jpg'] = File.read cookies
("image.jpg")
mail(:to => recipient.email) do |format|
format.html
format.text
end
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
70. ActionMailer new API
class Notifier < ActionMailer::Base
Variáveis default :from => "system@example.com"
de
instância def signup_notification(recipient)
@account = recipient Attachments tipo
attachments['image.jpg'] = File.read cookies
("image.jpg")
mail(:to => recipient.email) do |format|
format.html
format.text
end
end mail tipo respond_to do |format|
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
71. AbstractController::Base
ActionController::Metal
ActionMailer::Base ActionController::Base
Hugo Baraúna blog.plataformatec.com @hugobarauna
75. ActiveModel
• Google Summer of Code 2009:
Hugo Baraúna blog.plataformatec.com @hugobarauna
76. ActiveModel
• Google Summer of Code 2009:
• Extraira lógica comum entre ActiveRecord e
ActiveResource
Hugo Baraúna blog.plataformatec.com @hugobarauna
77. ActiveModel
• Google Summer of Code 2009:
• Extraira lógica comum entre ActiveRecord e
ActiveResource
• Hoje
Hugo Baraúna blog.plataformatec.com @hugobarauna
78. ActiveModel
• Google Summer of Code 2009:
• Extraira lógica comum entre ActiveRecord e
ActiveResource
• Hoje
• Desempenha papel no agnosticismo de ORM
Hugo Baraúna blog.plataformatec.com @hugobarauna
79. ActiveModel
• Google Summer of Code 2009:
• Extraira lógica comum entre ActiveRecord e
ActiveResource
• Hoje
• Desempenha papel no agnosticismo de ORM
• Permite a criação de models à la ActiveRecord
Hugo Baraúna blog.plataformatec.com @hugobarauna
80. ActiveResource::Base +
ActiveModel
module ActiveResource
...
class Base
extend ActiveModel::Naming
include CustomMethods, Observing, Validations
include ActiveModel::Conversion
include ActiveModel::Serializers::JSON
include ActiveModel::Serializers::Xml
end
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
81. ActiveRecord::Base +
ActiveModel
Base.class_eval do
...
extend ActiveModel::Naming
...
include ActiveModel::Conversion
include Validations
...
include Callbacks, ActiveModel::Observing, Timestamp
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
83. Agnosticismo de ORM
Agnosticismo de ORM
ActiveModel Rails::Railtie
Hugo Baraúna blog.plataformatec.com @hugobarauna
84. Agnosticismo de ORM
Agnosticismo de ORM
ActiveModel Rails::Railtie
Provê uma API para que o
ActionPack possa conversar com o
ORM
Hugo Baraúna blog.plataformatec.com @hugobarauna
85. Agnosticismo de ORM
Agnosticismo de ORM
ActiveModel Rails::Railtie
Provê uma API para que o
Integração do ORM com o Rails
ActionPack possa conversar com o
ORM
Hugo Baraúna blog.plataformatec.com @hugobarauna
87. module ActiveModel
module Lint
module Tests
def test_to_key
assert model.respond_to?(:to_key), "The model should respond to to_key"
def model.persisted?() false end
assert model.to_key.nil?
end
def test_to_param
assert model.respond_to?(:to_param), "The model should respond to to_param"
def model.persisted?() false end
assert model.to_param.nil?
end
def test_valid?
assert model.respond_to?(:valid?), "The model should respond to valid?"
assert_boolean model.valid?, "valid?"
end
...
def test_persisted?
assert model.respond_to?(:persisted?), "The model should respond to persisted?"
assert_boolean model.persisted?, "persisted?"
end
def test_errors_aref
assert model.respond_to?(:errors), "The model should respond to errors"
assert model.errors[:hello].is_a?(Array), "errors#[] should return an Array"
end
def test_errors_full_messages
assert model.respond_to?(:errors), "The model should respond to errors"
assert model.errors.full_messages.is_a?(Array), "errors#full_messages should return an Array"
end
end
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
88. def test_to_param
assert model.respond_to?(:to_param), "The model should
respond to to_param"
def model.persisted?() false end
assert model.to_param.nil?
end
def test_valid?
assert model.respond_to?(:valid?), "The model should
respond to valid?"
assert_boolean model.valid?, "valid?"
end
...
def test_errors_aref
assert model.respond_to?(:errors), "The model should
respond to errors"
assert model.errors[:hello].is_a?(Array), "errors#[]
should return an Array"
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
89. ActiveRecord-like
Ótimo exemplo de uso do ActiveModel
http://github.com/plataformatec/mail_form
Hugo Baraúna blog.plataformatec.com @hugobarauna
93. RAILS 2.3
def index
@users = User.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @users }
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
94. RAILS 2.3
def index
@users = User.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => @users }
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
95. RAILS 3.0
respond_to :html, :xml
def index
@users = User.all
respond_with(@users)
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
96. RAILS 3.0
respond_to :html, :xml
def index
@users = User.all
respond_with(@users)
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
97. Navigational API
GET
POST
PUT
DELETE
Hugo Baraúna blog.plataformatec.com @hugobarauna
98. Navigational API
GET
POST
PUT
DELETE
Hugo Baraúna blog.plataformatec.com @hugobarauna
99. Navigational API
GET
POST
PUT
DELETE
Hugo Baraúna blog.plataformatec.com @hugobarauna
100. respond_to :html, :xml
def index
@users = User.all
respond_with(@users)
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
101. Navigational API
render
GET render template
collection.to_format
POST
PUT
DELETE
Hugo Baraúna blog.plataformatec.com @hugobarauna
102. RAILS 2.3
def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
format.html { redirect_to @user, :notice => 'User was
successfully created' }
format.xml { render :xml => @user, :status
=> :created, :location => @user }
else
format.html { render :action => "new" }
format.xml { render :xml => @user.errors, :status
=> :unprocessable_entity }
end
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
103. RAILS 3.0
def create
@user = User.new(params[:user])
flash[:notice] = 'User was successfully created' if @user.save
respond_with(@user)
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
104. Navigational API
render
GET render template
collection.to_format
render
Success redirect_to resource
resource.to_format
POST
Failure render :new render resource.errors
PUT
DELETE
Hugo Baraúna blog.plataformatec.com @hugobarauna
105. Navigational API
render
GET render template
collection.to_format
render
Success redirect_to resource
resource.to_format
POST
Failure render :new render resource.errors
Success redirect_to resource head :ok
PUT
Failure render :edit render resource.errors
DELETE redirect_to collection head :ok
Hugo Baraúna blog.plataformatec.com @hugobarauna
106. respond_with(@users)
ActionController::Responder
Hugo Baraúna blog.plataformatec.com @hugobarauna
107. respond_with(@users)
ActionController::Responder
table.to_code
Navigational API
GET render template render
collection.to_format
Success redirect_to resource render
resource.to_format
POST
Failure render :new render resource.errors
Success redirect_to resource head :ok
PUT
Failure render :edit render resource.errors
DELETE redirect_to collection head :ok
Hugo Baraúna blog.plataformatec.com @hugobarauna
112. github.com/plataformatec/responders
FlashResponder: seta o flash baseado na action do
controller e no status do recurso
HttpCacheResponder: automaticamente adiciona o
cabeçalho HTTP Last-Modified para requests de API
format
Hugo Baraúna blog.plataformatec.com @hugobarauna
113. CONFIGURANDO O RESPONDERS
# lib/application_responder.rb
class ApplicationResponder < ActionController::Responder
include Responders::FlashResponder
include Responders::HttpCacheResponder
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
114. CONFIGURANDO O RESPONDERS
# lib/application_responder.rb
class ApplicationResponder < ActionController::Responder
include Responders::FlashResponder
include Responders::HttpCacheResponder
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
self.responder = ApplicationResponder
respond_to :html
protect_from_forgery
layout 'application'
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
115. SEM O RESPONDERS
# app/controllers/users_controller.rb
def create
@user = User.new(params[:user])
flash[:notice] = 'User was successfully created' if @user.save
respond_with(@user)
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
116. SEM O RESPONDERS
# app/controllers/users_controller.rb
def create
@user = User.new(params[:user])
flash[:notice] = 'User was successfully created' if @user.save
respond_with(@user)
end
O FlashResponder vai
fazer isso por mim
Hugo Baraúna blog.plataformatec.com @hugobarauna
117. COM O RESPONDERS
# app/controllers/users_controller.rb
def create
@user = User.new(params[:user])
@user.save
respond_with(@user)
end
# config/locales/en.yml
en:
flash:
users:
create:
success: “User was successfully created”
Hugo Baraúna blog.plataformatec.com @hugobarauna
121. Lazy loading
# Rails 2.3
Job.find(:all, :conditions => {:published => true})
Faz um query no DB imediatamente e retorna um array de Jobs
# Rails 3
Job.where(:published => true)
Não faz query no DB, retorna um ActiveRecord::Relation
Hugo Baraúna blog.plataformatec.com @hugobarauna
122. Onde a query roda?
# app/controllers/jobs_controller.rb
class JobsController < ApplicationController
def index
@jobs = Jobs.where(:published => true).order("created_at DESC")
end
end
# app/views/jobs/index.html.erb
<% cache do %>
<% @jobs.each do |job| %>
...
<% end %>
<% end %>
Hugo Baraúna blog.plataformatec.com @hugobarauna
123. Onde a query roda?
# app/controllers/jobs_controller.rb
class JobsController < ApplicationController
def index
@jobs = Jobs.where(:published => true).order("created_at DESC")
end
end
Não realiza query no DB
# app/views/jobs/index.html.erb
<% cache do %>
<% @jobs.each do |job| %>
...
<% end %>
<% end %>
Hugo Baraúna blog.plataformatec.com @hugobarauna
124. Onde a query roda?
# app/controllers/jobs_controller.rb
class JobsController < ApplicationController
def index
@jobs = Jobs.where(:published => true).order("created_at DESC")
end
end
Não realiza query no DB
# app/views/jobs/index.html.erb
<% cache do %>
<% @jobs.each do |job| %>
...
<% end %>
<% end %>
Só aqui que será feito a query no DB
Hugo Baraúna blog.plataformatec.com @hugobarauna
125. Onde a query roda?
# app/controllers/jobs_controller.rb
class JobsController < ApplicationController
def index
@jobs = Jobs.where(:published => true).order("created_at DESC")
end
end
Não realiza query no DB
# app/views/jobs/index.html.erb
Se estiver cacheado, não faz
<% cache do %>
a query no controller a toa
<% @jobs.each do |job| %>
...
<% end %>
<% end %>
Só aqui que será feito a query no DB
Hugo Baraúna blog.plataformatec.com @hugobarauna
126. Chainability:
it “quacks” like named_scope
Job.where(:title => 'Rails Developer')
Job.order('created_at DESC').limit(20).includes(:company)
cars = Car.where(:colour => 'black')
black_fancy_cars = cars.order('cars.price DESC').limit(10)
black_cheap_cart = cars.order('cars.price ASC').limit(10)
Hugo Baraúna blog.plataformatec.com @hugobarauna
127. Chainability:
it “quacks” like named_scope
Job.where(:title => 'Rails Developer')
Job.order('created_at DESC').limit(20).includes(:company)
ActiveRecord::Relation
cars = Car.where(:colour => 'black')
black_fancy_cars = cars.order('cars.price DESC').limit(10)
black_cheap_cart = cars.order('cars.price ASC').limit(10)
Hugo Baraúna blog.plataformatec.com @hugobarauna
128. Chainability:
it “quacks” like named_scope
Job.where(:title => 'Rails Developer')
Job.order('created_at DESC').limit(20).includes(:company)
ActiveRecord::Relation
cars = Car.where(:colour => 'black')
black_fancy_cars = cars.order('cars.price DESC').limit(10)
black_cheap_cart = cars.order('cars.price ASC').limit(10)
Reaproveitar uma Relation
e encadear mais finders
Hugo Baraúna blog.plataformatec.com @hugobarauna
143. JS para todos os gostos
• Prototype: default
• jQuery: http://github.com/rails/jquery-ujs
• MooTools: http://mootools.net/forge/p/rails_3_driver
• Você pode fazer o seu!
Hugo Baraúna blog.plataformatec.com @hugobarauna
145. Javascript no Rails 3
Agnosticismo de Javascript
HTML 5 custom data attributes JS driver para cada framework
Hugo Baraúna blog.plataformatec.com @hugobarauna
152. Helpers que retornam HTML
module ApplicationHelper
def strong(content)
"<strong>#{h content}</string>".html_safe
end
end
Hugo Baraúna blog.plataformatec.com @hugobarauna
153. Helpers que retornam HTML
module ApplicationHelper
def strong(content)
"<strong>#{h content}</string>".html_safe
end
end
Dicas:
1. Certificar-se de que todo input está sendo escapado
Hugo Baraúna blog.plataformatec.com @hugobarauna
154. Helpers que retornam HTML
module ApplicationHelper
def strong(content)
"<strong>#{h content}</string>".html_safe
end
end
Dicas:
1. Certificar-se de que todo input está sendo escapado
2. Chamar html_safe no output
Hugo Baraúna blog.plataformatec.com @hugobarauna
155. Tem muito mais aqui!
http://github.com/plataformatec
Hugo Baraúna blog.plataformatec.com @hugobarauna
156. Tem muito mais aqui!
http://github.com/plataformatec
ID blog twitter
Hugo Baraúna blog.plataformatec.com @hugobarauna