Rails is large and complex. This page is indended to contain just enough information on each subject to jog your memory. Comprehensive documentation can be found elsewhere.

Rails is written in Ruby.

Models

Associations

  • belongs_to
  • has_one
  • has_many
  • has_and_belongs_to_many is basically deprecated. Use has_many :through instead.

... etc.

Migrations

A very cool cheat sheet: http://garrettsnider.backpackit.com/pub/367902

Column Types

  • :integer -- Regular 4-byte signed integer
  • :float -- floating point number (single or double precision?)
  • :string -- String of any length. :length 40
  • :boolean -- true/false. 0, '0', 'f', 'false', '', and nil are all considered false. Everything else is true.
  • :date
  • :time
  • :datetime
  • :timestamp
  • :text
  • :binary, :limit => 2.megabytes -- a blob
  • :primary_key -- don't use this. The default primary key, id, is just fine.
  • absent: sets and enums. Here's the workaround.

General Column Attributes:

  • :limit => integer. Meaning is different for different fields.
  • :default => "No value"
  • :unique => true
  • :null => false
  • :precision, :scale -- precision and scale for :decimal column.

Automatically Maintained Fields [1]:

If you name a field one of the following magic names, ActiveRecord will maintain its value automatically.

  • :created_on, :updated_on -- saves date [2]
  • :created_at, :updated_at -- saves date and time

etc.

Calls:

  • create_database, drop_database
  • create_table, drop_table
  • add_column(table, name, type, ...)
  • rename_column(table, old_name, new_name)
  • change_column(table_name, column_name, type, options)
  • remove_column(table, column)
  • add_index, remove_index

Controllers

When the controller is done using models to manipulate the data, it must pass control to a view. Normally a view of the same name as the controller's method name (the 'action') is selected. These calls allow you to pass control to a different view.

  • render :action => name -- Renders the page using a different action. Note that the URL isn't changed.
  • redirect_to :action => name -- Sends a redirect to the given action. The URL is changed but all state variables set inside the controller are lost because the page gets rendered again. Say "render() and return false" to explicitly prevent this from happening (the false is to ensure that the filter chain is terminated if you called render from within a filter).

Many people expect processing to stop when one of these calls are made. This is not the case! These calls do return, so if your code produces more output, you will get a DoubleRenderError.

Try to make your controllers as small as possible. Push as much code as you reasonably can into the models. There it's easier to keep your app refactored and easier to test (both by unit tests and by typing at script/console).

Views

By default a controller's action will invoke a view with the same name.

Ways to Generate Views

Here are a few different packages that you can use to generate your views.

RHtml/ERb Templates

Ruby embedded in pseudotags much like jsp or php. rhtml is interpreted using ERb (embedded ruby).

  • <%= 1+2 %> replace the tag with the result of the expression
  • <% 3.times do %> ... <% end %> -- loops over content
  • <% -%> -- end the Erb tag with a minus to suppress the trailing newline.
  • <% h "<>" %> -- Escape using the h() function.
  • <%# %> -- a comment, the content of the tag is ignored.

Builder Templates

Uses methods to output XML.

RJS Templates

Used to upload ajax to already-rendered pages.

HAML Templates

Inteded to make typing HTML almost trivial. No more matching close tags, syntactic issues, etc. That's the intent, anyway. HAML is a plugin right now but it's popular enough that you're likely to see it elsewhere. It's a little strange.

http://haml.hamptoncatlin.com/

Liquid Templates

A very good templating engine. Also a plugin, also very popular. Unless you're letting your users enter templates (in which case ERb is totally insecure), I don't see much advantage to liquid though.

http://home.leetsoft.com/liquid/

Helpers

Functions that take arguments and return a corresponding chunk of HTML. Each controller has a corresponding helper in app/helpers/controller_helper.rb. Helpers in application_helper.rb are available to all controllers.

  • link_to -- generates a hyperlink
 <%= link_to "Click here", :action => "myresponse" %>
 <%= link_to "Click here", :url => "/chapter2/myresponse" %>
    :confirm => "Are you quite sure?", :before => "func();"
    :with => "'foo='+escape($F('myElement'))" -- set query variables
 <%= link_to_remote "Link", :url => {:action => "myresponse"}, :update => "object_id" %
 <%= link_to_remote "Link", :url => "/chapter2/myresponse", :success => "alert(request.responseText)" %>
 # allows you to write rjs without round-tripping to the server
 <%= link_to_function "update_page" do |page|
       page.alert "Hello from update_page" 
     end %
  • <%= h %> -- HTML-encodes its arguments (see example in RHtml/ERb Templates above.
  • <%= form_tag :action => 'reverse' %> <%= end_form_tag %>
  • <%= form_for %>, <%= remote_form_for %>

Render

Used to invoke other views. For instance, this will invoke app/views/controller/_person.rhtml passing the @person variable as a local named 'person'.

 <%= render :partial => "person" %>
 <%= render :partial => "person", :locals => { :person => @scott } %>  # passing @scott as 'person'
 <%= render :partial => "person", :collection => @people %> # rendering each person in @people
 <%= render :partial => "chapter1/person" %> # change controllers: renders app/views/chapter1/_person.rhtml
  :content_type => "text/javascript" # can change the content-type

Configuration / Running

To read configuration items from config/environment.rb and friends, try i.e.:

  ActionController::Base.cached_session_options[:secret]

Of course, you can just use constants directly (but without the $). [3]

Sessions / Cookies

http://www.quarkruby.com/2007/10/21/sessions-and-cookies-in-ruby-on-rails

Testing

  • first run rake db:test:clone to copy your development schema into your test database.
  • rake test -- runs all tests
  • ruby test.rb -- runs all tests in a single test file (located in test/unit or test/functional)

Remember that yaml files are parsed with ERb so you can do the following. In general, however, it's will make your life easier in the future to add parameters to your models so they can work in any context rather than spoon-feeding them custom data.

 date_available: <%= 1.day.from_now.strftime("%Y-%m-%d %H:%M:%S") %>

Assertions you can use:

  • assert_kind_of Worker, @worker
  • assert_equal "var", var

Add "_before_type_cast" to complex datatypes (like datetimes) to compare the raw text from the database. (why do this? why not just create an identical object to what the model produces and then test that?)

Scaffold

Remember that scaffold is not meant to be used as an API! It's meant to be completely replaced as your app matures. If you want dynamic scaffolding, see one of these projects:

Debugging

  • Any time you have a question about how your app is behaving, write a unit or functional test to try it out.
  • Run script/console and type at your program directly. (s=Shop.find(:first); s.workers(); etc...)
  • logger.error("string"), logger.warn, .debug, .info, .fatal
  • Set a breakpoint anywhere in your code just by inserting the word "breakpoint". Once the breakpoint is hit, interact with it by running script/breakpointer.

Deploying

Capistrano gets painful. I really want to try this out: http://rubyhitsquad.com/Vlad_the_Deployer.html

Version Control

There are scripts out there to automate the process of checking your project into svn. Since this happens so rarely (once a month maybe), I prefer to just do it manually. Here are the steps needed:

 $ svn mkdir http://example.com/svnproj    # create the directory that will hold your new project
 $ svn co http://example.com/svnproj       # check out to the local filesystem

Create the typical svn directory structure

 $ cd svnproj
 $ svn mkdir trunk branches tags           # (personally, I never use tags)

Add your app to the new trunk

 $ cp -r myproj/* trunk                    # myproj is the unversioned directory containing your app
 $ cd trunk
 $ mv config/database.yml config/database.yml.sample
 $ mkdir -p vendor/plugins                 # this needs to exist for Piston later
 $ svn add *                               # add all files to the repository

At this point, I recommend running 'rm -rf vendor' to get rid of all existing rails and plugins. You will re-create their checkouts later using Piston.

Get rid of the log files.

 $ svn rm log/*
 $ svn propset svn:ignore "*.log" log/

Ignore the tmp directory.

 $ svn rm tmp
 $ svn propset svn:ignore tmp .

Store an example database.yml with bogus passwords in the repo (created above) and ignore the actual database.yml so sensitive information doesn't get checked in.

 $ svn propset svn:ignore database.yml config/

Only do this step if you have files in vendor/rails or vendor/plugins: Re-create your plugins using Piston. Unfortunately, piston requires your vendor directory to already be checked in. This means that the initial revision of your project will be broken! Oh well, just make sure to note that in the commit message.

 $ svn ci -m "Initial checkin.  NOTE: this checkin is broken!  vendor/plugins hasn't been populated yet."
 $ piston import http://dev.rubyonrails.org/svn/rails/trunk vendor/rails
 $ piston import svn://hobocentral.net/hobo/trunk vendor/plugins/hobo
 $ piston import svn://errtheblog.com/svn/plugins/classic_pagination vendor/plugins/classic_pagination

Set up your database so that you can test out the app

 $ cp config/database.yml.sample config/database.yml
 $ vi config/database.yml
 $ script/server

Does it work? If so, you're ready to commit the first (or second) revision.

 $ svn ci

Documentation

TODO

Look at Merb, much simpler than RoR: http://merb.rubyforge.org/files/README.html