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
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 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
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
# File lib/terrazine/builders/clauses.rb, line 21 def build_from(structure, _) "FROM #{build_tables(structure)} " end
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
# File lib/terrazine/builders/clauses.rb, line 59 def build_limit(limit, _) "LIMIT #{limit || 8} " end
# File lib/terrazine/builders/clauses.rb, line 63 def build_offset(offset, _) "OFFSET #{offset || 0} " end
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
TODO!
# File lib/terrazine/builders/clauses.rb, line 55 def build_order(structure, _) "ORDER BY #{construct_order structure} " end
# 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
TODO? conditions like [:eq :name :Aeonax]
# File lib/terrazine/builders/predicates.rb, line 12 def build_predicates(structure) construct_condition(structure, true) end
# File lib/terrazine/builders/clauses.rb, line 46 def build_returning(structure, _) "RETURNING #{build_columns structure}" end
# 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
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
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
# File lib/terrazine/builders/clauses.rb, line 12 def build_union(structure, _) structure.map { |i| build_sql(i) }.join ' UNION ' end
TODO!
# File lib/terrazine/builders/clauses.rb, line 42 def build_update(structure, _) "UPDATE #{construct_update structure} " end
# File lib/terrazine/builders/clauses.rb, line 50 def build_where(structure, _) "WHERE #{build_predicates(structure)} " end
TODO: :with_recursive
# File lib/terrazine/builders/clauses.rb, line 8 def build_with(structure, _) "WITH #{construct_with(structure)} " end
all functions and column aliases begins from _
# File lib/terrazine/builder.rb, line 60 def check_alias(val) val.to_s =~ /^_/ end
# File lib/terrazine/builders/predicates.rb, line 89 def condition_and(structure) conditions_joiner structure, 'and' end
# File lib/terrazine/builders/predicates.rb, line 127 def condition_between(structure) "BETWEEN #{construct_condition structure}" end
common
# File lib/terrazine/builders/predicates.rb, line 48 def condition_column(structure) structure.to_s.sub(/__/, '.') end
# 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
# File lib/terrazine/builders/predicates.rb, line 140 def condition_ilike(structure) condition_pattern structure, :ilike end
# 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
# File lib/terrazine/builders/predicates.rb, line 112 def condition_is(structure) "#{construct_condition_value structure.first} IS #{construct_condition_value structure.second}" end
# File lib/terrazine/builders/predicates.rb, line 136 def condition_like(structure) condition_pattern structure, :like end
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
# File lib/terrazine/builders/predicates.rb, line 93 def condition_or(structure) conditions_joiner structure, 'or' end
# 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
# File lib/terrazine/builders/predicates.rb, line 144 def condition_reg(structure) condition_pattern structure, '~' end
# File lib/terrazine/builders/predicates.rb, line 152 def condition_reg_f(structure) condition_pattern structure, '!~' end
# File lib/terrazine/builders/predicates.rb, line 156 def condition_reg_fi(structure) condition_pattern structure, '!~*' end
# File lib/terrazine/builders/predicates.rb, line 148 def condition_reg_i(structure) condition_pattern structure, '~*' end
# 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
# File lib/terrazine/builders/predicates.rb, line 85 def conditions_joiner(structure, joiner) structure.map { |i| construct_condition i }.flatten.join(" #{joiner} ".upcase) end
- :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
# 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
# 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
{ 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
# 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
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
# 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
# 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
# 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
# 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
# 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
# File lib/terrazine/builders/operators.rb, line 47 def operator_avg(arguments, prefix) "AVG(#{build_columns(arguments.first, prefix)})" end
# 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
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
# File lib/terrazine/builders/operators.rb, line 14 def operator_missing(name, arguments, prefix) "#{name}(#{build_columns arguments, prefix})" end
# File lib/terrazine/builders/operators.rb, line 35 def operator_nullif(arguments, prefix) "NULLIF(#{build_columns(arguments.first, prefix)}, #{arguments[1]})" end
# 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
# 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
# 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
# File lib/terrazine/builders/params.rb, line 12 def wrap_result(sql) res = @params.count.positive? ? [sql, @params] : sql @params = [] res end