Testing your Rails views with Hpricot

Posted about 8 years back at Luke Redpath - Home

Update: This extension is now available as a Rails plugin. See below for more details.

_why released his great little Hpricot HTML parser the other day and the first thing that struck me would be that it would be a great tool for testing the output of Rails views. Rails comes with the built-in assert_tag method but I've always found it clunky and a pain in the arse to use.

By combining the Hpricot library and a few useful helper functions, you can now test the output of your Rails views in Test::Unit::TestCase easily using Hpricot's support for CSS selector searches.

First of all, install Hpricot if you don't have it already:

$ gem install hpricot --source code.whytheluckystiff.net

Next, download the Hpricot Test Extensions file and copy it to the lib folder of your Rails app.

Finally, require the file at the top of your test_helper.rb file:

# test_helper.rb
require 'hpricot_test_extension'

Alternatively, you can also install as a Rails plugin. The plugin is located in the Agile Evolved Open Source repository.

And thats it. You'll now have access to four new methods - tag, tags, element and elements. All four methods take a CSS selector as an argument. The pluralised methods return an array of search results for the CSS query, the singular methods just return the first (handy if you know you'll only have one of a particular element, say, "title"). The tag methods return the inner contents of the returned elements, whereas the element methods return the raw Hpricot::Elem objects for you to work with. Here's a few examples:

assert_equal "My Funky Website", tag('title')
assert_equal 20, tags('div.boxout').size
assert_equal 'visible', element('div#site_container').attributes['class']

Finally, there is a small extension to the Hpricot::Elem object itself which lets you assert the presence of text within an element using an alternative syntax:

assert element('body').should_contain('My Funky Website')

I plan to integrate these helpers into rSpec as well using a more rSpec-style syntax. Any comments and suggestions are welcome.

Annoying IE bug with the script.aculo.us in-place editor

Posted about 8 years back at Luke Redpath - Home

I came across a rather annoying bug with the script.aculo.us effects library the other day. If you use pass in a loadTextUrl parameter to make the inline editor grab the value from the server, Internet Explorer would throw up the error 'Can't move focus to the control because it's invisible not enabled or of a type that does not accept focus.'.

After digging around in the inplace-editor code, it seems that it was disabling the inplace form field while it sent off an AJAX request to the server to grab the value, then re-enabling it once it the data was returned. The problem seemed to be down to the difference in the way IE and other browsers place focus on the field once it was inserted. Safari and Firefox were both placing focus on the field before it was disabled, whilst IE was trying to focus it after it had been disabled. This is what was causing IE to barf.

The solution? Instead of simply calling the function that retrieves the remote text from the server (which does both the disabling/reenabling and AJAX request) directly, attach it to the field's onfocus event handler. This ensures that it will always get executed after the field has focus.

I've submitted a patch for this which fixes this problem which will hopefully be accepted, so if you've ever had problems with this in IE, this should help you out.

RubySlim - Ruby API for SlimServer

Posted about 8 years back at Luke Redpath - Home

Just a small announcement for any other Squeezebox/Soundbridge owners out there who use the SlimServer music server. I've started work on a Ruby API for it and you can view the latest source on the Agile Evolved Open Source Trac.

You can also check out the latest source code from Subversion.

Here's a small code example:

slimserver = RubySlim::SlimServer.open('localhost')
slimserver.connect('myusername', 'mypassword')
puts slimserver.version

squeezebox = slimserver.players.first
squeezebox.turn_on
squeezebox.current_playlist.play

And in the future...a full RubyOnRails, AJAX-enabled front-end to the SlimServer for browsing your music collection, creating playlists, controlling your SqueezeBox and more. I've not checked anything in on this but I've already been experimenting with creating ActiveRecord models that interact with the SlimServer database. You could do something like this (this is actual code that I have got working):

my_favorite_album = Album.find_by_title('The Number Of The Beast')
squeezebox.current_playlist.load :album => my_favorite_album.id

Finally, if you have access to the SlimServer CLI API (click on technical information on your SlimServer web interface menu) you can play with any parts of the CLI API that I'm yet to add to the Ruby API by using the raw connection and API object:

# make sure you are using the CLI port, not the web interface port
connection = RubySlim::SlimServer.raw_connection('localhost', 9090)
slimapi = RubySlim::SlimAPI.new(connection)

# invoke any api commands
slimapi.invoke("version")
slimapi.invoke("myplayerid player status")

For those interested in the concept of behaviour-driven development, the whole thing is specced using rSpec. Admittedly its not the greatest exercise in BDD because I'm simply wrapping an existing API in a Ruby API so there wasn't more than 1 context for most classes, but it was still great to use and the built-in mock objects API -- which seems very similar to FlexMock -- was very easy to get to grips with - great for mocking out those telnet connections to the SlimServer CLI.

Update: RubySlim is now hosted as a RubyForge project. The 0.1 release is available as a gem.

$ gem install rubyslim

Moving from Eyelook to Smugmug

Posted about 8 years back at

I needed to host photos from a recent trip to New Zealand. I started modifying eyelook to make it easier to do what I wanted such as geocoding, bulk upload, hierarchial grouping, different sizes, different layouts, themes etc. It was taking a bit of time so I decided to look around at existing services but none of them suited my needs.

Then I found SmugMug and I was hooked. This is the best photo-sharing service I have seen. It seems to be aimed at professional photographers – there is no free version but you get what you pay for. It is far from perfect but there is no way I would consider anything else at this stage.

Luckily it has a referral system that gets you $5 off your first year if you indicate an account that got you onto the system so I got mine a little cheaper. I also helped out the friend that referred me by making it a little cheaper for them next year. If anyone wants to get same deal, it’s simple: enter my email address ( peter at realityforge dot org ) or my coupon ( EqCTz7hz7wkW2 ) in the ‘Referred by’ field on the signup form and you get $5 off.

Best photo sharing service yet!

RailsConf 2006: Marcel Molina and Sam Stephenson - Ruby on Rails Podcast

Posted about 8 years back at Ruby on Rails Podcast

Hear about integration tests, optimization, and the tumblelog from Sam Stephenson (creator of Prototype, Rails core, 37signals) and Marcel Molina (Rails core, 37signals).

RailsConf2006 Aftermath

Posted about 8 years back at RailsExpress.blog

As promised during my talk at RailsConf, I have uploaded a new version of my railsbench package to rubyforge. You can either download the tar ball or get the new version using svn. Apologies to all users which have a checked out and modified version of the old CVS archive. I decided to convert from CVS to subversion since I kept typing svn commands at my CVS archive, so it had to go.

The svn url for railsbench is svn://rubyforge.org//var/svn/railsbench/trunk/railsbench

The new version now supports running ruby-prof as an alternative to SVL's Ruby Performance Validator. This should make some Mac and Linux users happy. However, ruby-prof is only a weak substitute for a good profiler.

Profiling with ruby-prof requires ruby 1.8.4 and works like this:

perf_prof n "-bm=benchmark_name other_options" config_name

This will run the named benchmark n times using ruby-prof and store the resulting HTML formatted profiler graph into $RAILS_PERF_DATA

Happy profiling!

PS: My presentation given at RailsConf2006 is available for download.

Updating a single record attribute with ActiveRecord

Posted about 8 years back at Luke Redpath - Home

On a Rails project I'm working on at the moment, the Rails app had to make calls to a third-party application using web services - we would then update certain attributes on a particular model instance with data returned by the web service. The only problem was that the third-party app had to update (different) attributes on the same record. Whilst the web service would always respond in a timely fashion, there was no way of predicting when the third-party app would run its update (it could be delayed if the server the server was under heavy load).

This resulted in a race condition that could potentially result in a loss of data. Because Rails already had a loaded instance of the object in memory before the web service call was made, if the third-party app saved its changes before Rails saved the record with its own changes, the values already loaded in the Rails object would overwrite the new values written by the third-party ap

The solution seemed obvious at first - instead of doing:

obj.some_attribute = 'foo'
obj.save

We could simply do:

obj.update_attribute('some_attribute', 'foo')

But we were mistaken. Whilst the method's name would seem to imply that ONLY a single attribute would be updated, this is in fact not the case. All update_attribute() does is exactly the same thing as the first code snippet. From the Rails source:

def update_attribute(name, value)
  send(name.to_s + '=', value)
  save
end

As it turns out, there isn't a built-in way of updating just a single attribute that we could see. So we wrote our own, simple solution:

def update_single_attribute(attribute, value)
  connection.update(
    "UPDATE #{self.class.table_name} " +
    "SET #{attribute.to_s} = #{quote(value)} " +
    "WHERE #{self.class.primary_key} = #{quote(id)}",
    "#{self.class.name} Attribute Update"
  )
end

Feel free to use this in your own apps if needed - the easiest way to use is it is as a monkey patch to ActiveRecord - simply paste the following code into a file called active_record_patches.rb in your app's lib folder and require it at the bottom of your environment.rb file. Alternatively, you could patch the Rails source directly or convert this to a Rails plugin.

Josh Susser - Ruby on Rails Podcast

Posted about 8 years back at Ruby on Rails Podcast

Newly knighted Rails blogger Josh Susser muses on database relationships and reliable apps.

Common Rails Performance Problems

Posted about 8 years back at RailsExpress.blog

InfoQ has published an article on common Rails performance problems (written by me). You can head over to read all about it.

David Black - Ruby on Rails Podcast

Posted about 8 years back at Ruby on Rails Podcast

The RubyCentral co-founder talks about his new book, Ruby for Rails.

Update to Unobtrusive Javascript Plugin

Posted about 8 years back at Luke Redpath - Home

Update 15 Feb 2009: The UJS plugin for Rails is no longer actively maintained but you can find the the source on github.

Some small changes and one major bug fix have been checked into Subversion today:

  • The controller action that serves up event-selector.js, eliminating the need to manually copy it to your public/javascripts folder, was looking for the js file in the wrong place, stopping the whole plugin from working. If you've already discovered this, please run an update.
  • There is now an unobtrusive version of the link_to_remote function - this also dynamically appends a return false to the onclick event to ensure it works in Safari (see this blog post)
  • The rules are now assigned using an addLoadEvent function instead of window.onload - this is more of a quick fix and will be improved in the future (I plan to use Prototype's onReady extension) but in the meantime this makes sure it doesn't mess up any other onload events you might have.
  • The rules are re-applied automatically on completion of any AJAX request

Please post any bugs to the Agile Evolved Open Source Trac.

Introducing Unobtrusive Javascript for Rails

Posted about 8 years back at Luke Redpath - Home

Update 15 Feb 2009: The UJS plugin for Rails is no longer actively maintained but you can find the the source on github.

Rails makes a lot of things easier for a developer. One of those things is AJAX. The built-in Javascript and AJAX helpers, such as form_remote_tag and link_to_remote, make developing AJAX apps a breeze. But if there has every been one major bone of contention with these helpers, it's the markup they produce. Developers have been well aware of the need to separate content from presentation with CSS for a while now - less prevalent is the recognition of the benefits in separating behaviour from content.

Update 21/08/2006: The latest version of this plugin is 0.3 - please see this post and the official UJS website for more information.

Rails makes a lot of things easier for a developer. One of those things is AJAX. The built-in Javascript and AJAX helpers, such as form_remote_tag and link_to_remote, make developing AJAX apps a breeze. But if there has every been one major bone of contention with these helpers, it's the markup they produce. Developers have been well aware of the need to separate content from presentation with CSS for a while now - less prevalent is the recognition of the benefits in separating behaviour from content.

I'm not going to cover the ins and outs of why you should separate behaviour from content here, but or those unfamiliar with the reasons and benefits, Peter-Paul Koch of QuirksMode published a great article in 2004 highlighting the benefits of separating behaviour from content. More recently, Dan Webb from Vivabit published an article about the problem with Rails AJAX helpers.

As Dan points out in his article, the subject has been mentioned on several occasions on the RubyOnRails mailing list but has often been shot down for a multitude of reasons ranging from "its not worth it" to "it would be too awkward". Invitations to submit patches were made but nothing came about.

So I'd like to present to you something I've been working on here at Agile Evolved - Unobtrusive Javascript for Rails. The plugin makes use of a Javascript library called event:Selectors by Justin Palmer at EncyteMedia. Similar to Ben Nolan's behaviour.js library but making full use of Prototype, it allows you to use CSS selectors to attach Javascript events to your page. This plugin allows you to make use of the event:Selectors library, but in Ruby, directly from your controller or view and have the resulting behaviour rules dynamically generated at runtime in an external javascript file. Let me demonstrate:

First of all, you'll need to grab the plugin from the Subversion repository. Please note - the plugin is in its early stages at the moment and is likely to be updated quite a bit over the coming weeks - I do not recommend using this in a production site just yet. In the meantime, if you want to have a play I recommend using svn:externals to make sure you keep your copy of the plugin up-to-date.

$ ./script/plugin install -x http://source.ujs4rails.com/current/unobtrusive_javascript

Next, you'll need to load in the Prototype javascript and the Unobtrusive Javascript scripts. Somewhere between your layout's head tags, add the following:

<%= javascript_include_tag :defaults %>
<%= unobtrusive_javascript_files %>

You can now attach events to elements in your page from either your controller or your view, using the register_js_behaviour() function. The function takes two parameters; the first is the CSS selector and event (for more details see the event:Selectors documentation) to attach your behaviour to and the second is a string of Javascript that you want to execute. Here's a small example:

<% register_js_behaviour "#my_funky_link:click", "alert('Hello World')" %&gt;

Of course, writing out large strings of javascript could become cumbersome. The first solution is use the built-in Rails helpers to generate your Javascript strings. For instance, if we want to highlight a div when we hover over it with the mouse, you could do the following:

<% register_js_behaviour "#my_funky_div:mouseover",
        visual_effect(:highlight, "my_other_div", :duration =&gt; 0.5) %>

Now, if you load up a page with one of the above calls and View Source, you might expect to see a whole load of generated Javascript. Except you won't - thats because the unobtrusive_javascript plugin makes use of a special controller and view to dynamically generate your behaviour rules at runtime, which are then linked to using a normal script tag. That means you can attach as many behaviours to your page as you like, from anywhere in your view or controller, without clogging up your rendered HTML with Javascript.

Next, there is the issue of graceful degradation. The two primary candidates for graceful degradation are the link_to_remote and form_remote_tag helpers - links and forms both have natural fallbacks - the HREF and ACTION respectively, and by using some unobtrusive Javascript we can make sure those fallbacks are used when somebody has Javascript disabled.

This initial release contains an updated version of the form_remote_tag helper - by default it does exactly the same thing as the built-in Rails helper but making it use unobtrusive Javascript is a case of one small addition to your code:

<%= form_remote_tag :url => { :action => 'foo' }, :unobtrusive => true %

Because the event:Selector library depends on an HTML ID to attach an event, your forms will need an ID but worry not - another modification to the form_remote_tag helper will mean that if you do not specify your own HTML ID, one will be automatically generated for you.

Finally, some of you may be aware that Dan Webb, who I mentioned earlier in this article, has been working on his own unobtrusive javascript plugin. I have been in touch with Dan and he preferred the simpler syntax that my plugin uses. That said, he's plugin contains some very cool stuff and we've agreed to merge the best of both of our plugins together over the coming weeks, including the ability to supply the register_\javascript_behaviour() function with a block which will let you write the Javascript functionality you want to attach using Dan's RJS-style JavascriptProxy classes.

Please do download the plugin and have a play around. You can report any bugs on the Agile Evolved Open Source Software website and any opinions and ideas are welcomed.

Unobtrusive Javascript in Rails...what if?

Posted about 8 years back at Luke Redpath - Home

Update 15 Feb 2009: The UJS plugin for Rails is no longer actively maintained but you can find the the source on github.

What if...you could produce accessible, unobtrusive javascript, using Rails built-in javascript/prototype helpers, with just one extra line of code in your layout, a plugin, and one small enhancement to the helpers? Something like this:

<% form_remote_tag :controller => 'foo', :action => 'bar', :unobtrusive => true %>

Which produces:

<form id="form_foo_bar" action="/foo/bar" method="post">

But which still acts as an ajax form? You would simply handle the response for ajax and non ajax requests using Rails' responds_to function.

In addition, what if you could attach javascript functionality to your page elements in a Behaviour-like fashion, but using pure Ruby, anywhere in your view, but loaded from an external Javascript file?

Sorry to tease. Stay tuned, we have something very cool to show off in the next day or two.

http://opensource.agileevolved.com

Sebastian Delmont - Ruby on Rails Podcast

Posted about 8 years back at Ruby on Rails Podcast

The lead developer of StreetEasy talks about bridging web 1.0 and web 2.0 for the New York real estate market.

Simpler Piggy Backing

Posted about 8 years back at RailsExpress.blog

A while ago I posted an article about the performance improvements obtainable by piggy backing. Although the speedups where substantial, I received some negative comments about exposing too much SQL to the programmer.

Exposure to the underlying SQL is not such a big problem for me, that it would deter me from applying the technique. But I found that I had a lot of repetition in my code, which I didn't like.

After ignoring the problem for a while, I decided it was about time to deliver a better solution than manually coding selects and joins.

I wrote a small extension to ActiveRecord, which makes piggy backing extremely simple.

Instead of coding joins and selects manually, you can now declare the piggy backed attributes from a has_one or belongs_to association in your model classes like so:

class Article
  belongs_to :user
  piggy_back :user_name_and_birthday, :user, :name, :birthday
end

This will generate all necessary select and join statements, which can then be added to a query rather elegantly:

article = Article.find :first,
            :piggy => :user_name_and_birthday,
            :conditions => "articles.title LIKE '%piggy%'"

would retrieve the first article which contains 'piggy' in its title attribute, along with the birthday and name attributes of the corresponding User object, storing them in the attributes hash of the AR instance.

But this is not all! After declaring the piggy backed attributes, you will also get reader methods which retrieve the attribute for you from the AR instance, and, should a type cast be required, convert the value to the correct type.

The piggy backed attributes are accessed like this:

article.user_name        ==> a string
article.user_birthday    ==> a date

Additionally, should you forget to piggy back a query,

article = Article.find :first,
            :conditions => "articles.title LIKE '%piggy%'"

methods user_name and birthday will silently use the user association to retrieve the values for you. This make is possible to use user_name and user_birthday regardless of whether your original query was piggy backed or not.

I've ensured that all of this works with pagination. In fact, using the extension is slightly faster than coding the select and joins manually (1%-3%).

You can download the code here. I haven't turned it into a plugin yet, so you need to add a require 'piggy_back' to your environment.rb.

Enjoy!

Update:

  • I failed to mention that piggy backing also works with finds on associations, so
article.comments.find(:all, :piggy => :user_name)

works as expected.

  • Upon popular demand it's now possible to use a more verbose syntax. You can write
class Article
  belongs_to :user
  piggy_back :user_name_and_birthday,
      :from => :user, :attributes => [:name, :birthday]
end

for the example above.

  • Piggy backing is now available as a plugin using the URL svn://railsexpress.de/plugins/piggy_back/trunk