class SQL::Maker::Condition

Attributes

auto_bind[RW]
bind[RW]
name_sep[RW]
quote_char[RW]
sql[RW]
strict[RW]

Public Class Methods

new(args = {}) click to toggle source
# File lib/sql/maker/condition.rb, line 9
def initialize(args = {})
  @quote_char = args[:quote_char] || ''
  @name_sep = args[:name_sep] || '.'
  @strict = args[:strict] || false
  @auto_bind = args[:auto_bind] || false

  @sql = args[:sql] || []
  @bind = args[:bind] || []
end

Public Instance Methods

&(other) click to toggle source
# File lib/sql/maker/condition.rb, line 27
def &(other)
  self.compose_and(other)
end
_make_in_term(col, op, v) click to toggle source
# File lib/sql/maker/condition.rb, line 96
def _make_in_term(col, op, v)
  if v.respond_to?(:as_sql)
    # make_term(foo => { 'IN' => sql_raw('SELECT foo FROM bar') }) => foo IN (SELECT foo FROM bar)
    term = "#{self._quote(col)} #{op} (#{v.as_sql})"
    [term, v.bind]
  elsif v.is_a?(Array)
    if v.size == 0
      if op == 'IN'
        # make_term(foo => {'IN' => []}) => 0=1
        return ['0=1', []]
      else
        # make_term(foo => {'NOT IN' => []}) => 1=1
        return ['1=1', []]
      end
    else
      # make_term(foo => { 'IN' => [1,2,3] }) => [foo IN (?,?,?), [1,2,3]]
      term = "#{self._quote(col)} #{op} (#{(['?'] * v.size).join(', ')})"
      return [term, v]
    end
  else
    croad("_make_in_term: arguments must be either of query instance or array")
  end
end
_make_or_term(col, op, values) click to toggle source
# File lib/sql/maker/condition.rb, line 83
def _make_or_term(col, op, values)
  binds = []
  terms = []
  values.each do |v|
    term, bind = self._make_term(col => v)
    terms.push "(#{term})"
    binds.push bind
  end
  term = terms.join(" #{op} ")
  bind = binds.flatten
  return [term, bind]
end
_make_term(*args) click to toggle source

_make_term(:x => 1)

# File lib/sql/maker/condition.rb, line 40
def _make_term(*args)
  col, val = parse_args(*args)
  col = col.to_s

  if val.is_a?(SQL::QueryMaker)
    return [val.as_sql(col, self.method(:_quote)), val.bind]
  elsif self.strict
    croak("Condition#add: can pass only SQL::QueryMaker object as an argument in strict mode")
  end

  if val.is_a?(Array)
    if val.first.is_a?(Hash)
      # {'foo'=>[{'>' => 'bar'},{'<' => 'baz'}]} => (`foo` > ?) OR (`foo` < ?)
      return self._make_or_term(col, 'OR', val)
    else
      # {'foo'=>['bar','baz']} => `foo` IN (?, ?)
      return self._make_in_term(col, 'IN', val)
    end
  elsif val.is_a?(Hash)
    op, v = val.each.first
    op = op.to_s.upcase
    if ( op == 'AND' || op == 'OR' ) && v.is_a?(Array)
      # {'foo'=>{'or' => [{'>' => 'bar'},{'<' => 'baz'}]}} => (`foo` > ?) OR (`foo` < ?)
      return self._make_or_term(col, op, v)
    elsif ( op == 'IN' || op == 'NOT IN' )
      # {'foo'=>{'in' => ['bar','baz']}} => `foo` IN (?, ?)
      return self._make_in_term(col, op, v)
    elsif ( op == 'BETWEEN' ) && v.is_a?(Array)
      croak("USAGE: add(foo => {BETWEEN => [a, b]})") if v.size != 2
      return [self._quote(col) + " BETWEEN ? AND ?", v]
    else
      # make_term(foo => { '<' => 3 }) => foo < 3
      return [self._quote(col) + " #{op} ?", [v]]
    end
  elsif val
    # make_term(foo => "3") => foo = 3
    return [self._quote(col) + " = ?", [val]]
  else
    # make_term(foo => nil) => foo IS NULL
    return [self._quote(col) + " IS NULL", []]
  end
end
_quote(label) click to toggle source
# File lib/sql/maker/condition.rb, line 35
def _quote(label)
  quote_identifier(label, self.quote_char, self.name_sep)
end
add(*args) click to toggle source
# File lib/sql/maker/condition.rb, line 120
def add(*args)
  term, bind = self._make_term(*args)
  self.sql.push "(#{term})" if term
  self.bind += array_wrap(bind) if bind

  return self # for influent interface
end
add_raw(*args) click to toggle source
# File lib/sql/maker/condition.rb, line 128
def add_raw(*args)
  term, bind = parse_args(*args)
  self.sql.push "(#{term})"
  self.bind += array_wrap(bind) if bind
  return self
end
as_sql() click to toggle source
# File lib/sql/maker/condition.rb, line 183
def as_sql
  sql = self.sql.join(' AND ')
  @auto_bind ? bind_param(sql, self.bind) : sql
end
compose_and(other) click to toggle source
# File lib/sql/maker/condition.rb, line 135
def compose_and(other)
  if self.sql.empty?
    if other.sql.empty?
      return new_condition
    end
    return new_condition(
      :sql => ['(' + other.as_sql() + ')'],
      :bind => other.bind,
    )
  end
  if other.sql.empty?
    return new_condition(
      :sql => ['(' + self.as_sql() + ')'],
      :bind => self.bind,
    )
  end

  return new_condition(
    :sql => ['(' + self.as_sql() + ') AND (' + other.as_sql() + ')'],
    :bind => self.bind + other.bind,
  )
end
compose_or(other) click to toggle source
# File lib/sql/maker/condition.rb, line 158
def compose_or(other)
  if self.sql.empty?
    if other.sql.empty?
      return new_condition
    end
    return new_condition(
      :sql => ['(' + other.as_sql() + ')'],
      :bind => other.bind,
    )
  end
  if other.sql.empty?
    return new_condition(
      :sql => ['(' + self.as_sql() + ')'],
      :bind => self.bind,
    )
  end

  # return value is enclosed with '()'.
  # because 'OR' operator priority less than 'AND'.
  return new_condition(
    :sql => ['((' + self.as_sql() + ') OR (' + other.as_sql() + '))'],
    :bind => self.bind + other.bind,
  )
end
new_condition(args = {}) click to toggle source
# File lib/sql/maker/condition.rb, line 19
def new_condition(args = {})
  SQL::Maker::Condition.new({
    :quote_char => self.quote_char,
    :name_sep   => self.name_sep,
    :strict     => self.strict,
  }.merge(args))
end
|(other) click to toggle source
# File lib/sql/maker/condition.rb, line 31
def |(other)
  self.compose_or(other)
end