require 'active_record' require 'polo/configuration' module Polo class SqlTranslator def initialize(object, configuration=Configuration.new) @record = object @configuration = configuration end def to_sql records = Array.wrap(@record) sqls = records.map do |record| raw_sql(record) end if @configuration.on_duplicate_strategy == :ignore sqls = ignore_transform(sqls) end if @configuration.on_duplicate_strategy == :override sqls = on_duplicate_key_update(sqls, records) end sqls end private def on_duplicate_key_update(sqls, records) insert_and_record = sqls.zip(records) insert_and_record.map do |insert, record| values_syntax = record.attributes.keys.map do |key| "#{key} = VALUES(#{key})" end on_dup_syntax = "ON DUPLICATE KEY UPDATE #{values_syntax.join(', ')}" "#{insert} #{on_dup_syntax}" end end def ignore_transform(inserts) inserts.map do |insert| insert.gsub("INSERT", "INSERT IGNORE") end end def raw_sql(record) connection = ActiveRecord::Base.connection attributes = record.attributes keys = attributes.keys.map do |key| "`#{key}`" end values = attributes.map do |key, value| column = record.column_for_attribute(key) connection.quote(cast_attribute(record, column, value)) end "INSERT INTO `#{record.class.table_name}` (#{keys.join(', ')}) VALUES (#{values.join(', ')})" end module ActiveRecordLessThanFourPointTwo def cast_attribute(record, column, value) attribute = record.send(:type_cast_attribute_for_write, column, value) if record.class.serialized_attributes.include?(column.name) attribute.serialize else attribute end end end module ActiveRecordFourPointTwoOrGreater def cast_attribute(record, column, value) column.type_cast_for_database(value) end end if ActiveRecord::VERSION::STRING.start_with?('3.2') || ActiveRecord::VERSION::STRING.start_with?('4.0') || ActiveRecord::VERSION::STRING.start_with?('4.1') include ActiveRecordLessThanFourPointTwo else include ActiveRecordFourPointTwoOrGreater end end end