# Enumerable extensions.
module Enumerable
  # Invokes the specified method for each item, along with the supplied
  # arguments.
  def send_each(sym, *args)
    each {|i| i.send(sym, *args)}
  end
end

# Array extensions
class Array
  # Concatenates an array of strings into an SQL string. ANSI SQL and C-style
  # comments are removed, as well as excessive white-space.
  def to_sql
    map {|l| (l =~ /^(.*)--/ ? $1 : l).chomp}.join(' '). \
      gsub(/\/\*.*\*\//, '').gsub(/\s+/, ' ').strip
  end
end

module Sequel
  # LiteralString is used to represent literal SQL expressions. An 
  # LiteralString is copied verbatim into an SQL statement. Instances of
  # LiteralString can be created by calling String#expr.
  class LiteralString < ::String
  end
end

# String extensions
class String
  # Converts a string into an SQL string by removing comments.
  # See also Array#to_sql.
  def to_sql
    split($/).to_sql
  end
  
  # Splits a string into separate SQL statements, removing comments
  # and excessive white-space.
  def split_sql
    to_sql.split(';').map {|s| s.strip}
  end

  # Converts a string into an LiteralString, in order to override string
  # literalization, e.g.:
  #
  #   DB[:items].filter(:abc => 'def').sql #=>
  #     "SELECT * FROM items WHERE (abc = 'def')"
  #
  #   DB[:items].filter(:abc => 'def'.lit).sql #=>
  #     "SELECT * FROM items WHERE (abc = def)"
  #
  def lit
    Sequel::LiteralString.new(self)
  end
  
  alias_method :expr, :lit
  
  # Converts a string into a Time object.
  def to_time
    Time.parse(self)
  end
  
  # Converts a string into a field name.
  def to_field_name
    self
  end
end

# Methods to format field names and associated constructs. This module is
# included in String and Symbol.
module FieldCompositionMethods
  # Constructs a DESC clause for use in an ORDER BY clause.
  def DESC
    "#{to_field_name} DESC"
  end
  
  # Constructs an AS clause for field aliasing.
  def AS(target)
    "#{to_field_name} AS #{target}"
  end

  # Constructs a qualified wildcard (*) clause.
  def ALL
    "#{to_s}.*"
  end
  
  FIELD_TITLE_RE1 = /^(.*)\sAS\s(.+)$/i.freeze
  FIELD_TITLE_RE2 = /^([^\.]+)\.([^\.]+)$/.freeze
  
  # Returns the field name. If the field name is aliased, the alias is 
  # returned.
  def field_title
    case s = to_field_name
    when FIELD_TITLE_RE1, FIELD_TITLE_RE2: $2
    else
      s
    end
  end
end

class String
  include FieldCompositionMethods
end

# Symbol extensions
class Symbol
  include FieldCompositionMethods
  

  FIELD_REF_RE1 = /^(\w+)__(\w+)___(\w+)/.freeze
  FIELD_REF_RE2 = /^(\w+)___(\w+)$/.freeze
  FIELD_REF_RE3 = /^(\w+)__(\w+)$/.freeze
  
  # Converts a symbol into a field name. This method supports underscore
  # notation in order to express qualified (two underscores) and aliased 
  # (three underscores) fields:
  #
  #   :abc.to_field_name #=> "abc"
  #   :abc___a.to_field_name #=> "abc AS a"
  #   :items__abc.to_field_name #=> "items.abc"
  #   :items__abc___a.to_field_name #=> "items.abc AS a"
  #
  def to_field_name
    s = to_s
    case s
    when FIELD_REF_RE1: "#{$1}.#{$2} AS #{$3}"
    when FIELD_REF_RE2: "#{$1} AS #{$2}"
    when FIELD_REF_RE3: "#{$1}.#{$2}"
    else
      s
    end
  end
  
  # Converts missing method calls into functions on columns, if the
  # method name is made of all upper case letters.
  def method_missing(sym)
    ((s = sym.to_s) =~ /^([A-Z]+)$/) ? \
      "#{s.downcase}(#{to_field_name})".lit : super
  end
  
  # Formats an SQL function with optional parameters
  def [](*args)
    "#{to_s}(#{args.join(', ')})".lit
  end
end