Friday, August 14, 2009

Lightbox for Rails

   There are plugins available that will create a lightbox effect for you, but in each case I ran into problems adapting the plugin to my needs. I'm going to share with you the steps I took to introduce my own lightbox effect that has proven to be extremely flexible and easy to use.

Gray Out
   The first thing we need is the gray-out effect. I did a little searching and found a javascript function, grayOut, that has worked out really nicely. The code has some useful comments for explaining the various options it accepts, so I won't go into the details.
   Paste this code into your application.js file to ensure it will be available by default in all your layouts (assuming you are using the javascript_tag :defaults option)

HTML/CSS
   The next thing we need is an HTML container to hold the lightbox and some CSS to style it so it should show up center screen. Here are the steps:
  1. somewhere inside the body tag add an empty div tag with class "lightbox" and id "lightboxContainer and the style set to "display:none" inline.
  2. in default.css add the lightbox class: .lightbox{position:absolute; z-index:1000; width: 100%; margin: 0 auto; top: 50px}. The top should be adjusted to whatever fits your site template best.
Rails
   Finally we write some rjs code to display content in a lightbox. I like to use helpers for commonly used rjs code. This has proven really useful in keeping the controllers DRY. In whatever helper you wish (since helpers are global) create the following method:

def display_lightbox(partial)
  page << "grayOut(true, {'z-index':'25','opacity':'50'});"
  page['lightboxContainer'].show
  page['lightboxContainer'].replace_html(render(:partial=>partial))
end

Now in your controller to display a lightbox in response to a js request (ajax), you only need a single line of code inside your render block to display the lightbox. Example:

respond_to do |format|
  format.js do
    render :update do |page|
      page.display_lightbox("/[controller_views_directory]/some_partial")
    end
  end
end

And that's all there is to it. I went a couple steps further in my project to allow stacking n number of lightboxes that are managed by a javascript class. If I get enough requests I may post that as well.

Inherit Filters, Don't Inundate Your Controllers With Them

Filters are a great way to keep your controllers DRY, but to fully optimize their use a developer should take advantage of controller inheritance (filters are inherited as well as the actions).

I happen to be a big believer in nested controllers. We tend to use the small controller, large model approach in our Rails structure. This philosophy naturally results in numerous controllers. I also think a nested structure is especially relevant in a site that has many Ajax functions sprinkled throughout every page.

Since nested controllers tend to represent a subset of functionality provided by the higher level controller, it makes sense to inherit from this controller (Note: ALL controllers by default inherit from the application controller. When creating a nested controller, the namespace will reflect the parent, but you must manually change the controller from which it inherits).

It's common practice to implement filters on the Application controller that apply to the entire site (ie an authentication filter). However, specialized filters such as functionality specific permissions or state checking should not be applied to the Application controller. Otherwise, you will end up with skip_before_filters all over the place. This is poor practice.
So here's a simple example of a good way to use nested controllers in conjunction with filters:

class ClientController < ApplicationController

before_filter :verify_client_permission

def view_client_information
//some code
end

end

class Client::EditClientController < ClientController
//inherits filter from ClientController
end

Of course this is an extremely simple example, but I think it gets the point across. As with everything in Rails, there are probably numerous ways to accomplish the same idea. This is the method I have found that works well in pretty large Rails projects with a complex permissions model and lots of Ajax.

Sunday, August 9, 2009

Keep Your Rails Controllers Clean

One of the things I really love about Ruby on Rails is that, for every discovery, there seem to be two more waiting on the horizon; better, neater, faster ways of doing things. I, as programmer, am prone to becoming sidetracked by trying out newly discovered methods, and sometimes I lose site of some basic software principles. One of the most basic of these is "separation of concerns" (SoC).

I have found that the extreme accessibility of Rails models allowed me to pick up some bad habits: namely just grabbing models willy-nilly and having my way with them in my controllers. Although Rails implements MVC, it can only do so much to encourage good coding practices, and the rest is up to you. Here are a few lessons I've learned about keeping my controllers clean:

1) Stop playing with your parameters: Formatted user input is extremely common, and there are several approaches to dealing with the validation, character stripping, and reformatting required to support it. I'm ashamed to admit that the last project I worked on "gsubs" were splattered everywhere in the controller actions in order to remove formatting characters. First, assuming that formatting characters were input correctly by the user is bad bad. And this is essentially what's happening when validation occurs after character stripping. For example, say we have a phone number that was typed in as 12345-67890. Now, if we strip the '-' character and validate numericality and length, whoila, we have a valid phone number. WRONG! How do we know if it's supposed to be 123-456-7890 OR 1-234-567-890 and they forgot a digit? The answer is, we don't, so don't assume. There is a world of ready to use regular expressions out there (lately I've been referencing Alexey's Blog). Make use of them in conjunction with ActiveRecord's validates_format_of method.

OK, now we know that we can validate our user's input with formatted characters and all. We know what our user's intentions are, but now we still have to get rid of those pesky formatting characters before storage. Well, we could create a method for the model and call it from the controller (ugh), or, the cleaner approach is to add a method to the model's ActiveRecord callback before_save method: something like "before_save :remove_those_pesky_characters".

The result of implementing the above procedures is we don't have to do any parameter manipulation inside our actions to deal with formatting characters. Data manipulation and validation is handled in the model where it belongs.

The next part in this series I will talk about rolling up model dependencies using the ActiveRecord after_create callback.