qdsl 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/and.rb +6 -2
- data/lib/boolean.rb +5 -2
- data/lib/call.rb +28 -0
- data/lib/column.rb +14 -2
- data/lib/{funky_proxy.rb → column_proxy.rb} +10 -2
- data/lib/column_set.rb +25 -0
- data/lib/context.rb +6 -8
- data/lib/equals.rb +5 -8
- data/lib/expression.rb +14 -6
- data/lib/format_call.rb +37 -0
- data/lib/inner_join.rb +3 -3
- data/lib/inner_join_builder.rb +4 -5
- data/lib/is_true.rb +17 -0
- data/lib/or.rb +8 -2
- data/lib/qdsl/version.rb +1 -1
- data/lib/qdsl.rb +4 -18
- data/lib/render_result.rb +13 -0
- data/lib/select.rb +86 -53
- data/lib/select_query.rb +30 -0
- data/lib/simple_render_result.rb +11 -0
- data/lib/table_query.rb +1 -1
- data/lib/util.rb +8 -0
- data/test/column_test.rb +19 -8
- data/test/end_to_end_test.rb +8 -1
- data/test/expression_test.rb +72 -56
- data/test/inner_join_test.rb +44 -16
- data/test/is_true_test.rb +68 -0
- data/test/minitest_helper.rb +2 -0
- data/test/query_test.rb +1 -0
- data/test/select_test.rb +89 -15
- data/test/subquery_test.rb +177 -29
- data/test/where_test.rb +9 -7
- metadata +13 -5
- data/test/context_test.rb +0 -19
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 3cfa45077d918ab12b58525ffdc20fda82587730
         | 
| 4 | 
            +
              data.tar.gz: f52f010312f89176572fbfb903353a005a830254
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: e85a49678f55f66921aad11e26f73e259674befb8fa970b9734429fdf4b90f7dfef524791181190ecfa05b0b0a15f104a5ea0b4c1778653c42a338b211f64edc
         | 
| 7 | 
            +
              data.tar.gz: 338110b7b6397848979cdd07323b0b28fbaa3ce86d4da6667276fe621bd12c7a5cbbe82871a06fee87dbfa377a08f6cdbc670caefeb1c53dc062d2b08eb4b1c1
         | 
    
        data/lib/and.rb
    CHANGED
    
    
    
        data/lib/boolean.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            require_relative 'expression'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Qdsl
         | 
| 2 4 | 
             
              class Boolean < Expression
         | 
| 3 5 | 
             
                def initialize(operator, exprs)
         | 
| @@ -5,9 +7,10 @@ module Qdsl | |
| 5 7 | 
             
                  @exprs = exprs
         | 
| 6 8 | 
             
                end
         | 
| 7 9 |  | 
| 8 | 
            -
                def  | 
| 10 | 
            +
                def render_sql(context, ids)
         | 
| 9 11 | 
             
                  expr_results = @exprs.collect { |x| render_operand(context, ids, x) }
         | 
| 10 | 
            -
                  expr_results. | 
| 12 | 
            +
                  parameters = expr_results.inject({}) { |acc, x| acc.merge(x.parameters) }
         | 
| 13 | 
            +
                  SimpleRenderResult.new(expr_results.collect { |x| "(#{x.sql})" }.join(" #{@operator} "), parameters)
         | 
| 11 14 | 
             
                end
         | 
| 12 15 | 
             
              end
         | 
| 13 16 | 
             
            end
         | 
    
        data/lib/call.rb
    ADDED
    
    | @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            module Qdsl
         | 
| 2 | 
            +
              class Call
         | 
| 3 | 
            +
                attr_reader :source, :name
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(source, function_name, parameters, name = nil)
         | 
| 6 | 
            +
                  @source = source
         | 
| 7 | 
            +
                  @function_name = function_name
         | 
| 8 | 
            +
                  @parameters = parameters
         | 
| 9 | 
            +
                  @name = name ? name.to_s : nil
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def as(name)
         | 
| 13 | 
            +
                  Call.new(@source, @function_name, @parameters, name)
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def exists?
         | 
| 17 | 
            +
                  @parameters.all? { |x| x.exists? }
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def render_sql(context, id)
         | 
| 21 | 
            +
                  parameter_results = @parameters.collect { |x| x.render_sql(context, id) }
         | 
| 22 | 
            +
                  query_parameters = parameter_results.collect(&:parameters).inject({}) { |acc, x| acc.merge!(x) }
         | 
| 23 | 
            +
                  call = "#{@function_name}(#{parameter_results.collect(&:sql).join(', ')})"
         | 
| 24 | 
            +
                  SimpleRenderResult.new(@name ? "#{call} AS #{@name}" : call, query_parameters)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
| 28 | 
            +
             | 
    
        data/lib/column.rb
    CHANGED
    
    | @@ -31,8 +31,20 @@ module Qdsl | |
| 31 31 | 
             
                  Column.new(@name, @alias_name, source)
         | 
| 32 32 | 
             
                end
         | 
| 33 33 |  | 
| 34 | 
            -
                def  | 
| 35 | 
            -
                  @ | 
| 34 | 
            +
                def exists?
         | 
| 35 | 
            +
                  @source.column_names.include?(@name)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                def to_expression
         | 
| 39 | 
            +
                  true?
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def true?
         | 
| 43 | 
            +
                  IsTrue.new(self)
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                def render_sql(context, ids)
         | 
| 47 | 
            +
                  SimpleRenderResult.new(@alias_name ? "#{@source.id}.#{@name} AS #{@alias_name}" : "#{@source.id}.#{@name}", {})
         | 
| 36 48 | 
             
                end
         | 
| 37 49 |  | 
| 38 50 | 
             
                def equals(column)
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            module Qdsl
         | 
| 2 | 
            -
              class  | 
| 2 | 
            +
              class ColumnProxy
         | 
| 3 3 | 
             
                def initialize(source)
         | 
| 4 4 | 
             
                  @source = source
         | 
| 5 5 | 
             
                  source.column_names.each do |column|
         | 
| @@ -12,9 +12,17 @@ module Qdsl | |
| 12 12 | 
             
                end
         | 
| 13 13 |  | 
| 14 14 | 
             
                def [](name)
         | 
| 15 | 
            -
                  raise unless @source.column_names.include?(name.to_s)
         | 
| 15 | 
            +
                  raise "Unknown column \"#{name}\"" unless @source.column_names.include?(name.to_s)
         | 
| 16 16 | 
             
                  Column.new(name.to_s, nil, @source)
         | 
| 17 17 | 
             
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def _call(function_name, *parameters)
         | 
| 20 | 
            +
                  Call.new(@source, function_name, parameters)
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                def _formatcall(format, *parameters)
         | 
| 24 | 
            +
                  FormatCall.new(@source, format, parameters)
         | 
| 25 | 
            +
                end
         | 
| 18 26 | 
             
              end
         | 
| 19 27 | 
             
            end
         | 
| 20 28 |  | 
    
        data/lib/column_set.rb
    ADDED
    
    | @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            module Qdsl
         | 
| 2 | 
            +
              class ColumnSet
         | 
| 3 | 
            +
                attr_reader :columns, :block
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(columns, block)
         | 
| 6 | 
            +
                  @columns = columns.collect { |x| Column[x] }
         | 
| 7 | 
            +
                  @block = block
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def capture(source, join_sources)
         | 
| 11 | 
            +
                  sources = [source] + join_sources
         | 
| 12 | 
            +
                  columns = @columns.collect { |x| x.with_source(source) } + if @block
         | 
| 13 | 
            +
                    proxies = sources.collect { |x| ColumnProxy.new(x) }
         | 
| 14 | 
            +
                    [*@block.call(Util::fix_block_params(proxies))]
         | 
| 15 | 
            +
                  else
         | 
| 16 | 
            +
                    []
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  undefined_columns = columns.select { |x| !x.exists? }
         | 
| 20 | 
            +
                  raise "One or more undefined columns: #{undefined_columns.collect(&:name).join(', ')}" unless undefined_columns.empty?
         | 
| 21 | 
            +
                  columns
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
              end
         | 
| 24 | 
            +
            end
         | 
| 25 | 
            +
             | 
    
        data/lib/context.rb
    CHANGED
    
    | @@ -1,22 +1,20 @@ | |
| 1 1 | 
             
            module Qdsl
         | 
| 2 2 | 
             
              class Context
         | 
| 3 | 
            -
                attr_reader :parameters
         | 
| 4 | 
            -
             | 
| 5 3 | 
             
                def initialize
         | 
| 6 4 | 
             
                  @id_base = 0
         | 
| 7 | 
            -
                  @ | 
| 5 | 
            +
                  @parameter_id_base = 0
         | 
| 8 6 | 
             
                end
         | 
| 9 7 |  | 
| 10 | 
            -
                def  | 
| 8 | 
            +
                def create_id
         | 
| 11 9 | 
             
                  result = "_#{@id_base.to_s.rjust(2, '0')}"
         | 
| 12 10 | 
             
                  @id_base += 1
         | 
| 13 11 | 
             
                  result
         | 
| 14 12 | 
             
                end
         | 
| 15 13 |  | 
| 16 | 
            -
                def  | 
| 17 | 
            -
                   | 
| 18 | 
            -
                  @ | 
| 19 | 
            -
                   | 
| 14 | 
            +
                def create_parameter_id
         | 
| 15 | 
            +
                  result = "_param#{@parameter_id_base.to_s.rjust(2, '0')}"
         | 
| 16 | 
            +
                  @parameter_id_base += 1
         | 
| 17 | 
            +
                  result
         | 
| 20 18 | 
             
                end
         | 
| 21 19 | 
             
              end
         | 
| 22 20 | 
             
            end
         | 
    
        data/lib/equals.rb
    CHANGED
    
    | @@ -5,18 +5,15 @@ module Qdsl | |
| 5 5 | 
             
                  @column1 = column1
         | 
| 6 6 | 
             
                end
         | 
| 7 7 |  | 
| 8 | 
            -
                def  | 
| 9 | 
            -
                   | 
| 8 | 
            +
                def to_expression
         | 
| 9 | 
            +
                  self
         | 
| 10 10 | 
             
                end
         | 
| 11 11 |  | 
| 12 | 
            -
                def  | 
| 13 | 
            -
                  Or.new([self, expr])
         | 
| 14 | 
            -
                end
         | 
| 15 | 
            -
             | 
| 16 | 
            -
                def render(context, ids)
         | 
| 12 | 
            +
                def render_sql(context, ids)
         | 
| 17 13 | 
             
                  column0_result = render_operand(context, ids, @column0)
         | 
| 18 14 | 
             
                  column1_result = render_operand(context, ids, @column1)
         | 
| 19 | 
            -
                   | 
| 15 | 
            +
                  parameters = column0_result.parameters.merge(column1_result.parameters)
         | 
| 16 | 
            +
                  SimpleRenderResult.new("#{column0_result.sql} = #{column1_result.sql}", parameters)
         | 
| 20 17 | 
             
                end
         | 
| 21 18 | 
             
              end
         | 
| 22 19 | 
             
            end
         | 
    
        data/lib/expression.rb
    CHANGED
    
    | @@ -1,19 +1,27 @@ | |
| 1 1 | 
             
            module Qdsl
         | 
| 2 2 | 
             
              class Expression
         | 
| 3 | 
            +
                def and(expr)
         | 
| 4 | 
            +
                  And.new([self, expr])
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def or(expr)
         | 
| 8 | 
            +
                  Or.new([self, expr])
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 3 11 | 
             
                protected
         | 
| 4 12 |  | 
| 5 13 | 
             
                def render_operand(context, ids, operand)
         | 
| 6 14 | 
             
                  if operand.is_a?(String)
         | 
| 7 | 
            -
                    parameter_id = context. | 
| 8 | 
            -
                    ":#{parameter_id}"
         | 
| 15 | 
            +
                    parameter_id = context.create_parameter_id
         | 
| 16 | 
            +
                    SimpleRenderResult.new(":#{parameter_id}", {parameter_id => operand})
         | 
| 9 17 | 
             
                  elsif operand.is_a?(TrueClass)
         | 
| 10 | 
            -
                    'TRUE'
         | 
| 18 | 
            +
                    SimpleRenderResult.new('TRUE', {})
         | 
| 11 19 | 
             
                  elsif operand.is_a?(FalseClass)
         | 
| 12 | 
            -
                    'FALSE'
         | 
| 20 | 
            +
                    SimpleRenderResult.new('FALSE', {})
         | 
| 13 21 | 
             
                  elsif operand.is_a?(Expression)
         | 
| 14 | 
            -
                    operand. | 
| 22 | 
            +
                    operand.render_sql(context, ids)
         | 
| 15 23 | 
             
                  else
         | 
| 16 | 
            -
                    operand. | 
| 24 | 
            +
                    operand.render_sql(context, ids[operand.source])
         | 
| 17 25 | 
             
                  end
         | 
| 18 26 | 
             
                end
         | 
| 19 27 | 
             
              end
         | 
    
        data/lib/format_call.rb
    ADDED
    
    | @@ -0,0 +1,37 @@ | |
| 1 | 
            +
            module Qdsl
         | 
| 2 | 
            +
              class FormatCall
         | 
| 3 | 
            +
                attr_reader :source, :name
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(source, format, parameters, name = nil)
         | 
| 6 | 
            +
                  @source = source
         | 
| 7 | 
            +
                  @format = format
         | 
| 8 | 
            +
                  @parameters = parameters
         | 
| 9 | 
            +
                  @name = name.to_s
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def as(name)
         | 
| 13 | 
            +
                  FormatCall.new(@source, @format, @parameters, name)
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def exists?
         | 
| 17 | 
            +
                  @parameters.all? { |x| x.exists? }
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def render_sql(context, id)
         | 
| 21 | 
            +
                  parameter_results = @parameters.collect { |x| x.render_sql(context, id) }
         | 
| 22 | 
            +
                  query_parameters = parameter_results.collect(&:parameters).inject({}) { |acc, x| acc.merge!(x) }
         | 
| 23 | 
            +
                  call = do_format(@format, parameter_results.collect(&:sql))
         | 
| 24 | 
            +
                  SimpleRenderResult.new(@name ? "#{call} AS #{@name}" : call, query_parameters)
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                private
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                def do_format(format, parameters)
         | 
| 30 | 
            +
                  format.gsub /%(\d+)/ do |m, x|
         | 
| 31 | 
            +
                    index = Integer(Regexp.last_match[1]) - 1
         | 
| 32 | 
            +
                    parameters[index]
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| 37 | 
            +
             | 
    
        data/lib/inner_join.rb
    CHANGED
    
    | @@ -1,10 +1,10 @@ | |
| 1 1 | 
             
            module Qdsl
         | 
| 2 2 | 
             
              class InnerJoin
         | 
| 3 | 
            -
                attr_reader :source, : | 
| 3 | 
            +
                attr_reader :source, :predicate_block
         | 
| 4 4 |  | 
| 5 | 
            -
                def initialize(source,  | 
| 5 | 
            +
                def initialize(source, predicate_block)
         | 
| 6 6 | 
             
                  @source = source
         | 
| 7 | 
            -
                  @ | 
| 7 | 
            +
                  @predicate_block = predicate_block
         | 
| 8 8 | 
             
                end
         | 
| 9 9 | 
             
              end
         | 
| 10 10 | 
             
            end
         | 
    
        data/lib/inner_join_builder.rb
    CHANGED
    
    | @@ -1,13 +1,12 @@ | |
| 1 1 | 
             
            module Qdsl
         | 
| 2 2 | 
             
              class InnerJoinBuilder
         | 
| 3 | 
            -
                def initialize( | 
| 4 | 
            -
                  @ | 
| 3 | 
            +
                def initialize(select, source)
         | 
| 4 | 
            +
                  @select = select
         | 
| 5 5 | 
             
                  @source = source
         | 
| 6 6 | 
             
                end
         | 
| 7 7 |  | 
| 8 | 
            -
                def on
         | 
| 9 | 
            -
                   | 
| 10 | 
            -
                  @query_builder.add_inner_join(@source, predicate)
         | 
| 8 | 
            +
                def on(&predicate_block)
         | 
| 9 | 
            +
                  @select.add_inner_join(@source, predicate_block)
         | 
| 11 10 | 
             
                end
         | 
| 12 11 | 
             
              end
         | 
| 13 12 | 
             
            end
         | 
    
        data/lib/is_true.rb
    ADDED
    
    | @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            module Qdsl
         | 
| 2 | 
            +
              class IsTrue < Expression
         | 
| 3 | 
            +
                def initialize(column)
         | 
| 4 | 
            +
                  @column = column
         | 
| 5 | 
            +
                end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def to_expression
         | 
| 8 | 
            +
                  self
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def render_sql(context, ids)
         | 
| 12 | 
            +
                  column_result = render_operand(context, ids, @column)
         | 
| 13 | 
            +
                  column_result
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
| 17 | 
            +
             | 
    
        data/lib/or.rb
    CHANGED
    
    | @@ -1,16 +1,22 @@ | |
| 1 | 
            +
            require_relative 'boolean'
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Qdsl
         | 
| 2 4 | 
             
              class Or < Boolean
         | 
| 3 5 | 
             
                def initialize(exprs)
         | 
| 4 6 | 
             
                  super 'OR', exprs
         | 
| 5 7 | 
             
                end
         | 
| 6 8 |  | 
| 7 | 
            -
                def  | 
| 8 | 
            -
                   | 
| 9 | 
            +
                def to_expression
         | 
| 10 | 
            +
                  self
         | 
| 9 11 | 
             
                end
         | 
| 10 12 |  | 
| 11 13 | 
             
                def or(expr)
         | 
| 12 14 | 
             
                  Or.new(@exprs + [expr])
         | 
| 13 15 | 
             
                end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                def true?
         | 
| 18 | 
            +
                  IsTrue.new(self)
         | 
| 19 | 
            +
                end
         | 
| 14 20 | 
             
              end
         | 
| 15 21 | 
             
            end
         | 
| 16 22 |  | 
    
        data/lib/qdsl/version.rb
    CHANGED
    
    
    
        data/lib/qdsl.rb
    CHANGED
    
    | @@ -1,27 +1,13 @@ | |
| 1 1 | 
             
            require 'qdsl/version'
         | 
| 2 | 
            +
            require 'byebug'
         | 
| 2 3 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
               | 
| 5 | 
            -
              boolean
         | 
| 6 | 
            -
              and
         | 
| 7 | 
            -
              column
         | 
| 8 | 
            -
              context
         | 
| 9 | 
            -
              equals
         | 
| 10 | 
            -
              funky_proxy
         | 
| 11 | 
            -
              inner_join
         | 
| 12 | 
            -
              inner_join_builder
         | 
| 13 | 
            -
              or
         | 
| 14 | 
            -
              qdsl
         | 
| 15 | 
            -
              select
         | 
| 16 | 
            -
              table
         | 
| 17 | 
            -
              table_query
         | 
| 18 | 
            -
            }.each do |file_name|
         | 
| 19 | 
            -
              require_relative file_name
         | 
| 4 | 
            +
            Dir.glob("#{File.dirname(__FILE__)}/*.rb").each do |file_name|
         | 
| 5 | 
            +
              require file_name
         | 
| 20 6 | 
             
            end
         | 
| 21 7 |  | 
| 22 8 | 
             
            module Qdsl
         | 
| 23 9 | 
             
              def self.select(*columns, &block)
         | 
| 24 | 
            -
                Select.new(columns | 
| 10 | 
            +
                Select.new(ColumnSet.new(columns, block))
         | 
| 25 11 | 
             
              end
         | 
| 26 12 | 
             
            end
         | 
| 27 13 |  | 
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            module Qdsl
         | 
| 2 | 
            +
              class RenderResult < SimpleRenderResult
         | 
| 3 | 
            +
                attr_reader :id, :column_names, :parameters
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(id, sql, column_names, parameters)
         | 
| 6 | 
            +
                  super sql, parameters
         | 
| 7 | 
            +
                  @id = id
         | 
| 8 | 
            +
                  @column_names = column_names
         | 
| 9 | 
            +
                  @parameters = parameters
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
            end
         | 
| 13 | 
            +
             | 
    
        data/lib/select.rb
    CHANGED
    
    | @@ -1,94 +1,127 @@ | |
| 1 1 | 
             
            module Qdsl
         | 
| 2 | 
            -
              class SelectQuery
         | 
| 3 | 
            -
                attr_reader :column_names
         | 
| 4 | 
            -
             | 
| 5 | 
            -
                def initialize(select, column_names)
         | 
| 6 | 
            -
                  @select = select
         | 
| 7 | 
            -
                  @column_names = column_names
         | 
| 8 | 
            -
                end
         | 
| 9 | 
            -
             | 
| 10 | 
            -
                def render(context, depth, id)
         | 
| 11 | 
            -
                  select_result = @select.render(context, depth)
         | 
| 12 | 
            -
                  "(\n#{select_result}) AS #{id}"
         | 
| 13 | 
            -
                end
         | 
| 14 | 
            -
              end
         | 
| 15 | 
            -
             | 
| 16 2 | 
             
              class Select
         | 
| 17 3 | 
             
                attr_reader :source
         | 
| 18 4 |  | 
| 19 | 
            -
                def initialize( | 
| 20 | 
            -
                  @ | 
| 21 | 
            -
                  @block = block
         | 
| 5 | 
            +
                def initialize(column_set)
         | 
| 6 | 
            +
                  @column_set = column_set
         | 
| 22 7 | 
             
                  @inner_joins = []
         | 
| 23 8 | 
             
                end
         | 
| 24 9 |  | 
| 25 10 | 
             
                def from(source)
         | 
| 26 | 
            -
                  @source = source | 
| 11 | 
            +
                  @source = source
         | 
| 27 12 | 
             
                  self
         | 
| 28 13 | 
             
                end
         | 
| 29 14 |  | 
| 30 | 
            -
            def  | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 15 | 
            +
                def where(&predicate_block)
         | 
| 16 | 
            +
                  @where_predicate_block = predicate_block
         | 
| 17 | 
            +
                  self
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def order_by(*columns, &block)
         | 
| 21 | 
            +
                  @order_by_column_set = ColumnSet.new(columns, block)
         | 
| 22 | 
            +
                  self
         | 
| 23 | 
            +
                end
         | 
| 35 24 |  | 
| 36 | 
            -
                def  | 
| 37 | 
            -
                   | 
| 38 | 
            -
                  proxies = sources.collect { |x| FunkyProxy.new(x) }
         | 
| 39 | 
            -
                  @where = yield(proxies.size == 1 ? proxies.first : proxies)
         | 
| 25 | 
            +
                def limit(count)
         | 
| 26 | 
            +
                  @count = count
         | 
| 40 27 | 
             
                  self
         | 
| 41 28 | 
             
                end
         | 
| 42 29 |  | 
| 43 30 | 
             
                def inner_join(source)
         | 
| 44 | 
            -
                  InnerJoinBuilder.new(self, source | 
| 31 | 
            +
                  InnerJoinBuilder.new(self, source)
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def create_query
         | 
| 35 | 
            +
                  SelectQuery.new(self, @column_set)
         | 
| 45 36 | 
             
                end
         | 
| 46 37 |  | 
| 47 | 
            -
                def add_inner_join(source,  | 
| 48 | 
            -
                  @inner_joins << InnerJoin.new(source,  | 
| 38 | 
            +
                def add_inner_join(source, predicate_block)
         | 
| 39 | 
            +
                  @inner_joins << InnerJoin.new(source, predicate_block)
         | 
| 49 40 | 
             
                  self
         | 
| 50 41 | 
             
                end
         | 
| 51 42 |  | 
| 52 | 
            -
                def render(context = nil, depth = 0)
         | 
| 53 | 
            -
                   | 
| 43 | 
            +
                def render(context = nil, depth = 0, id = nil)
         | 
| 44 | 
            +
                  raise 'No FROM clause was specified' unless @source
         | 
| 54 45 |  | 
| 55 46 | 
             
                  indent = '  ' * depth
         | 
| 56 47 |  | 
| 57 | 
            -
                   | 
| 58 | 
            -
                  raise "One or more unknown columns: #{unknown_columns.collect(&:name).join(', ')}" unless unknown_columns.empty?
         | 
| 48 | 
            +
                  context ||= Context.new
         | 
| 59 49 |  | 
| 60 | 
            -
                   | 
| 50 | 
            +
                  parameters = {}
         | 
| 61 51 |  | 
| 62 | 
            -
                   | 
| 52 | 
            +
                  source_query = @source.create_query
         | 
| 53 | 
            +
                  source_query_id = context.create_id
         | 
| 54 | 
            +
                  source_result = source_query.render(context, depth + 1, source_query_id)
         | 
| 63 55 |  | 
| 64 | 
            -
                   | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
                     | 
| 56 | 
            +
                  inner_join_fragments = []
         | 
| 57 | 
            +
                  inner_join_results = []
         | 
| 58 | 
            +
                  @inner_joins.each do |inner_join|
         | 
| 59 | 
            +
                    inner_join_query = inner_join.source.create_query
         | 
| 60 | 
            +
                    inner_join_query_id = context.create_id
         | 
| 61 | 
            +
                    inner_join_result = inner_join_query.render(context, depth + 1, inner_join_query_id)
         | 
| 62 | 
            +
                    inner_join_results << inner_join_result
         | 
| 63 | 
            +
                    x = ColumnProxy.new(source_result)
         | 
| 64 | 
            +
                    y = ColumnProxy.new(inner_join_result)
         | 
| 65 | 
            +
                    predicate = inner_join.predicate_block.call(x, y)
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    # Should just use id attribute instead of passing dictionary!
         | 
| 68 | 
            +
                    bah = {
         | 
| 69 | 
            +
                      source_result => source_result.id,
         | 
| 70 | 
            +
                      inner_join_result => inner_join_result.id,
         | 
| 71 | 
            +
                    }
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                    predicate_result = predicate.render_sql(context, bah)
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    inner_join_fragments << "#{indent}INNER JOIN #{inner_join_result.sql}"
         | 
| 76 | 
            +
                    inner_join_fragments << "#{indent}ON #{predicate_result.sql}\n"
         | 
| 69 77 | 
             
                  end
         | 
| 70 78 |  | 
| 71 | 
            -
                   | 
| 79 | 
            +
                  columns = @column_set.capture(source_result, inner_join_results)
         | 
| 72 80 |  | 
| 73 | 
            -
                  column_results =  | 
| 81 | 
            +
                  column_results = columns.collect { |x| x.render_sql(context, x.source.id) }
         | 
| 74 82 |  | 
| 75 83 | 
             
                  fragments = []
         | 
| 76 84 |  | 
| 77 | 
            -
                  fragments << "#{indent}SELECT | 
| 78 | 
            -
                   | 
| 85 | 
            +
                  fragments << "#{indent}SELECT\n"
         | 
| 86 | 
            +
                  column_results.each_with_index do |column_result, index|
         | 
| 87 | 
            +
                    fragments << "#{indent}  #{column_result.sql}#{index < column_results.size - 1 ? ',' : ''}\n"
         | 
| 88 | 
            +
                  end
         | 
| 79 89 |  | 
| 80 | 
            -
                   | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 90 | 
            +
                  fragments << "#{indent}FROM #{source_result.sql}"
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  fragments += inner_join_fragments
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                  if @where_predicate_block
         | 
| 95 | 
            +
                    blah = [source_result] + inner_join_results
         | 
| 96 | 
            +
                    proxies = blah.collect { |x| ColumnProxy.new(x) }
         | 
| 97 | 
            +
                    where_predicate = @where_predicate_block.call(Util::fix_block_params(proxies))
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                    # Should just use id attribute instead of passing dictionary!
         | 
| 100 | 
            +
                    bah = blah.inject({}) { |acc, x| acc[x] = x.id; acc }
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                    where_predicate_result = where_predicate.render_sql(context, bah)
         | 
| 103 | 
            +
                    parameters.merge!(where_predicate_result.parameters)
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    fragments << "#{indent}WHERE #{where_predicate_result.sql}\n"
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                  if @order_by_column_set
         | 
| 109 | 
            +
                    order_by_columns = @order_by_column_set.capture(source_result, inner_join_results)
         | 
| 110 | 
            +
                    order_by_column_results = order_by_columns.collect { |x| x.render_sql(context, x.source.id) }
         | 
| 111 | 
            +
                    fragments << "#{indent}ORDER BY #{order_by_column_results.collect(&:sql).join(', ')}\n"
         | 
| 84 112 | 
             
                  end
         | 
| 85 113 |  | 
| 86 | 
            -
                  if @ | 
| 87 | 
            -
                     | 
| 88 | 
            -
                    fragments << "#{indent}WHERE #{predicate_result}"
         | 
| 114 | 
            +
                  if @count
         | 
| 115 | 
            +
                    fragments << "LIMIT #{@count}"
         | 
| 89 116 | 
             
                  end
         | 
| 90 117 |  | 
| 91 | 
            -
                  fragments.join
         | 
| 118 | 
            +
                  sql = fragments.join
         | 
| 119 | 
            +
                  RenderResult.new(
         | 
| 120 | 
            +
                    id,
         | 
| 121 | 
            +
                    sql,
         | 
| 122 | 
            +
                    columns.collect(&:name),
         | 
| 123 | 
            +
                    parameters
         | 
| 124 | 
            +
                  )
         | 
| 92 125 | 
             
                end
         | 
| 93 126 | 
             
              end
         | 
| 94 127 | 
             
            end
         | 
    
        data/lib/select_query.rb
    ADDED
    
    | @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            module Qdsl
         | 
| 2 | 
            +
              class SelectQuery
         | 
| 3 | 
            +
                def initialize(select, column_set)
         | 
| 4 | 
            +
                  @select = select
         | 
| 5 | 
            +
                  @column_set = column_set
         | 
| 6 | 
            +
                end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                def render(context, depth, id)
         | 
| 9 | 
            +
                  indent = '  ' * depth
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  columns = @column_set.capture(@select.source, [])
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  select_result = @select.render(context, depth)
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  fragments = []
         | 
| 16 | 
            +
                  fragments << "(\n"
         | 
| 17 | 
            +
                  fragments << select_result.sql
         | 
| 18 | 
            +
                  fragments << ") AS #{id}\n"
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  sql = fragments.join
         | 
| 21 | 
            +
                  RenderResult.new(
         | 
| 22 | 
            +
                    id,
         | 
| 23 | 
            +
                    sql,
         | 
| 24 | 
            +
                    columns.collect(&:name),
         | 
| 25 | 
            +
                    {}
         | 
| 26 | 
            +
                  )
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
            end
         | 
| 30 | 
            +
             | 
    
        data/lib/table_query.rb
    CHANGED