Rails 3: With a Vengeance
Friday, May 7, 2010
Friday, May 7, 2010
Friday, May 7, 2010
Rails 3: With a Vengeance
Friday, May 7, 2010
Friday, May 7, 2010
New talk title:
Friday, May 7, 2010
Rails 3: Tasty Burgers
DHH introduced Rails 3 at last RailsConf. He likes whoppers. I actually hate burger king entirely. I’m not sure how anybody can eat there. But that’s OK, because I can make my own rails burger the way I like
Friday, May 7, 2010
Friday, May 7, 2010
Skill
Friday, May 7, 2010
A lot like Rails 2.3We tried really hard to not break any 2.x APIs unless it was really deemed necessary or a big win.
Friday, May 7, 2010
But not really...
Only surface level stuff is the same. A lot under the hood has changed and there are a lot more features. I obviously can’t cover everything in depth in an hour, so I’m going to gloss over a lot and you can all look into details later.
Friday, May 7, 2010
Quick Refresher
What hasn’t changed
Friday, May 7, 2010
MVC
Friday, May 7, 2010
REST
Friday, May 7, 2010
Resources
The router / controller / ActiveRecord model is still optimized for organizing your application in terms of Resources
Friday, May 7, 2010
Controllers
Friday, May 7, 2010
ActiveRecord
Some changes (Arel, query api - Not going to really cover this)General ideas are the sameassociationsActiveRecord pattern
Friday, May 7, 2010
Migrations
Friday, May 7, 2010
So, what did change?
Friday, May 7, 2010
File structure
There were some tweaks to the default File structure, mostly in the config directory.
This is what you get when you generate a new Rails 3 app
One big thing about Rails 3 is that the app file structure is not sacred, it’s just a convention, not obligation
Friday, May 7, 2010
config.ru
# This file is used by Rack-based servers# to start the application.
require ::File.expand_path( '../config/environment', __FILE__)run AwesomeBlog::Application
Rails 3 is pure Rack. When you start an app, it evaluates config.ru, which is a rack convention. If this file is there, all rack compatible rack servers know what to do.Could put middleware or whatever here (could even put a full Rails app in this one file).
Who doesn’t know what Rack is?
Could put middleware or whatever here (could even put a full Rails app in this one file).
Friday, May 7, 2010
config.ru
# This file is used by Rack-based servers# to start the application.
require ::File.expand_path( '../config/environment', __FILE__)run AwesomeBlog::Application
That’s specifically the line that rack is looking for.
The constant is a Rack app that encapsulates everything related to the entire rails application that we’re building
Friday, May 7, 2010
config/application.rb
require File.expand_path('../boot', __FILE__)require 'rails/all'
Bundler.require(:default, Rails.env) if defined?(Bundler)
module AwesomeBlog class Application < Rails::Application config.encoding = "utf-8" config.filter_parameters += [:password] endend
This is the file that defines the application object from the last slide.
Let’s look at some key components
Friday, May 7, 2010
config/application.rb
require File.expand_path('../boot', __FILE__)require 'rails/all'
Bundler.require(:default, Rails.env) if defined?(Bundler)
module AwesomeBlog class Application < Rails::Application config.encoding = "utf-8" config.filter_parameters += [:password] endend
Friday, May 7, 2010
config/application.rb
require File.expand_path('../boot', __FILE__)require 'rails/all'
Bundler.require(:default, Rails.env) if defined?(Bundler)
module AwesomeBlog class Application < Rails::Application config.encoding = "utf-8" config.filter_parameters += [:password] endend
Friday, May 7, 2010
config/application.rb
require File.expand_path('../boot', __FILE__)require 'rails/all'
Bundler.require(:default, Rails.env) if defined?(Bundler)
module AwesomeBlog class Application < Rails::Application config.encoding = "utf-8" config.filter_parameters += [:password] endend
A Rack App!
Friday, May 7, 2010
Application ObjectOne day, we’ll have many application objects in one process.
Ruby summer of code project to get this done.
Contains all application specific information. Takes the “in the sky” constants / globals that were needed for a rails 2.3 application and encapsulates them.
Routes, Config , Middleware, Initializers
Friday, May 7, 2010
config/application.rb
require File.expand_path('../boot', __FILE__)require 'rails/all'
Bundler.require(:default, Rails.env) if defined?(Bundler)
module AwesomeBlog class Application < Rails::Application config.encoding = "utf-8" config.filter_parameters += [:password] endend
Friday, May 7, 2010
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.setuprescue Bundler::GemNotFound => e STDERR.puts e.message STDERR.puts "Try running `bundle install`." exit!end if File.exist?(gemfile)
Friday, May 7, 2010
config/boot.rb
require 'bundler' Bundler.setup
Friday, May 7, 2010
Bundler is a tool we built to manage gem dependencies. As it turns out, as rails applications grow, dependencies become painful to deal with.
Many years in the making. Many iterations. It grew out of projects that had many complicate dependencies and it was basically impossible to deal with it all by hand
Friday, May 7, 2010
Gemfile
source 'http://rubygems.org'
gem 'rails', '3.0.0.beta3'gem 'sqlite3-ruby', :require => 'sqlite3'
It all starts here. This file is located at the root of the application.
All gem dependencies will be listed here. So far our app is small, so we only depend on rails itself (note the version is the one that we are currently working with) and also we have a dependency on sqlite since that’s the database that we’re using.
Friday, May 7, 2010
$ bundle install
Step two is to run the “bundle install” command.
* Checks gems already on system* Downloads what is missing
Friday, May 7, 2010
No step 3
Friday, May 7, 2010
Wins:
What are the major wins that we get from using bundler?
Friday, May 7, 2010
Easy to update dependencies
Dependencies in applications will evolve a lot throughout the life of an application
Ruby is maturing. There are a LOT of gems out there that you’ll be wanting to use. Bundler makes doing it easy.
Friday, May 7, 2010
Gemfile
source 'http://rubygems.org'
gem 'rails', '3.0.0.beta3'gem 'sqlite3-ruby', :require => 'sqlite3'
What happens if you want to update your application to the latest version of rails?
Friday, May 7, 2010
Gemfile
source 'http://rubygems.org'
gem 'rails', '3.0.0'gem 'sqlite3-ruby', :require => 'sqlite3'
You’re done. All child dependencies will be handled for you.
The same goes if you want to roll back to a previous version. Just change the Gemfile
Friday, May 7, 2010
Isolation
You know all your dependencies are accounted for and you won’t be surprised when you deploy.
Bundler does not let you use a gem if it is not listed in the Gemfile. So, you can’t accidentally forget to install a gem on the server when you deploy.
Friday, May 7, 2010
Sharing
Your Gemfile accounts for all gems. You share your app with other developers. They run `bundle install` and they’re good to go.
Bundler tracks all child dependencies as well as top level app dependencies
Come back to a project 3 months later. What version of UUID was I using? (yeah, that broke deploys) Nokogiri?
Designers
Friday, May 7, 2010
No conflicts
Anybody ever hit the “can’t activate gem foo, already activated foo at another version error”?
Friday, May 7, 2010
Without Bundler
Friday, May 7, 2010
Friday, May 7, 2010
The Solution!Step 1) VendorRails + Rack
Friday, May 7, 2010
The Solution!Step 1) Modify the Rails source
Friday, May 7, 2010
While we’re on the topic of picard....
Friday, May 7, 2010
Friday, May 7, 2010
Friday, May 7, 2010
Unobtrusive Javascript
Friday, May 7, 2010
Helpers do not generate JS anymore
In Rails 2.3, all view helpers that required JS would spew out a whole bunch of Prototype specific javascript inline.
This is bad.
Friday, May 7, 2010
<%= link_to "hello", hello_path, :remote => true %>
<a href="/hello" data-remote="true">hello</a>
Rails 3.0 helpers will now only output HTML. All required information will be added to the HTML tags via data-*
Custom data-* attributes were added in HTML5, but they work in all old and busted (IE 6).
Friday, May 7, 2010
public/javascripts/rails.js
$(document.body).observe("click", function(event) { // .... var element = event.findElement( "a[data-remote]"); if (element) { handleRemote(element); event.stop(); return true; } // ....});
The javascript that handles all the data-* attributes is in rails.js
Friday, May 7, 2010
Using jQuery?http://github.com/rails/jquery-ujs
Rails will ship with the Prototype driver, but you can just drop in the jquery driver at public/javascripts/rails.js
We maintain it and it’s on github at the URL.
You can finally easily completely rid your Rails app of all Prototype
Who uses jQuery?
Friday, May 7, 2010
public/javascripts/rails.js
$('a[data-remote],input[data-remote]').live('click', function (e) { $(this).callRemote(); e.preventDefault();});
This is the code that is in the jQuery rails.js driver
Friday, May 7, 2010
Pick your JS framework when you write JS, not when you generate
your Rails app.
Other wins too.
Friday, May 7, 2010
Customizable
Just JS events being triggered. You can easily replace specific default behavior.
There are events that are triggered that you can bind to. ajax:before, ajax:after, etc...
Friday, May 7, 2010
Everything is a “plugin”
Rails is like a toolkit to build rails. Think about it.
Friday, May 7, 2010
ActiveRecord?
Friday, May 7, 2010
Plugin!
Friday, May 7, 2010
ActionController?
Friday, May 7, 2010
Plugin!
Friday, May 7, 2010
TestUnit
Friday, May 7, 2010
Plugin!
Friday, May 7, 2010
ActiveResource?
Friday, May 7, 2010
...
Friday, May 7, 2010
You can build plugins too.
Friday, May 7, 2010
Railtie
Railtie is a class that integrates a library with rails.
Look at how ActiveRecord does it. Look at how everything else does it.
When we built Rails 3, we didn’t add hook points for all these other libraries after the fact. We made hooks for ourselves to use, which has the nice side effect of other libraries being able to use them.
RSpec, DataMapper, HAML, etc... all use the same hooks that Rails uses
Friday, May 7, 2010
Configuration
Rake Tasks
Generator Overrides
Initializers
You actually rarely need a Railtie to integrate with Rails. Railties are for a few specific things.
Friday, May 7, 2010
Configuration
Rake Tasks
Generator Overrides
Initializers
Provide configuration opportunities to Rails applications
config.active_record in config/application.rb
If you want to provide default config values, you need a Railtie
Friday, May 7, 2010
Configuration
Rake Tasks
Generator Overrides
Initializers
All ActiveRecord’s rake tasks are in it’s Railtie, which means that when AR isn’t required, the rake tasks go away.
If you want to easily provide Rake tasks to a Rails app, you need a Railtie
Friday, May 7, 2010
Configuration
Rake Tasks
Generator Overrides
Initializers
If you want to hook in to:* rails generate controller Foo* rails generate model Foo
you need a Railtie. RSpec uses this to provide the same level of integration that TestUnit gets
Friday, May 7, 2010
Configuration
Rake Tasks
Generator Overrides
Initializers
If you want to hook into the initialization cycle of a Rails application, you’ll need a Railtie
Friday, May 7, 2010
HTML escaping
Friday, May 7, 2010
<p> <%= h @comment.title %> (by <%= h @comment.author.username %>)</p>
<%= simple_format h(@comment.body) %>
Rails 2.3
This is what a view might have looked in Rails 2.3
Friday, May 7, 2010
Rails 2.3
If you forget one spot, you’re site gets hacked
... I’m sure everybody here has missed at least 1 spot to escape
<p> <%= h @comment.title %> (by <%= h @comment.author.username %>)</p>
<%= simple_format h(@comment.body) %>
Friday, May 7, 2010
<p> <%= @comment.title %> (by <%= @comment.author.username %>)</p>
<%= simple_format(@comment.body) %>
Rails 3.0
New! No more pesky h()
Now, you don’t have to worry about escaping anything anymore. Rails does it for you.
Friday, May 7, 2010
XSS Protection by default
Friday, May 7, 2010
def escaped(str) strend
def not_escaped(str) str.html_safeend
app/helpers/application_helper.rb
Simply returning a string a string will be escaped.
If you don’t want a string to be escaped, you just mark it as html_safe and it won’t be modified by Rails.
Friday, May 7, 2010
Block helpers
Friday, May 7, 2010
<% form_for @post do |f| %> <%= f.text_field :title %><% end %>
<% box do %> <p>Hello World!</p><% end %>
Rails 2.3
This is what block helpers might look like in 2.3
Friday, May 7, 2010
def box(&block) content = "<div class='box'>" << capture(&block) << "</div>"
if block_called_from_erb? concat(content) else content endend
Rails 2.3
This is the implementation.
I’m not even going to really talk about this, Just notice that it’s crazy
Most of this is needed to make the helper work in and outside of ERB.
Friday, May 7, 2010
<% form_for @post do |f| %> <%= f.text_field :title %><% end %>
<% box do %> <p>Hello World!</p><% end %>
Rails 2.3
Outputs method’s return value
Ignores method’s return value
The reason why you need the craziness is because of how ERB works.
Friday, May 7, 2010
Rails 3.0
<%= form_for @post do |f| %> <%= f.text_field :title %><% end %>
<%= box do %> <p>Hello World!</p><% end %>
In Rails 3.0 block helpers have become consistent with ERB.
form_for will actually output content to the view, aka, the form tags. So, now you use %=
The same goes for custom block helpers
Friday, May 7, 2010
def box(&block) "<div class='box'>" \ "#{capture(&block)}" \ "</div>"end
Rails 3.0
All you do now for block helpers is return the string that you want outputted to the view.
Block helpers aren’t special anymore. They work the same inside and outside ERB
Friday, May 7, 2010
def box(&block) "<div class='box'>" \ "#{capture(&block)}" \ "</div>".html_safeend
Rails 3.0
Friday, May 7, 2010
Router
Massive API overhaul.
Friday, May 7, 2010
Old DSL still works, just deprecated.
You don’t have to rush to update your route file to get on Rails 3.0
The old DSL will probably stop working in Rails 3.1 or 3.2, but you have time before that happens.
Friday, May 7, 2010
Matching
map.connect "/posts", :controller => :posts, :action => :index
Friday, May 7, 2010
Matching
map.connect "/posts", :controller => :posts, :action => :index
match "/posts" => "posts#index"
posts#index is short hand for specifying the controller and action. It’s just much easier to write.
Friday, May 7, 2010
Optional Segments
match "/posts(/:page)" => "posts#index"
Will match a request to /posts, and a request to /posts/5
if page is not there, params[:page] will be nil, if it is present in the request, the param will be set.
Friday, May 7, 2010
Defaults
match "/posts(/:page)" => "posts#index",:defaults => { :page => 1 }
You can specify default parameters. If the page segment is not in the Request, params[:page] will be set to 1
Friday, May 7, 2010
Named Routes
match "/posts(/:page)" => "posts#index",:defaults => { :page => 1 }, :as => "posts"
The same helpers are available. * posts_path * posts_url
Friday, May 7, 2010
ScopesLet’s you set the same options to a group of routes.
Almost anything can be scoped.
Friday, May 7, 2010
Path Scopes
scope :path => "/admin" do match "/posts" => "posts#index" match "/users" => "users#index"end
/admin/posts
Friday, May 7, 2010
Path Scopes
scope "/admin" do match "/posts" => "posts#index" match "/users" => "users#index"end
Paths are probably the most common item to scope on, so it’s the default.
Friday, May 7, 2010
Module Scope
scope :module => "admin" do match "/posts" => "posts#index" match "/users" => "users#index"end
Routes to Admin::PostsController / Admin::UsersController
Friday, May 7, 2010
Both
namespace "admin" do match "/posts" => "posts#index" match "/users" => "users#index"end
Friday, May 7, 2010
HTTP Methods
Routing by specific HTTP methods
Friday, May 7, 2010
POST Request
match "/posts" => "posts#index",:via => "delete"
Any HTTP method can be used here
Friday, May 7, 2010
Scoping
scope "/posts" do controller :posts do get "/" => :index endend
Friday, May 7, 2010
Scoping
scope "/posts" do controller :posts do get "/" => :index endend
Yet another shorthand
Friday, May 7, 2010
Scoping
scope "/posts" do controller :posts do get "/" => :index endend
get URL
Friday, May 7, 2010
Scoping
get "/posts" => "posts#index"
get / post / post / delete methods are shorthands for :via => “get”
This method can be used anywhere in the routes file
Friday, May 7, 2010
Constraints
Friday, May 7, 2010
Regexp Constraint
get "/:id" => "posts#index", :id => /\d+/
Friday, May 7, 2010
Regexp Constraint
get "/posts" => "posts#mobile", :user_agent => /iPhone/
Constraints on arbitrary methods of Request object.
Will use the “user_agent” method on the Request object
Friday, May 7, 2010
Defaults + Constraints
Friday, May 7, 2010
Constraint + Default
get "/posts" => "posts#mobile", :user_agent => /iPhone/, :mobile => true
How does it not get confused?
If you use a Regex on as the value of the hash, it is a constraint, otherwise, it is a default
Friday, May 7, 2010
Object Constraints
class DubDubConstraint def self.matches?(request) request.host =~ /^(www.)/ # true / false endend
get "/" => "posts#index", :constraints => DubDubConstraint
Any object that responds to matches? can be a router constraint
Friday, May 7, 2010
Rack EverywhereCan be used without AC and only Rack. This is the minimum requirement for “Rails”
Everything that used to be in ActionController that made sense without ActionController. There is a lot, all rack middleware.* Session* Cookies* Router* Flash* etc..
Friday, May 7, 2010
Rack
PostsController.action(:index)
You can get a rack app for any controller action
Friday, May 7, 2010
Routing + Rack
get "/posts" => PostsController.action(:index)
This is what happens behind the scenes in my previous examples
Friday, May 7, 2010
Routing + Rack
get "/posts" => PostsController.action(:index)
Just a Rack app
Friday, May 7, 2010
Routing + Rack
get "/posts" => PostsController.action(:index)
Any Rack app
Friday, May 7, 2010
Routing + Rack
get "/posts" => MySinatraPostsApp
Sinatra app
Friday, May 7, 2010
Routing + Rack
get "/posts" => MyCampingPostsApp
Camping app
Friday, May 7, 2010
Routing + Rack
get "/posts" => MyRamazePostsApp
Ramaze app
Friday, May 7, 2010
Routing + Rack
get "/posts" => MyConstantThatRespondsToCall
Any Rack app
The possibilities are endless
Friday, May 7, 2010
ActionMailer
Friday, May 7, 2010
It’s newMassive API overhaul
I’m out of time
Friday, May 7, 2010
A lot of detailed stuff about what’s new with Rails 3.
* Articles* Blog posts* Screencasts* QAs* Aggregation of other Rails 3 info
Friday, May 7, 2010
http://www.railsdispatch.com/posts/actionmailer
Friday, May 7, 2010
UpgradingI want to mention upgrading really quick
Rails 1.x apps -> 3.0 is really easy (15 minutes)
Rails 2.x is a little bit harder
Friday, May 7, 2010
Step 1) Generate a new appLook at the generated file structure
Friday, May 7, 2010
config.ru
Friday, May 7, 2010
config/*
Friday, May 7, 2010
http://github.com/rails/rails_upgrade* Mail API* Known broken plugins* Helpers
* But all this stuff still will work in rails 3, just will spew out deprecation notices.
It will try to find all deprecated but still working methods as well* Router* AR API* config/** Deprecated constants
Friday, May 7, 2010
Questions?We have 2 other Rails core people here, they can answer
questions too.
Friday, May 7, 2010
Questions?
Email: [email protected] (I accept fan mail)
Twitter : @carllerche (I’m interesting. Really!)
I will tweet the slides. So make sure you are following me.
Friday, May 7, 2010
Top Related