Friday, January 09, 2009

Rails Select Filters that Map to In Queries

Example of a Rails select box that performs filtering and is backed by an IN query. First a constant is defined in the controller. It is an array of arrays where the "first" is the text to display in the select and the "last" is an array of the values the first represents.



Then we render that in our page with typical code:



Back in the controller, when the form is submitted, the following code sets up the conditions for a find call:



The only somewhat interesting bit is how the conditions for "Other" are set up with an inject from the valid options defined in the SERVICE_GRADE_CODE_OPTIONS constant.

Wednesday, January 07, 2009

Apache Tip: Encourage Browsers to Open Documents in the Native Application

Jakob Nielsen says that you shouldn't open "non-web documents" within the browser window. Some of our users had the same complaint and were asking for us to prevent pdf, doc, and other file formats from opening inline within the browser window.

The way to get this to happen is to add a "Content-disposition" header with the value of "Attachment". Of course if you are using Rails send_file or send_data methods you can easily add this header. If, however, the files are being served up directly by your web server then you need to find another way to add this header. If you are using Apache 2 the following directives in your conf file should do the trick:

Monday, December 29, 2008

Javascript Event Bubbling with Prototype

Update: After I wrote this post I remembered that change events don't bubble in IE6 for select elements and thus likely don't bubble for check boxes either. After an investigation I found out that no - they don't bubble. So this code won't work correctly for IE 6 which still commands a 20% + share of the market. Please STOP IE6 NOW...and on with the post.

Bubbling is a good thing...and no I'm not talking about the fact that it might help you to locate a turtle. Javascript events bubble from the source element outward. This gives you a couple of important benefits when attaching event handlers to elements:

  • reduced resource usage - one global handler instead of a handler on each element
  • simpler wiring with dynamic content - instead of adding handlers to new elements inserted into the page, the parent element already has the lone handler


The following example shows some code where a div element with an id of "overlay-entries" contains a number of, well, overlay entries in which each overlay entry has an checkbox element that allows that overlay to be shown or hidden. Instead of wiring up a handler for the "change" event for each checkbox we can merely catch the events as they bubble to the containing div with the id "overlay-entries." With prototype Event objects are normalized to make sure that the .element() method returns the element that the event actually originated on. In my situation it is enough to check that the event fired on a checkbox to allow the corresponding event to fire:

Monday, December 22, 2008

Send HTML to Javascript Functions from Rails Partials

Many Javascript libraries, such as ExtJS, allow you to pass HTML to functions. A good example of this is the Ext.Msg.alert call which accepts a a title and a message where the message string can have HTML markup in it to form the content of the alert popup. When this message is a large block of HTML, for example a large disclaimer that you have to stick up in the face of your customer, it becomes very awkward to include this HTML text in the same page that shows the popup. It bloats the page and mixes together what would be better edited by a "subject matter expert." What you really want to do is to put this disclaimer into its own partial but then you have to figure out how to render the string so that newlines and other content don't break the Javascript. How do you do that? Here are two ways that work:

Saturday, December 13, 2008

Fix Broken Delimiter Separated Values Files

We get broken delimiter separated values files that are not properly quoted. Actually they don't use any type of quoting and expect the delimiter (a pipe character) to not appear in the output. Invariably these files end up having a handful or rows with fields with carriage returns in them. These rows end up getting dropped during later processing. Below is a simple script to fix such files. Note that the default delimiter is a pipe symbol and the default number of separators comes from counting the separators in the first row (typically the header row).



This simple script demonstrates optparse and writing a script that uses standard in / out or opens files according to arguments.

Sunday, November 23, 2008

Sinatra is Kind of Cool - Passenger Tips

Sinatra is another very cool Ruby web framework that packs quite a punch in what is currently only 1,576 LOC. It has pretty decent routing, filters, layouts, and more. If you are building something that functions primarily as a web service it would definitely be an option worth checking out.

Sinatra is Rack compatible and thus deploying behind Phusion Passenger works well. Unfortunately, the documentation on the Passenger site leaves a bit to be desired and may leave you scratching your head as to why your Sinatra application isn't working correctly. Here is a sample config.ru script that will bring up your application behind passenger with an explanation coming after the code:



First off, my config.ru provides the ability to "freeze" to a certain version of Sinatra with the $:.unshift line near the top of the file. This line just checks for the existence of a vendor/sinatra directory in your application and if present pushes its lib directory to the front of the load path. The setup of the default options is where I add some information not given by the Passenger instructions which will otherwise cause your application to fall down go boom - that is you must tell Sinatra how to find its views directory (so that you can use views) and its app_file (so that it will reload correctly when running under :development mode). Also notice how the environment is picked up from the RackEnv variable coming from the conf file. The real guts of the application are defined in app.rb which is required at the bottom of the file.

This is my first post using gist to host code snippets and it seems to be a winner. I think I will also start hosting my Javascript example code straight out of gist raw as well.

Saturday, November 15, 2008

Merb Javascript Delete link_to with Prototype.js

I got on the elevator to go home from work the other day and this other guy in the elevator says to me "Those look like comfortable shoes." Huh? Did I just step in the world of Forest Gump? What is that all about?

Currently there is quite a pissing match going on in the world of Rails with fronts against Zed Shaw and Merb - see here and here. I hope all this energy can be channeled to improving frameworks and not just spewing internet bile.

Anyway, did a little playing around with Merb - just wanted to see what it would take to set up a resource, a restful controller, and how it would compare to Rails. I came away impressed, Merb appears to be another great Ruby web framework. Right now it would appear the biggest deficiency in the project is documentation. Merb is currently woefully lacking in documentation compared to Rails.

Among a bunch of small difference the url helpers are on of the biggest differences. The Merb way is demonstrated here. The last thing I needed to figure out was how to get Merb to generate a delete link that uses javascript to post back to the controller - you know something like this in Rails:



After looking a Merb's link_to documentation for a while I realized - "ah, Merb is Javascript library agnostic and also wants to be unobtrusive." So, instead of looking for someone else's canned solution I cooked up my own using Prototype. Just add a class to your link:



and then hook in your events with some prototype:



The url(:delete_xxx, @xxx) in Merb actually creates a link to /xxx/:id/delete where you can place an "Are you sure?" form for users with Javascript turned off. The above code will bypass that if Javascript is enabled.

Ok, enough playing around with Merb for now, time to look at Sinatra - only 1576 LOC!