There’s a contest on Railscasts that has me feeling compelled to participate. So I am. To enter, the contest requires 5 tips concerning Rails, all in one post. So, this post will more than likely be one of the longest I’ve written in a while, but hopefully it will serve as a good resource/reference for other Rails developers. Without further ado, here are my five tips:
Note: I haven’t wrapped my View code examples in the ERB <%= -%> tags, as it currently makes the syntax highlighting terribly hard to read. I just wanted to make new comers aware until I can get this issue fixed.
Tip 1: Navigation Helper Plugin
Nearly every web application has some sort of navigation. Albeit tabs, menus, plain links, whatever, there needs to be a way to click through the application. And more often than not, keeping track of the current section/tab is a desired feature. Well, I wrote a Rails plugin called navigation_helper to assist with these needs.
Basic Usage
Once installed, here’s how the plugin works:
1 2 3 4 5 6 7 8 9 | # somewhere in your view (typically your layout) navigation [:home, :about, :contact] # HTML output: # <ul class="nav_bar"> # <li class="current"><a href="/home">Home</a></li> # <li><a href="/about">About</a></li> # <li><a href="/contact">Contact</a></li> # </ul> |
You pass an array of symbols representing the sections that the links will navigate to. The link text will be a capitalized version of the symbol passed in. So :home becomes ‘Home’; :contact_me becomes ‘Contact Me’; and so on.
An important thing to be aware of: the helper will be expecting a named route mapped to the section passed in the array. For instance, for the :home section, the navigation helper will use home_path to build the link. And for :about, it will look for about_path. And so on. You need to make sure those routes are defined in your routes.rb file (it’s good practice to use named routes, this plugin just enforces that practice).
And as you can see, the markup is very extensible (in terms of CSS) with the use of an unordered list. The list item that holds the current section will get a CSS class of “current”. And the unordered list itself gets a CSS class rather than an ID, so that it can be used multiple times on the same page and not break your strict XHTML markup :-)
Adding Subtitles
Sometimes it’s a nice touch to add subtitles to your navigation (see my portfolio for example). This plugin has support for that. All you have to do is pass a string of the subtitle right after the section that the subtitle should be associated with. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | navigation [:home, 'Start Here', :about, 'Learn More', :contact, 'Get In Touch'] # HTML output: # <ul class="nav_bar"> # <li class="current"> # <a href="/home">Home</a> # <span>Start Here</span> # </li> # <li> # <a href="/about">About</a> # <span>Learn More</span> # </li> # <li> # <a href="/contact">Contact</a> # <span>Get In Touch</span> # </li> # </ul> |
As you can see, the markup generated is clean and is ready for some CSS love. Now, what if you would rather those subtitles just be hover text on the link instead of full-blown span elements? No problem, just let the navigation helper know with the :hover_text => true option:
1 2 3 4 5 6 7 8 | navigation [:home, 'Start Here', :about, 'Learn More', :contact, 'Get In Touch'], :hover_text => true # HTML output: # <ul class="nav_bar"> # <li class="current"><a href="/home" title="Start Here">Home</a></li> # <li><a href="/about" title="Learn More">About</a></li> # <li><a href="/contact" title="Get In Touch">Contact</a></li> # </ul> |
The span elements will transform into title attributes on the links, so the text will only show up on hover.
Authorized Sections
Now, a definite need (for me at least) is to have certain tabs show up depending on some condition, such as a user being logged in. For instance, on this very site, the public sees ‘Portfolio’, ‘Words’, ‘Archives’, and ‘About’ tabs. But if I’m logged in, there’s also an ‘Admin’ tab, which allows me to access the area to post new entries, add categories, edit/delete comments, etc. But I only want that tab visible based on me being logged in. Well, I’ve added support for that, too. Just specify which sections should be “authorized” sections, like so:
1 2 3 4 5 6 7 8 9 | navigation [:home, :about, :admin, :reports], :authorize => [:admin, :reports] # HTML output: # <ul class="nav_bar"> # <li class="current"><a href="/home">Home</a></li> # <li><a href="/about">About</a></li> # <li class="authorized_nav_link"><a href="/admin">Admin</a></li> # <li class="authorized_nav_link"><a href="/reports">Reports</a></li> # </ul> |
By default, the link will be “authorized” by checking a logged_in? method. If you don’t have a logged_in? method, or need some other means of authorization (such as checking if the current_user has the admin role), then you can specify which method you’d like the navigation helper to check against by using the :with option:
1 | navigation [:home, :about, :admin, :reports], :authorize => [:admin, :reports], :with => :administrator? |
Now the plugin will only show the ‘Admin’ and ‘Reports’ tabs if the administrator? method exists and returns true. (Note: if the authorization method doesn’t exist, the helper won’t crash, it will simply not show the authorized tabs)
Also notice how the admin links get a special “authorized_nav_link” CSS class. That’s because sometimes you want your “admin” tabs to appear differently, and this gives you the power to do so. For instance, you may have your common tabs to the left, but your admin tabs floating to the right. And just to be clear, if an authorized link happens to be the current section, it will simply append the CSS classes (i.e. class=”authorized_nav_link current”).
I’ll admit, the “authorized_nav_link” isn’t the best CSS class name, but it’s best to keep defaults such as that awkward so they don’t interfere with your current CSS classes. However, you can pass another option to override that class, like so:
1 | navigation [:home, :about, :contact, :admin], :authorize => [:admin], :authorize_css => 'custom_css' |
There’s one more thing to mention about authorized links. Let’s say you’re using this for an admin menu, where all of the links are to be “authorized”. Rather than list out all of the authorized links again, you can pass an :all to the :authorize option, like so:
1 | navigation [:users, :reports, :logs], :authorize => [:all] |
Now all tabs will be authorized. Pretty simple, huh?
Setting Current Tab
Up to this point, you may be wondering how the helper knows which tab/section is the “current” tab/section. Well, it’s simple. By default the plugin uses the controller’s name to determine the current tab (or link or section) you’re on. But as we all know, that’s not always feasible. Just because you have a PublicController doesn’t mean you want your link to be “Public” in the navigation menu. To fix that issue, you can specify the current tab for any controller by doing:
1 2 3 | class PublicController < ApplicationController current_tab :home end |
Now something like this will work as intended:
1 | navigation [:home, :about, :contact] |
The PublicController will be seen as :home to the navigation helper, and will choose the current tab accordingly. This is also very handy for namespaced controllers.
Install via Git
You can find this plugin on GitHub (and see the README for more/better examples), and can be installed by doing:
$> cd path/to/your/rails/project/vendor/plugins $> git clone git://github.com/rpheath/navigation_helper.git
You can download a tarball if you don’t have Git installed. And for completeness, here’s the original announcement of the navigation_helper plugin.
Tip 2: Input CSS Plugin
This next tip is more for designers who work with Rails applications. As any designer would know, designing Forms can be somewhat of a pain, as there are many different types of INPUT tags (text boxes, password fields, submit buttons, file fields, etc). Obviously, you would not want to apply the same text box styling to a submit button, which means it requires extra work on every form to make sure you’re styling the correct INPUT tag.
Since I personally design and develop Rails applications, I’ve decided that it was time to attack this problem at its core. I wrote a plugin that will automatically add a CSS class based on the “type” attribute of any INPUT tag. And I’ve called it, input_css.
How it Works
Just install it. Then your standard form helpers will automatically have a class assigned to them. For example:
1 2 3 4 5 6 7 8 | f.text_field :name # => <input type="text" class="text" name="user[name]" value="" /> submit_tag 'Create', :class => 'button' # => <input name="commit" type="submit" class="button submit" value="Create" /> f.check_box :is_preview # => <input type="checkbox" class="checkbox" name="article[is_preview]" value="" /> |
So once the plugin is installed, the CSS to style your INPUT tags is simple:
input.text { ... }
input.submit { ... }
input.file { ... }
input.checkbox { ... }
input.radio { ... }
...
Install via Git
This plugin can be found on GitHub, and you can install it by doing:
$> cd path/to/your/rails/project/vendor/plugins $> git clone git://github.com/rpheath/input_css.git
Once installed, you’re good to go. There’s no setup, customization, etc. It taps directly into the existing Rails tag helper, so all of your existing form elements will be updated! Now you have an easy and consistent way to style your forms.
Remember, if you don’t have Git installed, you can download a tarball. Just put the source in your vendor/plugins directory.
Tip 3: Acts As Lookup Plugin
Maybe I’m alone on this one, but do you find it a pain to deal with select (i.e. drop down) lists? If you’re like me, I don’t always remember the proper Rails syntax, or I want to add a default nil option to the list, etc. They always just feel awkward to me. Here’s a typical select field you might find in one of my forms. It’s simply a lookup to choose a category:
1 | f.select :category_id, [['--', nil]] + Category.find(:all).collect { |m| [m.name, m.id] } |
Yikes! That’s sort of nasty to have that right in your views. At least, to me it is. So I wrote a plugin that assists with this “lookup table” issue. I’ve so cleverly called it, acts_as_lookup.
How it Works
Once installed, you need to tell your model(s) that they should be acting as a lookup table. This is pretty straightforward, though:
1 2 3 | class Category < ActiveRecord::Base acts_as_lookup :name end |
You have to pass it the field/column name you want to show up in your drop down list. So in the example above, I want to be able to choose from a list of category names, hence the :name parameter.
From there, you get two things: 1) a lookup_for helper (used in your views) and 2) an options_for_select class method for any model that is “acting as a lookup table”. Here’s how you use both:
Using options_for_select
This is a clean way to get rid of that collect nonsense I showed previously. So, redoing my above example using this plugin (and using options_for_select), we have:
1 | f.select :category_id, Category.options_for_select |
That’s much cleaner. And it automatically adds the nil ”- -” option as the default in your list. Arguably, though, there’s even a better way: the lookup_for helper.
Using lookup_for
The lookup_for helper automatically generates the above, but it’s a little cleaner:
1 | lookup_for :product, :category_id |
And if you’re using a FormBuilder (read: “f.” in front of your form helpers), it’s even better. You only have to give it the foreign key to the lookup:
1 | f.lookup_for :category_id |
Viola! The above example(s) will automagically generate something similar to (of course depending on the real data):
<select> <option value="" selected="selected">--</option> <option value="1">Appliances</option> <option value="2">Furniture</option> </select>
One thing to note, though: the foreign key must follow the traditional Rails convention for foreign keys. Meaning, for a Category, you’d have category_id foreign key reference; for a Task, you’d have task_id foreign key reference; and so on. The reason is because the plugin uses that foreign key to get at the Model in which it represents. But since that’s pretty standard to most Rails developers, I don’t think it bears too much constraint :-)
Customization
Now, you may be asking “what if I don’t want ’- -’ as my default option?” or “what if I don’t want every category to show up?” or “what if I have a category order, other than alphabetical?”. Not a problem. This plugin supports that sort of customization.
Changing the Default Text
1 2 3 | class Category < ActiveRecord::Base acts_as_lookup :name, :default_text => "- Choose Category -" end |
And if you don’t want a default nil option at all, just set the :default_text option to :first (meaning, the first option of your data returned):
1 2 3 | class Category < ActiveRecord::Base acts_as_lookup :name, :default_text => :first end |
Now there will be no default option in your drop down (in this example, it would be set to the first category of the collection instead).
Limiting the Options
1 2 3 4 | # only show categories from within the last 4 months class Category < ActiveRecord::Base acts_as_lookup :name, :conditions => ["created_at > ?", 4.months.ago] end |
Displaying in a Different Order
By default, the plugin will sort your options alphabetically, but you can change that, too:
1 2 3 | class Category < ActiveRecord::Base acts_as_lookup :name, :order => 'category_order ASC' end |
And of course, you can combine the options and pass all three at once, I’ve just shown them separately to emphasize each point.
Install via Git
You can find this plugin on GitHub, and it can be installed by doing:
$> cd path/to/your/rails/project/vendor/plugins $> git clone git://github.com/rpheath/acts_as_lookup.git
Remember, if you don’t have Git installed, you can download a tarball. Just put the source in your vendor/plugins directory.
Tip 4: Automatic Fading Flash Messages
This next tip is rather minor, but I think it adds a nice touch, and I seem to use it in all of my Rails applications. Basically, the idea is to show a flash message for a predetermined amount of time, then slowly fade the message out. (I personally think it’s tacky to leave it there, waiting on the user to navigate away from that area)
So, using a tip from a prior Railscast, we put this somewhere in our layout:
<% flash.each do |key, msg| -%> <%= content_tag :p, msg, :id => key -%> <% end -%>
This will create a <p> tag with an id of the flash type. Meaning, flash[:notice] = '...' will give <p id="notice">...</p> and flash[:error] = '...' will give <p id="error">...</p> and so on. Now, what we want to do is fade this <p> element out. We’ll use JavaScript to do this, and we’ll want to put that JavaScript in public/javascripts/application.js of our main Rails project root.
Fading Messages using jQuery
$(document).ready(function() {
setTimeout(hideFlashMessages, 10000);
});
function hideFlashMessages() {
$('p#notice, p#warning, p#error').fadeOut(5000)
}
Fading Messages using Prototype/Scriptaculous
document.observe("dom:loaded", function() {
setTimeout(hideFlashMessages, 10000);
});
function hideFlashMessages() {
$$('p#notice, p#warning, p#error').each(function(e) {
if (e) Effect.Fade(e, { duration: 5.0 });
});
}
(Note: the above examples were based on jQuery 1.2.3 and Prototype 1.6)
The above functions will allow the message to be shown for 10 seconds (10000 milliseconds) in full, then begin to fade the message out over a 5 second duration. Of course, just change the values to fit your needs/desires.
Tip 5: Interfacing with SOAP Web Services
This tip might actually target a small niche of Rails developers, but it’s something that I couldn’t find too much about when “Googling” for some help. So I eventually just came up with my own solution. It deals with creating an interface for legacy .NET SOAP web services. Rather than go into the explanation, I’m going to provide an example skeleton class that you can tailor to your needs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | require 'digest/sha1' require 'soap/wsdlDriver' module WebServices class SOAPInterface attr_accessor :endpoint, :service def initialize(endpoint='www.yoursite.com', service='YourWebServices', options={}) @endpoint = endpoint @service = service end # -- # your methods go here # -- private def wsdl SOAP::WSDLDriverFactory.new("http://#{@endpoint}/services/#{@service}.asmx?WSDL") end end # pulls out the diffgram of SOAP::Mapping::Object's into an array class DataSetParser def initialize(soap_response, data_set) @response = soap_response @data_set = data_set end # allows for @obj['CourseListing'] to be @obj.course_listing def method_missing(name, *args) @response.send(:diffgram).send(@data_set.to_sym).send(:[], name.to_s.camelize) end end end |
We’ll talk about about what goes in “your methods go here” in just a second. I wanted to mention what the DataSetParser is all about. Firstly, it’s specific to the .NET framework, so if you’re wanting to use this for SOAP services coming from something other than .NET, you can wipe out the DataSetParser class entirely. Now, onto what it does.
.NET returns it’s data collections as a DataSet, which then gets wrapped in a DiffGram. I know, goofy. From the Microsoft site:
A DiffGram is an XML format that is used to identify current and original versions of data elements. The DataSet uses the DiffGram format to load and persist its contents, and to serialize its contents for transport across a network connection.
What does that mean to us? It means that it’s another level of tedious parsing that we need to deal with when getting a SOAP response. If your API is returning a DataSet, then you can pass the result to the DataSetParser class, and it will return the collection as an array of SOAP::Mapping::Object. It also will allow you to access the data via an underscored method based on the camelized name of the DataSet returned. If that’s confusing, see the comment above the method_missing definition in the DataSetParser class.
Now, here’s an example of how you might use this skeleton module. Here, we’ll be replacing the “your methods go here” comment.
1 2 3 4 5 6 7 8 9 10 | # returns a user in the form of a SOAP object def authenticate(username, password) soap = wsdl.create_rpc_driver response = soap.Authenticate(:username => username, :password => password) result = response.authenticateResult soap.reset_stream DataSetParser.new(result, 'user').user rescue # do your graceful error catching/logging stuff end |
This would allow you to do something like:
1 2 3 | $> api = WebServices::SOAPInterface.new('www.mysite.com', 'MyWebServices') $> api.authenticate('login', 'password') $> # => <#SOAP::Mapping::Object ... > |
The Authenticate and authenticateResult is dependent on the WSDL of the SOAP service, but the rest is pretty close to what each of your methods might look like. For more info, you can visit an earlier post of mine which discusses the same issues, and provides a more Rails-esque example.
Again, I realize that this is for a rather niche group of Rails developers, but like I said, I had a hard time finding any information on this, so maybe someone else will find my solution to this problem useful.
Conclusion
That concludes my 5 tips related to Rails. Hopefully they’ve proven to be worthwhile. Let me know if you have any trouble implementing any of these tips, as I’d be glad to help you make use of them. Thanks for reading my first novel :-)








01
Aaron H. on Tue May 06 at 03:02PM
Great tips all.
I’ve seen you mention the nav plugin before, but it’s so good to be reminded about it. I also really like the CSS input one. Such a simple idea but a huge help.
02
Kyle on Mon May 19 at 06:11PM
Very nice.
03
deadsunrise on Fri May 23 at 02:36AM
Ryan, thanks a lot for the plugins, I’m using the navigation helper in most of my apps.
There is a ) missing from the Fading Messages using jQuery code.
$(document).ready( is not closed.
04
Ryan on Fri May 23 at 06:33AM
No problem, I’m glad you find the plugins useful. And thanks for catching the syntax hiccup—it’s fixed now.
05
Nick on Wed Jul 02 at 05:18AM
Hey Ryan -
I really enjoyed this post. I think I’m going to use the nav helper in my next app, as I’m getting tired of recycling my old code. Yours is more elegant, anyways. Thanks for sharing these tips!
06
Chadwick on Thu Jul 24 at 08:10AM
Great tips Ryan. By the way, the five tips didn’t have to be in the same post for the contest. :)
07
Ryan on Thu Jul 24 at 09:05AM
Thanks :-)
Yeah, I just thought it’d be easier to submit a single link. Of course, I could have posted separately and then submitted a post with an aggregate of links, but oh well.
08
Matthew on Mon Sep 01 at 02:01PM
A lot of effort and some great contributions in this post!
Some possibly-useful points:
Instead of using the Input CSS Plug-In, it is possible to selectively style form inputs using CSS attribute selectors. For example, in your stylesheet, input[type=”text”] { ... } will style only the text inputs in a form. The caveat with this is browser compatibility, which is not perfect. (Specifically, IE 6 and earlier do not support attribute selectors.)
In the section on acts_as_lookup, the “limiting options” example using :conditions => [“created_at > ?”, 4.months.ago] would probably not work as intended, since the 4.months.ago would only be evaluated once when the model is initially loaded. (Using a lambda-style named_scope could be a solution to this problem!)
Also, the Rails collection_select helper offers somewhat similar functionality and may be useful in some situations.
09
Ryan on Mon Sep 01 at 07:29PM
Mathew -
At the time, I still had to support IE6 so that was the motivation for the input CSS plugin. I definitely should’ve looked around before writing my own plugin, though, as there are several already written that solve the same issue.
But yes, CSS 2+ selectors definitely do take care of that if IE6 and the like aren’t in your way.
And for the acts_as_lookup “limiting options” part, very good point. I might add a fix for that in the next day or two.