kaspernj/baza

View on GitHub
lib/baza/sql_queries/select.rb

Summary

Maintainability
B
6 hrs
Test Coverage
class Baza::SqlQueries::Select
  def initialize(args)
    @db = args.fetch(:db)
    @selects = []
    @froms = []
    @joins = []
    @wheres = []
    @groups = []
    @orders = []
  end

  def count
    @count = true
    query.fetch.fetch(:count).to_i
  ensure
    @count = false
  end

  def current_page
    @page || 1
  end

  def select(arg)
    @selects << arg
    self
  end

  def from(arg)
    @froms << arg
    self
  end

  def join(arg)
    @joins << arg
    self
  end

  def where(*args)
    @wheres << args
    self
  end

  def group(arg)
    @groups << arg
    self
  end

  def order(arg)
    @orders << arg
    self
  end

  def page(number)
    @page = number.try(:to_i) || 1
    self
  end

  def per_page(number)
    @per_page = number
    self
  end

  def limit(limit)
    @limit = limit
    self
  end

  def offset(offset)
    @offset = offset
    self
  end

  def to_sql
    "#{select_sql} #{from_sql} #{where_sql} #{group_sql} #{limit_sql}"
  end

  def to_a
    each.to_a
  end

  def total_pages
    @per_page ||= 30
    per_page_value = @per_page
    (count.to_f / per_page_value.to_f).ceil
  end

  def each(&blk)
    query(&blk)
  end

  def each_row
    query do |data|
      yield Baza::Row.new(
        db: @db,
        table: first_from,
        data: data
      )
    end
  end

  def to_enum
    Enumerator.new do |yielder|
      query do |data|
        yielder << data
      end
    end
  end

  def query(&blk)
    @db.query(to_sql, &blk)
  end

private

  def select_sql
    sql = "SELECT"

    if @count
      sql << " COUNT(*) AS count"
    elsif @selects.empty?
      sql << " *"
    else
      first = true
      @selects.each do |select|
        sql << "," unless first
        first = false if first

        if select.is_a?(Symbol)
          select << " #{@db.quote_column(select)}"
        else
          select << @db.quote_value(select)
        end
      end
    end

    sql
  end

  def from_sql
    sql = "FROM"

    first = true
    @froms.each do |from|
      sql << "," unless first
      first = false if first
      sql << " #{@db.quote_table(from)}"
    end

    sql
  end

  def first_from
    @first_from ||= @froms.first
  end

  def where_sql
    return if @wheres.empty?

    sql = " WHERE"

    first = true
    @wheres.each do |args|
      where = args.shift

      sql << " AND " unless first
      first = false if first

      if where.is_a?(Hash)
        where.each do |key, value|
          sql << "#{@db.quote_column(key)} = #{@db.quote_value(value)}"
        end
      elsif where.is_a?(String)
        sql_arg = where.clone
        args.each do |arg|
          sql_arg.sub!("?", @db.quote_value(arg))
        end

        sql << sql_arg
      else
        raise "Dont know what to do with that argument: #{where}"
      end
    end

    sql
  end

  def group_sql
    return if @groups.empty?
  end

  def limit_sql
    unless @count
      if @page
        @per_page ||= 30
        sql = "LIMIT #{@db.quote_value(@per_page)} OFFSET #{@per_page * (current_page - 1)}"
      elsif @limit
        sql = "LIMIT #{@db.quote_value(@limit)}"
        sql << ", #{@db.quote_value(@offset)}" if @offset
      end
    end

    sql
  end
end