Using Routes Instead of Custom REST Actions 9

Posted by jeff Friday, August 22, 2008 01:40:00 GMT

Photo credit

Suppose you’re trying to be a good Rails developer and use RESTful routing wherever possible in your application. Using the ever-present blog example1, you might implement your PostController’s index action like this:

def index
  @posts = Post.all unless request.format.rss?

  respond_to do |format|
    format.html  # render posts.html.erb
    format.xml   { render :xml => @posts }
    format.rss   { @posts = Post.all(:limit => 10, :order => 'created_at desc') }
  end
end

In other words, for HTML and XML clients, we return all the posts, formatted accordingly. For RSS readers, we only give out the 10 most recent posts2.

Without doing anything special, this url:

/posts.rss

will automatically use /app/views/posts/index.rss.rxml (which we have to write) to generate our RSS data feed.

But I Knew That Already

Ok, but suppose you’re converting an existing site, and your readers already grab your feed at this url:

/posts/feed

Now, our code won’t work. Rails will try to call the show action, using feed as an :id parameter. Not good.

At this point, the easiest thing to do is to add a custom action to your controller:

def feed
  @posts = Post.all(:limit => 10, :order => 'created_at desc')
  # render default template
end

Rails will automatically find a template named, say, app/views/posts/feed.rxml and use it generate the feed.

But those who know me, know that I despise custom actions. Yes, once in a blue moon I have to use them. But in this situation, I prefer to use a more elegant solution: routes.

Ok, But Did You Know How To Do This?

We need to support /posts/feed as our url for RSS feeds. Remember that Rails routing allows us to route any url we want into any controller action we want. So somewhere above the map.resources :posts line in our routes.rb file, we do this3:

  map.feed 'posts/feed', :controller => 'posts', :format => 'rss'

And now if you go to /posts/feed, your glorious index action will be called and will respond as if an RSS client has made the request.

Cool, no?

1 At our workshop, we will build something more interesting than a blog.

2 We’ll also learn how to use named scopes to simplify this kind of code.

3 Even in development mode, you might have to restart your local server (mongrel or webrick or thin or whatever) to get Rails to pickup your routing changes.


Ready to learn more about RESTful development? Register now for REST for Rails before the seats are all gone.

Do Your Own (Ruby) Olympics 1

Posted by jeff Monday, August 18, 2008 04:46:00 GMT

Suppose someone told you that they are an Olympic athlete. What would you know about them? You could safely assume that they’re in great physical shape. But you’d have to ask more questions: what kind of athlete are you? Are you a swimmer? A runner? Maybe you do archery?

Even knowing the sport might not reveal all of their talents. For example, many Olympic events have a variety of events associated with them. To qualify for the swim team, you have to swim in multiple ways: freestyle, butterfly, backstroke, and breast stroke. Gymnasts also have to be good at a lot of things to be considered Olympic material: not only the floor exercise, but also the pommel horse, vault, and rings, and I don’t know what else.

Swimmers who only excel at freestyle swimming might be great at freestyle swimming, but they’ll never really be able to be good enough to make the Olympic team. Gymnasts who are great at the pommel horse, but never bother to learn the other disciplines, won’t make the team either.

Umm… Isn’t This Blog About Rails?

Ok, I’m getting to the point… Perhaps you currently consider yourself to be a real cool Rails developer. But do you really take the time to learn all of different skills that entails?

If there was a Rails event at the Olympics, would you qualify?

Here are the main “events” that I think Rails developers need to be good at to be considered “Olympic” quality.

Ruby

First and foremost, how is your Ruby? When was the last time you learned something new about Ruby? I don’t mean Rails. I mean Ruby. If you haven’t been able to refactor your code into something simpler and clearer, then you probably need to spend more time with the Ruby language.

ActiveRecord

ActiveRecord has actually gotten pretty big. Some features may exist that you don’t know about, or don’t know how to use (has_many :through and named_scope come to mind). Learn them. Use them. They can simplify your code. Not sure about the difference between association proxies and arrays? Wondering what SQL calls ActiveRecord is really making on your behalf? Then it’s time to do some strength conditioning and learn more about it.

Views

Maybe you’re feeling pretty good with your HTML views. How about your Javascript skills? Are you using the best possible form_for syntax in your views? Have you learned how to use view helpers to DRY up your view code?

SQL

Just because ActiveRecord writes SQL for you doesn’t mean you don’t have to learn SQL. It’s a muscle that we Rails developers can avoid using, but that probably mean that it’s atrophied. I know for myself, SQL is probably my weakest link when it comes to Rails development. I need to spend more time in the SQL gym, so I can better understand and improve upon the database performance in my apps.

REST

If you’re not doing RESTful development, but you’re using Rails 1.2 or higher, then you really need to get on the ball. Or maybe you think you’ve got the RESTful thing down pat, but you still end up adding a bunch of custom actions to your controllers. If so, then it’s time to see your friendly neighborhood trainer and get your REST skills in shape. Your apps (and your boss) will thank you.

Anything Else

You probably feel a twinge of guilt about some area of Rails that you know you should get better at, but you just never seem to have the time. Do you know how to write a plugin? Do you know how to contribute a patch to Rails? Do you need to get better with subversion or git? How about deployment? The list could go on and on.

If you want to say you’re a professional Rails developer, you can’t just be good at script/generate model Product. Identify what you want to learn next, and learn it.

Have you learned something recently that really helped you get to the next level in your Rails coding? Or, is there a weak area that you know you want to get better at? Drop us a comment and let us know.


Ready to learn all about REST with Rails? Our next workshop is coming up fast (Oct 4, 2008), so register now before all the seats are taken.

Ruby on Rails Google Group Getting Better 4

Posted by jeff Thursday, August 14, 2008 20:45:00 GMT

Frederick Cheung has done a great job eliminating the spam problem that had been plaguing the Ruby on Rails Google Group. I had more or less abandoned the group months ago, because the value of the group had plummeted.

But lately I’ve enjoyed posting questions and answers there again. If you have never tried the Google Group (also mirrored here if you prefer a different UI than Google’s), I recommend it.

Thanks to Frederick for volunteering his time and energy to serve the Rails community this way.


New to REST in Rails? Join us at REST with Rails Oct 4, 2008, in Austin, TX

REST with Rails: Registration Now Open 2

Posted by jeff Sunday, August 10, 2008 14:53:00 GMT

Registration is officially now open for REST with Rails, coming to Austin, Texas on Saturday, October 4, 2008.


REST with Rails

When: Saturday, October 4, 2008, 8:30am – 5:00pm

Where: FiveRuns Offices: 209 West 9th Street, Suite 100, Austin, Texas 78701

Cost: $295.00

If you’re new to REST and want to learn what all the fuss is about, join us for a fun, one-day workshop! We’ll cover all the basics of RESTful development with Rails. This class is geared for those new to REST, regardless of how long you’ve been using Rails. (We assume you can at least create a simple Rails app, but not much more than that.)

All of our workshops have a friendly atmosphere. There really are no dumb questions. We use an informative combination of slides and live coding demos instead of long, boring powerpoint presentations.

And, thanks to FiveRuns, this public workshop will be held at their brand new offices in downtown Austin, Texas!

Seating is quite limited, so read all about it and then sign up!

(Those who signed up for early notification should have received your discount code. If for some reason you didn’t get it, please let us know.)

Thanks, and we hope to see you there!

Using Rails Generators

Posted by jeff Tuesday, August 05, 2008 19:43:00 GMT

Most people who start learning Rails today quickly learn about generators. They’re easy to use and very handy.

Still, like any other code-generation device, it’s important to understand what they’re doing when you use them. I thought that would go without saying, but I should know better. I remember back in the day of ATL COM programming on Windows, adoption of ATL really took off when Visual Studio added a few wizards to help you create COM components easily. The number of people who didn’t realize what it was doing was surprising. When the smallest bug or issue would come up, they were at a loss to know what their program was doing. To be fair, I also didn’t know what the wizards were doing when I started learning, either. I finally learned how to create COM components by hand (IDL….yuck), and once I did a couple, I knew everything I needed to know. Using the wizards after that was fun and helpful, since I knew how to change (or fix) anything I needed to after the wizard had generated its code.

Ok, back to the present… once in a while I notice that people don’t really understand Rails generators. I don’t really think it’s worth a blog post to explain all of them, but I did want to mention a quick tip that you might not already be aware of if you’ve just recently started with Rails: if you do this,

c:\myapp> ruby script\generate

(and then wait a minute) you’ll get a page showing all the options and generators available.

You can get specific help on a generator (say, the scaffold generator) like this:

c:\myapp> ruby script\generate scaffold

The help that’s emitted to the console is dense but usually quite complete.

Just do me one favor: after using a generator, learn what it did for you. There’s nothing a generator can do that you can’t do yourself. They just take the tedium out of some common Rails tasks that you’d otherwise have to do by hand.

Now, if you’d like to see an article on a specific generator, or have other questions about Rails generators, just drop us a comment here.


New to REST in Rails? Come to REST with Rails Oct 4, 2008, in Austin, TX

REST with Rails: Our Next Workshop is Coming Soon

Posted by jeff Thursday, July 31, 2008 16:40:00 GMT

Announcing the next Softies on Rails class!

Here’s the scoop:


REST with Rails

When: Saturday, October 4, 2008, 8:30am – 5:00pm

Where: Downtown Austin, Texas

Cost: $295.00

Wait… Austin?

We usually hold our classes in Chicago, but we know that not everyone can make it to Chicago. So earlier this year we asked you to vote on where we should hold our next class, and what the topic should be.

Here’s the results of the location poll. Austin won by a slim margin over Southern California, 22% to 20%:

location poll

And here’s the result of the topic poll. A one-day “REST and Web Services” class was the clear winner:

topic poll

Thanks for everyone’s patience – we know it’s taken a little while to pull this together (and we’ve been a little busy this year).

We’ll be able to run this class as a Purple Workshop, Jeff’s side venture for putting on short, fun classes for beginning Rails developers.

What To Expect

If you’ve never attended a Softies class before, here’s the 10-second summary: we aim for a “no question is too dumb” atmosphere; geared for those new to Rails (or at least to the topic of REST); and value live coding demonstrations over boring powerpoint presentations.

For complete details, head on over to the official workshop page . Sign up to be notified when registratation opens (which should be in about a week or so). We’re limiting the class size to only 15 this time, so we recommend you join the notification list.

Questions? Reactions? Let us know by leaving a comment.

Thanks, and we hope to see you in Austin!

What Was Your First Rails App?

Posted by jeff Tuesday, July 15, 2008 20:26:00 GMT

Came across an interesting thread on the Ruby-Talk google group. Eric Hegwer started it by asking the question, “What was YOUR first Ruby project?”

It’s fun seeing the wide spectrum of answers. People were using Ruby for things I would never have dreamed of.

I’d like to ask our readers a similar question: what was the first Rails app you wrote? Maybe you never made it public, but what was your first Rails project?

TakeFive Interview

Posted by jeff Tuesday, July 08, 2008 01:42:00 GMT

Just a quick post to mention that I was interviewed on the FiveRuns blog as part of their Take Five Series. Be sure to post a comment there if you have any thoughts or questions about the interview.

Laziness and Stupidity, Part 2

Posted by jeff Tuesday, May 20, 2008 14:23:00 GMT

Last time, I presented a couple screenshots from a recent user experience I had on the web. (If you missed it, go back and read it real quick, or the rest of this post won’t make much sense).

Thanks to everyone who responded. It was fun to see the variety of approaches that people took to solving the problem of cleaning up the “miles driven” field automatically.

For what it’s worth, the approach I would have taken would have been to use the before_validation helper in the model, as a couple of people suggested (hence the words “model validation” in the original article title). There were a few suggestions that seemed to imply that the cleanup would happen in the controller, but I don’t think that’s the right place. By making the model responsible for its data, this logic becomes more reusable and it becomes easier to attach error messages as needed.

For the curious, to just remove commas and ignore any digits after a decimal point, this will work:

miles.delete(',').to_i

but this doesn’t remove any other non-digit characters, so a value like “A123.4” will become zero. This is one of those rare moments where C# is a bit better, as Int32.Parse would come in real handy here.

But rather than try to come up with a more complicated regular expression to get rid of letters and other invalid characters, I would simply use another model validation helper to enforce a greater-than-zero value:

validates_numericality_of :miles, :greater_than => 0, :message => "must be higher than zero"

I guess my point is, I think sometimes we try to do too much. Rails is a pretty big framework and often has something built-in to make your job easier than you think.

Laziness and Stupidity; or, Model Validation in Rails

Posted by jeff Sunday, May 18, 2008 00:47:00 GMT

I thought my post on how to customize the ActiveRecord error header messages was a one-off about how we, as web developers, should treat our users as humans. But it looks like this is turning into an occasional series.

Today’s Victim

I’m an avid fan of Consumer Reports. I’ve subscribed for about as long as I can remember. Subscribers get to take part in their annual survey, which they mainly use for their car ratings. I kind of hated in the past, since their survey site was so lame and hard to use. I think it might have been an old-school perl-driven cgi script, if I remember correctly.

But this year, I was pleasantly surprised that it had been given a nice facelift. From the url I can see that they’re now using something more modern (ASP.NET).

But then, about four questions in, I got this:

Don’t use commas? Are you kidding me?

This is the kind of crap that permeates the web and is just sheer stupidity.

Would it really be so hard for the .NET app to remove commas from whatever value I enter? (No, it wouldn’t be, it’s actually pretty easy… but I digress). My guess is, the database column is an integer column, and if there’s a comma in the number, the insert statement will fail. So instead of writing code to take commas out, they tell the user to make sure they only enter integer numbers.

This is amazing. Shame on CR for letting their developers get away with this.

Since I want to help CR by taking the survey, I made sure not to use commas and entered “5000” and clicked Next.

It Gets Even Worse

But curiosity got the better of me, and so I clicked “Previous” to go back. Then I entered “5,000” with the comma, just to see what would happen. I began to think, maybe the bark is worse than the bite? Surely, the asp.net code really could deal with commas in numbers, right?

Nope:

Look at that awful hideous generic error message. Listen to the tone: you did something wrong, you idiot! Now, fix it!

We’ve already gone over how to customize these kinds of error messages in Rails. But even better is to avoid this situation in the first place.

If this kind of thing is hard to do in your favorite framework, then switch to Rails. Because it’s really easy to do in Rails.

Your Mission, Should You Choose To Accept It

I was planning on doing a little “Validation 101” here to demonstrate how your Rails applications can elegantly clean up user input before it gets to the database.

But I thought it might be more fun this time around to let you do the talking. So here’s my challenge to everyone reading this blog:

  1. Leave a comment to this post, giving the most elegant solution you can think of to remove commas from a text field in Rails.
  2. You can assume that there is an ActiveRecord class named Vehicle that has an attribute named miles.
  3. IMPORTANT: Be sure to put your code inside of a big <pre> tag.

Bring it on!

Tooltips for Rails Validation Errors

Posted by jeff Friday, May 09, 2008 17:47:00 GMT

A while back we talked about humanizing validation error header messages in Rails. Mike Sepcot now shows you to create error-specific tooltips by using a custom FormBuilder. Very cool stuff, so check it out.

Using Thin Instead Of Mongrel 18

Posted by jeff Sunday, April 27, 2008 17:13:00 GMT

I’m learning how to switch from mongrel to thin. If you’ve never heard about thin, you can try to read the documentation, learn about the new-cool-kid-on-the-block Rack specification, or just take my word for it: thin is better than mongrel, so just use it, ok?

It can be intimidating to try something that’s got buzz about it but you don’t really know how to get started and no one seems to really be stopping to explain it in English. So if you’re still using script/server to start mongrel or webrick, and are interested in something new, then read on.

The main reason people switch from mongrel to thin is for improved performance on their servers. But for me to learn something new, I have to first dogfood it in my development environment first. That’s what I’ll be covering in this article, and in an article coming soon to a blog near you I’ll explain how to replace your mongrel configurations with a thin configuration instead.

One note: thin is currently best suited for Mac and Linux people. I’ve used it on Windows as simple script\server replacement, but on Windows background (“daemon”) mode isn’t supported, nor is clustering, so Mongrel is still my choice on Windows boxes.

gem install thin

It won’t wreck your computer. Honest. Just do it.

Using thin to replace script/server

script/server doesn’t know to startup thin instead of mongrel (as of Rails 2.0.2). So instead, you just go to your RAILS_ROOT and do this:

myapp$ thin start

You’ll see it start up on port 3000 using the “Rails adapter.” (Turns out thin can support a variety of Ruby frameworks, not just Rails).

Need to use a different port, say, 4000?

myapp$ thin start -p 4000

On Mac/Linux, you could use script/server -d. With thin, you also use -d:

myapp$ thin start -d

To stop thin that’s been start with -d, you just, um, stop it:

myapp$ thin stop

By default, thin will create .pid files in your applications /tmp folder. If you prefer to keep your .pid files in the log directory like Mongrel did, you can specify that:

myapp$ thin start --pid log/thin.pid

Just be sure to stop it the same way:

myapp$ thin stop --pid log/thin.pid

More Options

Do a thin -h to see all the possible options. I’ll highlight just a couple more.

To specify your Rails environment, use -e just like with Mongrel:

myapp$ thin start -e production

Stopping and restarting the server is easier than it was with mongrel:

myapp$ thin restart

(Remember to also use your -e, -d,—pid, etc. options here as well if you need to.)

Next Time

Next time we’ll talk about how to setup a cluster of thin instances on your server, instead of using mongrel_cluster.

And I’d also like to be able to answer any other questions about script/server, mongrel, thin, nginx, apache, or why the Blackhawks missed the playoffs again this year. Leave a comment with your suggestions and questions if you’re so inclined.

Creating Edge Rails Projects via Git on Windows or Mac 4

Posted by jeff Thursday, April 24, 2008 15:19:00 GMT

Brian Hogan has a sequel to his original article for creating a local Rails project with the edge version of Rails. Now that Rails is in Git instead of Subversion, Windows users especially have to jump through a hoop or two to get the code down.

For those new to Rails, “edge Rails” referes to the latest bleeding edge of the Rails code (the “nightly builds”, if you will, though edge rails is built continuously as patches are submitted, not just nightly). Creating a new Rails project with edge rails, instead of a released version like 2.0.2, allows you to start learning about the features that will be in the next version. If you (like me) like to do that, Brian’s article is a great resource.

Better messages for ActiveRecord validation errors 22

Posted by jeff Wednesday, April 23, 2008 02:14:00 GMT

So you’ve been working with Rails for some time and you think you know what you’re doing, right? And you know to remove all your scaffolding before you ship, right?

Suppose you’re writing an app to let two people play a game over them thar tubes.

Would you still do this?

<%= error_messages_for :game %>

Do you realize that your users will see something like this?

lame_error

error_messages_for is scaffolding in disguise. It will help you get going but it’s meant to be thrown away. Only computer geeks like to know about “fields” and things being “saved.”

Your users just want to start a game. Treat them like humans and specify your own header and subtitle messages. (Rails documentation does exist despite everyone’s complaints that there’s “no documentation”).

So how about something like this instead:

<%= error_messages_for :game, :header_message => "Please Try Again!", :message => "We had some problems starting the game:" %>

which will produce something like:

nice_error

Absolute Moron's Guide to Forms in Rails, Part 5 22

Posted by jeff Thursday, April 10, 2008 13:05:00 GMT

List Boxes in Rails

Today we will tackle the more thornier controls: list boxes and combo boxes. Let’s add the ability to select the origin and destination airports for our flight from a list of available airports.

Airports

We need a small list of airports to choose from, so let’s create a lookup table.

script/generate scaffold Airport code:string
rake db:migrate

Use the generated scaffold UI at /airports to add a few airports. Here’s what I chose:

We also need add two columns to our flight model:

script/generate migration AddAirportsToFlight origin_id:integer destination_id:integer
rake db:migrate

Notice that we’re storing integers, not strings this time. This is so we can hold foreign keys to the appropriate rows in the Airports table. We also need to open up the flight.rb model file and specify the relationship between flights and airports.

class Flight < ActiveRecord::Base

  belongs_to :origin, :class_name => "Airport" 
  belongs_to :destination, :class_name => "Airport" 

end

If you’re not very familiar with ActiveRecord yet, the code here uses a slightly more advanced syntax of the belongs_to method since the lookup table class name is not related to the attribute names we want in our Flight class. (If Rails knew that “origin_id” and “destination_id” are intended to be foreign keys to the Airports table, that would be magic indeed).

Similarly, let’s hook things up on the Airport side of the equation:

class Airport < ActiveRecord::Base

  has_many :departures, :class_name => "Flight", :foreign_key => "origin_id" 
  has_many :arrivals, :class_name => "Flight", :foreign_key => "destination_id" 

end

Before we use any GUI sugar, open up your console and hook up a flight to two airports (I’ve omitted a bunch of the console output for clarity):

>> f = Flight.find(1)
=> #<Flight id: 1, number: "123", created_at: "2008-04-05 20:05:24", updated_at: "2008-04-05 20:05:24", meal: false, equipment: nil, origin_id: nil, destination_id: nil>

>> ord = Airport.find_by_code 'ORD'
>> pdx = Airport.find_by_code 'PDX'

>> f.origin = ord
>> f.destination = pdx
>> f.save!
=> true

And now, behold the beauty and power of ActiveRecord associations that give us a natural-language vocabulary for our models.


>> ord.departures
=> [#<Flight id: 1, number: "123", created_at: "2008-04-05 20:05:24", updated_at: "2008-04-06 10:22:23", meal: false, equipment: nil, origin_id: 1, destination_id: 2>]
>> pdx.arrivals
=> [#<Flight id: 1, number: "123", created_at: "2008-04-05 20:05:24", updated_at: "2008-04-06 10:22:23", meal: false, equipment: nil, origin_id: 1, destination_id: 2>]

Awesome!

Now let’s see how we can make those associations in our form instead of through the console.

Selecting Airports

In HTML, listboxes and combobox (drop-down list) controls are both known as selection lists, and are implemented by the <select> tag. Nested inside the <select> tag are one or more option tags. Each option describes one list item.

The form_for builder gives us two waysto construct selection lists without having to manually construct each <option> tag individually. One is the select helper, which provides full control over how the <select> and <option> tags are generated. Another is collection_select, which provides a simpler syntax if:

  • You can provide the list of items as a Ruby collection
  • Each item in the collection has a method that can be called to provide a displayable string for that item
  • Each item also has a method that can be called to supply the value that should be sent to the application when the item is selected by the user.

These three conditions are the 80% case when you’re constructing the list from an ActiveRecord table. Some code will make it clearer. Open up new.html.erb again and add this code into the form:


<% airports = Airport.find(:all, :order => :code) %>
<p>Depart from: 
  <%= f.collection_select :origin_id, airports, :id, :code %>
</p>

<p>Arrive at: 
  <%= f.collection_select :destination_id, airports, :id, :code %>
</p>

We start by capturing all of the airports into the airports variable. Note that we use the <% %> syntax, without the equal sign, whenever we don’t want the result to be sent back to the browser.

We then use the collection_select helper to construct our combo box. Here are the parameters we had to pass:

  • First is the attribute of our model we’re trying to assign to (origin_id or destination_id)
  • Second is the Ruby collection. We will get one <option> tag – one item in the list – for each object in the collection.
  • Third is the method on the collection items that should be called on the selected item when it’s time to transmit the selection to the application.
  • Last is the method on the collection items that should be called to generate that item’s displayed string in the list.

Navigate to the form in your browser:

Finally, let’s make sure we understand how this ends up in our new flight model. Select some values and click Create.

Back To That Bridge Again

In my case, I created flight #444 from Chicago to Portland on a Boeing 777. Take a look at your log file (log/development.log) and find where the create action was called. Mine looks like this:

Processing FlightsController#create (for 127.0.0.1 at 2008-04-06 14:31:39) [POST]
  Session ID: BAh7BzoMY3NyZl9pZCIlZmRhM2FmODI0ZjA1MjYwMzgxMGJiMjcyYjBiOTNi%0AMTUiCmZsYXNoSUM6J0FjdGlvbkNvbnRyb2xsZXI6OkZsYXNoOjpGbGFzaEhh%0Ac2h7AAY6CkB1c2VkewA%3D--273a533fb4cee21ea44cb1ccdc6d70db908d7525
  Parameters: {"commit"=>"Create", "authenticity_token"=>"e9e1c0b0d744f33db348cd70a69cf70d6126c66b", "action"=>"create", "controller"=>"flights", "flight"=>{"number"=>"444", "origin_id"=>"1", "meal"=>"0", "destination_id"=>"2", "equipment"=>"747"}}
  Flight Create (0.000350)   INSERT INTO flights ("updated_at", "number", "origin_id", "meal", "destination_id", "equipment", "created_at") VALUES('2008-04-06 14:31:39', '444', 1, 'f', 2, '747', '2008-04-06 14:31:39')
Redirected to http://localhost:3000/flights/4
Completed in 0.00958 (104 reqs/sec) | DB: 0.00035 (3%) | 302 Found [http://localhost/flights]

Zooming in on just the parameters and reformatting for clarity:

Parameters: { "commit"=>"Create",
                   "authenticity_token"=>"e9e1c0b0d744f33db348cd70a69cf70d6126c66b",
                   "action"=>"create", 
                   "controller"=>"flights", 
                   "flight"=> { "number"=>"444", 
                                "origin_id"=>"1", 
                                "meal"=>"0", 
                                "destination_id"=>"2", 
                                "equipment"=>"747"}
                              }

See how all of our form data is in the tidy flight hash? We can easily access all the form values with params[:flight]. The important thing to notice are the values that came across for the selected airports.

Here’s another way to confirm to ourselves that it worked. Open up the Rails console again and find the flight:

>> f = Flight.find_by_number '444'
=> #<Flight id: 4, number: "444", created_at: "2008-04-06 14:31:39", updated_at: "2008-04-06 14:31:39", meal: false, equipment: "747", origin_id: 1, destination_id: 2>
>> f.origin.code
=> "ORD"

And for fun, check out the arrivals for the Portland airport:

>> Airport.find_by_code("PDX").arrivals
=> [#<Flight id: 1, number: "123", created_at: "2008-04-05 20:05:24", updated_at: "2008-04-06 10:22:23", meal: false, equipment: nil, origin_id: 1, destination_id: 2>, #<Flight id: 4, number: "444", created_at: "2008-04-06 14:31:39", updated_at: "2008-04-06 14:31:39", meal: false, equipment: "747", origin_id: 1, destination_id: 2>]

List Box instead of Drop-down Box

If you’d prefer to show a listbox instead of a combobox, we need to tell the browser to show more than one item at a time. To do that we need to pass two more parameters to the collection_select method:

  • An empty hash. See the Rails API docs for more info here – this hash can contain optional elements like whether to include a blank item at the top of the list, etc.
  • A hash of attributes that should be glued into the HTML <select> tag. We need to insert a size attribute with the value 3. Anything larger than 1 will end up looking like a listbox instead of a combobox.

Adding these options and playing with our <p> tags a bit means we have code that now looks like this:

  <p>Depart from:</p> 
    <%= f.collection_select :origin_id, airports, :id, :code, {}, {:size => 3}  %>

  <p>Arrive at:</p>
    <%= f.collection_select :destination_id, airports, :id, :code, {}, {:size => 3} %>
  </p>

and the form now looks like this:

You Are Now Free To Create Your Own Forms

We’ve gotten a few questions regarding the best way to handle several different models on the same form, multiples of the same model on the same form, and how to use advanced options of how to use form_for when your controller is a namespace (if your controller is within an “admin” namespace, for example). We’ll try to address those issues in subsequent postings in the very near future.

But for now, that’s it for our whirlwind tour of forms in Rails. Remember:

  • Ruby templates generate HTML. So learn HTML.
  • The params has is your bridge between your user and your application. Learn to use the log files well.
  • Follow Rails conventions to make life easy.

Questions? Drop us a comment.