Wednesday 29 October 2008

Rails a little over-protective?

Was busy developing and minding my own business, when suddenly my functional tests all stopped working giving me this error:

ActionController::InvalidAuthenticityToken: No :secret given to the #protect_from_forgery call. Set that or use a session store capable of generating its own keys (Cookie Session Store).

Now we happen to have just switched on the use of the cookie store - and the actions all work just fine from the browser. Checking the forms even shows up the authenticity tokens etc... this only occurs during testing.

Googling gives a few rspec-based solutions, but very few are even possible for Test::Unit. I tried disabling config.action_controller.allow_forgery_protection - but that did a big nothing for me. So I bodgied up a quick-n-dirty workaround just for the test environment as per below.

Right now I still don't know what's causing the bug, but this gets me past it until I can figure out what's actually wrong. YMMV :)

  # See ActionController::RequestForgeryProtection for details
  if RAILS_ENV == 'test'
    # dodgy workaround for Rails 2.0 bug in functional tests - which don't
    # seem to use a cookie store properly. Reference to issue:
    # http://groups.google.com/group/railsspace/browse_thread/thread/fcdbfa4e65bf86de
    protect_from_forgery :secret => 'c1c6ebaee01fecc9aa9bc105d235b2c2'
  else
    # Uncomment the :secret if you're not using the cookie session store
    protect_from_forgery # :secret => 'c1c6ebaee01fecc9aa9bc105d235b2c2'
  end

Tuesday 28 October 2008

Headhunters strike out again

A follow-on from my negative experience with headhunters. I've begun receiving exactly the sort of "job offers" that I was fearing. I've got offers (from all over the world, mind) asking me if I'd like to work as (for example) a junior developer on an embedded-kernel contract in Belgium... which'd be nice and useful if I were interested in that sort of thing instead of having only 6 months non-commercial experience (at uni) doing this stuff as a elective subject... or at least held a work-permit that allowed me to work in Belgium.

The end result makes it fairly obvious that their recruiters are just too damn lazy to do more than a quick search on the db... followed by spamming all the results. They've ripped out the db-friendly data (ie my naked skillset) and divorced it from the important stuff (ie what sort of job I'm actually interested in). Leaving me to be spammed by every idiot with a contract to push.

If I were a junior developer, desperate for any job I could get, I'd probably just have to suck it up... but I'm not - and they're alienating an in-demand developer with lots of experience. This doesn't make me feel like responding positively to any of their future offers... even if they do suit my requirements.

What gets me is that it'd be so easy to make this system work. Even if they had a few extra fields in their db for "what type of job is the candidate looking for", they could run these as a filter over their resultset and turn that spam into much-less-despised targetted advertising.

I'd even be interested (possibly even grateful) to receive such a service - instead of so annoyed that I've blogged about it twice.

Enumerable.average

Was looking around for a quickie function to do an average on an Enumerable/Array just like max and min - and found there isn't one. So here's a quick-n-dirty one that you can drop into config/environment.rb. Examples for use in the comments:

module Enumerable
  # returns a simple average of each item
  # If a block is passed - it will try to perform the operation on the item.
  # if the result is nil - it won't be counted towards the average.
  # Egs: 
  # [1,2,3].average == 2
  # ["a","ab","abc"].average &:length == 2
  # @purchases.average &:item_price == 10.45
  def average(&block)
    the_sum = 0
    total_count = 0
    self.each do |item|
      # either the actual item, or a method called on the item
      next_value = block_given? ? yield(item) : item
      unless next_value.nil?
        the_sum += next_value
        total_count += 1
      end
    end
    return nil unless total_count > 0
    the_sum / total_count
  end
end

I used it for displaying neat averages in the view thus:

    <p>Averages:
    <br />Number of items: <%= @purchases.average &:num_items -%>
    <br />Unit price: <%= @purchases.average &:price -%>
    <br />Total price: <%= @purchases.average {|p| p.num_items * p.price } -%> </p>

Thursday 23 October 2008

That doesn't help me, it helps you

I just got off the phone with Australia Post and I'm not happy. Even though they were very polite to me, they failed to provide a good customer service experience. This issue requires a bit of backstory.

While in Melbourne last weekend, I'd bought two half-cases of wine and paid for them to be delivered. I have no car, so I left explicit instructions for the parcels to be left on the back doorstep if I was not at home.

I got one of the little blue cards in the post yesterday, telling me that they tried to deliver one of the parcels to me in the 1 hour when I was out of the house getting some lunch. I figured that the winery must have forgotten to put the "in case not at home" message on the parcel, so today I decided to just fetch it from the post office.

I damaged my knees fencing, a couple of years ago, so walking eight blocks while carrying a heavy box is painful for me. My arms and knees both ache, now, but I got the parcel home... only to find a second little blue card in the mailbox.

The parcel from yesterday clearly has the "in case not at home" instructions printed on the outside, so I figured I'd call up Australia Post and see if they could actually do what I paid them to - which is to deliver my second parcel to my house.

I called them up and waded through their long telephone menu-system to get to a real human being. The man at the other end seemed kind enough, but the only option available to him was to allow me to lodge a complaint against the contractor who failed to follow the given instructions.

I don't really want to lodge a complaint - I just wanted my parcel delivered - which is what I paid for.

I asked the man if he could find anyone that would be able to help me get what I wanted. He went back to his supervisor, leaving me on hold, and returned to tell me that the only way I could get it redelivered was to call up the people that I originally bought it from (some winery out in the Yarra valley - I don't know which one as the parcel is still sitting in the aussie post office), and to get *them* to call aussie post and figure out if their contract supports "redelivery-on-failure"...

Good grief! From my POV it's pretty simple:

  1. I paid for delivery to my home
  2. Aussie Post stuffed it up
  3. Aussie Post should find a way to get it right

Instead of finding a way to get my parcel to me, they kept insisting that they were helping me by starting up a complaints process and making sure it "never happens again". If you want to reprimand your contractor, that's your business. It will help you improve your own processes over time, and no doubt his non-instruction-following is the weak link that broke in this particular chain of events...

But doing this isn't going to help me. Getting my parcel to me helps me - that's all I want, and Aussie Post wasn't willing ot help in that regard. I was a customer on the line right then and there with a problem that Australia Post caused - and wasn't able to get any resolution that actually solved that problem.

I was left with the feeling that Australia Post wasn't willing take responsibility for this mistake and do something about it. Instead they wanted to shift the blame to the contractor, and then claim that responsibility was out of their hands.

This reeks of bad customer service.

If you want to leave a good impression with your customers, follow one of the basic rules of human conduct: if you stuff up, take responsibility and try to fix it. Whatever you do, don't just give a reassurance that it won't happen again... we won't believe you. After all, you've just proven that you can't be trusted. At least if you try to find a way to give me what you promised in the first place - it'll go a long way towards reassuring me that you can act in good faith.

Tuesday 14 October 2008

soap4r + rails can't find SOAP::Mapping::EncodedRegistry

Joining a new project, I had to go through the usual setting-up rigmarole - which means finding several new things to break/configure/reinstall. One of which was to get soap4r to run.

I'd run gem install soap4r just fine, but running rake was giving me the error below:

rake aborted!
uninitialized constant SOAP::Mapping::EncodedRegistry

Scrounging around gave several solutions that didn't work, mainly using two of the three lines below. I required all three of the following before it ran for me.

# Add these lines to config/environment.rb just before the Rails initializer code
require 'rubygems'
gem 'soap4r'
require 'soap/mapping'

Tuesday 7 October 2008

Words and pictures

Ok, this is a neat toy: Wordle is a site that runs a java applet that takes some text (or a URL for a feed) and make a pretty word-picture... like the one below for this blog.

Hmmm... I wonder if I can use it to improve the editing of my blogposts. Looks like I use "going" far too much!;)