Cheap, Fast, and Good
You can have it all with Ruby on Rails
Brian McCallister
brianm@chariotsolutions.com
http://www.chariotsolutions.com/
(c) 2005, Brian McCallister
What is Ruby?
• Dynamic and Interpreted
• Strong support for OO programming
• Everything is an object ( 2.next == 3 )
• Strong support for functional-style
programming
• Blocks, closures, first-class functions
• Child of Perl and Smalltalk
(c) 2005, Brian McCallister
What is Rails?
• Model-2 Web Framework
• Object/Relational Mapping Library
• SOAP Producer/Consumer Framework
• Email Templating/Mailing Library
• Code Generator
• Very Rapidly Evolving!
(c) 2005, Brian McCallister
Principles Involved
• Less Code
• Convention over Configuration
• Proximity
• Least Surprise
• Make components play nicely together!
(c) 2005, Brian McCallister
Action Pack
That Web Stuff
(c) 2005, Brian McCallister
Request Cycle
(c) 2005, Brian McCallister
Some Code
require 'date'
class AggregateController < ApplicationController
model :entry
def for_date
date = DateTime.parse @params[:date]
logger.debug \"for_date: #{date}\"
@entries = Entry.on_date date
end
# Additional actions removed for slide’s benefit`
end
(c) 2005, Brian McCallister
Action
require 'date'
class AggregateController < ApplicationController
model :entry
Action
def for_date
date = DateTime.parse @params[:date]
logger.debug \"for_date: #{date}\"
@entries = Entry.on_date date
end
# Additional actions removed for slide’s benefit`
end
(c) 2005, Brian McCallister
Controller
Controller
require 'date'
class AggregateController < ApplicationController
model :entry
Action
def for_date
date = DateTime.parse @params[:date]
logger.debug \"for_date: #{date}\"
@entries = Entry.on_date date
end
Action
def list_entries
@entries = Entry.find_all
end
# Additional actions removed for slide’s benefit`
end
(c) 2005, Brian McCallister
How We Got Here
ActionController::Routing::Routes.draw do |map|
# map.connect ':controller/service.wsdl',
# :action => 'wsdl'
map.connect 'wombat/is/friendly',
:controller => :feeds,
:action => :list
map.connect '',
:controller => :feeds,
:action => :list
# Default Route
map.connect ':controller/:action/:id'
end
(c) 2005, Brian McCallister
Picking Views
• Default Views
• The View for that one over there...
• Named Views
• Something completely different?
(c) 2005, Brian McCallister
Default Views
require 'date'
class AggregateController < ApplicationController
model :entry
aggregate/for_date.rhtml
def for_date
date = DateTime.parse @params[:date]
logger.debug \"for_date: #{date}\"
@entries = Entry.on_date date
end
# Additional actions removed for slide’s benefit`
end
(c) 2005, Brian McCallister
render_*
require 'date'
class AggregateController < ApplicationController
model :entry
def today
@entries = Entry.on_date Date.today
render_action :list_entries
end
aggregate/list_entries.rhtml
def list_entries
@entries = Entry.find_all
end
# Additional actions removed for slide’s benefit`
end
(c) 2005, Brian McCallister
Named Views
require 'date'
class AggregateController < ApplicationController
model :entry
def another_one
@entries = Entry.find_all
render :a_template
a_template.rhtml
end
# Additional actions removed for slide’s benefit`
end
(c) 2005, Brian McCallister
Raw Rendering
require 'date'
class AggregateController < ApplicationController
model :entry
def direct_write
render_text \"Hello World!\"
end
No template!
# Additional actions removed for slide’s benefit`
end
(c) 2005, Brian McCallister
Additional Options
• send_data
• send_file
• render_to_string
• render_nothing
• render_text with a block
• redirect_to
• redirect_to_url
• redirect_to_path
• builders
• and more!
(c) 2005, Brian McCallister
Helpers
• Programmatic output generation
• Global, Controller Specific, Importable
• Like tag libraries
• kind of
• Lots of built-ins
(c) 2005, Brian McCallister
#{Controller}Helper
module AggregateHelper
def say_hello(name)
Helper
\"<h1>Hello, #{name}\"
end
end
<h1>Recent Stories...</h1>
Usage
<%= say_hello \"Brian\" %>
<table>
<% for entry in @entries %>
...
(c) 2005, Brian McCallister
Rules, well Suggestions
• #{Controller}Helper applied from
matching Controller
• ApplicationHelper available everywhere
• Declare reliance on a specific Helper
from any Controller
• Rarely need to do this, though
(c) 2005, Brian McCallister
Action Pack Magic 3
• Controllers
• Helpers
• Views
• Things not discussed:
• Components
• Caching
• Filters
(c) 2005, Brian McCallister
Active Record
You get the data from the database
and shake it all about...
(c) 2005, Brian McCallister
Active Record Basics
• Not Required!
• One Row == One Instance
• Dynamic Properties by Default
• Query by SQL or Criteria
• Including joins
• PostgreSQL, MySQL, Oracle, DB2 ... more
(c) 2005, Brian McCallister
The Schema
(c) 2005, Brian McCallister
The Classes
class Feed < ActiveRecord::Base
has_many :entries
validates_presence_of :title, :url
end
...
class Entry < ActiveRecord::Base
belongs_to :feed
validates_presence_of :title, :body, :entry_uid
validates_uniqueness_of :entry_uid
end
(c) 2005, Brian McCallister
Relations
class Feed < ActiveRecord::Base
has_many :entries
validates_presence_of :title, :url
end
...
class Entry < ActiveRecord::Base
belongs_to :feed
validates_presence_of :title, :body, :entry_uid
validates_uniqueness_of :entry_uid
end
(c) 2005, Brian McCallister
Queries
def Entry.recent(count)
Entry.find :all,
:order =>'created_on DESC',
:limit => count,
:include => [:feed]
end
def Entry.after(date)
Entry.find :all,
:conditions => ['created_on >= ?', date],
:order => 'created_on DESC',
:include => [:feed]
end
def Entry.before(date)
Entry.find_by_sql [\"select e.* from entries e where
created_on < ?\", date]
end
(c) 2005, Brian McCallister
Criteria
def Entry.recent(count)
Entry.find :all,
:order =>'created_on DESC',
:limit => count,
:include => [:feed]
end
def Entry.after(date)
Entry.find :all,
:conditions => ['created_on >= ?', date],
:order => 'created_on DESC',
:include => [:feed]
end
def Entry.before(date)
Entry.find_by_sql [\"select e.* from entries e where
created_on < ?\", date]
end
(c) 2005, Brian McCallister
SQL
def Entry.recent(count)
Entry.find :all,
:order =>'created_on DESC',
:limit => count,
:include => [:feed]
end
def Entry.after(date)
Entry.find :all,
:conditions => ['created_on >= ?', date],
:order => 'created_on DESC',
:include => [:feed]
end
def Entry.before(date)
Entry.find_by_sql [\"select e.* from entries e where
created_on < ?\", date]
end
(c) 2005, Brian McCallister
Code Generator
This stuff rocks!
(c) 2005, Brian McCallister
A Table
apachecon=> create table talks (
id serial primary key,
name varchar(255) not null,
presenter varchar(255) not null,
description text not null);
NOTICE: CREATE TABLE will create implicit sequence
\"sessions_id_seq\" for serial column \"sessions.id\"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index
\"sessions_pkey\" for table \"sessions\"
CREATE TABLE
apachecon=>
(c) 2005, Brian McCallister
Start the Server...
brianm@kite:~/Sites/apachecon$ ./script/server
=> Rails application started on http://0.0.0.0:3000
[2005-05-25 18:00:12] INFO WEBrick 1.3.1
[2005-05-25 18:00:12] INFO ruby 1.8.2 (2004-12-25)
[2005-05-25 18:00:12] INFO WEBrick::HTTPServer#start:
pid=26996 port=3000
(c) 2005, Brian McCallister
List
(c) 2005, Brian McCallister
TalksController
class TalksController < ApplicationController
def index
list
render_action 'list'
end
def list
@talk_pages, @talks = paginate :talk, :per_page => 10
end
def show
@talk = Talk.find(@params[:id])
end
...
end
(c) 2005, Brian McCallister
Unit and System Tests
brianm@kite:~/Sites/apachecon$ rake
(in /Users/brianm/Sites/apachecon)
ruby -Ilib:test \"/usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/
rake/rake_test_loader.rb\" \"test/unit/talk_test.rb\"
Loaded suite /usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/
rake_test_loader
Started
.
Finished in 0.11654 seconds.
1 tests, 1 assertions, 0 failures, 0 errors
ruby -Ilib:test \"/usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/
rake/rake_test_loader.rb\" \"test/functional/
talks_controller_test.rb\"
Loaded suite /usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/rake/
rake_test_loader
Started
........
Finished in 0.456943 seconds.
8 tests, 26 assertions, 0 failures, 0 errors
brianm@kite:~/Sites/apachecon$
(c) 2005, Brian McCallister
.htaccess
# General Apache options
AddHandler fastcgi-script .fcgi
AddHandler cgi-script .cgi
Options +FollowSymLinks +ExecCGI
# If you don't want Rails to look in certain directories,
# use the following rewrite rules so that Apache won't ... <snip />
#
# Example:
# RewriteCond %{REQUEST_URI} ^/notrails.*
# RewriteRule .* - [L]
# Redirect all requests not available on the filesystem to Rails
# By default the cgi dispatcher is used which is very slow
#
# For better performance replace the dispatcher with the fastcgi one
#
# Example:
# RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
RewriteEngine On
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ dispatch.cgi [QSA,L]
...
(c) 2005, Brian McCallister
Deployment Time
Stuff to be aware of...
(c) 2005, Brian McCallister
Environment
• Development
• Test
• Production
(c) 2005, Brian McCallister
Web Servers
• Webrick
• Apache Web Server
• CGI
• mod_ruby
• fastcgi
• lighttpd
• fastcgi
(c) 2005, Brian McCallister
Session Storage
• Serialize to /tmp
• DRb Server
• Database
• memcached
• Custom
• No Sessions
(c) 2005, Brian McCallister
Scaling Up
• Multi-processing model (like prefork)
• DB Connection per FCGI Process
• Remote FCGI instances
• Static and Dynamic Caching
• Easy to interface with C
• (Almost as easy to interface with OCaml)
(c) 2005, Brian McCallister
The Diagram
(c) 2005, Brian McCallister
Web Resources
• Ruby
• http://www.ruby-lang.org/
• Why’s Poignant Guide to Ruby
• http://poignantguide.net/
• Ruby on Rails
• http://www.rubyonrails.com/
• Ruby Documentation
• http://www.ruby-doc.org/
(c) 2005, Brian McCallister
That’s all folks!
Brian McCallister
brianm@chariotsolutions.com
http://www.chariotsolutions.com/
0 comments
Post a comment