Two steps for faster browser specs

March 11, 2013Posted by Tom Meier

Keeping specs fast is a never ending battle for the average Rails developer. There are 2 quick lines of javascript that can make excellent and stable improvements to spec speed performance.

Most people are aware of JQuery giving the option to disable animations (see – fx.off), we can disable this in test mode making all browser based specs faster by skipping JS animation. This is acceptable, as we know it works, it’s still being triggered but skips the time-consuming part for our specs.

The same can be said for CSS transformations; sliding a menu down and hiding a div for example. We know they work, but don’t want our specs held up by the animation.

To acheive this, simply apply the following code to your Rails app :

app/views/layouts/_spec_performance.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<% if Rails.env.test? -%>
  <%= content_for :head do %>
    <style type="text/css">
      .notransition * {
        /*CSS transitions*/
        -webkit-transition: none !important;
        -moz-transition: none !important;
        -o-transition: none !important;
        -ms-transition: none !important;
        transition: none !important;
        /*CSS transition properties*/
        -webkit-transition-property: none !important;
        -moz-transition-property: none !important;
        -o-transition-property: none !important;
        -ms-transition-property: none !important;
        transition-property: none !important;
        /*CSS transforms*/
        -webkit-transform: none !important;
        -moz-transform: none !important;
        -o-transform: none !important;
        -ms-transform: none !important;
        transform: none !important;
        /*CSS animations*/
        -webkit-animation: none !important;
        -moz-animation: none !important;
        -o-animation: none !important;
        -ms-animation: none !important;
        animation: none !important;
    }
    </style>
  <% end %>

  <script type="text/javascript">
    $.fx.off = true;
    $('body').addClass('notransition');
  </script>
<% end -%>
app/views/layouts/any_layout.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
<html>
  <head>
  <%= yield :head %>
  </head>
  <body>
  ...
  <%= yield %>
  ...
  <%= render partial: "layouts/spec_performance" %>
  </body>
</html>

Thats it. I’m seeing ~31% speed improvements on our mobile specs, and ~18% speed improvements on our normal Capybara features.

Tagged ruby, rails, programming, rspec, testing, capybara, selenium


32 bits of Ruby

April 11, 2011Posted by Tom Meier

A recent rabbit hole that I fell in, related to a rather obscure Ruby 32bit → 64 bit Time parsing issue. I’ll lay it all out here in the hope that no-one else falls into it.

As per usual, all the code was working swimmingly on my own machine (macbook pro – 64 bit with the trimmings, hold the fat), but the minute it was deploying onto multiple servers and the testing environment (see – Jenkins or whatever continuos integration philosophy you employ), a few inserts into the database were chucking nasty “ArgumentError: time out of range” errors.

First picked up when attempting to load fixtures for the test suite. With a bit of hunting this narrowed down to simply one of the date values being set at ‘3000-01-01’ (legacy systems groan), as you can see here:

my 64 bit machine:
1
2
3
4
>> Time.parse('3000-01-01')
=> Wed Jan 01 00:00:00 +1100 3000
>> DateTime.parse('3000-01-01')
=> Wed, 01 Jan 3000 00:00:00 +0000
on 32 bit testing server:
1
2
3
4
5
6
7
8
ree-1.8.7-2011.03 :001 > Time.parse('3000-01-01')
ArgumentError: time out of range
        from /usr/local/rvm/rubies/ree-1.8.7-2011.03/lib/ruby/1.8/time.rb:184:in `local'
        from /usr/local/rvm/rubies/ree-1.8.7-2011.03/lib/ruby/1.8/time.rb:184:in `make_time'
        from /usr/local/rvm/rubies/ree-1.8.7-2011.03/lib/ruby/1.8/time.rb:243:in `parse'
        from (irb):1
ree-1.8.7-2011.03 :002 > DateTime.parse('3000-01-01')
 => Wed, 01 Jan 3000 00:00:00 +0000

So the long and short of it is that Ruby in a 32 bit environment, when running Time.parse tends to have a ‘bit’ of a hissy fit with any years greater than 2038. Time has a limited range of 1901 – 2038, anything, and I mean anything, even a second outside this range, requires DateTime to be used instead.

With my particular issue, I had to track down where it was calling Time.parse, and why the festicky was it not using a superior DateTime.parse call on the input data. Long live the testing suite! A quick change added to the spec_helper.rb:

spec/spec_helper.rb
1
2
3
4
5
6
  #Raise on the method call - REMOVEME
  class Time
    def self.parse(date, now=self.now)
      raise "THIS LITTLE BUGGER CALLED ME : #{caller.inspect}"
    end
  end

On the next run of the relevant spec, which loaded the problematic date times, I could then see the cause of my current despair was none other than Sequel . Sequel has been a fantastic utility, and I’m preferring it over Arel and ActiveRecord for the extra control it gives me. However, this was the root cause.

Turns out Sequel is well aware of the issue and had an immediate fix to hand already, boomshanka!

http://sequel.rubyforge.org/rdoc/classes/Sequel.html

datetime_class [RW] Sequel can use either Time or DateTime for times returned from the database. It defaults to Time. To change it to DateTime:

Sequel.datetime_class = DateTime

For ruby versions less than 1.9.2, Time has a limited range (1901 to 2038), so if you use datetimes out of that range, you need to switch to DateTime. Also, before 1.9.2, Time can only handle local and UTC times, not other timezones. Note that Time and DateTime objects have a different API, and in cases where they implement the same methods, they often implement them differently (e.g. + using seconds on Time and days on DateTime).

Job done. Setting the Sequel default to use DateTime instead of time resolves this issue for both 32 bit and 64 bit machines, and will work on any modern ruby version.

Tagged ruby, rails, programming


Static content in Rails 3

January 30, 2011Posted by Tom Meier

To auto-generate static pages in Rails 3, other than by simply adding html files to the ‘public’ directory, is very easy.

The most simple way is to just; add a controller such as ‘PagesController’, redirect all unmatched routes there, create a view and it will be displayed if the html/haml view exists – with any others raising a template not found error (display 500 to the production user).

However, I usually want to capture whats going on behind the scenes, or share variables across a group of pages.

Disclaimer – this isn’t the ideal or the best way, but it is my convenient way, let me know if you have any tips ’n tricks to improve it further!

Static Schoolgirls

My usual setup can be handy, if you want custom static pages for both scoped areas of those logged in and those not, or even subdomains, anything is possible really with the Rails 3 routing setup. Simply add the following:

Create a pages controller class, under any submodule or folder structure you like.
This will simply check if the view template exists (even haml) and render that, otherwise render out your custom 404 page, the only params being sent are ‘base_page’ which will be set by the routes.

app/controllers/pages_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class PagesController < ApplicationController

  def base_page_processor
    view_prefix = "pages"

    if params[:base_page].present? && template_exists?(params[:base_page], view_prefix)
      render "#{view_prefix}/#{params[:base_page]}"
    else
      #TODO : Notify missing url via email error or error notification service
      render '/public/404.html', :status => 404
    end
  end

end

To ensure all routes are matched, that aren’t specifically set in the config/routes.rb file, add this line right at the very end of the file (or at the end of a specific scope, such as a logged in section) :

config/routes.rb
1
match  ':base_page', :controller => 'pages', :action => 'base_page_processor', :as => :page_processor

Some specs to help you along the way (using RSpec), and ensure you don’t break the setup as time goes on:

spec/controllers/pages_controller_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
require 'spec_helper'

describe PagesController, 'automatic paths' do
  before(:all) do
    @routes = Rails.application.routes
  end

  it "should route all traffic and render if template exists" do
    ["/some_random_path", "/something_else", "/doesnt_exist"].each do |dynamic_path|
      @routes.recognize_path(dynamic_path).should == {:controller => 'pages', :action => 'base_page_processor', :base_page => dynamic_path.gsub(/^\//,'')}
    end
  end

  it 'should not route any traffic with sub paths' do
    ["/some_random_path/with_sub/lines", "/something_else/123/something.csv", "/doesnt_exist/nope"].each do |dynamic_path|
      lambda { @routes.recognize_path(dynamic_path) }.should raise_error(ActionController::RoutingError)
    end
  end

  describe 'with templates existing' do

    it 'should render the template' do
      controller.stub!(:template_exists?).and_return(true)
      controller.stub!(:render)

      controller.should_receive(:render).with( 'pages/something_that_exists' )

      get 'base_page_processor', :base_page => 'something_that_exists'

      response.status.should == 200
    end
  end

  describe 'when template doesnt exist' do

    it 'should render the 404 page' do
      get 'base_page_processor', :base_page => 'something_that_doesnt_exist'

      response.status.should == 404
      response.should render_template('public/404')
    end
  end

end

Now simply add any view template in your set page prefix, in the above example this would be ‘/app/views/pages/any_template_file’, this will now immediately load in your app. Easy.

Tagged ruby, rails, programming


Programming my way through a music festival

January 11, 2011Posted by Tom Meier

Like many things, I have to agree with an often overlooked corner stone of the Google empire, that started right at the inception of this life/behavior changing company. It seems the little known players Sergey Brin & Larry Page had been experimenting (with different social scenes, of course) during University and attended a now world famous event: The Burning Man (wiki).

In fact, the key reason why they hired Eric Schmidt was because he was the ‘only candidate who had been to Burning man’, pretty obvious indicator of the sentiment by our Google overlords. If this isn’t enough, the very first ‘doodle’ was actually a scribble of the burning man (the effigy burned at the close of the festival, which became its namesake).

But why discuss Google? I mean, you wouldn’t jump off a metaphorical bridge and follow them to Bing if they sold out right? The point is I have your attention to some of the most successful characters in the industry (right now), and with their philosophy on this subject, I have to completely agree with them!

What better way to get to know your work mates? Your peers? Your fellow man? (your fellow alien?) Other than to get so completely, absolutely and absurdly out of your perceived comfort zone. If only for a few days. Every self-help book cluttering up the interesting sections of your local bookstore are filled with methods and techniques to change and alter the way you perceive your surrounds, looking at the world at a slight tilt different to your day to day.

Seriously, its a pretty far jump from a programmers day to day, frolicking around the fields, in sunshine (yes – it exists), taking in the smells, the jaw dropping sights, the amazing sounds, the good, the bad and the ugly. So when you’re considering your new year resolutions, or better yet, that ‘thing you should try once’ this year/month/week. Try a festival, something really detached from anything that you would normally do. Rope in some friends, share the experience. Make the most of it, you just might like it. You have nothing to lose, and everything to gain. The unfortunate truth of many of these festivals are a steeper slope of acceptance the older we get, so why not try it now. I know I’d rather look back when I’m 97, sitting on my Atari 2600 wondering why the toilet paper isn’t coming out, reflecting back on my life and thinking about the event, and not ‘I wish I tried that’.

You don’t need rhythm, dancing ability, or even confidence. That just seems to come. Just by stepping out your comfort zone and trying something new. It might not happen even while you’re there, you might not even notice, but you’ll have changed. Pick a festival that isn’t the run of the mill, that isn’t ‘where you go after schoolies’. Try them all.

If you manage to get to Burning Man, please, please keep an eye out for the giant hippies with dreadlocks that don’t have peace and happiness in their eyes, more “I’m going to break your neck and turn you into a fart bucket” stare. I’m guessing a certain pair of Google founders may be nearby.

The closest you’ll get to Burning Man in Australia would be Rainbow Serpent. With subtle twists, that make it truly Australian such as a blessing ceremony by the same beautiful indigenous lady that has done it every year! By far, one of the most absurd and enjoyable experiences of my life. Learn a few things about sustainable living, accidentally eat a meal from a different nationality every day, gyrate nervously to the music until that sudden epiphany: no-one cares, no-one sees me as fool, no-one is watching you to see you fail, everyone wanting to see a smile and reckless abandon from letting go in some unbelievable surrounds.

Decide how you want to look back on your life when you’re on your death bed. Push those boundaries. Try something new. Look me up if you’re coming. I’ll be at the front, leaving my work and stress and inhibitions at the gate.

RSF2010-25155222

Tagged festivals, lifestyle, programming