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).
ActiveRecord Refactoring Project
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 comment