Cheap, Fast, and Good
You can have it all with Ruby on Rails
              Brian McCallister
          brianm@chariotsolut...
What is Ruby?
• Dynamic and Interpreted
• Strong support for OO programming
 • Everything is an object ( 2.next == 3 )
• S...
What is Rails?
• Model-2 Web Framework
• Object/Relational Mapping Library
• SOAP Producer/Consumer Framework
• Email Temp...
Principles Involved
• Less Code
• Convention over Configuration
• Proximity
• Least Surprise
• Make components play nicely ...
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 = DateTi...
Action
require 'date'

class AggregateController < ApplicationController

 model :entry
                                  ...
Controller
                                                 Controller
require 'date'

class AggregateController < Applica...
How We Got Here
ActionController::Routing::Routes.draw do |map|

  # map.connect ':controller/service.wsdl',
  #          ...
routes.rb
ActionController::Routing::Routes.draw do |map|
                     http://localhost/wombat/is/friendly
  # map...
routes.rb
ActionController::Routing::Routes.draw do |map|

  # map.connect ':controller/service.wsdl',
  #             :ac...
routes.rb
ActionController::Routing::Routes.draw do |map|

  # map.connect ':controller/service.wsdl',
  #             :ac...
Show Us Something!
<h1>Recent Stories...</h1>
<table class=quot;entryTablequot; >
<% for entry in @entries %>
  <tr>
    <...
Directives
<h1>Recent Stories...</h1>
<table class=quot;entryTablequot; >
<% for entry in @entries %>
  <tr>
    <td class...
Expressions
<h1>Recent Stories...</h1>
<table class=quot;entryTablequot; >
<% for entry in @entries %>
  <tr>
    <td clas...
Helpers & Partials
<h1>Recent Stories...</h1>
                                      my_view.rhtml
<%=

   render_partial q...
Helpers
<h1>Recent Stories...</h1>
                                      my_view.rhtml
<%=

   render_partial quot;entry_l...
Parameterized Partials
<h1>Recent Stories...</h1>
                                      my_view.rhtml
<%=

   render_parti...
Picking Views
• Default Views
• The View for that one over there...
• Named Views
• Something completely different?



   ...
Default Views
require 'date'

class AggregateController < ApplicationController

 model :entry
                         ag...
render_*
require 'date'

class AggregateController < ApplicationController

 model :entry

 def today
   @entries = Entry....
Named Views
require 'date'

class AggregateController < ApplicationController

 model :entry

 def another_one
   @entries...
Raw Rendering
require 'date'

class AggregateController < ApplicationController

 model :entry

 def direct_write
   rende...
Additional Options
• send_data
• send_file
• render_to_string
• render_nothing
• render_text with a block
• redirect_to
• r...
Helpers
• Programmatic output generation
• Global, Controller Specific, Importable
• Like tag libraries
 • kind of
• Lots o...
#{Controller}Helper
module AggregateHelper

 def say_hello(name)
                                                 Helper
 ...
Rules, well Suggestions
• #{Controller}Helper applied from
  matching Controller
• ApplicationHelper available everywhere
...
Action Pack Magic 3
• Controllers
• Helpers
• Views
• Things not discussed:
 • Components
 • Caching
 • Filters
          ...
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
...
The Schema




  (c) 2005, Brian McCallister
The Classes
class Feed < ActiveRecord::Base
  has_many :entries

 validates_presence_of :title, :url

end


...


class En...
Relations
class Feed < ActiveRecord::Base
  has_many :entries

 validates_presence_of :title, :url

end


...


class Entr...
Queries
def Entry.recent(count)
  Entry.find :all,
             :order =>'created_on DESC',
             :limit => count,
...
Criteria
def Entry.recent(count)
  Entry.find :all,
             :order =>'created_on DESC',
             :limit => count,...
SQL
def Entry.recent(count)
  Entry.find :all,
             :order =>'created_on DESC',
             :limit => count,
    ...
Code Generator
    This stuff rocks!




     (c) 2005, Brian McCallister
Creating Rails Project
brianm@kite:~/Sites$ rails apachecon
      create app
      create app/apis
      create app/contro...
This Created...
• Project Hierarchy
• Config file (for database connection)
• Rakefile (Makefile)
• System Test Harness
• Unit...
Configure Database
brianm@kite:~/Sites/apachecon$ cat config/
database.yml
development:
  adapter: postgresql
  database: r...
Some CRUD
brianm@kite:~/Sites/apachecon$ ./script/generate scaffold Talk
  dependency model
      exists    app/models/
  ...
A Table
apachecon=> create table talks (

 id serial primary key,

 name varchar(255) not null,

 presenter varchar(255) n...
Start the Server...
brianm@kite:~/Sites/apachecon$ ./script/server
=> Rails application started on http://0.0.0.0:3000
[20...
List




(c) 2005, Brian McCallister
TalksController
class TalksController < ApplicationController
  def index
    list
    render_action 'list'
  end

 def li...
Create




(c) 2005, Brian McCallister
The Edit View
<h1>Editing talk</h1>
<%= start_form_tag :action => 'update', :id => @talk %>
  <%= render_partial quot;form...
List Again




 (c) 2005, Brian McCallister
Unit and System Tests
brianm@kite:~/Sites/apachecon$ rake
(in /Users/brianm/Sites/apachecon)
ruby -Ilib:test quot;/usr/loc...
.htaccess
# General Apache options
AddHandler fastcgi-script .fcgi
AddHandler cgi-script .cgi
Options +FollowSymLinks +Exe...
Extensible Generator
• Some 3rd Party Generators:
 • Login Generator
 • Salted Hash Login Generator
 • Tabbed Navbar Gener...
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...
Session Storage
• Serialize to /tmp
• DRb Server
• Database
• memcached
• Custom
• No Sessions

                (c) 2005, ...
Scaling Up
• Multi-processing model (like prefork)
• DB Connection per FCGI Process
• Remote FCGI instances
• Static and D...
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 Ra...
That’s all folks!
      Brian McCallister
  brianm@chariotsolutions.com
 http://www.chariotsolutions.com/
Upcoming SlideShare
Loading in...5
×

Apachecon Rails

2,894

Published on

Published in: Technology, Spiritual
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,894
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
101
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Transcript of "Apachecon Rails"

  1. 1. 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
  2. 2. 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
  3. 3. 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
  4. 4. Principles Involved • Less Code • Convention over Configuration • Proximity • Least Surprise • Make components play nicely together! (c) 2005, Brian McCallister
  5. 5. Action Pack That Web Stuff (c) 2005, Brian McCallister
  6. 6. Request Cycle (c) 2005, Brian McCallister
  7. 7. Some Code require 'date' class AggregateController < ApplicationController model :entry def for_date date = DateTime.parse @params[:date] logger.debug quot;for_date: #{date}quot; @entries = Entry.on_date date end # Additional actions removed for slide’s benefit` end (c) 2005, Brian McCallister
  8. 8. Action require 'date' class AggregateController < ApplicationController model :entry Action def for_date date = DateTime.parse @params[:date] logger.debug quot;for_date: #{date}quot; @entries = Entry.on_date date end # Additional actions removed for slide’s benefit` end (c) 2005, Brian McCallister
  9. 9. Controller Controller require 'date' class AggregateController < ApplicationController model :entry Action def for_date date = DateTime.parse @params[:date] logger.debug quot;for_date: #{date}quot; @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
  10. 10. 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
  11. 11. routes.rb ActionController::Routing::Routes.draw do |map| http://localhost/wombat/is/friendly # 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
  12. 12. routes.rb ActionController::Routing::Routes.draw do |map| # map.connect ':controller/service.wsdl', # :action => 'wsdl' map.connect 'wombat/is/friendly', http://localhost/ :controller => :feeds, :action => :list map.connect '', :controller => :feeds, :action => :list # Default Route map.connect ':controller/:action/:id' end (c) 2005, Brian McCallister
  13. 13. 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, http://localhost/feeds/list :action => :list # Default Route map.connect ':controller/:action/:id' end (c) 2005, Brian McCallister
  14. 14. Show Us Something! <h1>Recent Stories...</h1> <table class=quot;entryTablequot; > <% for entry in @entries %> <tr> <td class=quot;entryTitlequot;> <%= entry.feed.title + quot;: quot; + entry.title %> </td> </tr> <tr> <td class=quot;entryBodyquot;> <%= entry.body %> </td> </tr> <% end %> </table> (c) 2005, Brian McCallister
  15. 15. Directives <h1>Recent Stories...</h1> <table class=quot;entryTablequot; > <% for entry in @entries %> <tr> <td class=quot;entryTitlequot;> <%= entry.feed.title + quot;: quot; + entry.title %> </td> </tr> <tr> <td class=quot;entryBodyquot;> <%= entry.body %> </td> </tr> <% end %> </table> (c) 2005, Brian McCallister
  16. 16. Expressions <h1>Recent Stories...</h1> <table class=quot;entryTablequot; > <% for entry in @entries %> <tr> <td class=quot;entryTitlequot;> <%= entry.feed.title + quot;: quot; + entry.title %> </td> </tr> <tr> <td class=quot;entryBodyquot;> <%= entry.body %> </td> </tr> <% end %> </table> (c) 2005, Brian McCallister
  17. 17. Helpers & Partials <h1>Recent Stories...</h1> my_view.rhtml <%= render_partial quot;entry_listquot;, :stories => @entries %> <table class=quot;entryTablequot; > _entry_list.rhtml <% for entry in stories %> <tr> <td class=quot;entryTitlequot;> <%= entry.feed.title + quot;: quot; + entry.title %> </td> </tr> <tr> <td class=quot;entryBodyquot;> <%= entry.body %> </td> </tr> <% end %> </table> (c) 2005, Brian McCallister
  18. 18. Helpers <h1>Recent Stories...</h1> my_view.rhtml <%= render_partial quot;entry_listquot;, :stories => @entries %> <table class=quot;entryTablequot; > _entry_list.rhtml <% for entry in stories %> <tr> <td class=quot;entryTitlequot;> <%= entry.feed.title + quot;: quot; + entry.title %> </td> </tr> <tr> <td class=quot;entryBodyquot;> <%= entry.body %> </td> </tr> <% end %> </table> (c) 2005, Brian McCallister
  19. 19. Parameterized Partials <h1>Recent Stories...</h1> my_view.rhtml <%= render_partial quot;entry_listquot;, :stories => @entries %> <table class=quot;entryTablequot; > _entry_list.rhtml <% for entry in stories %> <tr> <td class=quot;entryTitlequot;> <%= entry.feed.title + quot;: quot; + entry.title %> </td> </tr> <tr> <td class=quot;entryBodyquot;> <%= entry.body %> </td> </tr> <% end %> </table> (c) 2005, Brian McCallister
  20. 20. Picking Views • Default Views • The View for that one over there... • Named Views • Something completely different? (c) 2005, Brian McCallister
  21. 21. Default Views require 'date' class AggregateController < ApplicationController model :entry aggregate/for_date.rhtml def for_date date = DateTime.parse @params[:date] logger.debug quot;for_date: #{date}quot; @entries = Entry.on_date date end # Additional actions removed for slide’s benefit` end (c) 2005, Brian McCallister
  22. 22. 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
  23. 23. 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
  24. 24. Raw Rendering require 'date' class AggregateController < ApplicationController model :entry def direct_write render_text quot;Hello World!quot; end No template! # Additional actions removed for slide’s benefit` end (c) 2005, Brian McCallister
  25. 25. 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
  26. 26. Helpers • Programmatic output generation • Global, Controller Specific, Importable • Like tag libraries • kind of • Lots of built-ins (c) 2005, Brian McCallister
  27. 27. #{Controller}Helper module AggregateHelper def say_hello(name) Helper quot;<h1>Hello, #{name}quot; end end <h1>Recent Stories...</h1> Usage <%= say_hello quot;Brianquot; %> <table> <% for entry in @entries %> ... (c) 2005, Brian McCallister
  28. 28. 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
  29. 29. Action Pack Magic 3 • Controllers • Helpers • Views • Things not discussed: • Components • Caching • Filters (c) 2005, Brian McCallister
  30. 30. Active Record You get the data from the database and shake it all about... (c) 2005, Brian McCallister
  31. 31. 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
  32. 32. The Schema (c) 2005, Brian McCallister
  33. 33. 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
  34. 34. 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
  35. 35. 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 [quot;select e.* from entries e where created_on < ?quot;, date] end (c) 2005, Brian McCallister
  36. 36. 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 [quot;select e.* from entries e where created_on < ?quot;, date] end (c) 2005, Brian McCallister
  37. 37. 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 [quot;select e.* from entries e where created_on < ?quot;, date] end (c) 2005, Brian McCallister
  38. 38. Code Generator This stuff rocks! (c) 2005, Brian McCallister
  39. 39. 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$ (c) 2005, Brian McCallister
  40. 40. 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... (c) 2005, Brian McCallister
  41. 41. 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$ (c) 2005, Brian McCallister
  42. 42. 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$ (c) 2005, Brian McCallister
  43. 43. 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 quot;sessions_id_seqquot; for serial column quot;sessions.idquot; NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index quot;sessions_pkeyquot; for table quot;sessionsquot; CREATE TABLE apachecon=> (c) 2005, Brian McCallister
  44. 44. 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
  45. 45. List (c) 2005, Brian McCallister
  46. 46. 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
  47. 47. Create (c) 2005, Brian McCallister
  48. 48. The Edit View <h1>Editing talk</h1> <%= start_form_tag :action => 'update', :id => @talk %> <%= render_partial quot;formquot; %> <%= submit_tag quot;Editquot; %> talks/edit.rhtml <%= end_form_tag %> <%= link_to 'Show', :action => 'show', :id => @talk %> | <%= link_to 'Back', :action => 'list' %> <%= error_messages_for 'talk' %> <!--[form:talk]--> <p><label for=quot;talk_descriptionquot;>Description</label><br/> <%= text_area 'talk', 'description' %></p> <p><label for=quot;talk_presenterquot;>Presenter</label><br/> <%= text_field 'talk', 'presenter' %></p> talks/_form.rhtml <p><label for=quot;talk_namequot;>Name</label><br/> <%= text_field 'talk', 'name' %></p> <!--[eoform:talk]--> (c) 2005, Brian McCallister
  49. 49. List Again (c) 2005, Brian McCallister
  50. 50. Unit and System Tests brianm@kite:~/Sites/apachecon$ rake (in /Users/brianm/Sites/apachecon) ruby -Ilib:test quot;/usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/ rake/rake_test_loader.rbquot; quot;test/unit/talk_test.rbquot; 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 quot;/usr/local/lib/ruby/gems/1.8/gems/rake-0.5.4/lib/ rake/rake_test_loader.rbquot; quot;test/functional/ talks_controller_test.rbquot; 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
  51. 51. .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
  52. 52. Extensible Generator • Some 3rd Party Generators: • Login Generator • Salted Hash Login Generator • Tabbed Navbar Generator • Search Generator • Configuration Generator • Postback Generator (c) 2005, Brian McCallister
  53. 53. Deployment Time Stuff to be aware of... (c) 2005, Brian McCallister
  54. 54. Environment • Development • Test • Production (c) 2005, Brian McCallister
  55. 55. Web Servers • Webrick • Apache Web Server • CGI • mod_ruby • fastcgi • lighttpd • fastcgi (c) 2005, Brian McCallister
  56. 56. Session Storage • Serialize to /tmp • DRb Server • Database • memcached • Custom • No Sessions (c) 2005, Brian McCallister
  57. 57. 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
  58. 58. The Diagram (c) 2005, Brian McCallister
  59. 59. 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
  60. 60. That’s all folks! Brian McCallister brianm@chariotsolutions.com http://www.chariotsolutions.com/
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×