Cheap, Fast, and Good You can have it all with Ruby on Rails Brian - - PowerPoint PPT Presentation

cheap fast and good
SMART_READER_LITE
LIVE PREVIEW

Cheap, Fast, and Good You can have it all with Ruby on Rails Brian - - PowerPoint PPT Presentation

Cheap, Fast, and Good You can have it all with Ruby on Rails Brian McCallister brianm@ninginc.com http://www.ning.com/ (c) 2005, Brian McCallister What is Ruby? Dynamic and Interpreted Strong support for OO programming Everything


slide-1
SLIDE 1

Cheap, Fast, and Good

You can have it all with Ruby on Rails

Brian McCallister

brianm@ninginc.com http://www.ning.com/

(c) 2005, Brian McCallister

slide-2
SLIDE 2

(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
slide-3
SLIDE 3

(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!
slide-4
SLIDE 4

(c) 2005, Brian McCallister

Principles Involved

  • Less Code
  • Convention over Configuration
  • Proximity
  • Least Surprise
  • Make components play nicely together!
slide-5
SLIDE 5

Action Pack

That Web Stuff

(c) 2005, Brian McCallister

slide-6
SLIDE 6

(c) 2005, Brian McCallister

Request Cycle

slide-7
SLIDE 7

(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

slide-8
SLIDE 8

(c) 2005, Brian McCallister

Action

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

Action

slide-9
SLIDE 9

(c) 2005, Brian McCallister

Controller

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 def list_entries @entries = Entry.find_all end # Additional actions removed for slide’s benefit` end

Action Action Controller

slide-10
SLIDE 10

(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

slide-11
SLIDE 11

(c) 2005, Brian McCallister

routes.rb

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

http://localhost/wombat/is/friendly

slide-12
SLIDE 12

(c) 2005, Brian McCallister

routes.rb

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

http://localhost/

slide-13
SLIDE 13

(c) 2005, Brian McCallister

routes.rb

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

http://localhost/feeds/list

slide-14
SLIDE 14

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

slide-15
SLIDE 15

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

slide-16
SLIDE 16

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

slide-17
SLIDE 17

(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

slide-18
SLIDE 18

(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

slide-19
SLIDE 19

(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

slide-20
SLIDE 20

(c) 2005, Brian McCallister

Picking Views

  • Default

Views

  • The

View for that one over there...

  • Named

Views

  • Something completely different?
slide-21
SLIDE 21

(c) 2005, Brian McCallister

Default Views

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

aggregate/for_date.rhtml

slide-22
SLIDE 22

(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

slide-23
SLIDE 23

(c) 2005, Brian McCallister

Named Views

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

slide-24
SLIDE 24

(c) 2005, Brian McCallister

Raw Rendering

require 'date' class AggregateController < ApplicationController model :entry def direct_write render_text "Hello World!" end # Additional actions removed for slide’s benefit` end

No template!

slide-25
SLIDE 25

(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!
slide-26
SLIDE 26

(c) 2005, Brian McCallister

Helpers

  • Programmatic output generation
  • Global, Controller Specific, Importable
  • Like tag libraries
  • kind of
  • Lots of built-ins
slide-27
SLIDE 27

(c) 2005, Brian McCallister

#{Controller}Helper

module AggregateHelper def say_hello(name) "<h1>Hello, #{name}" end end <h1>Recent Stories...</h1> <%= say_hello "Brian" %> <table> <% for entry in @entries %> ...

Helper Usage

slide-28
SLIDE 28

(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
slide-29
SLIDE 29

(c) 2005, Brian McCallister

Action Pack Magic 3

  • Controllers
  • Helpers
  • Views
  • Things not discussed:
  • Components
  • Caching
  • Filters
slide-30
SLIDE 30

Active Record

You get the data from the database and shake it all about...

(c) 2005, Brian McCallister

slide-31
SLIDE 31

(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
slide-32
SLIDE 32

(c) 2005, Brian McCallister

The Schema

slide-33
SLIDE 33

(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

slide-34
SLIDE 34

(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

slide-35
SLIDE 35

(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

slide-36
SLIDE 36

(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

slide-37
SLIDE 37

(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

slide-38
SLIDE 38

Code Generator

This stuff rocks!

(c) 2005, Brian McCallister

slide-39
SLIDE 39

(c) 2005, Brian McCallister

Creating Rails Project

brianm@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$

slide-40
SLIDE 40

(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...
slide-41
SLIDE 41

(c) 2005, Brian McCallister

Configure Database

brianm@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$

slide-42
SLIDE 42

(c) 2005, Brian McCallister

Some CRUD

brianm@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.rhtml brianm@kite:~/Sites/apachecon$

slide-43
SLIDE 43

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

slide-44
SLIDE 44

(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

slide-45
SLIDE 45

(c) 2005, Brian McCallister

List

slide-46
SLIDE 46

(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

slide-47
SLIDE 47

(c) 2005, Brian McCallister

Create

slide-48
SLIDE 48

(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

slide-49
SLIDE 49

(c) 2005, Brian McCallister

List Again

slide-50
SLIDE 50

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

slide-51
SLIDE 51

(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] ...

slide-52
SLIDE 52

(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
slide-53
SLIDE 53

Deployment Time

Stuff to be aware of...

(c) 2005, Brian McCallister

slide-54
SLIDE 54

(c) 2005, Brian McCallister

Environment

  • Development
  • Test
  • Production
slide-55
SLIDE 55

(c) 2005, Brian McCallister

Web Servers

  • Webrick
  • Apache Web Server
  • CGI
  • mod_ruby
  • fastcgi
  • lighttpd
  • fastcgi
slide-56
SLIDE 56

(c) 2005, Brian McCallister

Session Storage

  • Serialize to /tmp
  • DRb Server
  • Database
  • memcached
  • Custom
  • No Sessions
slide-57
SLIDE 57

(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)
slide-58
SLIDE 58

(c) 2005, Brian McCallister

The Diagram

slide-59
SLIDE 59

(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/
slide-60
SLIDE 60

That’s all folks!

Brian McCallister

brianm@chariotsolutions.com http://www.chariotsolutions.com/

slide-61
SLIDE 61

(c) 2005, Brian McCallister

Thank You to Sponsors!