Accessible Rails Apps – Images and Alternative Text

Posted by Brian in Accessibility, Rails, Usability (July 3rd, 2009)

Many types of users make use of alternative text for images. Users who use screen reading software rely on those tags to describe the images they can’t see. Mobile users with slow connections or ‘pay by the minute’ plans often times turn off images to speed up the process.

Rails does something a little crazy with the image_tag helper… it automatically generates the ALT tag, which is just a horrible horrible idea.

  <%= image_tag "rails.png" %>

generates

  <img src="/images/rails/png" alt="rails" />

This is an absolutely awful idea, and I presume this was done just to shut up the XHTML Transitional validator which will complain if you leave ALT text off of your image elements.

You shouldn’t be adding ALT text to images to satisfy a validator. You should be doing it because it’s The Right Thing To Do.

ALT text is designed to describe the image, to be an alternative to the image. So, when placing the image on the page, take a few seconds to describe what it is.

  <%=image_tag "rails.png", :alt => "The Ruby on Rails Logo"
  <%=image_tag "banner.jpg", :alt => "Awesome Consulting Services - We Make Websites" %>

And for goodness sakes, please don’t prefix your description with “An image of…”. Screenreaders and other devices will make it completely clear to us that the content is supposed to be an image.

Some images convey more information than the ALT may allow, and so we’ll talk about that next.

Accessible Rails Apps – Rails Links, REST and JavaScript Hidden Forms

Posted by Brian in Accessibility, Rails, Usability (July 1st, 2009)

This is the first in a series of articles on developing accessible Rails applications. Accessibility is really important to me as I spend a lot of time working on web sites and applications that need to adhere to Section 508 guidelines. Making web sites accessible is also the Right Thing To Do.

JavaScript and Accessibility

When developing Rails applications, I try really hard to develop the entire application without using JavaScript, since a lot of users who rely on screen reading software can experience problems with AJAX, so these people tend to disable scripting. Using a screen reader is a lot like looking at a web page through a paper towel tube. You can’t see the whole thing at once, and it’s not likely that you are going to see something changing on the screen, and I’ve never come across screen reading software that can really let you know when a section of the page has been replaced. It might tell me that the content has changed, but I have to make it read the whole page back to me again and figure out what happened. Popup advertisements add to the problem, so people tend to just turn of the JavaScript completely.

Accessibility doesn’t just mean the users with disabilities, though. It can also mean those BlackBerry users who have horrible JavaScript support. So, what we’re really talking about is the age-old idea of “progressive enhancement.” Make a low-tech version work first, then spice it up.

Rails and REST

When you develop Rails applications and you’re working to keep your applications adhering to a RESTful design, certain URLS can only be accessed by certain methods. For example, to delete a record, you’re supposed to send a DELETE request to the destroy action of the controller.

Web browsers can only do GET and POST requests. GET requests are only supposed to read data, and POST requests are intended to change data. You POST to create something. Rails uses hidden form fields to emulate the DELETE and PUT methods.

However, Rails allows you to build links that can do other types of requests, like the DELETE request that’s planted in the default scaffolding. Honestly this is one of the single biggest mistakes the Rails core team has ever made. Here’s why:

This code:

  <%=link_to "Delete", project_path(@project), :method => :delete %>

actually produces a hidden HTML form. When you click this link, it actually submits the hidden form. This requires JavaScript to run and also mixes behavior with HTML markup. It’s a truly ugly situation that really should never be used.

A simple solution

The simple solution is to use the button_to helper instead. It generates the same form, but requires no JavaScript to activate it.

  <%=button_to "Delete", project_path(@project), :method => :delete %>

But I don’t want a button!

You don’t have to have one. As Tore Darell points out on his blog, the appropriate way to handle this is to do the opposite of what Rails was doing. You build the form yourself using button_to, but use unobtrusive JavaScript to replace the form with a link, by simply hiding the form, adding an observer to the newly created link which submits the form.

Add this to your application.js file to replace all of your button_to instances.

document.observe('dom:loaded', function(){
  $$('form.button-to').each(function(form){
      var link = new Element('a', {href:'#', 'class':'button-to'});
      link.update(form.down('input[type=submit]').value);
      link.observe('click', function(e){
        e.stop();
        form.submit();
      });
      form.insert({after:link});
      form.hide();
  });
});

Tore got it right, and the Rails team got it wrong. This solution is great because

  • It’s accessible. With JS disabled, the buttons will work and perform the appropriate actions.
  • It’s clean. The JS is unobtrusive. It’s not intermixed with your code, and it doesn’t use DOM Level 0 “onclick” which, in some instances, is considered deprecated or invalid markup.
  • It makes it look like a link without the need to resort to crazy CSS antics.
  • A single fix replaces all occurrances. You’re not generating duplicate JavaScript code all over your views like you would with link_to :method => :delete.

Replacing a single instance

You can replace button_to instances that contain a specific class if you simply wrap the JavaScript code in a simple if... block:

   <%=button_to "Dismiss", user_message_dismissals(:message_url => message.id), :class=>"dismissible" %>
    if(form.descendants().any(function(element){ return element.hasClassName("dismissible")})){
         var link = new Element('a', {href:'#', 'class':'button-to'});
         ...
    }

And I’m sure there are better ways to do that, too.

That’s it for now. I welcome your comments and suggestions on this topic and others.

lazy_developer gets some TLC

Posted by Brian in News, Projects, Rails (June 10th, 2009)

lazy_developer is a Ruby on Rails plugin I use on a lot of my projects to make my life easier as a developer. It’s a collection of Rake tasks that automate some common operations. Some of the more interesting features it provides are

  • the ability to dump a database to YAML format and then pull it back in again
  • a simple way to obliterate models, controllers, views, helpers, and related files easily (in the case of a refactor or a fat-fingered typo
  • a method to compact your migrations into a single migration file
  • and of course, automatically clone your test database whenever you migrate

Today, Kevin Gisi and I gave this plugin some much-needed love and attention after we discovered a few problems. Here’s what’s new:

Data exporting works with Rails versions prior to 2.0

Got an old database you’d like to pull in? This now works in Rails 1.2.3!

Data dumping works much better now!

We noticed some duplicate records sneaking into the output files, and it was due to a mistake I made when I implemented my own version of record pagination. It wasn’t limiting correctly, and Kevin quickly spotted the reason why. It also wasn’t storing records in the YAML file properly either, which I also resolved.

This is tested on Microsoft SQL Server, MySQL, and SQLite3.

Migration compacting works now

This was patched a few days ago and merged in, but I flip-flopped a couple of lines during a merge and it made it into the master branch that way. Kevin decided he’d like support for Subversion for this, so he added it. I’ll add in Git support very soon.

Interested in using this on your projects? Go get lazy_developer right now!

Rails and Legacy Databases

Posted by Brian in News, Rails (May 7th, 2009)

Here are the slides from my RailsConf 2009 talk. You can watch the slides below, download the PDF.

Rails and Legacy Databases (PDF slides)

Bob Martin – What Killed Smalltalk Can Kill Ruby Too

Posted by Brian in Projects, Rails, Testing (May 7th, 2009)

I had the extreme pleasure to watch Bob Martin talk at RailsConf. He is an amazing speaker for more than just his delivery style (which is amazing). He’s incredibly bright, and has a wealth of experience that everyone can learn from. The majority of his talk was focused on acting professionally, and his definition of professionalism means, specifically, writing tests for every line of code in your application.

I can’t agree with this more. Without a robust test suite, your application is garbage. As Bob said in his talk, without tests you will be afraid to change your coe because it will break and you’ll have to spend hours finding out why. When you have tests, making changes is much easier because you no longer have fear.

If you have applications without tests, write them. Follow Bob Martin’s rule – “Check the code back in a little cleaner than you found it.” If you don’t know how, let me know. I offer effective one-on-one or group virtual training sessions.

If you are writing applications for clients, you need to test your code. If you’re working with a development firm, you should ask them how they test the code you pay them to write. Professional developers write tests and can then respond quickly to changes. Amateurs just hack things together, and then charge you hourly to fix their mistakes.

RailsConf – Don’t Mock Yourself Out.

Posted by Brian in News, Rails (May 5th, 2009)

David Chelimsky gave a great presentation on mocking and stubbing. I liked the fact that he talked about a lot of the fears that people have when they do rely on mocks and stubs. It was also nice to see someone clearly state that one of the problems we have in the Rails community when it comes to testing frameworks (and most other libraries) is that people tend to promote their own projects while trashing other libraries. He challenged an audience member to create a site where we could write up comparisons.

Highlights of the talk:

  • Stubs vs. mocks
  • “Ravioli” code where everything is clumped up in nice separate concerns
  • “Calzone” code, like Rails, which is harder to test in isolation.
  • Stubble, a wonderful-sounding library that can completely stub out an ActiveRecord object, which will be usable in every testing framework.

RailsConfigModel updated

Posted by Brian in News, Projects, Rails (April 22nd, 2009)

The Rails Config Model gem makes it extremely easy to create a “settings” table in your application. It creates a configuration controller and model that can be used to quickly create configuration table for your system so you can store system-wide variables that you’d like the site administrator to be able to set.

You can see instructions on usage or contribute to the project.

LazyDeveloper update

Posted by Brian in News, Projects, Rails (April 22nd, 2009)

For those unfamiliar, LazyDeveloper is a Rails plugin that provides tasks to simplify some cumbersome development tasks. I find it to be an indispensable tool. The latest version (1.1.4) fixes a couple of bugs related to running tests, and introduces a couple of new features which you can read about at the project page.

This is the last release of the plugin. I’ve decided to move this to a gem instead.

If you’re interested in contributing, feel free to fork the project and send pull requests!

Reverse Proxy Fix for Rails updated to support Rails 2.3

Posted by Brian in News, Products, Rails (March 16th, 2009)

Hot on the heels of the Rails 2.3 release is the new version of Reverse Proxy Fix. This new version only adds support for Rails 2.3 applications while continuing to support all previous versions.

Learn more about the Reverse Proxy Fix plugin.

Using Git to fork and contribute to a Rubyforge project

Posted by Brian in Howto, News, Rails, tips (January 13th, 2009)

If you’ve never comtributed to an open-source project before, there’s no better time to start. This article will walk you through forking an open-source project on RubyForge and making changes to it using Git. At the end, you’ll create a unified diff that can be sent back to the original author.

My current project FeelMySkills helps creative professionals promote themselves by creating an online portfolio that shows what they can do rather than who they know. I wanted the site to let a user export their profile page as a PDF, and so I found the amazing HTMLDoc gem which takes simple HTML pages and converts them to PDF documents.

While developing the site, I discovered that there is a bug in PDF::HTMLDoc that pops up when you embed images into the PDF. It turns out that it has to do with extra whitespace getting into the content which causes HTMLDoc to choke. The fix is really simple, and after I fixed it I found that there is already a patch for this posted to Rubyforge but it hasn’t been applied. I assume that the reason it’s not applied is that there were no tests supplied with the patch.

Let’s get the code, write the test, and submit the patch!

GITing the code

The HTMLDoc project (http://htmldoc.rubyforge.org) has a Subversion repository that we can use as our master branch. Now, if you’ve never used Git before, don’t worry about it because we’re only going to use it here as a really easy way to create patches.

Installing Git

Mac users with XCode and Macports installed can do it with

sudo port install git-core +svn

Windows users can install Msysgit.

Linux users should install Git using their package manager or from source.

Forking the code from Subversion

The git svn command lets you pull and push to a Subversion repository and is perfect for watching projects that don’t use Github yet.

Visit the project page at http://rubyforge.org/projects/htmldoc/ and click the link on the bottom for SCM Repository. That page lists the repo as http://htmldoc.rubyforge.org/svn.

Grab the trunk for HTMLDoc from RubyForge.

git svn clone http://htmldoc.rubyforge.org/svn/trunk htmldoc

Test before you start

If you want to start things off on the wrong foot, just start hacking away at the code. A much better approach is to see what tests are broken before you start working. Good Ruby projects should have a test suite that completely passes.

In the htmldoc folder, run rake which will run the test suite for this app.
Unfortunately the test suite shows an error:

Loaded suite -e
Started
..E........
Finished in 3.500649 seconds.

  1) Error:
test_get_command_pages(BasicTest):
NoMethodError: undefined method `path' for nil:NilClass
    ./test/basic_test.rb:91:in `test_get_command_pages'

11 tests, 361 assertions, 0 failures, 1 errors

However, on further inspection, the error is because of a path issue. The tests pass when you run them individually:

cd test
ruby basic_test.rb && ruby generation_test.rb
cd ..

See? Everything works!

Loaded suite basic_test
Started
......
Finished in 0.010653 seconds.

6 tests, 324 assertions, 0 failures, 0 errors
Loaded suite generation_test
Started
.....
Finished in 3.403335 seconds.

5 tests, 38 assertions, 0 failures, 0 errors

Creating a new branch for your changes

We’ll want to keep our work in a new branch. This makes it easy for us to create the patch later, as we can make Git give us the difference between the master branch, which we forked from Rubyforge, with our new branch which will contain our fixes.

$ git checkout -b fix_images

Writing a new test

Before you start hacking away on a new feature, you should write a test to prove that things are really broken. In this case, a PDF that contains a reference to an image causes things to break. A test that tries to put an image reference into the PDF data should break. Add this test to test/generation_test.rb

  def test_generation_results_with_image
    pdf = PDF::HTMLDoc.new
    pdf.set_option :webpage, true
    pdf.set_option :toc, false
    pdf << "

Random title

something you have to follow either.

Run this test

cd test
ruby generation_test.rb -n test_generation_results_with_image
cd ..

and you'll see that things don't work as expected:

Loaded suite generation_test
Started
F
Finished in 0.349939 seconds.

  1) Failure:
test_generation_results_with_image(GenerationTest) [generation_test.rb:43]:

expected to be kind_of?
 but was
.

So we can reproduce the bug, and now we just have to fix the problem.

A simple fix (this time)

The reason for the error is because HTMLDoc hates extra whitespace created by the inclusion of the image. Open up lib/htmldoc.rb and change line 186 from

          case line

to

          case line.strip

Save the file. Now run the entire test suite again to make sure that nothing else broke.

Loaded suite basic_test
Started
......
Finished in 0.007855 seconds.

6 tests, 324 assertions, 0 failures, 0 errors
Loaded suite generation_test
Started
......
Finished in 3.434714 seconds.

6 tests, 43 assertions, 0 failures, 0 errors

Hurray! We fixed it! Now we just have to make a patch!

Commit your changes!

You should commit your changes to your branch so you don't lose them.

  git commit -a -m "Fixed problem when embedding images in PDFs"

Making a patch

If you're quick, you can probably just create the patch right now, but let's assume the project is a fast-moving one, like Rails. Youl want to pull down the latest version of the project and fix any conflicts.

git checkout master
git svn rebase
git checkout fix_images
git rebase master

There won't be any collisions you have to fix now, so you can just make the patch file.

git format-patch master --stdout > htmldoc_fix_images.diff

This creates the file htmldoc_fix_images.diff which is a unified diff containing both the fix and the test. You can now send the patch file to the maintainer who will happily apply it because it has tests!

Wrapping up

Working with open-source projects gets easier every day, and you can really take ownership of the tools you use if you get involved. Hopefully this article gets you started on the path to contributing to projects. Good luck, and leave comments if something needs more clarification!

« Previous PageNext Page »