Apachecon Rails

60
Cheap, Fast, and Good You can have it all with Ruby on Rails Brian McCallister [email protected] http://www.chariotsolutions.com / (c) 2005, Brian McCallister

Transcript of Apachecon Rails

Page 1: Apachecon Rails

Cheap, Fast, and GoodYou can have it all with Ruby on Rails

Brian [email protected]

http://www.chariotsolutions.com/

(c) 2005, Brian McCallister

Page 2: Apachecon Rails

(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

Page 3: Apachecon Rails

(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!

Page 4: Apachecon Rails

(c) 2005, Brian McCallister

Principles Involved• Less Code

• Convention over Configuration

• Proximity

• Least Surprise

• Make components play nicely together!

Page 5: Apachecon Rails

Action PackThat Web Stuff

(c) 2005, Brian McCallister

Page 6: Apachecon Rails

(c) 2005, Brian McCallister

Request Cycle

Page 7: Apachecon Rails

(c) 2005, Brian McCallister

Some Coderequire '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

Page 8: Apachecon Rails

(c) 2005, Brian McCallister

Actionrequire '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

Action

Page 9: Apachecon Rails

(c) 2005, Brian McCallister

Controllerrequire '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

def list_entries @entries = Entry.find_all end

# Additional actions removed for slide’s benefit`

end

Action

Action

Controller

Page 10: Apachecon Rails

(c) 2005, Brian McCallister

How We Got HereActionController::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

Page 11: Apachecon Rails

(c) 2005, Brian McCallister

routes.rbActionController::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

http://localhost/wombat/is/friendly

Page 12: Apachecon Rails

(c) 2005, Brian McCallister

routes.rbActionController::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

http://localhost/

Page 13: Apachecon Rails

(c) 2005, Brian McCallister

routes.rbActionController::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

http://localhost/feeds/list

Page 14: Apachecon Rails

(c) 2005, Brian McCallister

Show Us Something!<h1>Recent Stories...</h1><table class="entryTable" ><% for entry in @entries %> <tr> <td class="entryTitle"> <%= entry.feed.title + ": " + entry.title %> </td> </tr> <tr> <td class="entryBody"> <%= entry.body %> </td> </tr><% end %></table>

Page 15: Apachecon Rails

(c) 2005, Brian McCallister

Directives<h1>Recent Stories...</h1><table class="entryTable" ><% for entry in @entries %> <tr> <td class="entryTitle"> <%= entry.feed.title + ": " + entry.title %> </td> </tr> <tr> <td class="entryBody"> <%= entry.body %> </td> </tr><% end %></table>

Page 16: Apachecon Rails

(c) 2005, Brian McCallister

Expressions<h1>Recent Stories...</h1><table class="entryTable" ><% for entry in @entries %> <tr> <td class="entryTitle"> <%= entry.feed.title + ": " + entry.title %> </td> </tr> <tr> <td class="entryBody"> <%= entry.body %> </td> </tr><% end %></table>

Page 17: Apachecon Rails

(c) 2005, Brian McCallister

Helpers & Partials<h1>Recent Stories...</h1><%= render_partial "entry_list", :stories => @entries %>

<table class="entryTable" ><% for entry in stories %> <tr> <td class="entryTitle"> <%= entry.feed.title + ": " + entry.title %> </td> </tr> <tr> <td class="entryBody"> <%= entry.body %> </td> </tr><% end %></table>

my_view.rhtml

_entry_list.rhtml

Page 18: Apachecon Rails

(c) 2005, Brian McCallister

Helpers<h1>Recent Stories...</h1><%= render_partial "entry_list", :stories => @entries %>

<table class="entryTable" ><% for entry in stories %> <tr> <td class="entryTitle"> <%= entry.feed.title + ": " + entry.title %> </td> </tr> <tr> <td class="entryBody"> <%= entry.body %> </td> </tr><% end %></table>

my_view.rhtml

_entry_list.rhtml

Page 19: Apachecon Rails

(c) 2005, Brian McCallister

Parameterized Partials<h1>Recent Stories...</h1><%= render_partial "entry_list", :stories => @entries %>

<table class="entryTable" ><% for entry in stories %> <tr> <td class="entryTitle"> <%= entry.feed.title + ": " + entry.title %> </td> </tr> <tr> <td class="entryBody"> <%= entry.body %> </td> </tr><% end %></table>

my_view.rhtml

_entry_list.rhtml

Page 20: Apachecon Rails

(c) 2005, Brian McCallister

Picking Views• Default Views

• The View for that one over there...

• Named Views

• Something completely different?

Page 21: Apachecon Rails

(c) 2005, Brian McCallister

Default Viewsrequire '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

aggregate/for_date.rhtml

Page 22: Apachecon Rails

(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 def list_entries @entries = Entry.find_all end

# Additional actions removed for slide’s benefit`

end

aggregate/list_entries.rhtml

Page 23: Apachecon Rails

(c) 2005, Brian McCallister

Named Viewsrequire 'date'

class AggregateController < ApplicationController model :entry def another_one @entries = Entry.find_all render :a_template end

# Additional actions removed for slide’s benefit`

end

a_template.rhtml

Page 24: Apachecon Rails

(c) 2005, Brian McCallister

Raw Renderingrequire 'date'

class AggregateController < ApplicationController model :entry def direct_write render_text "Hello World!" end

# Additional actions removed for slide’s benefit`

end

No template!

Page 25: Apachecon Rails

(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!

Page 26: Apachecon Rails

(c) 2005, Brian McCallister

Helpers• Programmatic output generation

• Global, Controller Specific, Importable

• Like tag libraries

• kind of

• Lots of built-ins

Page 27: Apachecon Rails

(c) 2005, Brian McCallister

#{Controller}Helpermodule AggregateHelper

def say_hello(name) "<h1>Hello, #{name}" end

end

<h1>Recent Stories...</h1><%= say_hello "Brian" %><table><% for entry in @entries %>...

Helper

Usage

Page 28: Apachecon Rails

(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

Page 29: Apachecon Rails

(c) 2005, Brian McCallister

Action Pack Magic 3• Controllers

• Helpers

• Views

• Things not discussed:

• Components

• Caching

• Filters

Page 30: Apachecon Rails

Active RecordYou get the data from the database

and shake it all about...

(c) 2005, Brian McCallister

Page 31: Apachecon Rails

(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

Page 32: Apachecon Rails

(c) 2005, Brian McCallister

The Schema

Page 33: Apachecon Rails

(c) 2005, Brian McCallister

The Classesclass 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

Page 34: Apachecon Rails

(c) 2005, Brian McCallister

Relationsclass 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

Page 35: Apachecon Rails

(c) 2005, Brian McCallister

Queriesdef 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

Page 36: Apachecon Rails

(c) 2005, Brian McCallister

Criteriadef 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

Page 37: Apachecon Rails

(c) 2005, Brian McCallister

SQLdef 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

Page 38: Apachecon Rails

Code GeneratorThis stuff rocks!

(c) 2005, Brian McCallister

Page 39: Apachecon Rails

(c) 2005, Brian McCallister

Creating Rails Projectbrianm@kite:~/Sites$ rails apachecon create app create app/apis create app/controllers create app/helpers create app/models ... create log/test.log

brianm@kite:~/Sites$ ls -F apachecon/

CHANGELOG Rakefile components/ db/ lib/ public/ test/ README app/ config/ doc/ log/ script/ vendor/

brianm@kite:~/Sites$

Page 40: Apachecon Rails

(c) 2005, Brian McCallister

This Created...• Project Hierarchy

• Config file (for database connection)

• Rakefile (Makefile)

• System Test Harness

• Unit Test Harness

• Apache HTTPD Configs (.htaccess)

• Additional Code Generation Scripts...

Page 41: Apachecon Rails

(c) 2005, Brian McCallister

Configure Databasebrianm@kite:~/Sites/apachecon$ cat config/database.yml development: adapter: postgresql database: ruby_blogs_dev host: localhost username: blogs password: **********

...

production: adapter: postgresql database: ruby_blogs host: localhost username: blogs password: **********brianm@kite:~/Sites/apachecon$

Page 42: Apachecon Rails

(c) 2005, Brian McCallister

Some CRUDbrianm@kite:~/Sites/apachecon$ ./script/generate scaffold Talk dependency model exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/talk.rb create test/unit/talk_test.rb create test/fixtures/talks.yml exists app/controllers/ exists app/helpers/ create app/views/talks exists test/functional/ create app/controllers/talks_controller.rb create test/functional/talks_controller_test.rb create app/helpers/talks_helper.rb create app/views/layouts/talks.rhtml create public/stylesheets/scaffold.css create app/views/talks/list.rhtml create app/views/talks/show.rhtml create app/views/talks/new.rhtml create app/views/talks/edit.rhtml create app/views/talks/_form.rhtmlbrianm@kite:~/Sites/apachecon$

Page 43: Apachecon Rails

(c) 2005, Brian McCallister

A Tableapachecon=> 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 TABLEapachecon=>

Page 44: Apachecon Rails

(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

Page 45: Apachecon Rails

(c) 2005, Brian McCallister

List

Page 46: Apachecon Rails

(c) 2005, Brian McCallister

TalksControllerclass 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

Page 47: Apachecon Rails

(c) 2005, Brian McCallister

Create

Page 48: Apachecon Rails

(c) 2005, Brian McCallister

The Edit View<h1>Editing talk</h1><%= start_form_tag :action => 'update', :id => @talk %> <%= render_partial "form" %> <%= submit_tag "Edit" %><%= end_form_tag %>

<%= link_to 'Show', :action => 'show', :id => @talk %> |<%= link_to 'Back', :action => 'list' %>

<%= error_messages_for 'talk' %>

<!--[form:talk]--><p><label for="talk_description">Description</label><br/><%= text_area 'talk', 'description' %></p>

<p><label for="talk_presenter">Presenter</label><br/><%= text_field 'talk', 'presenter' %></p>

<p><label for="talk_name">Name</label><br/><%= text_field 'talk', 'name' %></p><!--[eoform:talk]-->

talks/edit.rhtml

talks/_form.rhtml

Page 49: Apachecon Rails

(c) 2005, Brian McCallister

List Again

Page 50: Apachecon Rails

(c) 2005, Brian McCallister

Unit and System Testsbrianm@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_loaderStarted.Finished in 0.11654 seconds.

1 tests, 1 assertions, 0 failures, 0 errorsruby -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_loaderStarted........Finished in 0.456943 seconds.

8 tests, 26 assertions, 0 failures, 0 errorsbrianm@kite:~/Sites/apachecon$

Page 51: Apachecon Rails

(c) 2005, Brian McCallister

.htaccess# General Apache optionsAddHandler fastcgi-script .fcgiAddHandler cgi-script .cgiOptions +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 OnRewriteRule ^$ index.html [QSA]RewriteRule ^([^.]+)$ $1.html [QSA]RewriteCond %{REQUEST_FILENAME} !-fRewriteRule ^(.*)$ dispatch.cgi [QSA,L]...

Page 52: Apachecon Rails

(c) 2005, Brian McCallister

Extensible Generator• Some 3rd Party Generators:

• Login Generator

• Salted Hash Login Generator

• Tabbed Navbar Generator

• Search Generator

• Configuration Generator

• Postback Generator

Page 53: Apachecon Rails

Deployment TimeStuff to be aware of...

(c) 2005, Brian McCallister

Page 54: Apachecon Rails

(c) 2005, Brian McCallister

Environment• Development

• Test

• Production

Page 55: Apachecon Rails

(c) 2005, Brian McCallister

Web Servers• Webrick

• Apache Web Server

• CGI

• mod_ruby

• fastcgi

• lighttpd

• fastcgi

Page 56: Apachecon Rails

(c) 2005, Brian McCallister

Session Storage• Serialize to /tmp

• DRb Server

• Database

• memcached

• Custom

• No Sessions

Page 57: Apachecon Rails

(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)

Page 58: Apachecon Rails

(c) 2005, Brian McCallister

The Diagram

Page 59: Apachecon Rails

(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/

Page 60: Apachecon Rails

That’s all folks!Brian McCallister

[email protected]://www.chariotsolutions.com/