safrano 0.4.1 → 0.4.6
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/core_ext/Dir/iter.rb +18 -0
- data/lib/core_ext/Hash/transform.rb +21 -0
- data/lib/core_ext/Integer/edm.rb +13 -0
- data/lib/core_ext/REXML/Document/output.rb +16 -0
- data/lib/core_ext/String/convert.rb +25 -0
- data/lib/core_ext/String/edm.rb +13 -0
- data/lib/core_ext/dir.rb +3 -0
- data/lib/core_ext/hash.rb +3 -0
- data/lib/core_ext/integer.rb +3 -0
- data/lib/core_ext/rexml.rb +3 -0
- data/lib/core_ext/string.rb +5 -0
- data/lib/odata/attribute.rb +15 -10
- data/lib/odata/batch.rb +15 -13
- data/lib/odata/collection.rb +144 -535
- data/lib/odata/collection_filter.rb +47 -40
- data/lib/odata/collection_media.rb +155 -99
- data/lib/odata/collection_order.rb +50 -37
- data/lib/odata/common_logger.rb +36 -34
- data/lib/odata/complex_type.rb +152 -0
- data/lib/odata/edm/primitive_types.rb +184 -0
- data/lib/odata/entity.rb +183 -216
- data/lib/odata/error.rb +195 -31
- data/lib/odata/expand.rb +126 -0
- data/lib/odata/filter/base.rb +74 -0
- data/lib/odata/filter/error.rb +49 -6
- data/lib/odata/filter/parse.rb +44 -36
- data/lib/odata/filter/sequel.rb +136 -67
- data/lib/odata/filter/sequel_function_adapter.rb +148 -0
- data/lib/odata/filter/token.rb +26 -19
- data/lib/odata/filter/tree.rb +113 -63
- data/lib/odata/function_import.rb +168 -0
- data/lib/odata/model_ext.rb +639 -0
- data/lib/odata/navigation_attribute.rb +44 -61
- data/lib/odata/relations.rb +5 -5
- data/lib/odata/select.rb +54 -0
- data/lib/odata/transition.rb +71 -0
- data/lib/odata/url_parameters.rb +128 -37
- data/lib/odata/walker.rb +20 -10
- data/lib/safrano.rb +17 -37
- data/lib/safrano/contract.rb +143 -0
- data/lib/safrano/core.rb +29 -104
- data/lib/safrano/core_ext.rb +13 -0
- data/lib/safrano/deprecation.rb +73 -0
- data/lib/safrano/multipart.rb +39 -43
- data/lib/safrano/rack_app.rb +68 -67
- data/lib/safrano/{odata_rack_builder.rb → rack_builder.rb} +18 -2
- data/lib/safrano/request.rb +102 -51
- data/lib/safrano/response.rb +5 -3
- data/lib/safrano/sequel_join_by_paths.rb +2 -2
- data/lib/safrano/service.rb +274 -219
- data/lib/safrano/version.rb +3 -1
- data/lib/sequel/plugins/join_by_paths.rb +17 -29
- metadata +34 -11
    
        data/lib/odata/filter/error.rb
    CHANGED
    
    | @@ -1,18 +1,31 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative '../error'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Safrano
         | 
| 2 6 | 
             
              class SequelAdapterError < StandardError
         | 
| 3 7 | 
             
                attr_reader :inner
         | 
| 8 | 
            +
             | 
| 4 9 | 
             
                def initialize(err)
         | 
| 5 10 | 
             
                  @inner = err
         | 
| 6 11 | 
             
                end
         | 
| 7 12 | 
             
              end
         | 
| 13 | 
            +
             | 
| 8 14 | 
             
              module Filter
         | 
| 9 15 | 
             
                class Parser
         | 
| 10 16 | 
             
                  # Parser errors
         | 
| 11 | 
            -
             | 
| 17 | 
            +
             | 
| 18 | 
            +
                  class Error
         | 
| 19 | 
            +
                    def Error.http_code
         | 
| 20 | 
            +
                      const_get(:HTTP_CODE)
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                    HTTP_CODE = 400
         | 
| 23 | 
            +
             | 
| 12 24 | 
             
                    attr_reader :tok
         | 
| 13 25 | 
             
                    attr_reader :typ
         | 
| 14 26 | 
             
                    attr_reader :cur_val
         | 
| 15 27 | 
             
                    attr_reader :cur_typ
         | 
| 28 | 
            +
             | 
| 16 29 | 
             
                    def initialize(tok, typ, cur)
         | 
| 17 30 | 
             
                      @tok = tok
         | 
| 18 31 | 
             
                      @typ = typ
         | 
| @@ -24,31 +37,61 @@ module OData | |
| 24 37 | 
             
                  end
         | 
| 25 38 | 
             
                  # Invalid Tokens
         | 
| 26 39 | 
             
                  class ErrorInvalidToken < Error
         | 
| 40 | 
            +
                    include ::Safrano::ErrorInstance
         | 
| 41 | 
            +
                    def initialize(tok, typ, cur)
         | 
| 42 | 
            +
                      super
         | 
| 43 | 
            +
                      @msg = "Bad Request: invalid token #{tok} in $filter"
         | 
| 44 | 
            +
                    end
         | 
| 27 45 | 
             
                  end
         | 
| 28 46 | 
             
                  # Unmached closed
         | 
| 29 47 | 
             
                  class ErrorUnmatchedClose < Error
         | 
| 48 | 
            +
                    include ::Safrano::ErrorInstance
         | 
| 49 | 
            +
                    def initialize(tok, typ, cur)
         | 
| 50 | 
            +
                      super
         | 
| 51 | 
            +
                      @msg = "Bad Request: unmatched #{tok} in $filter"
         | 
| 52 | 
            +
                    end
         | 
| 30 53 | 
             
                  end
         | 
| 31 54 |  | 
| 32 | 
            -
                  class ErrorFunctionArgumentType | 
| 55 | 
            +
                  class ErrorFunctionArgumentType
         | 
| 56 | 
            +
                    include ::Safrano::ErrorInstance
         | 
| 33 57 | 
             
                  end
         | 
| 34 58 |  | 
| 35 | 
            -
                  class ErrorWrongColumnName | 
| 59 | 
            +
                  class ErrorWrongColumnName
         | 
| 60 | 
            +
                    include ::Safrano::ErrorInstance
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  # attempt to add a child to a Leave
         | 
| 64 | 
            +
                  class ErrorLeaveChild
         | 
| 65 | 
            +
                    include ::Safrano::ErrorInstance
         | 
| 36 66 | 
             
                  end
         | 
| 37 67 |  | 
| 38 68 | 
             
                  # Invalid function arity
         | 
| 39 69 | 
             
                  class ErrorInvalidArity < Error
         | 
| 70 | 
            +
                    include ::Safrano::ErrorInstance
         | 
| 71 | 
            +
                    def initialize(tok, typ, cur)
         | 
| 72 | 
            +
                      super
         | 
| 73 | 
            +
                      @msg = "Bad Request: wrong number of parameters for function #{cur.parent.value.to_s} in $filter"
         | 
| 74 | 
            +
                    end
         | 
| 40 75 | 
             
                  end
         | 
| 41 76 | 
             
                  # Invalid separator in this context (missing parenthesis?)
         | 
| 42 77 | 
             
                  class ErrorInvalidSeparator < Error
         | 
| 78 | 
            +
                    include ::Safrano::ErrorInstance
         | 
| 43 79 | 
             
                  end
         | 
| 44 80 |  | 
| 45 81 | 
             
                  # unmatched quot3
         | 
| 46 82 | 
             
                  class UnmatchedQuoteError < Error
         | 
| 83 | 
            +
                    include ::Safrano::ErrorInstance
         | 
| 84 | 
            +
                    def initialize(tok, typ, cur)
         | 
| 85 | 
            +
                      super
         | 
| 86 | 
            +
                      @msg = "Bad Request: unbalanced quotes #{tok} in $filter"
         | 
| 87 | 
            +
                    end
         | 
| 47 88 | 
             
                  end
         | 
| 48 89 |  | 
| 49 90 | 
             
                  # wrong type of function argument
         | 
| 50 | 
            -
                  class ErrorInvalidArgumentType <  | 
| 51 | 
            -
                     | 
| 91 | 
            +
                  class ErrorInvalidArgumentType < Error
         | 
| 92 | 
            +
                    include ::Safrano::ErrorInstance
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    def initialize(tree, expected:, actual:)
         | 
| 52 95 | 
             
                      @tree = tree
         | 
| 53 96 | 
             
                      @expected = expected
         | 
| 54 97 | 
             
                      @actual = actual
         | 
    
        data/lib/odata/filter/parse.rb
    CHANGED
    
    | @@ -1,15 +1,19 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
            require_relative './tree.rb'
         | 
| 3 | 
            -
            require_relative './error.rb'
         | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 4 2 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 3 | 
            +
            require_relative './token'
         | 
| 4 | 
            +
            require_relative './tree'
         | 
| 5 | 
            +
            require_relative './error'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            # top level Safrano namespace
         | 
| 8 | 
            +
            module Safrano
         | 
| 7 9 | 
             
              # for handling $filter
         | 
| 8 10 | 
             
              module Filter
         | 
| 9 11 | 
             
                # Parser for $filter input
         | 
| 10 12 | 
             
                class Parser
         | 
| 11 13 | 
             
                  include Token
         | 
| 12 14 | 
             
                  attr_reader :cursor
         | 
| 15 | 
            +
                  attr_reader :error
         | 
| 16 | 
            +
             | 
| 13 17 | 
             
                  def initialize(input)
         | 
| 14 18 | 
             
                    @tree = RootTree.new
         | 
| 15 19 | 
             
                    @cursor = @tree
         | 
| @@ -18,10 +22,14 @@ module OData | |
| 18 22 | 
             
                    @binop_stack = []
         | 
| 19 23 | 
             
                  end
         | 
| 20 24 |  | 
| 25 | 
            +
                  def server_error
         | 
| 26 | 
            +
                    (@error = ::Safrano::ServerError)
         | 
| 27 | 
            +
                  end
         | 
| 28 | 
            +
             | 
| 21 29 | 
             
                  def grow_at_cursor(child)
         | 
| 22 | 
            -
                     | 
| 30 | 
            +
                    return server_error if @cursor.nil?
         | 
| 23 31 |  | 
| 24 | 
            -
                    @cursor.attach(child)
         | 
| 32 | 
            +
                    @cursor.attach(child).tap_error { |err| return (@error = err) }
         | 
| 25 33 | 
             
                    @cursor = child
         | 
| 26 34 | 
             
                  end
         | 
| 27 35 |  | 
| @@ -40,7 +48,7 @@ module OData | |
| 40 48 | 
             
                  def insert_before_cursor(node)
         | 
| 41 49 | 
             
                    left = detach_cursor
         | 
| 42 50 | 
             
                    grow_at_cursor(node)
         | 
| 43 | 
            -
                    @cursor.attach(left)
         | 
| 51 | 
            +
                    @cursor.attach(left).tap_error { |err| return (@error = err) }
         | 
| 44 52 | 
             
                  end
         | 
| 45 53 |  | 
| 46 54 | 
             
                  def invalid_separator_error(tok, typ)
         | 
| @@ -56,36 +64,29 @@ module OData | |
| 56 64 | 
             
                  end
         | 
| 57 65 |  | 
| 58 66 | 
             
                  def with_accepted(tok, typ)
         | 
| 59 | 
            -
                     | 
| 60 | 
            -
                    if acc
         | 
| 61 | 
            -
                      yield
         | 
| 62 | 
            -
                    else
         | 
| 63 | 
            -
                      @error = err
         | 
| 64 | 
            -
                    end
         | 
| 67 | 
            +
                    (err = @cursor.accept?(tok, typ)) ? (@error = err) : yield
         | 
| 65 68 | 
             
                  end
         | 
| 66 69 |  | 
| 67 70 | 
             
                  def build
         | 
| 68 71 | 
             
                    each_typed_token(@input) do |tok, typ|
         | 
| 69 72 | 
             
                      case typ
         | 
| 70 73 | 
             
                      when :FuncTree
         | 
| 71 | 
            -
                        with_accepted(tok, typ)  | 
| 72 | 
            -
             | 
| 73 | 
            -
                        end
         | 
| 74 | 
            +
                        with_accepted(tok, typ) { grow_at_cursor(FuncTree.new(tok)) }
         | 
| 75 | 
            +
             | 
| 74 76 | 
             
                      when :Delimiter
         | 
| 75 77 | 
             
                        case tok
         | 
| 76 78 | 
             
                        when '('
         | 
| 77 79 | 
             
                          with_accepted(tok, typ) do
         | 
| 78 | 
            -
                            unless @cursor.is_a? FuncTree
         | 
| 79 | 
            -
             | 
| 80 | 
            +
                            grow_at_cursor(IdentityFuncTree.new) unless @cursor.is_a? FuncTree
         | 
| 81 | 
            +
                            unless @error
         | 
| 82 | 
            +
                              openarg = ArgTree.new('(')
         | 
| 83 | 
            +
                              @stack << openarg
         | 
| 84 | 
            +
                              grow_at_cursor(openarg)
         | 
| 80 85 | 
             
                            end
         | 
| 81 | 
            -
                            openarg = ArgTree.new('(')
         | 
| 82 | 
            -
                            @stack << openarg
         | 
| 83 | 
            -
                            grow_at_cursor(openarg)
         | 
| 84 86 | 
             
                          end
         | 
| 87 | 
            +
             | 
| 85 88 | 
             
                        when ')'
         | 
| 86 | 
            -
                          unless (@cursor = @stack.pop)
         | 
| 87 | 
            -
                            break invalid_closing_delimiter_error(tok, typ)
         | 
| 88 | 
            -
                          end
         | 
| 89 | 
            +
                          break invalid_closing_delimiter_error(tok, typ) unless (@cursor = @stack.pop)
         | 
| 89 90 |  | 
| 90 91 | 
             
                          with_accepted(tok, typ) do
         | 
| 91 92 | 
             
                            @cursor.update_state(tok, typ)
         | 
| @@ -94,9 +95,7 @@ module OData | |
| 94 95 | 
             
                        end
         | 
| 95 96 |  | 
| 96 97 | 
             
                      when :Separator
         | 
| 97 | 
            -
                        unless (@cursor = @stack.last)
         | 
| 98 | 
            -
                          break invalid_separator_error(tok, typ)
         | 
| 99 | 
            -
                        end
         | 
| 98 | 
            +
                        break invalid_separator_error(tok, typ) unless (@cursor = @stack.last)
         | 
| 100 99 |  | 
| 101 100 | 
             
                        with_accepted(tok, typ) { @cursor.update_state(tok, typ) }
         | 
| 102 101 |  | 
| @@ -129,6 +128,7 @@ module OData | |
| 129 128 | 
             
                          end
         | 
| 130 129 | 
             
                          insert_before_cursor(binoptr)
         | 
| 131 130 | 
             
                        end
         | 
| 131 | 
            +
             | 
| 132 132 | 
             
                      when :BinopArithm
         | 
| 133 133 | 
             
                        with_accepted(tok, typ) do
         | 
| 134 134 | 
             
                          binoptr = BinopArithm.new(tok)
         | 
| @@ -151,6 +151,12 @@ module OData | |
| 151 151 | 
             
                          grow_at_cursor(Literal.new(tok))
         | 
| 152 152 | 
             
                        end
         | 
| 153 153 |  | 
| 154 | 
            +
                      when :NullLiteral
         | 
| 155 | 
            +
                        with_accepted(tok, typ) do
         | 
| 156 | 
            +
                          @cursor.update_state(tok, typ)
         | 
| 157 | 
            +
                          grow_at_cursor(NullLiteral.new(tok))
         | 
| 158 | 
            +
                        end
         | 
| 159 | 
            +
             | 
| 154 160 | 
             
                      when :Qualit
         | 
| 155 161 | 
             
                        with_accepted(tok, typ) do
         | 
| 156 162 | 
             
                          @cursor.update_state(tok, typ)
         | 
| @@ -168,19 +174,21 @@ module OData | |
| 168 174 | 
             
                          @cursor.update_state(tok, typ)
         | 
| 169 175 | 
             
                          grow_at_cursor(FPNumber.new(tok))
         | 
| 170 176 | 
             
                        end
         | 
| 177 | 
            +
             | 
| 171 178 | 
             
                      when :unmatchedQuote
         | 
| 172 179 | 
             
                        break unmatched_quote_error(tok, typ)
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                      when :space
         | 
| 182 | 
            +
                        with_accepted(tok, typ) do
         | 
| 183 | 
            +
                          @cursor.update_state(tok, typ)
         | 
| 184 | 
            +
                        end
         | 
| 173 185 | 
             
                      else
         | 
| 174 | 
            -
                         | 
| 186 | 
            +
                        server_error
         | 
| 175 187 | 
             
                      end
         | 
| 176 | 
            -
                      break if @error
         | 
| 177 | 
            -
                    end
         | 
| 178 | 
            -
                    begin
         | 
| 179 | 
            -
                      @tree.check_types unless @error
         | 
| 180 | 
            -
                    rescue ErrorInvalidArgumentType => e
         | 
| 181 | 
            -
                      @error = e
         | 
| 188 | 
            +
                      break(@error) if @error
         | 
| 182 189 | 
             
                    end
         | 
| 183 | 
            -
                    @error  | 
| 190 | 
            +
                    (@error = @tree.check_types) unless @error
         | 
| 191 | 
            +
                    @error ? @error : Contract.valid(@tree)
         | 
| 184 192 | 
             
                  end
         | 
| 185 193 | 
             
                end
         | 
| 186 194 | 
             
              end
         | 
    
        data/lib/odata/filter/sequel.rb
    CHANGED
    
    | @@ -1,19 +1,24 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative './base'
         | 
| 4 | 
            +
            require_relative './sequel_function_adapter'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Safrano
         | 
| 3 7 | 
             
              module Filter
         | 
| 4 8 | 
             
                # Base class for Leaves, Trees, RootTrees etc
         | 
| 5 | 
            -
                class Node
         | 
| 6 | 
            -
                end
         | 
| 9 | 
            +
                #    class Node
         | 
| 10 | 
            +
                #    end
         | 
| 7 11 |  | 
| 8 12 | 
             
                # Leaves are Nodes with a parent but no children
         | 
| 9 | 
            -
                class Leave < Node
         | 
| 10 | 
            -
                end
         | 
| 13 | 
            +
                #    class Leave < Node
         | 
| 14 | 
            +
                #    end
         | 
| 11 15 |  | 
| 12 16 | 
             
                # RootTrees have childrens but no parent
         | 
| 13 17 | 
             
                class RootTree
         | 
| 14 18 | 
             
                  def apply_to_dataset(dtcx, jh)
         | 
| 15 | 
            -
                     | 
| 16 | 
            -
             | 
| 19 | 
            +
                    @children.first.leuqes(jh).if_valid do |filtexpr|
         | 
| 20 | 
            +
                      jh.dataset(dtcx).where(filtexpr).select_all(jh.start_model.table_name)
         | 
| 21 | 
            +
                    end
         | 
| 17 22 | 
             
                  end
         | 
| 18 23 |  | 
| 19 24 | 
             
                  def sequel_expr(jh)
         | 
| @@ -26,57 +31,108 @@ module OData | |
| 26 31 | 
             
                end
         | 
| 27 32 |  | 
| 28 33 | 
             
                # For functions... should have a single child---> the argument list
         | 
| 34 | 
            +
                # note: Adapter specific function helpers like year() or substringof_sig2()
         | 
| 35 | 
            +
                # need to be mixed in on startup (eg. on publish finalize)
         | 
| 29 36 | 
             
                class FuncTree < Tree
         | 
| 30 37 | 
             
                  def leuqes(jh)
         | 
| 31 38 | 
             
                    case @value
         | 
| 32 39 | 
             
                    when :startswith
         | 
| 33 | 
            -
                       | 
| 34 | 
            -
             | 
| 40 | 
            +
                      Contract.collect_result!(args[0].leuqes(jh),
         | 
| 41 | 
            +
                                               args[1].leuqes_starts_like(jh)) do |l0, l1|
         | 
| 42 | 
            +
                        Sequel.like(l0, l1)
         | 
| 43 | 
            +
                      end
         | 
| 44 | 
            +
             | 
| 35 45 | 
             
                    when :endswith
         | 
| 36 | 
            -
                       | 
| 37 | 
            -
             | 
| 46 | 
            +
                      Contract.collect_result!(args[0].leuqes(jh),
         | 
| 47 | 
            +
                                               args[1].leuqes_ends_like(jh)) do |l0, l1|
         | 
| 48 | 
            +
                        Sequel.like(l0, l1)
         | 
| 49 | 
            +
                      end
         | 
| 50 | 
            +
             | 
| 38 51 | 
             
                    when :substringof
         | 
| 39 52 |  | 
| 40 53 | 
             
                      # there are multiple possible argument types (but all should return edm.string)
         | 
| 41 | 
            -
                      if  | 
| 54 | 
            +
                      if args[0].is_a?(QString)
         | 
| 42 55 | 
             
                        # substringof('Rhum', name)  -->
         | 
| 43 56 | 
             
                        # name contains substr 'Rhum'
         | 
| 44 | 
            -
                         | 
| 45 | 
            -
             | 
| 57 | 
            +
                        Contract.collect_result!(args[1].leuqes(jh),
         | 
| 58 | 
            +
                                                 args[0].leuqes_substringof_sig1(jh)) do |l1, l0|
         | 
| 59 | 
            +
                          Sequel.like(l1, l0)
         | 
| 60 | 
            +
                        end
         | 
| 61 | 
            +
             | 
| 46 62 | 
             
                      # special non standard (ui5 client) case ?
         | 
| 47 | 
            -
                      elsif  | 
| 48 | 
            -
                         | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
                         | 
| 52 | 
            -
                        # '__Route du Rhum__' contains name as a substring
         | 
| 53 | 
            -
                        # TODO... check if the database supports instr (how?)
         | 
| 54 | 
            -
                        # othewise use substr(postgresql) or whatevr?
         | 
| 55 | 
            -
                        instr_substr_func = if (Sequel::Model.db.adapter_scheme == :postgres)
         | 
| 56 | 
            -
                                              Sequel.function(:strpos, args[1].leuqes(jh), args[0].leuqes(jh))
         | 
| 57 | 
            -
                                            else
         | 
| 58 | 
            -
                                              Sequel.function(:instr, args[1].leuqes(jh), args[0].leuqes(jh))
         | 
| 59 | 
            -
                                            end
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                        Sequel::SQL::BooleanExpression.new(:>, instr_substr_func, 0)
         | 
| 63 | 
            +
                      elsif args[0].is_a?(Literal) && args[1].is_a?(Literal)
         | 
| 64 | 
            +
                        Contract.collect_result!(args[1].leuqes(jh),
         | 
| 65 | 
            +
                                                 args[0].leuqes_substringof_sig1(jh)) do |l1, l0|
         | 
| 66 | 
            +
                          Sequel.like(l1, l0)
         | 
| 67 | 
            +
                        end
         | 
| 62 68 |  | 
| 69 | 
            +
                      elsif args[1].is_a?(QString)
         | 
| 70 | 
            +
                        substringof_sig2(jh) # adapter specific
         | 
| 63 71 | 
             
                      else
         | 
| 64 72 | 
             
                        # TODO... actually not supported?
         | 
| 65 | 
            -
                        raise  | 
| 73 | 
            +
                        raise Safrano::Filter::Parser::ErrorFunctionArgumentType
         | 
| 66 74 | 
             
                      end
         | 
| 67 75 | 
             
                    when :concat
         | 
| 68 | 
            -
                       | 
| 69 | 
            -
             | 
| 76 | 
            +
                      Contract.collect_result!(args[0].leuqes(jh),
         | 
| 77 | 
            +
                                               args[1].leuqes(jh)) do |l0, l1|
         | 
| 78 | 
            +
                        Sequel.join([l0, l1])
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
             | 
| 70 81 | 
             
                    when :length
         | 
| 71 | 
            -
                       | 
| 82 | 
            +
                      args.first.leuqes(jh)
         | 
| 83 | 
            +
                          .map_result! { |l| Sequel.char_length(l) }
         | 
| 84 | 
            +
             | 
| 72 85 | 
             
                    when :trim
         | 
| 73 | 
            -
                       | 
| 86 | 
            +
                      args.first.leuqes(jh)
         | 
| 87 | 
            +
                          .map_result! { |l| Sequel.trim(l) }
         | 
| 88 | 
            +
             | 
| 74 89 | 
             
                    when :toupper
         | 
| 75 | 
            -
                       | 
| 90 | 
            +
                      args.first.leuqes(jh)
         | 
| 91 | 
            +
                          .map_result! { |l| Sequel.function(:upper, l)  }
         | 
| 92 | 
            +
             | 
| 76 93 | 
             
                    when :tolower
         | 
| 77 | 
            -
                       | 
| 94 | 
            +
                      args.first.leuqes(jh)
         | 
| 95 | 
            +
                          .map_result! { |l| Sequel.function(:lower, l)  }
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    # all datetime funcs are adapter specific (because sqlite does not have extract)
         | 
| 98 | 
            +
                    when :year
         | 
| 99 | 
            +
                      args.first.leuqes(jh)
         | 
| 100 | 
            +
                          .map_result! { |l| year(l) }
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                    when :month
         | 
| 103 | 
            +
                      args.first.leuqes(jh)
         | 
| 104 | 
            +
                          .map_result! { |l| month(l) }
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                    when :second
         | 
| 107 | 
            +
                      args.first.leuqes(jh)
         | 
| 108 | 
            +
                          .map_result! { |l| second(l) }
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                    when :minute
         | 
| 111 | 
            +
                      args.first.leuqes(jh)
         | 
| 112 | 
            +
                          .map_result! { |l| minute(l) }
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                    when :hour
         | 
| 115 | 
            +
                      args.first.leuqes(jh)
         | 
| 116 | 
            +
                          .map_result! { |l| hour(l) }
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                    when :day
         | 
| 119 | 
            +
                      args.first.leuqes(jh)
         | 
| 120 | 
            +
                          .map_result! { |l| day(l) }
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                    # math functions
         | 
| 123 | 
            +
                    when :round
         | 
| 124 | 
            +
                      args.first.leuqes(jh)
         | 
| 125 | 
            +
                          .map_result! { |l| Sequel.function(:round, l) }
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                    when :floor
         | 
| 128 | 
            +
                      args.first.leuqes(jh)
         | 
| 129 | 
            +
                          .if_valid { |l| floor(l) }
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                    when :ceiling
         | 
| 132 | 
            +
                      args.first.leuqes(jh)
         | 
| 133 | 
            +
                          .if_valid { |l| ceiling(l) }
         | 
| 78 134 | 
             
                    else
         | 
| 79 | 
            -
                       | 
| 135 | 
            +
                      Safrano::FilterParseError
         | 
| 80 136 | 
             
                    end
         | 
| 81 137 | 
             
                  end
         | 
| 82 138 | 
             
                end
         | 
| @@ -95,9 +151,9 @@ module OData | |
| 95 151 | 
             
                  def leuqes(jh)
         | 
| 96 152 | 
             
                    case @value
         | 
| 97 153 | 
             
                    when :not
         | 
| 98 | 
            -
                       | 
| 154 | 
            +
                      @children.first.leuqes(jh).map_result! { |l| Sequel.~(l) }
         | 
| 99 155 | 
             
                    else
         | 
| 100 | 
            -
                       | 
| 156 | 
            +
                      Safrano::FilterParseError
         | 
| 101 157 | 
             
                    end
         | 
| 102 158 | 
             
                  end
         | 
| 103 159 | 
             
                end
         | 
| @@ -123,12 +179,19 @@ module OData | |
| 123 179 | 
             
                                when :and
         | 
| 124 180 | 
             
                                  :AND
         | 
| 125 181 | 
             
                                else
         | 
| 126 | 
            -
                                   | 
| 182 | 
            +
                                  return Safrano::FilterParseError
         | 
| 127 183 | 
             
                                end
         | 
| 128 | 
            -
             | 
| 129 | 
            -
             | 
| 130 | 
            -
             | 
| 131 | 
            -
             | 
| 184 | 
            +
                    Contract.collect_result!(@children[0].leuqes(jh),
         | 
| 185 | 
            +
                                             @children[1].leuqes(jh)) do |c0, c1|
         | 
| 186 | 
            +
                      if c1 == NullLiteral::LEUQES
         | 
| 187 | 
            +
                        if @value == :eq
         | 
| 188 | 
            +
                          leuqes_op = :IS
         | 
| 189 | 
            +
                        elsif @value == :ne
         | 
| 190 | 
            +
                          leuqes_op = :'IS NOT'
         | 
| 191 | 
            +
                        end
         | 
| 192 | 
            +
                      end
         | 
| 193 | 
            +
                      Sequel::SQL::BooleanExpression.new(leuqes_op, c0, c1)
         | 
| 194 | 
            +
                    end
         | 
| 132 195 | 
             
                  end
         | 
| 133 196 | 
             
                end
         | 
| 134 197 |  | 
| @@ -147,12 +210,12 @@ module OData | |
| 147 210 | 
             
                                when :mod
         | 
| 148 211 | 
             
                                  :%
         | 
| 149 212 | 
             
                                else
         | 
| 150 | 
            -
                                   | 
| 213 | 
            +
                                  return Safrano::FilterParseError
         | 
| 151 214 | 
             
                                end
         | 
| 152 | 
            -
             | 
| 153 | 
            -
             | 
| 154 | 
            -
             | 
| 155 | 
            -
             | 
| 215 | 
            +
                    Contract.collect_result!(@children[0].leuqes(jh),
         | 
| 216 | 
            +
                                             @children[1].leuqes(jh)) do |c0, c1|
         | 
| 217 | 
            +
                      Sequel::SQL::NumericExpression.new(leuqes_op, c0, c1)
         | 
| 218 | 
            +
                    end
         | 
| 156 219 | 
             
                  end
         | 
| 157 220 | 
             
                end
         | 
| 158 221 |  | 
| @@ -163,44 +226,42 @@ module OData | |
| 163 226 | 
             
                # Numbers (floating point, ints, dec)
         | 
| 164 227 | 
             
                class FPNumber
         | 
| 165 228 | 
             
                  def leuqes(_jh)
         | 
| 166 | 
            -
                    Sequel.lit(@value)
         | 
| 229 | 
            +
                    success Sequel.lit(@value)
         | 
| 167 230 | 
             
                  end
         | 
| 168 231 |  | 
| 169 232 | 
             
                  def leuqes_starts_like(_jh)
         | 
| 170 | 
            -
                    "#{@value | 
| 233 | 
            +
                    success  "#{@value}%"
         | 
| 171 234 | 
             
                  end
         | 
| 172 235 |  | 
| 173 236 | 
             
                  def leuqes_ends_like(_jh)
         | 
| 174 | 
            -
                    "%#{@value | 
| 237 | 
            +
                    success  "%#{@value}"
         | 
| 175 238 | 
             
                  end
         | 
| 176 239 |  | 
| 177 240 | 
             
                  def leuqes_substringof_sig1(_jh)
         | 
| 178 | 
            -
                    "%#{@value | 
| 241 | 
            +
                    success  "%#{@value}%"
         | 
| 179 242 | 
             
                  end
         | 
| 180 243 | 
             
                end
         | 
| 181 244 |  | 
| 182 245 | 
             
                # Literals are unquoted words
         | 
| 183 246 | 
             
                class Literal
         | 
| 184 247 | 
             
                  def leuqes(jh)
         | 
| 185 | 
            -
                     | 
| 186 | 
            -
             | 
| 187 | 
            -
                     | 
| 188 | 
            -
                      raise OData::Filter::Parser::ErrorWrongColumnName
         | 
| 189 | 
            -
                    end
         | 
| 248 | 
            +
                    return Safrano::FilterParseErrorWrongColumnName unless jh.start_model.db_schema.key?(@value.to_sym)
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                    success Sequel[jh.start_model.table_name][@value.to_sym]
         | 
| 190 251 | 
             
                  end
         | 
| 191 252 |  | 
| 192 253 | 
             
                  # non stantard extensions to support things like
         | 
| 193 254 | 
             
                  # substringof(Rhum, name)  ????
         | 
| 194 255 | 
             
                  def leuqes_starts_like(_jh)
         | 
| 195 | 
            -
                    "#{@value}%"
         | 
| 256 | 
            +
                    success "#{@value}%"
         | 
| 196 257 | 
             
                  end
         | 
| 197 258 |  | 
| 198 259 | 
             
                  def leuqes_ends_like(_jh)
         | 
| 199 | 
            -
                    "%#{@value}"
         | 
| 260 | 
            +
                    success "%#{@value}"
         | 
| 200 261 | 
             
                  end
         | 
| 201 262 |  | 
| 202 263 | 
             
                  def leuqes_substringof_sig1(_jh)
         | 
| 203 | 
            -
                    "%#{@value}%"
         | 
| 264 | 
            +
                    success "%#{@value}%"
         | 
| 204 265 | 
             
                  end
         | 
| 205 266 |  | 
| 206 267 | 
             
                  def as_string
         | 
| @@ -208,31 +269,39 @@ module OData | |
| 208 269 | 
             
                  end
         | 
| 209 270 | 
             
                end
         | 
| 210 271 |  | 
| 272 | 
            +
                # Null
         | 
| 273 | 
            +
                class NullLiteral
         | 
| 274 | 
            +
                  def leuqes(jh)
         | 
| 275 | 
            +
                    # Sequel's representation of NULL
         | 
| 276 | 
            +
                    success LEUQES
         | 
| 277 | 
            +
                  end
         | 
| 278 | 
            +
                end
         | 
| 279 | 
            +
             | 
| 211 280 | 
             
                # Qualit (qualified lits) are words separated by /
         | 
| 212 281 | 
             
                class Qualit
         | 
| 213 282 | 
             
                  def leuqes(jh)
         | 
| 214 283 | 
             
                    jh.add(@path)
         | 
| 215 284 | 
             
                    talias = jh.start_model.get_alias_sym(@path)
         | 
| 216 | 
            -
                    Sequel[talias][@attrib.to_sym]
         | 
| 285 | 
            +
                    success Sequel[talias][@attrib.to_sym]
         | 
| 217 286 | 
             
                  end
         | 
| 218 287 | 
             
                end
         | 
| 219 288 |  | 
| 220 289 | 
             
                # Quoted Strings
         | 
| 221 290 | 
             
                class QString
         | 
| 222 291 | 
             
                  def leuqes(_jh)
         | 
| 223 | 
            -
                    @value
         | 
| 292 | 
            +
                    success @value
         | 
| 224 293 | 
             
                  end
         | 
| 225 294 |  | 
| 226 295 | 
             
                  def leuqes_starts_like(_jh)
         | 
| 227 | 
            -
                    "#{@value}%"
         | 
| 296 | 
            +
                    success "#{@value}%"
         | 
| 228 297 | 
             
                  end
         | 
| 229 298 |  | 
| 230 299 | 
             
                  def leuqes_ends_like(_jh)
         | 
| 231 | 
            -
                    "%#{@value}"
         | 
| 300 | 
            +
                    success "%#{@value}"
         | 
| 232 301 | 
             
                  end
         | 
| 233 302 |  | 
| 234 303 | 
             
                  def leuqes_substringof_sig1(_jh)
         | 
| 235 | 
            -
                    "%#{@value}%"
         | 
| 304 | 
            +
                    success "%#{@value}%"
         | 
| 236 305 | 
             
                  end
         | 
| 237 306 | 
             
                end
         | 
| 238 307 | 
             
              end
         |