Peter Marklund

Peter Marklund's Home

Mon May 12, 2008
Programming

Rails Tip: Validating Option Arguments in your Methods

I think it's a good convention to validate that options passed to methods have valid keys and values. Misspellings can otherwise lead to unnecessary debugging sessions. Rails comes with the Hash#assert_valid_keys method. I added the assert_value method:

module ActiveSupport #:nodoc:
  module CoreExtensions #:nodoc:
    module Hash #:nodoc:
      module Keys
        # Assert that option with given key is in list
        def assert_value(key, options = {})
          options.assert_valid_keys([:in])
          if !(options[:in].map(&:to_s) + ['']).include?(self[key].to_s)
            raise(ArgumentError, "Invalid value '#{self[key]}' for option '#{key}' must be one of '#{options[:in].join(', ')}'")
          end          
        end
      end
    end
  end
end

Example usage:

  def price(feature_set_ids, options = {})
    options.assert_valid_keys([:billing_period])
    options.assert_value(:billing_period, :in => ["annual", "monthly"])
    ...

Corresponding RSpec specifications:

    it "cannot be invoked with an invalid option" do
      lambda {
        @campaign.price([23], :foobar => true)        
      }.should raise_error(ArgumentError)
    end
    
    it "cannot be invoked with an invalid billing period" do
      lambda {
        @campaign.price([23], :billing_period => :foobar)        
      }.should raise_error(ArgumentError)      
    end

Make a Comment

Thu February 28, 2008
Programming

RSpec Presentation

I gave a presentation on RSpec today at Diino.com - an online backup provider - and the slides are available here.

1 Comment

Tue February 26, 2008
Programming

Stockholm Ruby User Group Meetup Tonight

We are having a Stockholm Ruby User Group (SHRUG) meetup tonight at Connecta here in Stockholm and it's good fun. Martin Kihlgren demoed his Grusome server based 2D game that uses a server written in Ruby and C for performance. Albert Ramstedt organized an RRobots workshop where you get to code your own little Ruby Robot that will fight other robots.

I gave the presentation "Building Web Apps with Rails 2 - Conventions and Plugins I Use" and the slides are available.

2 Comments

Tue November 27, 2007
Programming

Rails Internationalization with new gibberish_translate Plugin

We needed to internationalize the user interface of a Rails application that we are building and looked at a plethora of alternatives, such as Globalize, Globalite and the localization plugin by DHH. Finally we settled for the Gibberish plugin. Gibberish uses an unusual hybrid approach with both inline english texts in the code and keys for message lookups. A typical Gibberish lookup looks like this:

    update_show_page("The course has been booked"[:bookings_book_confirm])

To complement the Gibberish plugin I've drafted the gibberish_translate plugin that adds a script for extracting all message lookups from a Rails app and a controller with a web UI for doing translations. The plugin also keeps track of which english texts a translation was made from so that you can flag when english texts are changed. The plugin avoids using the database and works directly against the YAML message files in the lang directory.

7 Comments

Mon November 05, 2007
Programming

Rails Gotcha: Date Arithmetic, Time Zones, and Daylight Savings

Timezones and daylight savings can cause us programmers a lot of headache. This is evidenced by the fact that probably the most popular post in this blog ever is the one about a timezone aware datetime picker. Today I spent several hours debugging a problem a client was having with an eternal loop caused by the daylight saving transition October 28/29 in conjunction with 1.day.since. One of the chapters in the Code Review PDF by Geoffrey Grosenbach is about keeping time in your Rails applications in UTC. After todays exercises I must say I could not agree more with Geoffrey. In fact, I would go as far as to say that date arithmetic in Rails 1.2 is not reliable unless you set your ENV['TC'] variable.

Read More | 2 Comments

Fri November 02, 2007
Programming

Ruby on Rails 101: Presentation Slides for a Five Day Course

I've decided to share the presentation slides that I developed for the five day introductory Ruby on Rails course that I held in June here in Sweden. All in all it's 340 slides available under a creative commons license. You can download the slides as a PDF file here or view them over at Slideshare. To give you an idea of what's inside, here are the chapters:

I hope the slides will be useful in helping people learn and teach Rails. I'd like to thank David Heinemeier Hanson for creating such a wonderful framework and Dave Thomas for doing such a great work documenting it.

18 Comments

Fri November 02, 2007
Programming

Rails Search Plugin Review: acts_as_fulltextable

The other day I had the opportunity to install and evaluate the recently announced acts_as_fulltextable Rails plugin. The raison d'etre of the plugin is to offer easy access to the MySQL full-text search engine. The plugin uses a FulltextRow ActiveRecord model stored in a MyISAM table with a polymorphic reference (id and model name) to the model that you want indexed and a single column to hold the search index text. All you need to do is basically add an acts_as_fulltextable declaration to the model that you want to search and this will then create a has_one :fulltext_row relationship and the appropriate callbacks to populate the index on create, update, and delete.

The acts_as_fulltextable plugin mostly exceeded my expecations, especially in terms of easy setup and maintenance. It makes site-wide search (across all models) as well as model specific search (constrain your search to one or more models) very easy. A strength of the plugin is the simplicity. There is very little code and the architecture is straight forward and easy to grasp.

Something to be aware of is that MySQL uses OR searches by default. I wanted to follow the Google convention of AND searches and found myself having to prefix all query terms with a plus sign to accomplish this. Is there a better way? A more serious issue is that MySQL doesn't seem to support word stemming. It turns out that the acts_as_fulltextable plugin does a gsub on search queries to add a trailing * to each search term. MySQL calls this truncation. The star ("*") works as a wild card. If you are aware of this feature you can use it to manually work around the absence of stemming, just search for house* and that will match both "house" and "houses". It will also match "household" which may or may not be what you want.

A documented gotcha that I ran into is that if you combine the leading plus sign and the trailing star (i.e. use both boolean AND and truncation) then stop words will not be removed from your query and thus you may end up with zero results. This is very annoying. What is the solution? I don't know. Maybe go with OR searches, live with the stemming issue (without the star), or find a way to remove stop words yourself. Stop words are supposedly configurable in MySQL.

The acts_as_fulltextable plugin was created because of stability issues with Sphinx and Ferret and because Solr was considered overkill. Lucene/Solr appears to be the most well documented, reliable, scalable, and flexible open source search engine out there. My guess is that Solr is still your best best if you are a search power user and your requirements are high. Because of its simplicity and ease of maintenance though, acts_as_fulltextable offers a pretty attractive lower end alternative.

3 Comments

Wed October 31, 2007
Programming

Rails Plugin: mysql_requirement

I added the plugin mysql_requirement that allows me to check the encoding/charset settings of the MySQL server as well as its version when my Rails application starts up and abort otherwise.

1 Comment

Wed October 31, 2007
Programming

Rails Tip: Configuration Parameters

The PeepCode Code Review PDF has some nice advice about how best to deal with configuration parameters in your Rails applications. Traditionally most of us have probably just stuck global Ruby constants in our environment.rb files, but there are more structured ways of doing it. I've started using the app_config plugin and it seems to work fine so far. To make sure I haven't forgotten to define a parameter in an environment I access my parameters via a custom config_param method:

# Method to supplement the app_config plugin. I want to crash early and be alerted if
# I have forgotten to define a parameter in an environment.
def config_param(name)
  AppConfig.param(name) do
    raise("No app_config param '#{name}' defined in environment #{RAILS_ENV}, " +
      "please define it in config/environments/#{RAILS_ENV}.rb and restart the server")
  end
end

Make a Comment

Sun October 07, 2007
Programming

RailsConf Europe 2007: Rails and the Next Generation Web

A sponsored presentation at the conference that I think stood out in how professionally it was executed and also touched on interesting topics was the one by Craig R. McClanahan from Sun. As I learned later, Craig has one of the most impressive track records I've seen in the Java community, being the creator of Struts and a contributer to a wide range of technologies including Tomcat and JavaServer Faces. In his talk Craig was urging Rails developers to move beyond the traditional three tier architecture of web applications. In plain english this means moving away from the classic scenario of a dumb browser talking to a single Rails application connected to a single database. Three developments in the industry today that are moving us away from this architecture are:

Craig was urging Rails plugin developers to avoid hard wired dependencies on ActiveRecord. Examples of plugins that succeed with this are acts_as_authenticated, make_resourceful, and paginator. Craig ended with three pieces of advice:

The presentation slides are available online.

Make a Comment

Fri October 05, 2007
Programming

Ruby on Rails Deployment on FreeBSD

I did a Ruby on Rails FreeBSD deployment for a client the other day and I thought I'd share what I learned in an instructional format here. Previously I had mostly deployed on Linux (Suse, CentOS etc.) and I was curious to see what the FreeBSD experience would be like. I googled for instructions and immediately found the RailsOnFreeBSD page in the Rails Wiki. Other than that I couldn't find much Ruby on Rails and FreeBSD specific instruction out there. Note - most of the instructions in this post are not specific to FreeBSD but are generic Ruby on Rails deployment steps for Unix.

We were migrating from Windows to FreeBSD and the goal was to eliminate single points of failure. We settled on two application servers with FreeBSD 6.2 on HP hardware, both running the web and app tiers in the vanilla Rails deployment stack, i.e. Apache 2.2.4, mod_proxy_balancer, Mongrel cluster, and Mongrel. A load balancer external to the Rails system would load balance between the two Apache servers. The database we use is MySQL 5 and it sits on a separate server. The idea is to add another db server with some form of MySQL replication. We have yet to decide which replication to use and recommendations are welcome. For deployment we use Capistrano 2.

Read More | 3 Comments

Mon October 01, 2007
Programming

Rails Migration Gotcha: Forgetting to set active_record.schema_format to :sql

If you rely on any SQL not supported by Rails migrations such as foreign keys - don't forget to set config.active_record.schema_format = :sql in your environment.rb. Otherwise your test database will be out of synch with your development database. I really would prefer if Rails used the SQL dump format for setting up the test database by default as that would be more reliable.

The reason I came across this now was actually that I found what seems to be a bug with Rails migrations. Given this create_table statement in my migration:

    create_table :users do |t|
      t.string :username, :null => false
      t.string :role, :null => false
      t.timestamps 
    end

Rails will create this statement in the dump file:

  create_table "users", :force => true do |t|
    t.string   "username",   :default => "", :null => false
    t.string   "role",       :default => "", :null => false
    t.datetime "created_at"
    t.datetime "updated_at"
  end

The difference is subtle but important to me. In the dump format Rails insists that the default value is the empty string. This is not what I want when I say that a column is not null, since with MySQL 5, even with SQL mode set to traditional, the column will then be defaulted to the empty string when someone tries to set the column to NULL.

1 Comment

Mon October 01, 2007
Programming

RSpec Tip: Keeping Controller Specs DRY

I am not yet converted to the idea of testing/specing views in separation so I usually invoke integrate_views at the top of my controller specs. I also have a bunch of helper methods that I want to reuse across my specs. To encapsulate those needs and DRY up my specs I came up with this little method that I keep in my spec/spec_helper.rb file:

# Describe a controller the way we want to do it for this app, i.e. with
# views integrated and with certain controller spec helper methods available
require File.join(File.dirname(__FILE__), "controller_spec_helper")
def describe_controller(controller, &block)
  describe controller do
    include ControllerSpecHelper
    integrate_views
    block.bind(self).call
  end
end

Read More

Wed September 26, 2007
Programming

Rails Discussion: ActiveRecord vs SQL

Here is my Rails quote of the day, form a Rails mailinglist:

"When I was at the first RailsConf I was talking to someone about having used SQL for 15 years and that I was struggling with AR. At that moment someone grabbed me by the arm. I turned to look into the familiar face of Martin Fowler! He said to me, "If you know SQL that well you should just use SQL.""

1 Comment

Sat September 22, 2007
Experiences

The UPS Illusion of Guaranteed Delivery

My girlfriend was in a hurry to send 6 copies of her thesis (a 1 kg package) from Stockholm to Copenhagen. She sent it on wednesday and it said on the UPS website that the package would be delivered "no later than" end of office hours on thursday, i.e. the next day. My girlfriend chose UPS because she wanted to be sure the package would be delivered on time. Her examination is next friday and the printed thesis needs to reach the university well in time before that. For the delivery my girlfriend paid about three times as much as she would have paid the postal service. This means she paid about 600 SEK over the 200 SEK that the postal service would have charged.

Well, it's now Saturday and the UPS can't really tell us why the package has not been delivered yet. According to the UPS tracking service, the package has been sitting in the destination city for two days without any delivery attempts having been made. UPS cannot explain why. It's like the package is lost in limbo. When my girlfriend calls to explain the problematic situation she is in and asks for help she is not met by service mindedness or understanding. She is met by accusations and unfriendliness. They say she should have known to choose an even more expensive form of delivery for about 1000 SEK to be guaranteed the delivery date. They also say she misinterpreted the conditions. Apparently, when the UPS say "latest delivery" on a certain date, that means something else to them than it means to most people. The latest delivery date isn't really a part of the contract. There is no money back and there is no excuse when delivery is made at some arbitrarily later date.

Everybody knows the Postal service doesn't make guarantees about the delivery date. There is mutual understanding about the contract. A lot of us probably live under the illusion though that UPS makes guarantees like that. Well, it's time to wake up and smell the coffee because they don't. This naturally leaves the question open as to what it is that motivates the steep UPS prize premium?

2 Comments

Fri September 21, 2007
Programming

RailsConf Europe 2007 Notes: Dave Thomas Keynote

The Keynote by Dave Thomas was to me the most inspirational and profound talk at the conference. I have the greatest admiration for Dave Thomas. I can't offer Dave's entire talk in every detail here, nor do I know if that would be appropriate. What I have is key fragments from sentences in the talk presented sort of in the format of a poem. Hopefully someone who didn't hear the talk can make something out of it. Enjoy...

Read More

Fri September 21, 2007
Programming

RailsConf Europe 2007 Notes: BDD, RSpec, and Story Runner

For me, RailsConf Europe 2007 in Berlin started out with a BDD/RSpec tutorial with David Chelimsky, Dan North, and Aslak Hellesøy. In the first part of the tutorial the presenters gave an overview of the theory and background of Behaviour Driven Development (BDD). All I can offer here is an unstructured list of keywords and notes that I was able to scrabble down while listening:

  Domain Driven Design.
  Having engineers and business people speak the same language.
  SOA
  Using contracts that say: this is all I'm going to do for you
  Focusing on outcomes and reducing features  
  The Agile Alliance
  The false civil engineering analogy of building bridges
  SCRUM  
  Problem: programmers deleting tests they don't understand
  Format for user stories: index cards
  The given-when-then format
  Book: "User Stories Applied"
  All features should be traceable to a persona
  Keeping personas in FaceBook is a fun idea
  Example Driven Development  

Read More

Fri September 21, 2007
Programming, Experiences, Travel

RailsConf Europe 2007

I sometimes tell people how the best decision I made in life was to get into salsa dancing. It was how I met my girlfriend Janne and it has simply been countless hours of fun and magic. It has also made my confidence and social skills grow which has helped me in other areas of life as well. Visiting RailsConf Europe 2007 has to rank right up there with one of those great decisions and it feels like a milestone. It gave me inspiration on many levels and I enjoyed it more than I've enjoyed any previous conference. What made all the difference for me was the networking. Meeting so many friends and colleagues from the past and having the opportunity of talking to thought leaders in the Rails ecosystem is just amazing. Now that three intense days of excitement is over I miss it already.

Read More

Thu September 13, 2007
Programming

Rails Deployment Tip: FiveRuns.com

If you are a Rails developer you have probably heard of FiveRuns with their RM-Manage service offering, providing monitoring and statistics for your Rails apps. I've wanted to try the service for quite a while and today I finally got around to doing so on a production server at one of my clients. All I can say is you have to try it to believe it - it's a super slick, user friendly, and powerful service.

There was a minor glitch in the setup. The FiveRuns monitoring client didn't find our Rails app since it was in a non-standard location. I entered the FiveRuns Campfire chat room and got live support within minutes and quickly had a resolution. Once we installed the FiveRuns Rails plugin and restarted the server we had live Rails response time and profiling statistics within minutes. The first impression I have of the service is just great. We will be evaluating the service over the next 30 days and I'll post here any new findings we make. Barring any really big issues coming up, I consider FiveRuns to be a huge value add to the Rails ecosystem, almost like a milestone, and I will be recommending it wholeheartedly to all my clients from now on.

Make a Comment

Wed September 12, 2007
Programming

Rails Tip: Nested Layouts

This is just a feature I always wanted in Rails - nested layouts, i.e. the ability to have one master layout (application.rhtml) for your whole site, and then to have layouts within that that differ from section to section. Today I stumbled across this hack - just a single helper method that allows the use of nested layouts. Sweet. I'll re-post the code here:

module DocHelper
  # Nested layouts, see: http://fora.pragprog.com/rails-recipes/write-your-own/post/144
  def inside_layout(layout, &block)
    @template.instance_variable_set("@content_for_layout", capture(&block))

    layout = layout.include?("/") ? layout : "layouts/#{layout}" if layout
    buffer = eval("_erbout", block.binding)
    buffer.concat(@template.render_file(layout, true))
  end  
end

 

<% inside_layout 'application' do %>
  <div style="font-size: 150%">
    <%= yield %>
  </div>
<% end %>

Make a Comment