module Ambition module Where def select(*args, &block) query_context.add WhereProcessor.new(self, block) end def detect(&block) select(&block).first end end class WhereProcessor < Processor def initialize(owner, block) super() @receiver = nil @owner = owner @table_name = owner.table_name @block = block @key = :conditions end ## # Sexp Processing Methods def process_and(exp) joined_expressions 'AND', exp end def process_or(exp) joined_expressions 'OR', exp end def process_not(exp) _, receiver, method, other = *exp.first exp.clear return translation(receiver, negate(method), other) end def process_call(exp) receiver, method, other = *exp exp.clear return translation(receiver, method, other) end def process_lit(exp) exp.shift.to_s end def process_str(exp) sanitize exp.shift end def process_nil(exp) 'NULL' end def process_false(exp) sanitize 'false' end def process_true(exp) sanitize 'true' end def process_match3(exp) regexp, target = exp.shift.last.inspect.gsub(/\/([^\/]+)\/\S*/, '\1'), process(exp.shift) "#{target} REGEXP '#{regexp}'" end def process_dvar(exp) target = exp.shift if target == @receiver return @table_name else return value(target.to_s[0..-1]) end end def process_ivar(exp) value(exp.shift.to_s[0..-1]) end def process_lvar(exp) value(exp.shift.to_s) end def process_vcall(exp) value(exp.shift.to_s) end def process_gvar(exp) value(exp.shift.to_s) end def process_attrasgn(exp) exp.clear raise "Assignment not supported. Maybe you meant ==?" end ## # Processor helper methods def joined_expressions(with, exp) clauses = [] while clause = exp.shift clauses << clause end return "(" + clauses.map { |c| process(c) }.join(" #{with} ") + ")" end def value(variable) sanitize eval(variable, @block) end def negate(method) case method when :== '<>' when :=~ '!~' else raise "Not implemented: #{method}" end end def translation(receiver, method, other) case method.to_s when '==' case other_value = process(other) when "NULL" "#{process(receiver)} is #{other_value}" else "#{process(receiver)} = #{other_value}" end when '<>', '>', '<' "#{process(receiver)} #{method} #{process(other)}" when 'include?' "#{process(other)} IN (#{process(receiver)})" when '=~' "#{process(receiver)} LIKE #{process(other)}" when '!~' "#{process(receiver)} NOT LIKE #{process(other)}" when 'upcase' "UPPER(#{process(receiver)})" when 'downcase' "LOWER(#{process(receiver)})" else extract_includes(receiver, method) || "#{process(receiver)}.`#{method}`" end end end end