Thursday 30 December 2010

Simple private messaging

Ran across this plugin the other day. It gives you very simple, private messaging for Users on your app.

Needs some fiddling to make the messaging private - otherwise anybody can read anybody else's message... but otherwise, pretty neat. It basically does most of the heavy-lifting for you, and lets you get on with the customisation... which is the point of using other people's plugins.

Friday 24 December 2010

Gotcha: db index name must be a string

Might just be Rails 2.3.8 - an exception is thrown whenever I use a symbol for an index name. I get:

rake aborted!
An error has occurred, all later migrations canceled:
undefined method `length' for :my_index_name

The code causing this would be something like:

add_index :widgets, [:colour, :weight], :name => :index_by_colour_weight

it’s solved by just using a string instead:

add_index :widgets, [:colour, :weight], :name => 'index_by_colour_weight'

Sunday 19 December 2010

Dosbox and old games

So... my favourite game of all-time is still X-Com: terror the deep. I've been playing it off-and-on now since it first came out 15 years ago.

It is a game with extraordinarily long game-play time. Come to think of it, I think I still haven't actually completed a game yet... it takes months of gameplay to complete... but it's still fun enough that I like to play it anyway.

Now, the thing is, it's a really old game now - it's a DOS game that was able to be played on the then-new Win95... but it hasn't transferred well up the line. I've had to keep around an old laptop for the precise (and only) purpose of playing this game.

For obvious reasons (including the bulk of said ancient laptop), I did not bring that laptop with me to the UK. Which means that I've been deprived of my favourite game for some time.

Lately, however, I learned of Dosbox - which I can now rightly praise as an awesome DOS emulator that actually works on Linux (as well as other, lesser O/Ss). ;)

I had some issues with getting the game to play and save - before realising that I hadn't properly "installed" the game before trying to play it straight of the CD - and thus it didn't have write-privileges, causing it to fail to save a game... or to write the sound-file etc.

but after I figured that out - it's worked just fine. Awesome!

Monday 13 December 2010

The perils of pagerank

Now that RubyGlasses has gained a bit of PageRank, I've noticed a marked increase in the amount of people emailing me requesting "Link swapping".

I've generally been deleting these along with most of the spam... google doesn't like linkswapping, and I won't do it. (all hail oh mighty google!)

However, I've also received one or two offers of advertising... and, to me, that's different. I'm happy for an advertiser to pay me to put ads on my site that are relevant to my audience.

However, after a recent email-conversation, I now have to make it clear to potential advertisers that these ads are going to be obviously ads.

I will freely confess that I don't get very many offers of advertising. I've had two so far... one that I've accepted (see if you can spot them) and one that I have most definitely not.

The first did want me to *not* use certain ad-like keywords in proximity to their ads... no doubt they're after a bit of my pagerank "link juice" as well as the actual click-throughs that are likely to accrue (yes, I actually asked them about this). That was ok to me, as long as it was obvious to my readers that what was going on was actually an ad... ie paid-for content inviting people to purchase something.

The reason I turned down the second offer, however... was different. In fact, I didn't even need to turn them down - just stated what I was and was not going to do.

The second exchange began with an offer to "have cooperation in marketing". I'll admit that maybe I didn't completely read the email in question because it also explained that they expected me to "add our product information with links to < thecompany > on your blog"... I read this and thought "advertising"... but in the following exchange (I asked if that's what she meant), discovered that she expected to write articles about her products... and put them on this blog.

After explaining that I write my own articles she responded with (and I paraphrase) "Oh ok, so *you'll* write articles about our products on your site, and put links to our products"

I had to then explain that I write articles about Ruby-on-Rails or web-development or freelancing... if her products were relevant to that then I might write an article on them... but I would *not* promise to be favourable.

She then responded with a "suggestion":

I have read your email, I have one suggestion, how about we write articles suitable for your site, and you put it on your site. We can also pay for you on adding them, but how much per article will you charge?

I mean, what does it take to get through to these people? My final email to her reminded her that I write my own articles, and that I will happily post paid advertising at the bottom of my articles - and that she could even dictate the text of the advertising... but that I would not post fake articles written by somebody else about her products.

She said she'd stay in touch...

Saturday 11 December 2010

NaNoWriteOff

*sigh* I admit defeat.

Though I've "won" three years in a row, this year, I quit after a week... it's kinda sad, but I felt it was a necessary decision.

After a week of the usual noveling, I realised I had so many other commitments this year that one more was really piling on the stress a bit too thick. I weighed up whether or not I needed to complete this one time - and decided I didn't need to prove to myself that I *could* finish a novel as I've done it three times already.

So, for the first time since I discovered the wondrous new world of NaNoWriMo, I failed to finish.

I'm sad about it because this year I actually had a full plot. Most years I just began with an idea and a few characters... It was a Romance (I try a new genre every year) - which lent itself to easy plotting... but I realised a week in, that I just wasn't satisfied with that, and had already begun to add action/adventure elements (the hero was captured by desert pirates... leading to a chase...).

In any case - it'll be there, waiting for me in January - when hopefully I'll dig up a bit more spare time.

Wednesday 8 December 2010

Less Wrong: the art and practise of Rationality... and Harry Potter

I just found an amazing website that basically constitutes a broad exploration (with much discussion) on the art of "being less wrong".

What's interesting is that I happened upon it because my sister pointed out to me the most interesting Fanfic I've ever read: Harry Potter and the Methods of Rationality.

I don't read fanfic, as a general rule... it's usually just somebody's personal fantasies, and almost never as good as the original fiction.

But this one had me hooked from chapter one. It seems to constitute a kind of course-by-example into the Art of Rationality. No doubt more poignant if you've actually read the original, but fascinating none-the-less. Try it and see for yourself.

If you're not into fanfic at all - then feel free to cut to the chase - skip it and go straight to the Less Wrong wiki.

All this stuff is brought to you by the Singularity Institute which is dedicated to making sure that the predicted technological singularity is Friendly, rather than unfriendly, towards us mere mortals. Less Wrong was created in order to build a community of rationalists that would be able to improve the world in general (including, but not limited to the singularity). The soundbite mission statement being raising the sanity waterline

Thursday 2 December 2010

I'm not a militant feminist

I assume non-gender bias at the start; I don't care what pronouns you use, and most of those "war of the sexes" ads are just plain funny.

Which is why I was totally surprised by an ad I saw today which suddenly made my "feminism" detector spike off the charts.

I can't find a pic of it online, so I'll have to describe it.

It's for some kind of smartphone.

A smug-looking geek-boy is standing there, with a gorgeous-looking girl with her arm wrapped around him.

The caption: "It's about how smart you are".

...

In case I need to state the obvious - clearly, being totally smart means that if you're a smart guy you get the hot-looking bird... but what makes him look smug is just as clearly *not* about how smart she is.


Disclaimer...

I like geek-boys, and for me it *is* more about how smart they are... but I'd hope that for the geek-boys it'd be about how smart *I* am too!


On further reflection, I think I'd be almost as scandalised if the ad showed a smug geek-girl with an Adonis draped over her.

What mostly annoys me is that it implies that the main benefit of being smart is to be able to entice the beautiful people to hang with you despite how plain-looking you are. In other words - even though it says it's about how smart you are... it's still about how pretty you are... :P

Tuesday 30 November 2010

Gotcha: heroku with git+svn pushes your working copy

For the MatchFounders website, I've been working with svn on Heroku using git just as a deployment tool... and I came across a bit of a gotcha the other day.

git commit will add your *working copy* to your next deployment.

Luckily it didn't turn out too badly for us - I was just mucking about with some pagination links... and quickly fixed the bug I'd been working on before deploying again... but if I'd been right in the middle of some big hairy code refactor, or something... I'd have had to go find heroku's docs on rollbacks pretty darn quick.

So - just be aware that if you use this method, to add a step to your process-control to make sure you only deploy from a 'clean' working copy.

Tuesday 23 November 2010

Ruby median

Was trawling through the google analytics search keywords and noticed somebody was looking for an Enumerable#median method when they came to this site. Got me thinking...

I went googling for solutions and found that some of them were just plain wrong... can't have that.

module Enumerable
  def median
    # trivial cases
    return nil if self.empty?

    mid = self.length / 2
    if self.length.odd?
      (entries.sort)[mid]
    else
      s = entries.sort
      (s[mid-1] + s[mid]).to_f / 2.0
    end
  end
end

This one isn't the most time-efficient (the sort can get long). I'd need to implement the selection-algorithm for the most optimal solution... but it'll do for a quick fix. I'll post again here if I get around to the better one.

Friday 19 November 2010

MatchFounders hits 500

MatchFounders.com hit its first milestone today: 500 registered members. I'm pretty pleased with that, seeing as how it's been a part-time on-the-side project that started on what's basically a hackfest.

Still got heaps to do before it becomes a world-dominating site, of course... but I'm pretty happy with that amount of activity after only a month and a half. Enough to post about it on the matchfounders blog... along with our upcoming dastardly plans.

Saturday 13 November 2010

mobile broadband roaming

The directors of MoneySpyder have been reading "The 4-Hour Workweek" by Timothy Ferriss and are all fired up about the idea of us working from home (amongst other things). Basically, they're hoping to kick the expense of an office - which is fair enough.

So I casually asked "so, does this mean I can take my laptop on the road?"

I was kinda surprised when they answered yes.

w00t!

So, I finally have the option to live my original dream - spend a month in France, a month in germany, a month in spain...

BUT...

(you knew that was coming)...

I've just checked what it costs to get international roaming on mobile broadband, and it doesn't look pretty.

Actually, that's an understatement... it looks downright atrocious.

I'm currently on three mobile - and I pay around £15 a month for 5G of internet usage. As yet I've never busted that limit - I think I use around 1.5-2G. I used to have a 1G account (with O2) but that wasn't enough for me - eg when I needed to do a full ubuntu upgrade.

However - when I checked into the mobile broadband roaming rates I discovered that they don't bundle your monthly allowance - it costs per mg... and not at the usual overlimit pricing rate of around 10p per meg - it costs you £1.25 per meg!

This would mean my normal monthly usage would come to cost of £2000-£2500 PER MONTH!

Yes, that's over two thousand pounds per month!

Now - I can understand that it costs a fair bit more to use overseas carriers, than to use their own.... but even if *they* bought the data at retail prices, and then charged *us* retail on top of that - this would come to only *twice* our normal per-meg rate...

Not 166 TIMES my normal monthly amount.

There is something seriously wrong there!

Now - the pages all say "well, just don't use as much internet when on the road"... but unfortunately, I have no choice. I am a Web developer - that requires me to do a bit more than just checking email or checking the local weather online. I need to download the latest codebase, get copies of db dumps, upload and deploy sites, go to the online site and test it thoroughly... etc etc. Even if I cut out all recreational internet use (and where's the fun in that) I'd still be using about a Gig a month.

I've noticed that a very few enlightened companies have begun to offer something a *little* better (eg orange travel bundles), but even so - they're still not even approaching regular internet usage. The orange bundle offers 50M at £40 - which would make my monthly spend "only" £1200 - so, half the rate of the Three option... but still in the ridiculous zone.

Now, I've wandered about online in search of a better solution, but I have only a couple of viable options.

Firstly - I could spend my life in a Maccas or Starbucks (or equivalent) for the purposes of the free WiFi connections... rather than actually enjoying the variety of local establishments. Of course - they do expect you to actually purchase some of their wares - and I refuse to eat the former, and the latter is definitely not my cup of coffee-flavoured-milk.

Also - it's quite difficult to actually do real work in a busy cafe. It's ok for a few hours - but it's tough to be there all day. I'd also have to shut down, pack up and potentially lose my table every time I needed to dash to the loo. :P

The second alternative would be to just buy a new dongle from the country I'm visiting... for each country I visit. This feels expensive and wasteful... but if you compare it to the roaming rates - it actually works out cheaper... *far* cheaper.

With Three's quoted roaming rates, it would cost me an average of £85 per day.
Even if a new dongle cost me £100 (for the PAYG version) - I could buy one every two days - and still be paying less.

Ridiculous as this seems, it seems to be my best real option.

If anybody knows of a better solution, let me know!

Dongle-sharing?

But then I got to wondering: perhaps there's others out there wanting to do the same thing. If we each buy a dongle for a different country and then swap with each other - this would be far less expensive. Just buy our own PAYG sims. Or perhaps there are others of you out there already in a different country (that I want to visit) who'd be willing to rent me your own dongle for a month... I'd even pay a refundable deposit!

Any takers?

Wednesday 3 November 2010

NaNo Wrides again!

It is the month of November, and once again would-be-authors are hurriedly scribbling on pages, or tapping away furiously to meet their daily goal... the elusive 1667!

NaNoWriMo has begun!

I've got my wordcount up on the left again, but you can see more at taryneast's author page.

For those not in the know, "NaNoWriMo" Stands for "National Novel Writing Month" - a writerly self-challenge where people from all around the world pledge to write 50,000 words in the month of November. No, it's not a charity, and it's not a competition... it's just a chance to give it a go and see if you can finally pull that novel out of yourself that you've been procrastinating about for years...

Before you dismiss it as an obscure, minor little thing that only a few weirdo wannabe-writers have heard of... you might want to know that it's more popular than you might think! As of yesterday there were 156,415 authors signed up worldwide, who wrote a collective 55 million words. Yes, that's millions, and yes that's just yesterday!

It's crazy-fun and I recommend it to anyone - especially anyone who thinks they're a terrible writer, or who thinks nobody wants to read what they write... but secretly has a tiny desire to be known as an author. Trust me, NaNoWriMo is for you.

It's only day two - there's still plenty of time to join in the fun! Go on, give it a shot! What have you got to lose? :)

Sunday 31 October 2010

Gravatar in rails

Just a quick code-snippet today. A quick helper to take an email and spit out a gravatar image.

  # show the gravatar image for a profile - based on their email
  def gravatar(email, size = 100)
    the_hash = Digest::MD5.hexdigest(email).to_s
    image_tag "http://www.gravatar.com/avatar/#{the_hash}?s=#{size}&d=mm"
  end

This one displays the "mystery man" image if the person doesn't have a gravatar. More details on the options and how to tweak it further are available on the Gravatar Image Requests page.

Thursday 21 October 2010

Profiling inside of procs

I've been using ruby-prof and kcachegrind to delve into our code recently - in an attempt to speed up some of our heaviest views - these are mainly big admin pages listing, say, all orders that are waiting on backordered products.

One of the joys of kcachegrind is the ability to see the actual lines of source-code with the actual time spent in each LOC nicely indicated. You can then click on that line and it'll jump into the slow method that is called by that line.. so you can dig down to where the actual pain is being caused in your app.

One of the problems with with the source-code tracking, though is that it just doesn't deal well with ruby procs. Kcachegrind assumes it's some kind of method-call, but when you click on that LOC, it doesn't take you to the code inside that proc, but you instead end up diving into the *ruby* source-code itself... definitions of blocks and procs etc... which are all aggregated together and thus you can't tease out the bits that you actually want (ie the code operating inside the proc itself).

So if you have something such as the following wrapping almost your entire view page:

   @orders.each do |order|
      # tons of lines....
      # displaying and formatting the order values follow...
      # ...
   end

You'll often only get a single, aggregated line of information n the "orders.each" line... and clicking it only takes you to Ruby's Array#each rather than your actual code.

Entirely unhelpful.

For the sake of profiling that code, though, you can unroll it into a while loop - just to check which LOC are the ones giving the pain by switching it (temporarily) to:

   idx = 0
   while idx < @orders.size do
      order = @orders[idx]
      idx +=1 # don't forget this line or it will take some time to finish.. ;)
      # tons of lines....
      # displaying and formatting the order values follow...
      # ...
   end

It's a dirty hack, but in the absence of "each" it's kind of a necessary evil - and it works. Just make sure to remove it once you're done!!!

Saturday 16 October 2010

Quick RubyProf action filters

A good while back I spoke about using ruby_prof and kcachegrind at LRUG.

I was asked back then if I could stick up my ruby=prof filter-code... and I've been just a *little* bit slack about getting around to that... but recently I've been profiling again, so I thought I'd bring it out again for general use.

Here's a way to quickly add ruby-prof profiling to any given *action*. Use the following around_filter on your action. Note: this will *only* profile what happens in the controller-action. I use this on our uber-heavy summary-page views to make sure that the mammoth Active Record fetches aren't themselves the problem... before getting onto a full-stack profiling-run.

  around_filter :profile_it, :only => :my_slow_action

  # profiling around-filter - just does the controller-action only
  def profile_it
    require 'ruby-prof'

    logger.info "Starting profiling"
    # profile the first set
    RubyProf.start

    # actually do the action
    yield

    result = RubyProf.stop
    logger.info "Profiling finished, printing files"

    # Print the callgraph profile
    filename = Time.now.strftime("RubyProf_#{params[:controller].gsub('/','-')}_#{params[:action].gsub('/','-')}_%Y-%m-%d_%H:%M:%S.grind")
    File.open(filename,'w') do |the_file|
      printer = RubyProf::CallTreePrinter.new(result)
      printer.print(the_file, 0)    
    end
    true
  end

or to profile the full stack through to the template render, you'll want to split the start/stop. I tend to call start_profiling at the top of the controller action (to get rid of most of the rails loading time), and then call stop_profiling at the bottom of the relevant layout.

Put this into application_controller.rb

  # Will only start the profiling... to stop, you'll need to use the helper
  # at the bottom of your template.
  # Use this pair to catch template-rendering in your profiling data
  # Pass ":wall" as a variable to use wall-time instead of process-time
  def start_profiling(time_type = nil)
    require 'ruby-prof'

    logger.info "Starting profiling"

    RubyProf.measure_mode = RubyProf::WALL_TIME if time_type == :wall

    # profile the first set
    RubyProf.start
  end

Put this into application_helper.rb

  # Use this helper in your template to stop profiling. Asumes you have
  # called "start_profiling" in your action somewhere
  # Will asplode if you haven't actually *started* a profiler
  # (eg if the previous action threw an exception before completing the profiling run)
  def stop_profiling
    if RubyProf.running?
      result = RubyProf.stop
      logger.info "Profiling finished, printing files"

      # Print the callgraph profile
      filename = Time.now.strftime("RubyProf_#{params[:controller].gsub('/','-')}_#{params[:action].gsub('/','-')}_%Y-%m-%d_%H:%M:%S.grind")
      File.open(filename,'w') do |the_file|
        printer = RubyProf::CallTreePrinter.new(result)
        printer.print(the_file, 0)    
      end
    else
      raise "Stop profiling called when no profiling started!!!"
    end
    true
  end

Sunday 10 October 2010

RedBubble Gallery page

I've posted earlier about how cool RedBubble is... but now they let you set up a gallery page, to show off (and even sell) your best pics. So here's the Ruby-coloured glasses gallery

Sunday 3 October 2010

Heroku with svn

and here I was thinking I'd never get to play much with Heroku as I prefer svn over git... [*]

It seems you can easily use Heroku with svn, by using git just as a mode of transport onto Heroku.

If you've never used Heroku before, you must set up your keys and project before following the above instructions. Use the quickstart guide to get up and running in no time.

You may think that handling two version control systems is difficult, but so far I've actually found it fairly simple. Basically you treat git like you'd treat "tags" - you commit to git just before you push up to heroku.


[*] For most of my projects, I prefer having one source of truth. I've also had some rather spectacular failure-modes using git which I've not seen the like of in all my years of svn... if you're adamant on learning git, read my Number one thing to learn if new to git first...

Sunday 26 September 2010

MatchFounders: a launch48 company

So, you are a business guy with a dream, looking for a savvy techie to turn your dream into reality

OR

You are a rockstar developer, looking for a business guru to take your idea to market.

Both of you are looking for somebody hard-working, willing to commit and sharing your values and passions.

They say that looking for co-founders is like a marriage, so how do you find your perfect match?

Match Founders will match your skills, values and passions against other people that also match your needs.

A bit of explanation...

Launch48 has begun, and this time around I'm running a team.

We're building a match-making service to help co-founders find each other, called MatchFounders.

We've got a blog on posterous: MatchFounders and are working on our initial web demo. Stick around to find out what happens...

Wednesday 22 September 2010

To stealth, or not to stealth

I've been working on a new startup with two other founders that I found at launch48 last year. I've already mentioned launch48 on this blog, but I haven't yet mentioned my startup... That's because we've been keeping it quiet until recently... when we had a kind of "trial launch" at the previous launch48 weekend (in June).

Now, based on my reading on the matter (see links at bottom), I don't believe that "stealth mode" is actually a good way to go about building a web business. I think the reality is that if you've got a good idea, not only will it not instantly be stolen from you, but you'll have to cram it down people's throats before they take any notice...

But you see, I have two business partners. One of which is a strong believer in protecting your IP - and the other wanted to make peace between us... so we compromised and kept a lid on our idea until the trial launch.

Now, we're fairly new to the game, and we think there's a good idea in our startup, but it's no iPhone. It's not something people are screaming for and they aren't going to start beating path to our door immediately. Because of this, I was never afraid of people "stealing our idea" - because, to be honest, it just isn't sexy enough. When I explain it to people they go "oh, ok, cool I can see that... did you think about doing Y?" I've never seen a gleam of greed in their eye, followed by hasty notes scribbled down... and I never expected that this would happen.

So in my opinion, I don't see a big reason to keep it quiet.

Instead, I think it's important to get exposure to your idea as early as possible from people Out There in the Real World... that way you immediately get to see whether people think your idea is actually worth spending eight months of your life on... or not. This is simply my own reasoning behind Seth Godin's Ship it philosophy.

Stealth mode redux has some interesting case studies on which "startups" do and don't need to stay quiet about the product they have in production.

What I see most clearly is that stealth mode is mainly useful for companies that are large and already have a big audience... or who have enough funding to keep them going while they beaver away in secret on a product until it's perfect... ie companies that aren't actually startups.

The problem is that real startups - the tiny, self-funded groups of new founders - can't afford to be in stealth mode for very long - for the very simple reason that they do not have enough capital for long periods of development.

I'm not talking about people that slowly hack away doing a few hours every week while working a day job... I mean real "cash flow positive from day one" startups, rather than ones that require such extended self-funding.

Such things need to start gaining revenue asap, and the only way to do that is to attract customers... and the only way to do that is to tell everybody about them!

In the interest of balance, of course, there's a great post pointing out that releasing early is not always Good - which covers some of the reasons why *not* to release early... and they're all very fair points. But I'd argue that all of them point to a new variant of Einstein's famous "make it as simple as possible, but not too simple" ie:

"Release as early as possible, but not too early"


Update:

After having read The small man builds cages for everyone on The art of non-conformity, I'm convinced that NDAs and other stealth-measures are a cage-building habit.


Notes

Links on stealth-mode being bad, or the philosophy of "release early, release often":
Startup lessons learned
The Cathedral and the Bazaar

More links on stealth mode:
why choosing to go into stealth mode could be the kiss of death for your startup
Does Being In Stealth Mode Damage Your Business?

Wednesday 15 September 2010

Single sign-out using ruby-cas... Not!

We considered this... right up until the point where I read that you were advised to disable forgery-protection.

It's easy enough to send a message to the rubyCAS server to invalidate a given ticket - but mostly users only authenticate with rubyCAS once, and from then on deal only with a given client-application. Killing rubyCAS's ticket only comes into play when a user tries to re-authenticate with the rubyCAS server.

I don't have a good solution to this one...

This is one article in a series on Rails single-sign-on with rubyCAS

Friday 10 September 2010

Signup: Ask the right question

The sign-up process is much more than just the form that gets a potential customer's details. The majority of the sign-up process actually takes place inside the customer's head. A customer will sign up - even using a crappy signup form as long as they're actually motivated to buy your product... not that you should make a crappy signup form - because that also just puts hurdles in the way of an otherwise motivated potential customer.

This awesome presentation explains how you can design your signup process to get customers motivated and rearing to go on your site. It points out where the friction is in the process, where they usually fall out of the sales funnel, and the ways to change your way of thinking about your site: to provide ample opportunities for a confused or skeptical customer to change their minds. It also picks out the pain-points for the sign-up process - giving some great examples of how to get out of their way and let them get on with paying you money straight away... always a good thing.

I only wish I'd seen the original talk, but the slides themselves speak the story clearly and are well worth a few minutes of paging through.

Thursday 9 September 2010

gotchas: gem cleanup

Note to future self: do not type "gem cleanup"...

...now reinstalling rails v 2.3.4, 2.3.5, 2.3.8... etc

Wednesday 8 September 2010

Launch a startup in 48 hours!

Launch 48 teams up business-gurus and techies, where the aim is to pitch, plan and launch a viable web-product in the following 48 hour period. It's 48 hours of hard-slog startup-founding that is guaranteed to leave you buzzing (honest)

I've been twice now, and just signed up for a third time. It may sound a little crazy... but it is an awesome experience, and I confess I'm now addicted.

If you can make it to the conference (on the Friday) they also line up a set of speakers from some of the best IT companies in the world (google, paypal, et al), along with VC, successful IT businessmen and other assorted useful types to give a talk about how you can turn your idea into reality.

and it works too. Several of the startups have gone on to become successful online businesses... of course some don't but I can honestly say I've learnt a lot in the process. I even met my co-founders there, so I can attest to the benefit of working with people to see if you can work with them...

Early bird tickets are on sale for the next one (the 25th Sep)... so I hope to see you there :)

PS: just to make it clear - I'm not affiliated with launch48 in any way except for having gone along to it (twice now) and found it to be awesome!

Wednesday 1 September 2010

Rails 3.0 is Done!

As you've probably already heard, Rails 3.0 is done, no longer just a beta, it's out there and beginning to be used. Go look at all the goodies we've got lined up!

Tuesday 31 August 2010

The culture I prefer

This article named telecommuting culture is an interesting look at how job ads can make it pretty clear about the associated company culture. It pretty much sums up my own experiences of how to tell a company culture that I like from one where I'll just never fit in...

"The world’s a big place. There’s a lot of developers in it. There’s even a few kick ass ones. Most of those don’t live near you ... By being up front about telecommuting not being and option, you’re telling me that company comes first ... Hiring someone good is important, but hiring the best you can afford isn’t."

There's a follow-on post on taking stances that points out how much of a controversy was stirred up by his original, highly opinionated post. He's nicely pointed out that his stance was a strong one - in order to generate discussion, but that he's not unreasonable, basically he just wants people to understand that job-hunting is a two-way street. The following quotes best summarise his (modified) stance.

"Companies that start the conversation with prospective employees by outlining the things they aren’t going to stand for are starting off on the wrong foot..."
"Companies, particularly in the tech space, are asking their potential employees to take a chance on them. Companies are taking a risk too, but when companies expect the employees to be the only ones giving (broadly disregarding monetary compensation from the companies for the moment), they set the wrong tone."

I completely agree with this feeling. Sometimes employers forget that they're not just interviewing you, but that you are also interviewing them - and that this really is a business proposal that has to be fair to both sides. If a company comes across as unwilling to be flexible for any reason, it doesn't bode well for the rest of the employment relationship.

I've too often seen employers that seem to think they own your soul once you sign on the dotted line... that they can make any demand of you and that you'll jump-to. This is the business equivalent of "the customer can have any colour he likes so long as it's black". Which may well work just fine for people that like black... but if you find it overheats in the sun and you need white... you'll find it best to take your business elsewhere. In fact I wrote about the lack of viability of this attitude (at length) in Up or out: the attitude for contractors.

This seems especially to be the case in a down economy as has recently been the case - where I've even seen some employers demand unpaid overtime and drop loud hints about how people should be grateful for having any job at all in this economy.

Apart from being incredibly rude, such employers are short-sighted. Firstly - the economy is going to pick up again... at which point, the indentured slaves are quite capable of picking up sticks and going elsewhere. Secondly - even in the down economy, there were enough jobs (at least in my field) to go and find somewhere that treated their employees like human beings rather than indentured servants.

It pays, in the long run, to be considerate to your staff; to even be flexible, or generous. After all, they're the ones who are literally making your company's money, day-in and day-out.

Tuesday 24 August 2010

The two faces of Optimism

I've often been accused of being a pessimist.

I'm actually not. Far from it, I truly am an optimist. The problem seems to be that often people use the word "optimist" to mean something quite different to my (accepted) understanding of the word; and so, not agreeing with their worldview, they label me as a pessimist.

I think the real issue stems from divergent ways of viewing the term.

There are two definitions in common use.

A world of abundance

First is the one that I subscribe to. Optimism, for me, is to see a world of abundance, rather than a sea of troubles.

I believe that there is a solution to every problem (even if the solution is to throw out the old game and find new rules) and that anyone has the ability to get through the problems and find their dreams. Nobody is a lost cause and there is plenty of abundance to go around.

For me, that is optimism.

*You're* not an optimist

The other view I generally see in the pejorative sense. I can best explain by example:

You don't agree that my <* patently-impossible or highly-improbable *> plan will succeed, That's because you're just not an optimist!

I guess I'm just not a yes-man

Let me be clear: It's not "optimism" to be in deep denial about the efficacy of your plan, and I am not a "pessimist" just because I have looked at the numbers and found that they don't add up quite as well as what you hope them to be. I simply don't agree with you.

I am not a pessimist because I believe that *this* solution of yours won't work. I am an optimist because I believe that there *is* a solution, and that we will find it if we keep trying.

Optimism and employee relations

This issue often crops up at its most pathological in employee relations. In my experience, this is because there's a certain breed of manager that simply doesn't want to hear anything negative - even if it's the facts surrounding a potential solution. If solutions to a problem are proposed, then the most optimistic *sounding* solution will often win, regardless of actual likelihood of being the correct fit. Any nay-sayer to the offered solution will simply be dismissed as a curmudgeon, and thereafter will often be viewed as a "troublemaker".

As human folly goes, if the plan then fails, the nay-sayer is hardly likely to receive any recognition for having pointed out the potential... in fact, if the subject is ever brought up again, greater dislike is sure to follow - nobody likes being told "I told you so", especially on the heels of defeat.

More often than not, however, the initial solution will not fail miserably, but will simply provide sub-optimal results. The originator of the idea will be convinced that it was the best that could possibly have been achieved and that you were clearly a pessimist to think that it could fail...

Trust me... there is no point to trying to persuade them that your idea could have been better.

So, what to do?

The only way out of this dilemma is to make sure that instead of simply taking pot-shots at a bad idea, always provide an alternative solution that you can show is more likely to work. In this way you can show that you are optimistic about solving the problem, just not about the particular solution on the table. It's even better if you can reuse aspects of the original solution in your own - to show that the first proposal wasn't all bad, just needs re-alignment... but this isn't always possible.

It's certainly not a guaranteed solution. The first idea may well not be removed from the state of play - and may still be valued seeming more optimistic (ie providing a promise of a better end-solution... even if not more likely to succeed). This is even more likely to happen if the solution has been championed by a manager in an environment where Highest Paid Opinion wins reigns supreme). However, offering a solution will show that you are presenting a positive outlook, and you're less likely to be tarred as strongly with the pessimist/troublemaker brush. You show that at least you are trying to be helpful.

Of course... in some environments it still makes little difference, and even a dissenting opinion will brand you.

In this case, my suggestion is to be an optimist and trust that the world will get along in the end, and that perhaps there might be bigger, better places for you... if you choose to go looking for them. :)

Notes

Pessimist:

In reaction to them, I'll often tell them that I am a "realist" - which unfortunately makes them just snort and underscore the "pessimist" label in their heads. After all, I've found that a lot of optimists don't know the difference. Of course, mainly I'm just playing with their heads. :)

yes-man:

Sorry, "yes-woman" just sounds stupid, so I won't use it regardless of the truth of my actual sex; And I will bite anybody that suggests that I use "yes-person" :P

Wednesday 11 August 2010

Controversy: too much choice in Ruby

A tongue-in-cheek post called "So you want to be a ruby dev" has sparked a bit of controversy in the community by pointing out the overabundance of options now available to ruby learners. A lot of commentary has happened on the hacker news thread for the article, with the loudest voices complaining that while there is choice - that it isn't mandatory to learn all the options, for example in the same way as examples such as Java.

Opponents point out that the proliferation of options is confusing potential newcomers to the field, who don't rightly know where they should begin. That the community (and therefore the documentation) seems to be fragmenting, where it isn't simply missing, or so old as to be effectively obsolete.

The article sums up the situation by lamenting: "Remember when Ruby and Rails was about getting stuff done?" The implication being that newbies have to spend so much time learning about all the possible solutions... simply to choose which one they should begin to use.

A follow-on article on Ruby Inside continues the discussion, pointing out that the variety of options available to Ruby-users is its strength, rather than being a drawback. It points out that what we are missing is just a few simple "for newcomers" help-pages that clearly show how you can get started with the minimum of fuss.

The post ends by calling for ruby-developers to remember the philosophy of having many ways to do something, but to have an opinion on what the best approach should be. It also strongly calls for more straightforward "Start by doing X, then Y" how-to posts for beginner Ruby users... emulating the rather better documented beginner tutorials on the Ruby on Rails pages.

Thursday 5 August 2010

Link: Mind your ands and ors

I've posted before about the subtle (and insidious) difference between the 'and'/'or' operators and the nearly-but-not-quite-the-same '&&' and '||' operators... but likely lost amongst the rest of the noise in my acts_as-good_style post.

This post: Using “and” and “or” in Ruby does a much better job of explaining when to use each one - and goes further to suggest that you shouldn't simply give up on using 'and'/'or', describing their usage as control-flow operators.

It's a small thing, but every bit helps.

Wednesday 4 August 2010

Rails 3.0 is out...

The Rails 3.0 Release Candidate has been out for a few days, and it looks like it has some great goodies in store.

There's a lot to catch up on, but I suggest you start by having a look at these Rails 3 screencasts by Gregg Pollack.

Update: and now Rails 3.0 is done! - no more beta, it's really out there and ready to use. Go have fun!

Monday 2 August 2010

Life at 34

When I was 5 I liked to do jigsaw puzzles upside down (to make it harder) and blow bubbles off the balcony -- watching them drift over the street. I liked to walk to school by myself (one block) and learn Origami from the lady in the flats behind us.

When I was 7 I wished on every star that I could have a baby sister. When I was 8, I got one.

When I was 10 I liked to explore the backways of Oyster Bay, picking flowers to make perfume (which smelled terrible). I played fantasy make-believe games with my cousins - involving magic and unicorns, where we saved the world.

When I was 12 I got another sister.... I stopped wishing.

When I was 13 I liked to play make-believe with my sisters and all the younger cousins. Gordy and I plotted adventures for us all, in-amongst the bamboo.

When I was 15 I like to climb up on the roof and watch the clouds drift by. I liked to ride my bike home from school and play LARP in the park across the road.

When I was 17 I liked to swim in the backyard pool, drifting underwater with my hair floating loose around me. I liked to frantically scribble down my hurts in my diary and day-dream about the brat-packer boys; making up adventure stories or inserting myself as a character in my favourite movies.

When I was 20 I loved the freedom of being independent, waking up in my own flat to the sound of Cockatoos in the pine-trees. I liked being free to wake up in the afternoon and go for a walk in the twilight, or in the quiet time after the curfew painted the streets with night. I liked staying up all night, having coffee with friends, as television got progressively more idiotic. As the sky began to warm with first light - I went out again. I liked feeling the expectant hush of the cool dawn, then retiring before the hustle woke up.

When I was 22 I loved my writing diary - pouring out my heart or playing with words, crafting new worlds on a page. I liked learning new things -- drifting from subject to subject. I liked psychology, programming and the occult. I loved my cats. I liked it that I got married and was somebody's wife. I liked meditating with my husband -- humming toneful chords without meaning. I liked learning martial arts with him.

When I was 24 I loved my garden. I spent days drifitng through it and tending to the plants. I liked picking silver-beet and herbs and cooking up a meal that I'd taken from seed to table. I liked shopping at Bunnings for fruit trees. I liked spending Saturday nights with the Druids, singing, meditating, drinking mead and telling stories. Then the morning-afters, skinny-dipping in the wading-pool as the sun climbed the sky.

When I was 26 I liked exploring the back-areas of Chatswood in search of great food. I loved hacking together brilliant solutions for colleagues desperately late on their projects. I liked rock-climbing with my work-mates and playing network games in the training room until late at night. I yearned for a man that I couldn't have.

When I was 28 I loved freedom. The freedom to choose my own time, to choose what to learn, to work on my own projects. I liked smiling at my young class-mates who complained about the work - knowing this was easy compared with Real Work. I liked the meditation of archery. I liked spending my time reading while sipping coffee, or eating noodles at the local hole-in-the-wall. I liked long walks in the evening, scoping out my local territory.

When I was 30 I liked building my own small business, knowing I owned it and I could acheive whatever I wanted. I liked learning medieval crafts alongside eager students, and feasting with friends in a medieval campsite. I liked reading books, sipping coffee after a good workout at the gym. I liked watching myself learn how to walk again.

When I was 32 I enjoyed being a senior developer, I enjoyed being at the top of my form as well as earning high pay. I enjoyed choosing my first major investments in shares and buying my first property. I enjoyed planning what to do with my life, and choosing to gather interesting experiences around me - New Zealand, the Northern Territory, Thailand. I enjoyed learning photography, spanish and investing. I watched a *lot* of DVDs.

When I was 34 I loved exploring in a new country. I liked visiting ancient ruins and watching Shakespeare at the Globe. I enjoyed getting better at photography, meeting new people and places. I enjoyed building my own startup with friends. I enjoyed my morning coffee at the local in Windsor, and walking past a castle on my way to work in the morning. I enjoyed pottering around in my allotment late into the long, english summer nights.


This post began as an exercise: "for every five years, write what you enjoyed doing". It helps you find out what you most enjoy - and how your tastes change over time.

Tuesday 29 June 2010

Software: no compensation necessary

I've just been reading "The World Is Flat" by Thomas L Friedman. It's an interesting book with lots good to say[1] about how the world is rapidly flattening - ie everywhere is becoming reachable from anywhere.

Along the way (and only incidentally to the main point) the author brings up the concept of Open Source Software.

He discusses the concept in a fairly positive light. He seems quite fascinated by the phenomenon - though doesn't seem to really get why people would do it. Though he makes a valiant effort to see things from the POV of the average OSS contributor, it's still fairly clear that he thinks it's a bit of a quaint, idealistic example of altruism (recognising the "pay it forward" aspect), rather than an effective and useful business technique. This is exemplified by the fact that he interviews a Microsoft Manager to ask whether or not it is sustainable as a business concept. Not bothering with an Open Source advocate, just an employee of one of the biggest proprietary systems out there.

One point stands out from what this employee says about OSS. He states that money is the main motivator for building software. From this the author concludes that any "really good software" will more likely be built by big software houses (eg Microsoft) than by OSS initiatives... after all, they have a lot of money, that can buy a lot of development time. He therefore concludes that The big software houses will always build the best software - simply because they can sustainably pay the most developers.

It's an interesting theory, and the most logical for those who don't really understand the motivation of the average OSS contributor. The reasoning behind this conclusion is of itself quite interesting. They claim that software is developed because businesses need stuff to be done - and thus they pay developers to do it. Nothing wrong with that... but they seem to mistake the effect for the cause and assume that the motivator for software to be built - is the money.

This is not the case.

Software is susceptible to the old phrase of "Necessity is the mother of invention" - ie the cause of software to be built is the business's need for it. The money is a secondary affector - ie the need causes the business to find money to make it happen. The originating need will be present regardless of whether the business has money to pay for developers... after all, even if the business has no money, the need will still be there for the software.

If the business happens to be made up of developers, then they are quite capable of creating the software, regardless of payment... and that is how you get startups: built and run by developers who see the need but don't necessarily have money to pay other developers - they just need the software to get their own business running, and are ready and willing to work in their own spare time to make it happen (nothing wrong with Sweat Equity).

The Open Source world calls this motivation "scratching your own itch" and it is recognised as the primary motivator for Open Source contribution[2]. Even for the really big projects.

Linux, for example, was Linus Torvalds' project to build himself an OS that didn't suck. Since he released it, thousands of other individuals have contributed to it for the same reason. It didn't do what they needed, so rather than bitching about it, they put their money where their mouth was and wrote the fix themselves... contributing it back to the OSS community... for no compensation except the "pay it forward" principle.

Which brings me to the other book I've just finished reading: "Drive" by Daniel Pink. One of the principle findings of this book is that fiscal compensation actually gets in the way of intrinsic motivation[3]. You can incentivise a developer to build something, but then they dissociate their ownership of the end product to the person doing the incentivising... care factor drops radically and you end up with... well... Microsoft products. Corporate and cold, buggy and with no real way to guarantee that the problems will ever get fixed.

This difference in motivation easily accounts for the fact that MS products are made primarily for creating more money for Microsoft (for which purpose they perform admirably), but only a passing interest in actually being fit for use: ie only so much as they can get away with before too many people stop purchasing them.

Now, there's nothing inherently wrong with that. More power to them for staying alive so long, and rather profitably - by keeping so many people satisfied for so long with what they're prepared to shell out... but there's a reason that I haven't used Microsoft products for over a decade now...

By comparison, the OSS world is driven purely by user requirements. If software is not fit for purpose, it simply won't be used... but more importantly, because there's nobody paying for it - if it doesn't do what it's supposed to, it will never be born

Conversely, if it is built, you can guarantee that it meets the requirements of at least *one* real customer (unlike many products built purely with potential revenue as a motivator).

OSS lives and dies on it's own merits... no compensation necessary.

Notes:

[1] Though I'd take a lot of it with a pinch of salt as well. This guy is clearly of the "yay capitalism, it solves all the world's ills" school of thought... though is slightly tempered by realising that humans are human, and some of them actually need supporting by those who are better-off... it's an interesting act of comparison to read this side-by-side with "The Shock Doctrine" by Naomi Klein - just as equally biased in the opposite direction, but also an fascinating read. I think if I ever put these two books together on a shelf, they'd annihilate one another with a very loud bang.

[2] If you're interested in this topic, "The Cathedral & the Bazaar" by Eric S. Raymond is an excellent book that explains the difference between what can be built from the ground up by need-motivated developers rather than the top-down software built by big software houses spending money to pay for what they want.

[3] which can be paraphrased as that innate curiosity and enjoyment of a challenge that occurs when you take on a new puzzle

Wednesday 23 June 2010

Email campaign tracking with Silverpop

SilverPop offer themselves to the world as an "Engagement marketing solution". They provide click-stream and conversion tracking based on your email marketing campaigns, by putting unique identifiers in the links of your mailings (which they can send out in bulk). If you store these IDs and ping them back to SilverPop's click-stream and conversion-tracking servelets, this means you can get complete conversion analysis based on your email marketing campaigns.

I'm currently working for Moneyspyder, and one of our clients has signed up for a Silverpop account - which means we need a way to easily integrate with SilverPop - and the examples they give are all in php (and a little less than dynamic). So here's my Ruby-on-Rails solution.

1) Cookify the IDs

When a customer clicks on a link in one of the SilverPop emails - Silverpop has cleverly adding tracking IDs to identify the mailing-job and individual customer. When we send tracking information back to SilverPop - we need to send back these ids so the click-tracking is stored against the correct campaign and individual.

Of course, the parameters will disappear after the first time the user clicks another link, and we need to persist the data - to keep tracking all the pages they click on until they eventually convert!

The easiest way to keep track of this to stick it all into the session - then we can just check if it's there and send it each time we want to ping to SilverPop... and of course the best place to do session-wrangling is in a before_filter. So stick the following into something like application.rb

  before_filter :save_silverpop_data_in_session

  # This method does the storing of the ids from the URL into a session
  # cookie for later sending.
  # It will replace any existing values in the cookie - which represents the
  # user having returned to the site after viewing (and clicking on) another
  # link from a different email campaign.
  def save_silverpop_data_in_session
    silverpop_params = %w{spmailingid spuserid spjobid spreportid}
    if silverpop_params.all? {|f| params.keys.map{|k|k.to_s.downcase}.include?(f) }
      params_down = {} # ugly but necessary to get past browser case insensitivity issues
      params.each_pair {|k,v| params_down[:"#{k.to_s.downcase}"] = params[k]  if silverpop_params.include?(k.to_s.downcase) }
      session[:silverpop] = {:m => params_down[:spmailingid], :r => params_down[:spuserid],
                             :j => params_down[:spjobid], :rj => params_down[:spreportid]}
    end

  end

2) Configure your pod

SilverPop calls it's servers "pods" - and you could be using any one of them. This works better as a configuration option than hard-coded in your code - and lets you set up a link to the test-server on your dev/test environments. but in environment.rb you'll have something like this:

  # SilverPop URL = mailing list manager/ClickStream Analysis
  SILVERPOP_SITE_URL =  "recp.mkt41.net"
  SILVERPOP_SITE_HTTPS_URL =  "marketer4.silverpop.com"

3) Helping hands

Next up is to figure out how to ping SilverPop... which they helpfully give us an img tag example of how to build up the correct url with all the requisite values.

SilverPop has two servelets - one for accepting pings for click-stream tracking and one for the conversions. But they're almost identical, just taking different parameters... and I'm lazy and don't want to have to remember all the common details of how to do this in each place in the site. Thus: helpers to the rescue!

  # This method generates the code that calls the ClickStream tracking
  # servelet - we pass in the page name and page URL, along with the values
  # passed through from the last silverpop email - saved in the session
  def silverpop_click_stream_ping(page_name, page_url)
    silverpop_link('cst', :name => page_name, :s => page_url)
  end


  # This method generates the code that calls the Conversion Tracking
  # servelet - we pass in the "action", "detail" and "value" flags - 
  # eg "silverpop_conversion_ping 'CompletedOrder', order.id, order.grand_total" 
  # along with the values passed through from the last silverpop email -
  # saved in the session
  def silverpop_conversion_ping(action,detail,value = nil)
    silverpop_link('cot', :a => action, :d => detail, :amt => value)
  end


  # generate the SilverPop ping-image based on required servelet and parameters
  def silverpop_link(servelet, options)
    # skip out early if this user hasn't come through a silverpop email
    return nil unless session[:silverpop].present?

    base_url = (/https/ =~ request.protocol) ?  "https://#{SILVERPOP_SITE_HTTPS_URL}" : "http://#{SILVERPOP_SITE_URL}" 

    image_tag "#{base_url}/#{servelet}?#{session[:silverpop].merge(options).to_query}", 
      :height => 1, :width => 1, :alt => ""
    # we'd like this alt-tag: "Silverpop #{servelet.upcase} Servelet Ping"
    # to be accessible... but the above is not *really* an image, and thus will display 
    # the alt-text in the html at present... this sux, but I have yet to talk to silverpop 
    # about a way around this.
  end

4) Click-stream - analyse!

So, now it's time to get down and dirty with the click-stream analysis. This couldn't be more simple. Just use the helper to pop a link into your main layout. eg:

  <!-- silverpop cst ping -->>
  <%= silverpop_click_stream_ping(@page_title, url_for(:only_path => false)) -%>

Now, as you can see, this mainly works by using our dynamic page-title and a link to the current-page (using the empty url_for trick). Note that if you leave off the "only_path=false" option it won't provide the hostname. This may be what you want if you want to roll up multiple mirrored domains. You'll also have to adjust the page-name parameter as necessary for the way you generate the page-title... but otherwise you're good to go and this means every page-click gets tracked back to SilverPop from now on.

With one caveat... if you have AJAX-updating, you may need to figure out a neato trick of putting the ping-link into the newly-generated page-pieces or these "clicks" won't get tracked as the layout won't see them as new pages. I'll leave that as an exercise for the reader as it's very site-specific.

5) Mine your conversion gold

Now only the final, and most important, part is left - tracking actual conversions.

A lot has already been written about what constitutes an important conversion for your site. I won't repeat it all here as you can track heaps, and it's really down to what is important for your business. So I'll pretend we only care about when a customer completes an order - which we know because they land on the "thank you" page.

Which means we need to pop a link to the COT servelet there and pass in the important details... nothing easier:

  <!-- silverpop cot ping -->>
  <%= silverpop_conversion_ping("Order Complete", @order.id, @order.grand_total) -%>

Now SilverPop will monitor our marketing mails from go to whoa - and even know which order they completed and, most important, how much money they ended up giving us... Gold!

Update: Sorry for the repost (anyone that noticed). I fixed a few bugs after I finally got a chance to fully test this out.

Anybody implementing a Silverpop solution should also be aware that Silverpop's servers (at this moment) do not correctly redirect links from mailings sent from their test server. Instead you get redirected to your website, but without any values in the required query parameters (eg spMailingID etc are empty).

To do an end-to-end test of SilverPop's link redirection, you must create a real newsletter mailing on the live server, and just restrict the recipients (eg send it to yourself and any other devs on your team).

Friday 18 June 2010

Stored Procedure-fu

So, I've been tuning my db performance on our heaviest database call... and this involved adding a quickie stored procedure to make life a little simpler.

It's nothing complex, just does a really simple distance calculation (using pythagorus)... which we needed because most versions of MySql don't come fully-loaded with all those nice functions that you can find in the manual.

Now the problem with stored procedures, is how to add them to the db without too much hassle. I have heard there are gems out there - but they seem a little like overkill, and the ones I've seen have been "build it on the fly"... I just want to put it into the db and leave it there.

So what's the quickest and easiest way?

A migration:

class AddStoredProcedures < ActiveRecord::Migration
  def self.up
    # function distance(lat1,lng1,lat2,lng2)
    #   sqrt((lat2 - lat1)^2 + (lng2 - lng1)^2)
    # end
    execute <<EOS 
      CREATE FUNCTION Distance(lat1 FLOAT, lng1 FLOAT, lat2 FLOAT, lng2 FLOAT) 
      RETURNS FLOAT
      DETERMINISTIC
      BEGIN
        DECLARE dist FLOAT;
        SET dist = Sqrt(Pow((lat2 - lat1),2) + Pow((lng2 - lng1),2));
        RETURN dist;
      END
EOS
  end

  def self.down
    execute "DROP FUNCTION IF EXISTS `Distance`" 
  end
end

Down sides?

I'm told that using migrations for stored procedures has one major drawback - that if you want to change the procedure later then it's annoying to maintain - and can be the wrong version etc... In this case that's not a problem. Pythagorus isn't likely the change any time soon, so I'm unlikely to need to update it.

But there is one annoyance about putting it as a migration, and that's the fact that it doesn't show up as part of the schema... which means our Test environment can't find it. :P

This is a bit annoying, and I haven't found an elegant solution to the problem, but to get the test suite running, I have found a hack...

Re-run the migration:

class ActiveSupport::TestCase
  # ... lots of guff

  # Run stored procedures to add to the test db
  require File.expand_path(File.dirname(__FILE__) + "/../db/migrate/20100528190204_add_stored_procedures.rb")
  AddStoredProcedures.down # clear it out first, to be sure
  AddStoredProcedures.up
end

It's ugly, but it works. You may note I have to run the "down" then the "up" - this is because ActiveSupport ::TestCase actually gets loaded for each type of test-case you are running (eg once for Unit tests and again for Functional etc) and the "up" barfs if you try to create a procedure that has already been created in the db.

Otherwise, it runs and lets you get on with your work...

if anybody has a more elegant, but unheavy solution to the whole issue of Stored Procedures in test dbs... please do let me know.

Tuesday 15 June 2010

Blogger has a new template editor...

...I couldn't resist.

So my blog is now more ruby-ish. :)

PS: if you use the template editor... don't forget to add back stuff you customised in the template (eg your google analytics widget) *blush*

Alternatively make them into page objects so they aren't a part of the template at all

Friday 4 June 2010

How *NOT* to do customer service

  1. Start by telling your customer the work is not good enough. That the resolution will make it pixellated and that will look terrible.
  2. Then say that the image has "keylines" that have to be removed and "crop lines" added "outside the trim area" - without explaining what they are
  3. When your customer naiively asks what these mean, instead of explaining it - tell her that you haven't yet received the artwork because the only files they received don't have these things
  4. When the customer says she sent files through and asks what is needed to change them into files that will meet their requirements (giving a detailed guess at what that might be), tell her again that you need these things - without explaining exactly what is needed or confirming that what she suggested is actually what you needed - even if she's later proved correct. Tell her that she is running out of time to get this done...
  5. When your customer calls, make sure your receptionist (or colleague) answers, and that she tells your customer that you are with some other important client... and get *her* to also tell your customer that the artwork isn't good enough and go on at length about how it's such low resolution that it'll look all pixellated and low quality that it'll look terrible. Make sure she promises that you'll call her back before close of business.
  6. When your customer has received no calls or emails and it's near close of business... and calls you. Answer immediately and...
    1. Begin by telling her off again at the low quality of the images (due to the resolution).
    2. When she mentions she thought you'd be calling her back, tell her you have *so* many other clients and you're very busy, you know...
    3. When she asks if you'll please just explain what she needs to do to add the crop lines - don't explain, tell her that you were expecting to speak with a designer who should already know these things.
    4. When she suggests that not all clients know the industry jargon of printing businesses and again asks for you to explain what to do, tell her to go and look up "crop lines" in the dictionary
    5. When she says that sometimes in your industry you have to deal with people that aren't specialists (she gives an example that if you'd asked her to set up a website, she wouldn't expect you to know what Rails, MySQL or AJAX were)... then tell her that you simply don't have time to explain to your customers how to add crop lines to an image, and that you only deal with designers that already know what crop lines are...

background...

We're launching our new startup this weekend (ie tomorrow) and needed new business cards ASAP. So my colleague called around the local while-u-wait business card dealers and found one that was willing to print and ship in time for tomorrow.

I'd quickly thrown together some business card images that were the exact size/shape required. They aren't brilliant, but they'll do for the weekend after which we can get some better ones done up.

I have a day-job... so while I'd love to go out and find out all about crop lines and trim-borders and whatever... I need to actually spend the day making money for my employer...

When I got off the final phone call and calmed down for a few minutes, I managed to find some time to go look it up. Apparently, all I need to do is exactly what I suggested in the reply to the second email - ie, expand the image size and add borders a little larger than where the old image-shape used to be... this would have taken him all of one minute to explain - just as it did me just now.

I recommend that you *don't* purchase from Kall Kwik 781 in Eton Street, Richmond

Sunday 30 May 2010

Shower ambassador!

A shower company called Grohe came up with a brilliant Social networking strategy that has people talking about their products.

They began by setting themselves up with a Grohe facebook fan page, with a competition. 1000 people would become "shower ambassadors" for the company - sent one of their stylish rainshower icon showerheads as long as they filled in the standard "tell us why you deserve to be a shower ambassador" forms.

I dutifully filled it in - I thought they looked kinda funky, and free stuff is always neat... and then forgot all about it, until yesterday morning when the postie arrived at the door with a shiny new shower head!

Shower ambassador!

Awesome!

So, I've tested it and I think it's good. It's very stylish and would look right at home in most bathrooms - you can pick from a wide range of colours to suit. It's definitely a "rainshower" style head: very gentle; and eco-friendly as it uses less water for a very broad effect. It even has an extra-eco-friendly button that lets you use even less water... and the head comes with this set 'on' straight out of the factory - so they seem like they're serious about being eco-friendly.

Of course the really clever thing about this was the campaign itself. From the information they've posted me, it's clear they intend to get the message spread far and wide. The shower head came with a page of information suggesting that you share "thoughts, pictures and videos on our facebook page... your blog or your Twitter account".

Through the competition, they've made it a fun thing to do and share, and I wouldn't be surprised if they picked people solely on their social-media-integration. Apart from running their competition through facebook I vaguely recall they also had fields for your other social media accounts... which I'm sure is why I got picked.

Clearly these guys are switched on the the power of viral marketing - and aren't afraid to ask people to do it... but it's more than that. They also seem to realise that the best way to get people to spread the message is to put their actual product in the hands of the people most likely to spread the idea.

They've also got the be remarkable idea down pat. They clearly picked a product that was wroth talking about: an iconic, shower-head with a vibrant colour and notable green-credentials. Part of their success here, was making it *personal* to the individual participants. When filling out the competition-form, you got to pick your favourite colour[1] showerhead. This is such a small thing to do - but with great effect as it lets people feel they have the power to choose. I don't think most companies would think of doing this when it comes to items that they are, remember *giving away for free*. But it's such a powerful and brilliant strategy, when you think about it.

Grohe want people to talk about their product - and especially they want them to talk about how much they *love* this product... Letting a person choose their preference (just like they were buying it), means that it isn't just some random item that rocks up at your door, but something that *you chose* and is already personal before it even appears - thus increasing the likelihood of giving it a thumbs up...

All in all, I'm quite impressed by their strategy. Moreso even than the shower-head itself - which is, btw, quite funky.

[1] I picked their dark-blue - I like blue bathrooms.

Tuesday 18 May 2010

Truth vs Harmony

People live on a spectrum of preference. Some prefer to hear the hard truth, regardless of whether it will hurt their feelings. Some prefer a comforting lie, feeling that harmony is more important than being right.

Both views have their pros and cons, and different situations generally call for a different balance on this particular scale. Still, people tend to have a preference for one or the other.

People often prefer different levels in different circumstances eg when we're more emotional, we tend towards comforting lie, and people will generally react to that - keeping any harsh truths for when a person is more able to bear it. Lying to children is often rationalised in this way - falsely in my opinion.

People at opposite ends of the spectrum often simply can't understand what the other people want. What motivates them to prefer a way of dealing with truth so different from their own?

Harmony people can't understand why a person would tell the truth if it might be more hurtful. They'll say, with perplexity "what they don't know won't hurt them, but the truth will hurt their feelings, so why do it?"

Truth people feel like they have no control if people keep lying to them, they'll ask: "They deserve to know the truth, after all, how could I make a decent decision if I can't trust what people say to me?"

Harmony people believe it doesn't matter as long as everyone gets along. Life is about getting-along, after all. Often-times, telling the truth causes them to lose face, which doesn't help anybody. But are they just trying to avoid fixing a problem that's their responsibility to fix? Are they avoiding a truth within themselves because they're too lazy or ashamed to face it?

Truth people feel robbed of a valuable chance to learn something if not told the truth. They feel that they are doing a service to another person by giving them a chance to see themselves and learn something. But are they just nitpicking, poking at wounds or being too lazy to be tactful?

In a bizarre twist, many people will say they prefer truth, but are actually lying. It's more comforting to believe you are a hard-hitting, strong person - able to handle any truth... it's much harder to face up to the truth that you are in fact not able to face up to the truth.

Personally, I've always preferred the truth. Yes, it can be hurtful. Yes - its embarrassing and sometimes degrading to know the negative effects of you impact on the lives of others, and there are definitely times and places where it's better to bite your tongue; but unless pressed, I'll always opt for the truth.

Both giving and receiving.

This can be hard for harmony-people to understand. Just as it's hard to explain that introversion[1] is not actually unhealthy, just a different way of being.

I'll usually get a response along the lines of: "but life is about enjoying yourself, not being right". Saying that I cannot enjoy myself if I know there's something I'm doing wrong, or even "It's just the way I am" generally doesn't help much. Harmony-people still often shake their heads and seem to think there's something unhealthy about the whole thing.

I've had more success when I explained that for me, improving myself is something I like to do. I work hard at it, and try to strip myself of self-delusions (though I'm under no delusion that I've got rid of them all). I am not on the quest for hedonism, but the quest for self-improvement...

So how does that fit with truth? Well - if I do something wrong, and you tell me it's ok - just to keep the peace... you're depriving me of my right to learn from my mistakes. That makes me feel upset.

I can appreciate that others prefer the opposite. They are trying to enjoy life and get through with a minimum of fuss and anguish. Most people seem to feel that if there's something nasty, it's better to coat it in shiny mother-of-pearl so that it's not all sharp and spiky in your side.

I can't help feeling, though, that it'd be much easier in the long run if they just faced up to it and spat it out to begin with. Then whatever it is would be gone from their lives and they could move on...

It often seems to me that people make their lives much more difficult by not facing up to hard truths, but by trying to ignore them or put them off. To convince themselves of the comforting lie in a misguided effort to "make it all go away". We all recognise these in other people... "I'm just big-boned", "I'll get around to it someday", "maybe it'll get better"... but when it comes to ourselves it's much harder, because we realise it's *us* that has to do the work to get past whatever it is... instead of somebody else's time/effort (which is as easy to spend as other people's money).

I am not immune to this myself, but whenever I find it happening in myself I tend to make an effort to getting rid of whatever it is I'm deluding myself about.

Obviously both can be abused. Lying to somebody can lead them into trouble, and telling too harsh a truth can indeed be needlessly hurtful - especially if a person is vulnerable, and simply needs comfort. Telling the truth about a person in front of other people can degrade their worth in the eyes of the others (and themselves). This is a Bad Thing.

Still, covering for another person's bad behaviour can mean that a person is not stopped from doing something they shouldn't - or never realises that they're being inappropriate (no, they don't always know!).

There is a place for truth and a place for tact, and it's a hard line to walk between the two.

IMO, though, society currently seems to place a heavy burden on the "play nice" aspect, and not enough on "keeping each other accountable".

The other argument about this is the lash-out defense of "it's none of your business". Who am I to tell other people to shape up? Especially since I'm so flawed myself. is this a "log in thine own eyes" situation?

To a certain extent this is the case. I certainly shouldn't feel high and mighty simply because I see a speck in your eye... but neither should I let you needlessly destroy your vision if you aren't aware of the issue.

It's absolutely my business to tell somebody that they need to learn to pick up the phone if leave me waiting for hours in the middle of nowhere, after they said they were going to pick me up. That situation doesn't have much of a grey area - but there are a lot of areas where it's more cloudy.

It's none of my business that a person is deluding themselves that their latest fad diet will work just as badly as all the others... unless they happen to be a friend that I care about and don't want to see them being unhealthy and eventually unhappy when their diet fails to work... again.

Is it my business? Yes and no. Should I find a tactful way of telling the truth? sure thing... but it will be the truth. The comforting lie will actually by more hurtful in the long run, to her health, to her self-esteem, to her chances of success at the very goal she has chosen for herself. In this sense, the comforting lie seems to only be less-hurtful in the short-term.

I believe this is true of most comforting lies. They cover-up a hurt to make life easier in the short-term, but the long-term harm is far more pronounced. On balance, I believe that only the truth will set you free.

Notes

[1] Introversion in the Myers-Briggs sense, an introvert is a person who regroups and regains their strength when they are by themself, rather than an extrovert who does so when in company. It is not actually the same thing as "being withdrawn" or even "being socially backward" - though these can be a consequence of too little interaction with others.

[2] Lying to children I think children are just as capable of bearing truths as adults, so long as they aren't frightening. Things that may make an adult fearful need not be so to children. eg death is a fact of life, but it needn't be something feared - of course you don't explain the scary details to a child as that will make it fearful.

Thursday 6 May 2010

ActiveRecord - find in random order

So we wanted to be able to find a few, random products to put on the homepage of one of our client's sites. I went looking for solutions, and the most commonly spouted one is to do a find with "order by 'rand()'"...

Now, this does the trick, for sure, but the problem is that it will order the entire table by the random amount... and we have a table with many thousand products, and this leads us towards a less-than-optimal query-time - every time somebody hits the homepage. :P

Given we only wanted a handful of products each time - I figured there just had to be a way of pulling out a small set of randomly-selected products without killing the load-time.

then I stumbled upon this article on Random records in rails. It provides a quick-trick fix that will pull out a single random record without all the fuss of ordering the entire table...

...but it doesn't provide a complete, extensible solution. We need it to pull out more than one - and I don't want to have to hit the database multiple times, if I can find a way around it.

Also - we don't just want *any* random product. Some of them are archived or out of stock, and so we need to be able to pass in other finder-option or use our nifty named scopes (eg "in_stock" or "best_sellers").

So here's my new solution. It lets you choose the number of random records to return, and pass in options, and plays nice with named scopes.

 
    # pull out a unique set of random active record objects without killing
    # the db by using "order by rand()"
    # Note: not true-random, but good enough for rough-and-ready use
    #
    # The first param specifies how many you want.
    # You can pass in find-options in the second param
    # examples:
    #  Product.random     => one random product
    #  Product.random(3)  => three random products in random order
    #
    # Note - this method works fine with scopes too! eg:
    #  Product.in_stock.random    => one random product that fits the "in_stock" scope
    #  Product.in_stock.random(3) => three random products that fit the "in_stock" scope
    #  Product.best_seller.in_stock.random => one random product that fits both scopes
    #
    def find_random(num = 1, opts = {})
      # skip out if we don't have any
      return nil if (max = self.count(opts)) == 0

      # don't request more than we have
      num = [max,num].min

      # build up a set of random offsets to go find
      find_ids = [] # this is here for scoping

      # get rid of the trivial cases
      if 1 == num # we only want one - pick one at random
        find_ids = [rand(max)]
      else
        # just randomise the set of possible ids
        find_ids = (0..max-1).to_a.sort_by { rand } 
        # then grab out the number that we need
        find_ids = find_ids.slice(0..num-1) if num != max
      end

      # we've got a random set of ids - now go pull out the records
      find_ids.map {|the_id| first(opts.merge(:offset => the_id)) }
    end

Thursday 29 April 2010

current_user in rubyCAS-client

With restful_authentication (or other schemes) we have a requires_login before_filter which you stick at the top of your controllers, to make sure certain actions are protected by login. It also tends to take care of the slightly messy logic involved in setting up the extremely useful current_user variable.

Obviously our applications have already been built around this logic existing, so a smooth transition requires the same sort of functionality.

The ruby-CAS (client) provides a basic before_filter for us that takes care of the nasty talking-to-the-rubyCAS-server logic... at the end of which we know whether or not the person talking to us is known to the system. But we still to actually associate this person with a real live User model on our system, and save them into the current-user variable.

On our system, after going through the provided before_filter, we have configured the server to provide two things in the session:

  1. the :cas_user, which is the user's username and
  2. a set of :extra_attributes which is a set of other fields we've asked CAS to provide and which we'll use later for roles (see authorisation for more on that)

If we want any other user information - we will need to go fetch it by hand from our local db, and we'll need to do this directly after the main rubyCAS before_filter. So, we'll need something like the following, probably put into ApplicationController

  # this is the before_filter provided by rubyCAS client
  before_filter CASClient::Frameworks::Rails::Filter
  # this is a call to our own new before_filter
  before_filter :setup_cas_user

  # actions go here...

  # and at the bottom of the controller file...
  private
    # this before_filter takes the results of the rubyCAS-client filter and sets up the current_user
    def setup_cas_user
      # save the login_url into an @var so that we can later use it in views (eg a login form)
      @login_url = CASClient::Frameworks::Rails::Filter.login_url(self)

      # if we don't have the :cas_user in the session, then we are not logged into rubyCAS
      # we could have none because:
      #   1) we have failed login or 
      #   2) because we don't necessarily need to login to get here
      # either way we need to skip out here
      return unless session[:cas_user].present?

      # so now we go find the user in our db
      @current_user = User.find_by_username(session[:cas_user])
      @current_user.present?
    end

Pretty simple

What if the user isn't on this application yet?

Good question - it is possible to have created a user on one application and of course, using single sign-on, you may want to automatically sign your users up into another application. This could easily be done here with a slight addition to the before_filter:

      # so now we go find the user in our db - or create them if they don't yet exist.
      @current_user = User.find_or_create_by_username(session[:cas_user])
      @current_user.present?

Note: the only information linking the two user-records will be the username. You won't be able to access any other information (eg address or other details) unless you physically query your other application's user record... though this is a good use-case for passing extra information through the extra_attributes fields. Still, it does allow your users to seamlessly transition from one of your applications to another.

This is one article in a series on Rails single-sign-on with rubyCAS

Wednesday 28 April 2010

RedBubble

I have just discovered RedBubble an awesome site for displaying and selling your artwork (whether images, writing, T-shirts or otherwise)... and developed in Ruby-on-Rails too.

You can upload your art and offer it for sale in a variety of formats - they provide widgets for helping sell your stuff (eg the portfolio below). You just specify the markup you want and they take care of the shipping and payment - sending you your cut via checque or paypal. It also has groups a-la flickr - where you can join your art with other similar art, take part in challenges etc.

Here's my portfolio as an example of what they can do for you.

Wednesday 14 April 2010

Silicon Stilettos

I just went to a great meetup. Silicon Stilettos is a networking meetup aimed at business women in the IT industry.

I went along last night and loved it. It was a great way to catch up with like-minded women in the industry, and hopefully learn a few tips and tricks from them, and share my own experience.

Despite it being an IT industry Do, I seemed to be the only actual developer in the lot - but that didn't surprise me at all. Even after decades of equal-rights, I still find that even with the small number of women in the IT world, that majority of them tend to be in marketing or PR, which played out amongst this group. But that's fine - after all, if I wanted to learn yet more IT stuff, I could go for linuxchix, GirlGeekDinners or any one of the non-female-only[1] user groups I attend already. So it was great to see a different side of the IT industry - and there were certainly a few women there that were quite savvy in the field.

The best chat was about social media as it pertains to IT PR, how big corporates still haven't realised they should do it, and how those that realise they should don't know how to get started. Mainly because they're so used to hiding behind their corporate brand-projection that it's hard for them to dredge up a real, unique and human voice, which is what is needed for social media to actually be genuine and appealing.

Often, big companies try to continue the sort of push-style marketing they're used to, just dropping it into twitter/facebook as though that was how to win over the social media crowd. They couldn't be more wrong, of course. Social media demands a real and genuine connection with people, and you can't do that by braying about how good you are.

A lot of companies still don't get the "conversation" part of the equation and fail to understand that at least one half of a conversation should be devoted to really listening to the other person[2]. Failing to pay attention can lead to serious problems quicker than you think. One of the women spoke about some kerfuffle that happened over facebook/twitter when someone at nestle was obviously having a bad day. Clearly, social media can also go horribly horribly wrong...

So, I think the meetup wa a resounding success, and I'm really looking forward to the next one.

Notes

1
Why go to an all-women group? Well - that's hard to answer without offending people, but I'll try. It's not that I don't like co-ed groups - I'm not terrified of men. Being in an industry so obviously dominated by them means I've gotten pretty used to hanging around with them... but there are still cultural differences between the sexes (in an on-average way, and I'm sure entirely due to different upbringing). While it's great to do the mixed-thing it can also be good to get amongst other women too.

Being an expat I can immediately see similarities with hanging out with others from my own country. It's not that I don't want to assimilate - I spend > 95% of my time hanging around with locals... but it's also nice now and then to get together with other Aussies and bitch about how crap the NHS is compared with Medicare (for example). :)

The expectations and culture are simply different. For one thing, I noticed that most of the attendees had one small drink and I wasn't the only one leaving before 9 to get home. Being used to IT get-togethers where you get funny looks if you don't have a second or third drink and get a sense that you're just "not up to it" if you don't hang around until way past 10 (regardless of it being a school-night)... this came as a breath of fresh air. Seems like these other women also have a life... :)

2
It still surprises me that companies don't get this. After all, The Cluetrain Manifesto was written a decade ago, and folks like Seth Godin are doing their best to get the message out every which way they can. Yet I still see big companies pushing the same rhetoric out as though they could just transcribe their traditional TV-ads or print-media brochure-ware onto the web. They remind me of those obnoxious types that will talk over the top of everyone at dinner but only ever talk about how good they are, without realising that the adage of "show don't tell" isn't limited to novel-writing.