Thursday 27 September 2012

Link: Amazon Review-spoofing scandal

This is a lesson in how any system can be gamed.

Amazon reviews have been built up to be an integral part of how we evaluate books for sale on amazon. The ability to see what people are saying about a product has turned the process of book-buying on its head. Instead of briefly skimming the back of the jacket and taking a gamble on whether it's good or not, you can see the general consensus of the community of readers and *hopefully* get a better idea on whether this book is any good.

Unfortunately, like any process, it can be gamed, and this week we've seen how deep this can get.

A guy called Rutherford set up a review-buying service where authors could come and purchase good-reviews on amazon. In one case making a new author (John Locke) into an overnight millionaire.

Here's a link to the full story on the John Locke book review scandal

The uproar in the authorial world has been tremendous. and amazon are investigating what happened and what they can do about it. It's a tough job, because it's extremely difficult to figure out whether reviews are sincere.

Hopefully they can find a way because I'd hate to lose the ability to see the true measure of the community's feelings towards a book.

Friday 21 September 2012

Link: count vs length vs size in Rails

Every now and then I have to remind myself of the differences between "count", "length" and "size" in Rails. I've never found a better source for this than the has_many :through article: count vs length vs size

It's well worth a read, even if you're an old Rails pro.

Saturday 15 September 2012

make will_paginate always show matches/pages summary

will_paginate is the standard pagination plugin for many a Rails-2 site, and it's extremely flexible.

However it has one drawback: it will only show links - if there is more than one page.

This is mostly not a problem, if the links are all you show. But lets say you also use it to show the "Showing 26 to 50 of 342 matches" summary. Then lets say you want it to show "Showing 1 to 16 of 16 matches" - even when there's only a single page (for the sake of UI consistency). will_paginate does not provide an option for this.

There's talk of adding a :show_always => true option to will_paginate, but last I looked it hadn't been dragged into master...

So I wrote my own monkey-patch.

You'll need to put the following two snippets into config/intiializers/will_paginate.rb


The first thing you need to do, is make the "summary" sections public, so they are available to the renderer (which is the thing that generates all the page-links for you). Here's mine (both scavenged and adapted from the web many years ago):

module WillPaginate
  class LinkRenderer
    # generates eg: "1 - 15 of 15" or "26 - 50 of 342"
    def matches_summary
      if 0 == @collection.total_entries
        # because "0 - 0 of 0" is a bit much
        summary_text = "0 Matches"
      elsif 1 == @collection.total_entries
        summary_text = "1 Match"
      else
        summary_text = "%d - %d of %d" % [ @collection.offset + 1, 
               @collection.offset + @collection.length, @collection.total_entries ]
      end
      page_span(1, summary_text, :class => "summary")
    end    
 
    # generates "Page 4 of 62" or "Page 1 of 1"
    def pages_summary
      if 0 == @collection.total_pages
        # because "Page 0 of 0" is a bit much
        summary_text = "0 Pages"
      elsif 1 == @collection.total_pages
        summary_text = "1 Page"
      else
        summary_text = "Page %d of %d" % [ current_page, @collection.total_pages ]
      end
      page_span(1, summary_text, :class => "summary")
    end    
  end # LinkRenderer class
end # WillPaginate module

And this chunk is what makes will_paginate:

  1. Accept the ":show-always => true" option
  2. If the above is passed, and there are no page-links... displays the summaries
module WillPaginate
  module ViewHelpers
    # This lets us still use any will_paginate passed-in renderers if we want
    # adapted by code in original will_paginate
    def fetch_renderer(renderer)
      case renderer
      when String
        renderer.to_s.constantize.new
      when Class
        renderer.new
      else
        renderer
      end
    end

    # don't lose the old will_paginate
    alias :old_will_paginate :will_paginate

    # overload actual will_paginate call to allow us to choose to show the 
    # "number of matches" box even if there is only one page
    def will_paginate(collection, options = {})
      # strip out the show_always option
      show_always = options.delete(:show_always)

      # do standard pagination
      html = old_will_paginate(collection,options)

      # if we didn't get anything, but we have "show always" = true
      if html.nil? && show_always
        # set up the renderer (stolen directly from standard will_paginate)
        options[:container] = true
        renderer = fetch_renderer(options[:renderer])
        renderer.prepare collection, options, self

        # produce the "matches" boxes through the renderer
        links = [renderer.matches_summary, renderer.pages_summary]

        # generate the html for the pagination links - 
        # default the html-attributes to the plain pagination class
        html = content_tag(:div, links, (renderer.html_attributes||{}).merge(:class => 'pagination'))
      end

      html
    end

  end
end

Then you can call it with this:

   <%= will_paginate @things, :show_always => true %>

Monday 10 September 2012

Link: 6 home truths about rockstar developers

We all want to hire a team of rockstar developers don't we? after all - with a team of rockstars our project will be the Best Thing Evar! ...or maybe not?

6 home truths about rockstar developers brings us back down to earth and points out the downsides of having a team formed of purely "rockstar" types. Worth a read - whether you're a hiring manager, or a rockstar yourself.

Saturday 1 September 2012

validate_format_of with better test-names

validate_format_of is almost always called more than once on a single fieldname. Generally with different valid formats... and then with different invalid formats.

eg our set of date-format validations:

  # validate_our_date_format(:end_date, "Please enter correct format for end date")
  def self.validate_our_date_format(field_name,msg)
    # /^[0-9]{1,2}\/[0-9]{1,2}\/[0-9]{4}$/
    # dates we do match and should
    should validate_format_of(field_name).with('01/04/2010').with_message(msg)
    should validate_format_of(field_name).with('31/12/2012').with_message(msg)
    should validate_format_of(field_name).with(' 03/08/2010 ').with_message(msg)
    should validate_format_of(field_name).with('01/02/12').with_message(msg)
    should validate_format_of(field_name).with('02-01-2010').with_message(msg)
    should validate_format_of(field_name).with('1/2/2012').with_message(msg)
    should validate_format_of(field_name).with("04/10/2011\t\n").with_message(msg)
    # dates we don't match and shouldn't
    should validate_format_of(field_name).not_with('42/99/9999').with_message(msg)
    should validate_format_of(field_name).not_with('00/00/0000').with_message(msg)
    should validate_format_of(field_name).not_with('1').with_message(msg)
    should validate_format_of(field_name).not_with('abcd').with_message(msg)
    should validate_format_of(field_name).not_with('01/02/silly').with_message(msg)
  end

but there's a bug in validates_format_of... the description (which becomes the test name) is based on the following code: "{attribute} have a valid format" which makes tests named eg: "should :end_date have a valid format"... These test names are identical, not very useful, and not even properly grammatical.

*ALL* of them are named this eg, in the above example I'd have 12 tests all named as above...

At best, this identical-ness causes scads of Giant Flaming WARNINGS saying that
WARNING: 'test: <your enclosing scope here> should <fieldname> have a valid format.' is already defined
At worst, in certain (old) versions of rails - this means that only the first one gets run at all!

So - uniquifying the name is a Good Thing

The non-usefulness is that even if they do run, you don't know *which* version of your validates_format_of failed... because they're all named the same thing.

Thus this monkey-patch, which puts such things as the test-string *in the description*.

As with my previous shoulda backports, add these to a file in the config/initializers directory

module Shoulda # :nodoc:
  module ActiveRecord
    module Matchers

      # uniquify the description for validates_format_of "should" tests
      class NewValidateFormatOfMatcher < ValidateFormatOfMatcher
        
        # put a little more detail into the description
        def description
          if @value_to_fail
            "not accept #{@value_to_fail} as a valid format for #{@attribute}"
          elsif @value_to_pass
            "accept #{@value_to_pass} as a valid format for #{@attribute}"
          else
            super
          end
        end

      end
      # and override validate_format_of to use our new class
      def validate_format_of(attr)
        NewValidateFormatOfMatcher.new(attr)
      end
    end
  end
end

Note: this monkeypatch is for shoulda version 2.11.1 - not the latest Rails 3 version. You may need to modify accordingly if you're actually up-to-date...