Thanks Scott. I refactored it today to use what I called a QueryAdapter, namespaced inside the model. It basically subclasses Hash, takes the params from the controller into the constructor, and becomes the hash to be sent to find_all.

I feels much better, as I now have the code that's coupled to the database in one place, but I'd welcome feedback:
        
    # VenuesController
    def get_venues
      Venue.paginate( :all, Venue::QueryAdapter.new(params) )
    end

# Responsible for mapping a hash of parameters that will typically be POSTed to a controller into a hash that can be sent to find(:all)
    # containing SQL clauses in :conditions / :order.
# This helps us decouple the view / controller layers from any database specific stuff.
    class Venue::QueryAdapter < Hash

      def initialize(params)

        parse params

        self.merge!(get_find_params)

      end

      private

      def parse(params)
        @sort_column = params[:sort]
        @city_id = params[:city_id]
        @name = params[:name]
        @page = params[:page]
      end

      def get_find_params

        find_params = {}

find_params.merge!( :order => get_order_clause ) if get_order_clause.length > 0 find_params.merge!( :conditions => get_where_clause ) if get_where_clause.length > 0
        find_params.merge!( :page       => @page )

        return find_params

      end

      def get_where_clause

        clause = []

        clause << "city_id = [EMAIL PROTECTED]" if @city_id
        clause << "name like '[EMAIL PROTECTED]'" if @name

        return clause.join(" AND ")

      end

      def get_order_clause

        clause = []

        clause << 'created_on DESC' if @sort_column == 'created_at'
        clause << 'name ASC'

        return clause.join(", ")

      end

    end


cheers,
Matt
----
http://blog.mattwynne.net
http://songkick.com

In case you wondered: The opinions expressed in this email are my own and do not necessarily reflect the views of any former, current or future employers of mine.

On 16 Aug 2008, at 00:19, Scott Taylor wrote:


On Aug 15, 2008, at 9:29 AM, David Chelimsky wrote:

On Aug 15, 2008, at 6:46 AM, Matt Wynne <[EMAIL PROTECTED]> wrote:

On 15 Aug 2008, at 12:25, David Chelimsky wrote:

Hey Matt - welcome!

The paginate() method lives on the model class, so there's nothing
stopping you from wrapping those calls in methods on the model,
slinging around the params object.

# CityController

def get_cities
City.paginate_all(params)
end

# City

def self.paginate_all(params)
self.paginate(:all, get_find_params(params).merge!(:page => params[:page]))
end

etc


Aha. Cool, thanks.

For my next question: how do I go about driving out change to the model, spec-first?

I'm thinking I would call (in my spec)

City.should_receive(:paginate).with(:conditions => "name like '% #{test_params[:name}%'" .... )
  City.paginate_all(test_params)

Thereby covering the code in get_find_params()

Is that the right approach?

That's probably how I would do it. Might also consider wrapping the params in a separate object that manages the extraction.

That's how I've started doing it - putting sql statements in a module:

http://gist.github.com/5675

This allows me to test the sql statements seperately from the actual finder.

Also - just to give you the heads up - You should almost never use literal string substitutions in sql statements - it allows for sql injection attacks:

http://en.wikipedia.org/wiki/Sql_injection

Best,

Scott Taylor


_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

_______________________________________________
rspec-users mailing list
rspec-users@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Reply via email to