ActiveRecord Refactoring Project

February 26, 2007 § Leave a comment

I am excited that Zach Dennis has taken up last year’s cause of ActiveRecord Refactoring, since I never made much progress on my ActiveData efforts. Still, to help him out I’ve posted my pre-alpha ActiveData code, which builds on my earlier non-SQL ActiveRecord. I’ve pasted some excerpts below (Read More).


module ActiveData
  module ConnectionAdapters #:nodoc:
    class AbstractAdapter < ActiveRecord::ConnectionAdapters::AbstractAdapter
      include Query
    end
  end
end
# ONLY PARTIALLY IMPLEMENTED

module ActiveRecord
  module ConnectionAdapters # :nodoc:
    module Query
      # Define services for creating  queries for this connection
      # Defaults  to simply creating SQL strings
      #  Return query object for this connection

      def new_query()
        return QueryGenerator(self)
      end

      class  QueryGenerator
        def initialize(connection)
          @connection = connection
          @query =""
        end

        # Map all database statements directly
        # That way, adapters default to the simplest possible implementation
        # But, custom adapters can override this query to do special-purpose things
        def execute(action, log_msg)
          case action
          when :select_all then
            @connection.select_all(@query,log_msg)
          when :select_one then
            @connection.select_one(@query,log_msg)
          when :update then
            @connection.update(@query,log_msg)
          when :delete then
            @connection.delete(@query,log_msg)
          end 
        end

        def insert(log_msg)
          @connection.insert(@query,log_msg)
        end

        # Query Construction

        def add(verb, noun)
          @query << " #{verb} #{noun}"
        end

        #....


      end # class
    end # module  Query

  end
end

module ActiveData # ActiveRecord for  non-SQL Data Sources

  class Base <    ActiveRecord::Base
    # Override  SQL-creating data methods to instead use
    # generic Query objects to query/create/update other  datastores

    # Works like find(:all), but requires a  complete SQL string
    # (in this case, an  initialized Query object) 

    def  find_by_sql(sql)
      sql =  sanitize_sql(sql)
      sql.execute(:select_all, "#{name} Load").collect! { |record| instantiate(record) }
    end


    def update_all(updates, conditions = nil)
      sql =  connection.new_query()
      sql.add("UPDATE", table_name)
      sql.add("SET", sanitize_sql(updates))
      add_conditions!(sql, conditions)
      sql.execute(:update, "#{name} Update")
    end

    #...

    def count(conditions = nil, joins = nil)
      sql = connection.new_query()
      sql.add("SELECT", "COUNT(*)")
      sql.add("FROM", table_name)
      sql.add(nil, joins) if joins
      add_conditions!(sql, conditions)
      count_by_sql(sql)
    end

    def  count_by_sql(sql)
      sql = sanitize_conditions(sql)
      sql.execute(:select_value, "#{name} Count").to_i
    end

    def  construct_finder_sql(options)
      sql=connection.new_query()
      sql.add("SELECT",options[:select] || '*')
      sql.add("FROM",table_name)
      add_joins!(sql, options)
      add_conditions!(sql, options[:conditions])
      sql.add("GROUP BY",options[:group]) if
      options[:group]
      sql.add("ORDER BY",options[:order]) if options[:order]
      add_limit!(sql, options)
      sql
    end

    def add_limit!(sql,  options)
      options[:limit] ||=  scope(:find, :limit)
      options[:offset]  ||= scope(:find, :offset)
      sql.add_limit_offset!(options)
    end

    def add_joins!(sql,  options)
      join = scope(:find, :joins) ||  options[:joins]
      sql.add_join(join) if  join
    end

    # Adds a sanitized version of  +conditions+ to the +sql+ string.
    # Note  that the passed-in +sql+ string is changed.

    def add_conditions!(sql, conditions)
      segments = [scope(:find, :conditions)]
      segments << sanitize_sql(conditions) unless conditions.nil?
      segments << type_condition unless descends_from_active_record?
      segments.compact!

      sql.add_array("WHERE",segments) unless segments.empty?
      # need to explicitly join with "AND"
      sql << "WHERE (#{segments.join(") AND (")}) " unless segments.empty?

    end

  end # class Base

end # module ActiveData

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

What’s this?

You are currently reading ActiveRecord Refactoring Project at iHack, therefore iBlog.

meta

%d bloggers like this: