Category: Rails

  • Quick ‘n dirty Lindo step for Cucumber

    Lindo is great for verifying your Rails tests by opening the HTTP response body in a browser for inspection. It works with most popular testing frameworks including Test::Unit and RSpec. But what about Cucumber?

    It’s actually pretty easy to build a custom Cucumber step that triggers Lindo from your cukes. First, install the Lindo gem in your Rails app. Then create features/steps/lindo_steps.rb and insert this code:

    Then /^render the current page$/ do
      extend Lindo
      vr
    end
    

    The step can be named whatever you like best, but “render the current page” works for me. To trigger Lindo from within your cukes, simply reference the step like so:

    Given I am logged in
    When I follow "some link"
    Then render the current page
    

    When Cucumber hits the last step, the default browser window will open and the contents of the page at that step in the scenario will be displayed. This is very handy when troubleshooting why a specific scenario is failing. It’s also useful for determining what you should be testing for on a given page.

  • Dirty date/time attrs in Rails lose their time zone

    Dirty attributes were recently added to Rails and they’re quite useful. However, I ran into a problem where a dirty date/time attribute was losing its time zone information after a save. I was doing a comparison between two dates in a before_update callback like so:

    before_update do
      if started_at.to_s(:abbrev_date) != started_at_was.to_s(:abbrev_date)
        errors.add :started_at, "cannot be set to a different day"
      end
    end
    

    The started_at date was coming back in the Eastern time zone as expected. The started_at_was attribute, which was supposed to reflect the value prior to the update, was coming back as a UTC date/time. I would expect it to be returned in the Eastern time zone too. Apparently, I’m not alone in this assumption because a ticket was opened for this issue last month.

    An official fix hasn’t been made yet, but I got around the problem by calling in_time_zone on my dirty attribute:

    before_update do
      if started_at.to_s(:abbrev_date) != started_at_was.in_time_zone.to_s(:abbrev_date)
        errors.add :started_at, "cannot be set to a different day"
      end
    end
    
  • Mass rename files in UNIX

    Several of my Rails projects surface a RESTful API. I use integration tests to verify that the API calls work as expected. I also version my API calls so I can easily adapt the API to new circumstances while maintaining backwards compatibility.

    To move to a new API version, I copy all of the existing integration tests and rename their prefix to the new version. Instead of renaming the files by hand, there is a nifty UNIX command that handles it for me. For example, to rename all the “v2_*.rb” files to “v3_*.rb” I would type:

    for file in *; do mv "$file" "v3_${file#v2_}"; done
    
  • test_spec_on_rails now runs on Rails 2.3

    If anyone still happens to be using test_spec, you’ll be thrilled to know that the test_spec_on_rails plugin is now compatible with Rails 2.3. It has also been converted to a gem. Install with:

    sudo gem install test_spec_on_rails
    

    Add to your Rails app’s test.rb like so:

    config.gem 'test_spec_on_rails'
    

    Enjoy the goodness of test-spec helpers from inside your Rails tests. Fork and submit patches via the GitHub project. Tell your friends. Donate money. Vote for Pedro.

  • Time warping gem goodness

    The time zone warp code I posted about last week is now a gem:

    sudo gem install time-zone-warp
    

    To configure in your Rails app, add this line to the bottom of test.rb:

    config.gem 'time-zone-warp', :lib => 'time_zone_warp'
    

    You can also fork the code from the project on GitHub.

  • Time zone warp

    One of my Rails projects makes heavy use of time zones. I’ve run into some issues writing good tests for this type of thing. In particular, I’ve needed my tests to run within a time zone outside my own. But I don’t want to permanently change the time zone within the scope of the entire test run. I ended up coding this handler:

    module ZoneWarp
      def pretend_zone_is(zone)
        original_zone = Time.zone
        begin
          Time.zone = zone
          yield
        ensure
          Time.zone = original_zone
        end
      end
    end
    
    Test::Unit::TestCase.send(:include, ZoneWarp)
    

    Simply stick this code in a file inside your config/initializers directory (or include it from test_helper.rb or spec_helper.rb if you insist on doing it the right way) and you’re all set to write tests like this:

    test "code works in other time zones" do
      pretend_zone_is "Mountain Time (US & Canada)" do
        # assertions go here
      end
    end
    
  • Radiant hack night

    I attended my first Radiant hack night last week. We met at John’s apartment and spent several hours coding up new features for Radiant, which is an awesome Content Management System (CMS) built on Rails. Rick and John worked on a redesign of the admin backend, while Michael and I began adding support for using gems as Radiant extensions. There is still some work left to do, but we made decent progress.

    I really enjoyed the experience. If you’re at all interested in learning more about Radiant, or helping to contribute, I encourage you to consider attending the next hack night. Make sure you’re on the raleigh.rb mailing list to receive notification when it gets scheduled.

  • Auto timeout sessions in Rails

    Time Out!I released the initial version of my auto-session-timeout plugin for Rails at West End Ruby tonight.

    Have you ever wanted to force your users off your app if they go idle for a certain period of time? Many online banking sites use this technique. If your app is used on any kind of public computer system, this type of functionality is essential for maintaining the privacy of your data.

    After installing the plugin, a small snippet of JavaScript is placed on each page. The JS polls the server every minute to see if the session is still active. If the user has been idle for at least an hour, they are immediately redirected to a timeout page. The session will not timeout as long as the user keeps clicking around. The timeout and polling intervals are both configurable.

    The plugin is dead simple to install and configure. To get started:

    script/plugin install git://github.com/pelargir/auto-session-timeout.git
    

    Then hit the README for step-by-step instructions.

  • Introducing my latest Rails app: Fuelinator

    Gas CanI started building Fuelinator at the last West End Ruby meetup. The motivation behind the project was the lack of a decent system for tracking my business mileage. Existing apps like Fuelly and My Mile Marker make it unnecessarily difficult to enter mileage, and the statistics they produce just aren’t that useful to me.

    My initial goal with Fuelinator is to make mileage entry dead simple and to provide some compelling new features… for example, alerts via email or SMS when my vehicle’s mileage changes suddenly. This helps me track down maintenance problems early and gives me valuable information about what does and doesn’t improve my gas mileage. For example, I changed my air filter and inflated my tires last week. If my mileage changes drastically this week, I want to know. Now, Fuelinator will tell me.

    The ultimate goal is for Fuelinator to save its users gobs of money on their gas bills. I haven’t made Fuelinator public yet, but if you’d like to participate in the beta program make sure you sign up. It’s going to be a fun ride!

  • rspec’s route_for breaks when upgrading Rails

    Came across this interesting test failure after upgrading a project to Rails 2.2.2 earlier today:

    should route users's 'destroy' action correctly
    
    The recognized options <{"action"=>"show", "id"=>"1",
    "controller"=>"users"}> did not match <{"action"=>"destroy", "id"=>"1",
    "controller"=>"users"}>, difference: <{"action"=>"destroy"}>
    

    Turns out that rspec’s route_for method behaves differently when run against Rails 2.2+ due to the routing changes. The controller in question is setup as a resource, so the “destroy” action must be called via an HTTP DELETE, not a GET.

    The failing line in the test looked like this:

    route_for(:controller => 'users', :action => 'destroy', :id => 1).should == '/users/1'
    

    The fix was simple enough:

    route_for(:controller => 'users', :action => 'destroy', :id => 1).should == {:path => '/users/1', :method => :delete}
    

    Hope this helps anyone who is experiencing the same problem.