class Terrazine::Builder

builds structures in to sql string TODO: SPLIT!!! But how-_- Operators(sql_functions), Predicates, Clauses(select, from…), Expressions(columns, tables), Params… they are mixed… everything can contain everything and they must communicate with each other. And how it can be splitted?

Public Class Methods

new() click to toggle source

6ftdan.com/allyourdev/2015/05/02/private-module-methods-in-ruby/ TODO: all methods private except get_sql, get_partial_sql ?

# File lib/terrazine/builder.rb, line 17
def initialize
  @params = []
end

Public Instance Methods

get_sql(structure, options) click to toggle source

get complete sql structure for constructor.

# File lib/terrazine/builder.rb, line 22
def get_sql(structure, options)
  # get_partial_sql structure, key: 'sql'
  wrap_result send("build_#{options[:key] || 'sql'}", structure)
end

Private Instance Methods

build_columns(structure, prefix = nil) click to toggle source

TODO: split

# File lib/terrazine/builders/expressions.rb, line 30
def build_columns(structure, prefix = nil)
  case structure
  when Array
    # SQL function - in format: "_#{fn}"
    if check_alias(structure.first)
      build_operator structure, prefix
    else
      structure.map { |i| build_columns i, prefix }.join ', '
    end
  when Hash
    # sub_query
    if structure[:select]
      "(#{build_sql(structure)})"
    # colum OR table alias
    else
      iterate_hash(structure) do |k, v|
        if check_alias(k)
          # update ruby for delete_prefix? =)
          "#{build_columns(v, prefix)} AS #{k.to_s.sub(/^_/, '')}"
          # construct_as(build_columns(v, prefix), k)
        else
          build_columns(v, k.to_s)
        end
      end
    end
  when Symbol, String, Integer
    structure = structure.to_s
    if prefix && structure !~ /, |\.|\(/
      "#{prefix}.#{structure}"
    else
      structure
    end
  when Constructor
    "(#{build_sql structure.structure})"
  when true # choose everything -_-
    build_columns('*', prefix)
  else # TODO: values from value passing here... -_-
    structure
    # raise "Undefined class: #{structure.class} of #{structure}" # TODO: ERRORS class
  end
end
build_from(structure, _) click to toggle source
# File lib/terrazine/builders/clauses.rb, line 21
def build_from(structure, _)
  "FROM #{build_tables(structure)} "
end
build_join(structure, _) click to toggle source

TODO: -_-

# File lib/terrazine/builders/clauses.rb, line 26
def build_join(structure, _)
  if structure.is_a? Array
    # TODO: hash is sux here -_- !!!!!!
    if structure.second.is_a? Hash
      name = build_tables structure.first # (name.is_a?(Array) ? name.join(' ') : name)
      v = structure.second
      "#{v[:option].to_s.upcase + ' ' if v[:option]}JOIN #{name} ON #{build_predicates v[:on]} "
    else
      structure.map { |i| build_join(i, nil) }.join
    end
  else
    structure =~ /join/i ? structure : "JOIN #{structure} "
  end
end
build_limit(limit, _) click to toggle source
# File lib/terrazine/builders/clauses.rb, line 59
def build_limit(limit, _)
  "LIMIT #{limit || 8} "
end
build_offset(offset, _) click to toggle source
# File lib/terrazine/builders/clauses.rb, line 63
def build_offset(offset, _)
  "OFFSET #{offset || 0} "
end
build_operator(structure, prefix = nil) click to toggle source

now it doesnt use Predicates

# File lib/terrazine/builders/operators.rb, line 7
def build_operator(structure, prefix = nil)
  operator = structure.first.to_s.sub(/^_/, '')
  arguments = structure.drop(1)
  # puts operator
  send("operator_#{operator}", arguments, prefix)
end
build_order(structure, _) click to toggle source

TODO!

# File lib/terrazine/builders/clauses.rb, line 55
def build_order(structure, _)
  "ORDER BY #{construct_order structure} "
end
build_param(value) click to toggle source
# File lib/terrazine/builders/params.rb, line 6
def build_param(value)
  # no need for injections check - pg gem will check it
  @params << value
  "$#{@params.count}"
end
build_predicates(structure) click to toggle source

TODO? conditions like [:eq :name :Aeonax]

# File lib/terrazine/builders/predicates.rb, line 12
def build_predicates(structure)
  construct_condition(structure, true)
end
build_returning(structure, _) click to toggle source
# File lib/terrazine/builders/clauses.rb, line 46
def build_returning(structure, _)
  "RETURNING #{build_columns structure}"
end
build_select(structure, common_structure) click to toggle source
# File lib/terrazine/builders/clauses.rb, line 16
def build_select(structure, common_structure)
  distinct = construct_distinct common_structure[:distinct]
  "SELECT #{distinct}#{build_columns structure} "
end
build_sql(structure) click to toggle source

TODO: update, delete, insert, group.….

# File lib/terrazine/builder.rb, line 34
def build_sql(structure)
  structure = structure.is_a?(Constructor) ? structure.structure : structure
  sql = ''
  [:with, :union, :select, :insert, :update, :delete, :set, :from,
   :join, :where, :returning, :group, :order, :limit, :offset].each do |i|
     next unless structure[i]
     sql += send("build_#{i}", structure[i], structure)
   end
  sql
end
build_tables(structure) click to toggle source

TODO: split

# File lib/terrazine/builders/expressions.rb, line 10
def build_tables(structure)
  case structure
  when Array
    if check_alias(structure.first) # VALUES function or ...?
      build_operator(structure)
    # if it's a array with strings/values || array of tables/values
    else
      joiner = structure.select { |i| i.is_a? Array }.empty? ? ' ' : ', '
      structure.map { |i| build_tables i }.join joiner
    end
  when Hash
    "(#{build_sql structure})"
  when String, Symbol
    structure
  else
    raise "Undefined structure for FROM - #{structure}"
  end
end
build_union(structure, _) click to toggle source
# File lib/terrazine/builders/clauses.rb, line 12
def build_union(structure, _)
  structure.map { |i| build_sql(i) }.join ' UNION '
end
build_update(structure, _) click to toggle source

TODO!

# File lib/terrazine/builders/clauses.rb, line 42
def build_update(structure, _)
  "UPDATE #{construct_update structure} "
end
build_where(structure, _) click to toggle source
# File lib/terrazine/builders/clauses.rb, line 50
def build_where(structure, _)
  "WHERE #{build_predicates(structure)} "
end
build_with(structure, _) click to toggle source

TODO: :with_recursive

# File lib/terrazine/builders/clauses.rb, line 8
def build_with(structure, _)
  "WITH #{construct_with(structure)} "
end
check_alias(val) click to toggle source

all functions and column aliases begins from _

# File lib/terrazine/builder.rb, line 60
def check_alias(val)
  val.to_s =~ /^_/
end
condition_and(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 89
def condition_and(structure)
  conditions_joiner structure, 'and'
end
condition_between(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 127
def condition_between(structure)
  "BETWEEN #{construct_condition structure}"
end
condition_column(structure) click to toggle source

common

# File lib/terrazine/builders/predicates.rb, line 48
def condition_column(structure)
  structure.to_s.sub(/__/, '.')
end
condition_eq(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 116
def condition_eq(structure)
  case structure
  when Array
    conditions_construct_eq structure.first, structure.second
  when Hash
    iterate_hash(structure, false) { |k, v| conditions_construct_eq k, v }
  else
    raise "Undefinded structure: #{structure} for equality condition builder"
  end
end
condition_ilike(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 140
def condition_ilike(structure)
  condition_pattern structure, :ilike
end
condition_in(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 97
def condition_in(structure)
  values = case structure.second
           when Hash, Constructor
             build_sql structure.second
           else
             build_param structure.second
           end
  "#{construct_condition_value structure.first} IN (#{values})"
end
condition_is(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 112
def condition_is(structure)
  "#{construct_condition_value structure.first} IS #{construct_condition_value structure.second}"
end
condition_like(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 136
def condition_like(structure)
  condition_pattern structure, :like
end
condition_not(structure) click to toggle source

supporting eq if there is no array inside

# File lib/terrazine/builders/predicates.rb, line 76
def condition_not(structure)
  value = if structure.count == 1
            construct_condition structure.flatten(1)
          else
            condition_eq structure
          end
  "NOT #{value}"
end
condition_or(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 93
def condition_or(structure)
  conditions_joiner structure, 'or'
end
condition_pattern(structure, pattern) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 131
def condition_pattern(structure, pattern)
  "#{construct_condition_value structure.first} #{pattern.upcase} " \
  "#{construct_condition_value structure.second}"
end
condition_reg(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 144
def condition_reg(structure)
  condition_pattern structure, '~'
end
condition_reg_f(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 152
def condition_reg_f(structure)
  condition_pattern structure, '!~'
end
condition_reg_fi(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 156
def condition_reg_fi(structure)
  condition_pattern structure, '!~*'
end
condition_reg_i(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 148
def condition_reg_i(structure)
  condition_pattern structure, '~*'
end
conditions_construct_eq(column, value) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 107
def conditions_construct_eq(column, value)
  return condition_in([column, value]) if value.is_a? Array
  "#{construct_condition_value column} = #{construct_condition_value value}"
end
conditions_joiner(structure, joiner) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 85
def conditions_joiner(structure, joiner)
  structure.map { |i| construct_condition i }.flatten.join(" #{joiner} ".upcase)
end
construct_condition(structure, first_level = nil) click to toggle source
:or, { u__name: 'Aeonax', u__role: 'liar'}, # same as [[:eq, :u__name, 'Aeonax'], …
:not, [:in, :id, [1, 2, 531]]]
# File lib/terrazine/builders/predicates.rb, line 18
def construct_condition(structure, first_level = nil)
  case structure
  when Array
    key = structure.first
    return construct_condition(key) if structure.size < 2
    if key.is_a? Symbol
      parentizer send("condition_#{key}", structure.drop(1)), first_level, key
    elsif key.is_a?(String) && key =~ /\?/
      if [Hash, Constructor].include?(structure.second.class)
        key.sub(/\?/, "(#{build_sql(structure.second)})")
      else
        key.sub(/\?/, build_param(structure.second))
      end
    else
      parentizer condition_and(structure), first_level, :and
    end
  when Hash
    res = condition_eq structure
    first_level ? condition_and(res) : res
  when Symbol
    condition_column(structure)
  when String
    structure
  else
    raise "Unknow structure #{structure} class #{structure.class} for condition"
  end
end
construct_condition_value(structure) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 52
def construct_condition_value(structure)
  case structure
  when Symbol
    condition_column(structure)
  when TrueClass, FalseClass
    structure.to_s.upcase
  when nil
    'NULL'
  else
    build_param structure
  end
end
construct_distinct(structure) click to toggle source
# File lib/terrazine/builders/clauses.rb, line 82
def construct_distinct(structure)
  return unless structure
  if structure == true
    'DISTINCT '
  else
    "DISTINCT ON(#{build_columns structure}) "
  end
end
construct_order(structure) click to toggle source

{ name: :asc, email: [:desc, :last] }

:name, :email, { phone: :last }
# File lib/terrazine/builders/clauses.rb, line 117
def construct_order(structure)
  case structure
  when Array # function or values for order
    if check_alias structure.first
      build_operator structure
    else
      structure.map { |i| construct_order i }.join ', '
    end
  when Hash
    iterate_hash(structure) { |k, v| "#{construct_order k} #{construct_order_options v}" }
  else
    structure
  end
end
construct_order_options(option) click to toggle source
# File lib/terrazine/builders/clauses.rb, line 132
def construct_order_options(option)
  case option
  when Array
    option.sort.map { |i| construct_order_options i }.join ' '
  when :last, :first
    "nulls #{option}".upcase
  when :asc, :desc
    option.to_s.upcase
  else
    "USING#{option}"
  end
end
construct_set(structure) click to toggle source

TODO: (…, …) = (…, …)

# File lib/terrazine/builders/clauses.rb, line 104
def construct_set(structure)
  case structure
  when Hash
    iterate_hash(structure) { |k, v| "#{build_columns k} = #{build_columns v}" }
  when String
    structure
  else
    raise "Undefined structure for `UPDATE`: #{structure}"
  end
end
construct_update(structure) click to toggle source
# File lib/terrazine/builders/clauses.rb, line 91
def construct_update(structure)
  case structure
  when Array
    table = build_tables structure.first
    "#{table} SET #{construct_set structure.last}"
  when String
    structure
  else
    raise "Undefined structure for `UPDATE`: #{structure}"
  end
end
construct_with(structure) click to toggle source
# File lib/terrazine/builders/clauses.rb, line 67
def construct_with(structure)
  case structure
  when Array
    if structure.second.is_a? Hash
      "#{structure.first} AS (#{build_sql(structure.last)})"
    else
      structure.map { |v| construct_with(v) }.join ', '
    end
  when Hash
    iterate_hash(structure) { |k, v| "#{k} AS (#{build_sql v})" }
  else
    raise
  end
end
iterate_hash(data, join = true) { |k, v| ... } click to toggle source
# File lib/terrazine/builder.rb, line 64
def iterate_hash(data, join = true)
  iterations = []
  data.each { |k, v| iterations << yield(k, v) }
  join ? iterations.join(', ') : iterations
end
method_missing(name, *args) click to toggle source
Calls superclass method
# File lib/terrazine/builder.rb, line 45
def method_missing(name, *args)
  /(?<type>[^_]+)_(?<action>\w+)/ =~ name
  # arguments = [action]
  if type && respond_to?("#{type}_missing", true)
    send "#{type}_missing", action, *args
  else
    super
  end
end
operator_array(arguments, prefix) click to toggle source
# File lib/terrazine/builders/operators.rb, line 39
def operator_array(arguments, prefix)
  if [Hash, Constructor].include?(arguments.first.class)
    "ARRAY(#{build_sql arguments.first})"
  else # TODO? condition and error case
    "ARRAY[#{build_columns arguments, prefix}]"
  end
end
operator_avg(arguments, prefix) click to toggle source
# File lib/terrazine/builders/operators.rb, line 47
def operator_avg(arguments, prefix)
  "AVG(#{build_columns(arguments.first, prefix)})"
end
operator_case(arguments, _) click to toggle source
# File lib/terrazine/builders/operators.rb, line 57
def operator_case(arguments, _)
  else_val = "ELSE #{arguments.pop} " unless arguments.last.is_a? Array
  conditions = arguments.map { |i| "WHEN #{i.first} THEN #{i.last}" }.join ' '
  "CASE #{conditions} #{else_val}END"
end
operator_count(arguments, prefix) click to toggle source

without arguments smthng like this - “COUNT(#{prefix + '.'}*)”

# File lib/terrazine/builders/operators.rb, line 27
def operator_count(arguments, prefix)
  if arguments.count > 1
    arguments.map { |i| "COUNT(#{build_columns(i, prefix)})" }.join ', '
  else
    "COUNT(#{build_columns(arguments.first, prefix)})"
  end
end
operator_missing(name, arguments, prefix) click to toggle source
# File lib/terrazine/builders/operators.rb, line 14
def operator_missing(name, arguments, prefix)
  "#{name}(#{build_columns arguments, prefix})"
end
operator_nullif(arguments, prefix) click to toggle source
# File lib/terrazine/builders/operators.rb, line 35
def operator_nullif(arguments, prefix)
  "NULLIF(#{build_columns(arguments.first, prefix)}, #{arguments[1]})"
end
operator_params(arguments, _) click to toggle source
# File lib/terrazine/builders/operators.rb, line 18
def operator_params(arguments, _)
  if arguments.count > 1
    arguments.map { |i| build_param i }
  else
    build_param arguments.first
  end
end
operator_values(arguments, _) click to toggle source
# File lib/terrazine/builders/operators.rb, line 51
def operator_values(arguments, _)
  values = arguments.first.first.is_a?(Array) ? arguments.first : [arguments.first]
  values.map! { |i| "(#{build_columns i})" }
  "(VALUES#{values.join ', '}) AS #{arguments[1]} (#{build_columns arguments.last})"
end
parentizer(sql, first_level, key) click to toggle source
# File lib/terrazine/builders/predicates.rb, line 65
def parentizer(sql, first_level, key)
  if first_level || ![:or, :and].include?(key)
    sql
  else
    "(#{sql})"
  end
end
wrap_result(sql) click to toggle source
# File lib/terrazine/builders/params.rb, line 12
def wrap_result(sql)
  res = @params.count.positive? ? [sql, @params] : sql
  @params = []
  res
end