safrano 0.3.3 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/odata/attribute.rb +9 -8
- data/lib/odata/batch.rb +8 -8
- data/lib/odata/collection.rb +239 -92
- data/lib/odata/collection_filter.rb +40 -9
- data/lib/odata/collection_media.rb +159 -28
- data/lib/odata/collection_order.rb +46 -36
- data/lib/odata/common_logger.rb +37 -12
- data/lib/odata/entity.rb +188 -99
- data/lib/odata/error.rb +60 -12
- data/lib/odata/expand.rb +123 -0
- data/lib/odata/filter/base.rb +66 -0
- data/lib/odata/filter/error.rb +33 -0
- data/lib/odata/filter/parse.rb +6 -12
- data/lib/odata/filter/sequel.rb +42 -29
- data/lib/odata/filter/sequel_function_adapter.rb +147 -0
- data/lib/odata/filter/token.rb +5 -1
- data/lib/odata/filter/tree.rb +45 -29
- data/lib/odata/navigation_attribute.rb +60 -27
- data/lib/odata/relations.rb +2 -2
- data/lib/odata/select.rb +42 -0
- data/lib/odata/url_parameters.rb +51 -36
- data/lib/odata/walker.rb +6 -6
- data/lib/safrano.rb +23 -13
- data/lib/{safrano_core.rb → safrano/core.rb} +12 -4
- data/lib/{multipart.rb → safrano/multipart.rb} +17 -26
- data/lib/{odata_rack_builder.rb → safrano/odata_rack_builder.rb} +0 -1
- data/lib/{rack_app.rb → safrano/rack_app.rb} +12 -10
- data/lib/{request.rb → safrano/request.rb} +8 -14
- data/lib/{response.rb → safrano/response.rb} +1 -2
- data/lib/{sequel_join_by_paths.rb → safrano/sequel_join_by_paths.rb} +1 -1
- data/lib/{service.rb → safrano/service.rb} +162 -131
- data/lib/safrano/version.rb +3 -0
- data/lib/sequel/plugins/join_by_paths.rb +11 -10
- metadata +33 -16
- data/lib/version.rb +0 -4
data/lib/odata/error.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
require 'json'
|
4
2
|
require 'rexml/document'
|
5
3
|
require 'safrano.rb'
|
@@ -31,8 +29,8 @@ module OData
|
|
31
29
|
end
|
32
30
|
end
|
33
31
|
|
34
|
-
# base module for HTTP errors
|
35
|
-
module
|
32
|
+
# base module for HTTP errors, when used as a Error Class
|
33
|
+
module ErrorClass
|
36
34
|
def odata_get(req)
|
37
35
|
if req.accept?(APPJSON)
|
38
36
|
[const_get(:HTTP_CODE), CT_JSON,
|
@@ -42,9 +40,22 @@ module OData
|
|
42
40
|
end
|
43
41
|
end
|
44
42
|
end
|
43
|
+
|
44
|
+
# base module for HTTP errors, when used as an Error instance
|
45
|
+
module ErrorInstance
|
46
|
+
def odata_get(req)
|
47
|
+
if req.accept?(APPJSON)
|
48
|
+
[self.class.const_get(:HTTP_CODE), CT_JSON,
|
49
|
+
{ 'odata.error' => { 'code' => '', 'message' => @msg } }.to_json]
|
50
|
+
else
|
51
|
+
[self.class.const_get(:HTTP_CODE), CT_TEXT, @msg]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
45
56
|
# http Bad Req.
|
46
57
|
class BadRequestError
|
47
|
-
extend
|
58
|
+
extend ErrorClass
|
48
59
|
HTTP_CODE = 400
|
49
60
|
@msg = 'Bad Request Error'
|
50
61
|
end
|
@@ -59,12 +70,31 @@ module OData
|
|
59
70
|
HTTP_CODE = 400
|
60
71
|
@msg = 'Bad Request: $value request for a non-media entity'
|
61
72
|
end
|
73
|
+
class BadRequestSequelAdapterError < BadRequestError
|
74
|
+
include ErrorInstance
|
75
|
+
def initialize(err)
|
76
|
+
@msg = err.inner.message
|
77
|
+
end
|
78
|
+
end
|
62
79
|
|
63
80
|
# for Syntax error in Filtering
|
64
81
|
class BadRequestFilterParseError < BadRequestError
|
65
82
|
HTTP_CODE = 400
|
66
|
-
@msg = 'Bad Request: Syntax error in
|
83
|
+
@msg = 'Bad Request: Syntax error in $filter'
|
67
84
|
end
|
85
|
+
|
86
|
+
# for Syntax error in $expand param
|
87
|
+
class BadRequestExpandParseError < BadRequestError
|
88
|
+
HTTP_CODE = 400
|
89
|
+
@msg = 'Bad Request: Syntax error in $expand'
|
90
|
+
end
|
91
|
+
|
92
|
+
# for Syntax error in $orderby param
|
93
|
+
class BadRequestOrderParseError < BadRequestError
|
94
|
+
HTTP_CODE = 400
|
95
|
+
@msg = 'Bad Request: Syntax error in $orderby'
|
96
|
+
end
|
97
|
+
|
68
98
|
# for $inlinecount error
|
69
99
|
class BadRequestInlineCountParamError < BadRequestError
|
70
100
|
HTTP_CODE = 400
|
@@ -72,36 +102,54 @@ module OData
|
|
72
102
|
end
|
73
103
|
# http not found
|
74
104
|
class ErrorNotFound
|
75
|
-
extend
|
105
|
+
extend ErrorClass
|
76
106
|
HTTP_CODE = 404
|
77
107
|
@msg = 'The requested ressource was not found'
|
78
108
|
end
|
79
109
|
# Transition error (Safrano specific)
|
80
110
|
class ServerTransitionError
|
81
|
-
extend
|
111
|
+
extend ErrorClass
|
82
112
|
HTTP_CODE = 500
|
83
113
|
@msg = 'Server error: Segment could not be parsed'
|
84
114
|
end
|
85
115
|
# generic http 500 server err
|
86
116
|
class ServerError
|
87
|
-
extend
|
117
|
+
extend ErrorClass
|
88
118
|
HTTP_CODE = 500
|
89
119
|
@msg = 'Server error'
|
90
120
|
end
|
91
121
|
# not implemented (Safrano specific)
|
92
122
|
class NotImplementedError
|
93
|
-
extend
|
123
|
+
extend ErrorClass
|
94
124
|
HTTP_CODE = 501
|
95
125
|
end
|
96
126
|
# batch not implemented (Safrano specific)
|
97
127
|
class BatchNotImplementedError
|
98
|
-
extend
|
128
|
+
extend ErrorClass
|
99
129
|
HTTP_CODE = 501
|
100
130
|
@msg = 'Not implemented: OData batch'
|
101
131
|
end
|
132
|
+
|
102
133
|
# error in filter parsing (Safrano specific)
|
103
134
|
class FilterParseError < BadRequestError
|
104
|
-
extend
|
135
|
+
extend ErrorClass
|
105
136
|
HTTP_CODE = 400
|
106
137
|
end
|
138
|
+
|
139
|
+
class FilterFunctionNotImplementedError < BadRequestError
|
140
|
+
extend ErrorClass
|
141
|
+
include ErrorInstance
|
142
|
+
@msg = 'the requested $filter function is Not implemented'
|
143
|
+
HTTP_CODE = 400
|
144
|
+
def initialize(exception)
|
145
|
+
@msg = exception.to_s
|
146
|
+
end
|
147
|
+
end
|
148
|
+
class FilterInvalidFunctionError < BadRequestError
|
149
|
+
include ErrorInstance
|
150
|
+
HTTP_CODE = 400
|
151
|
+
def initialize(exception)
|
152
|
+
@msg = exception.to_s
|
153
|
+
end
|
154
|
+
end
|
107
155
|
end
|
data/lib/odata/expand.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'odata/error.rb'
|
2
|
+
|
3
|
+
# all dataset expanding related classes in our OData module
|
4
|
+
# ie do eager loading
|
5
|
+
module OData
|
6
|
+
# base class for expanding
|
7
|
+
class ExpandBase
|
8
|
+
EmptyExpand = new # re-useable empty expanding (idempotent)
|
9
|
+
EMPTYH = {}.freeze
|
10
|
+
|
11
|
+
def self.factory(expandstr)
|
12
|
+
expandstr.nil? ? EmptyExpand : MultiExpand.new(expandstr)
|
13
|
+
end
|
14
|
+
|
15
|
+
# output template
|
16
|
+
attr_reader :template
|
17
|
+
|
18
|
+
def apply_to_dataset(dtcx)
|
19
|
+
dtcx
|
20
|
+
end
|
21
|
+
|
22
|
+
def empty?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_error?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def template
|
31
|
+
EMPTYH
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# single expand
|
36
|
+
class Expand < ExpandBase
|
37
|
+
# sequel eager arg.
|
38
|
+
attr_reader :arg
|
39
|
+
attr_reader :template
|
40
|
+
|
41
|
+
# used for Sequel eager argument
|
42
|
+
# Recursive array to deep hash
|
43
|
+
# [1,2,3,4] --> {1=>{2=>{3=>4}}}
|
44
|
+
# [1] --> 1
|
45
|
+
DEEPH_0 = ->(inp) { inp.size > 1 ? { inp[0] => DEEPH_0.call(inp[1..-1]) } : inp[0] }
|
46
|
+
|
47
|
+
# used for building output template
|
48
|
+
# Recursive array to deep hash
|
49
|
+
# [1,2,3,4] --> {1=>{2=>{3=>4}}}
|
50
|
+
# [1] --> { 1 => {} }
|
51
|
+
DEEPH_1 = ->(inp) { inp.size > 1 ? { inp[0] => DEEPH_1.call(inp[1..-1]) } : { inp[0] => {} } }
|
52
|
+
|
53
|
+
NODESEP = '/'.freeze
|
54
|
+
|
55
|
+
def initialize(exstr)
|
56
|
+
exstr.strip!
|
57
|
+
@expandp = exstr
|
58
|
+
@nodes = @expandp.split(NODESEP)
|
59
|
+
build_arg
|
60
|
+
end
|
61
|
+
|
62
|
+
def apply_to_dataset(dtcx)
|
63
|
+
dtcx
|
64
|
+
end
|
65
|
+
|
66
|
+
def build_arg
|
67
|
+
# 'a/b/c/d' ==> {a: {b:{c: :d}}}
|
68
|
+
# 'xy' ==> :xy
|
69
|
+
@arg = DEEPH_0.call(@nodes.map(&:to_sym))
|
70
|
+
@template = DEEPH_1.call(@nodes)
|
71
|
+
end
|
72
|
+
|
73
|
+
def parse_error?
|
74
|
+
# todo
|
75
|
+
false
|
76
|
+
end
|
77
|
+
|
78
|
+
def empty?
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Multi expanding logic
|
84
|
+
class MultiExpand < ExpandBase
|
85
|
+
COMASPLIT = /\s*,\s*/.freeze
|
86
|
+
attr_reader :template
|
87
|
+
|
88
|
+
def initialize(expandstr)
|
89
|
+
expandstr.strip!
|
90
|
+
@expandp = expandstr
|
91
|
+
@exlist = []
|
92
|
+
|
93
|
+
@exlist = expandstr.split(COMASPLIT).map { |exstr| Expand.new(exstr) }
|
94
|
+
build_template
|
95
|
+
end
|
96
|
+
|
97
|
+
def apply_to_dataset(dtcx)
|
98
|
+
# use eager loading for each used association
|
99
|
+
@exlist.each { |exp| dtcx = dtcx.eager(exp.arg) }
|
100
|
+
dtcx
|
101
|
+
end
|
102
|
+
|
103
|
+
def build_template
|
104
|
+
# 'a/b/c/d,xy' ==> [ {'a' =>{ 'b' => {'c' => {'d' => {} } }}},
|
105
|
+
# { 'xy' => {} }]
|
106
|
+
#
|
107
|
+
@template = @exlist.map(&:template)
|
108
|
+
|
109
|
+
# { 'a' => { 'b' => {'c' => 'd' }},
|
110
|
+
# 'xy' => {} }
|
111
|
+
@template = @template.inject({}) { |mrg, elmt| mrg.merge elmt }
|
112
|
+
end
|
113
|
+
|
114
|
+
def parse_error?
|
115
|
+
# todo
|
116
|
+
false
|
117
|
+
end
|
118
|
+
|
119
|
+
def empty?
|
120
|
+
false
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module OData
|
4
|
+
module Filter
|
5
|
+
# Base class for Leaves, Trees, RootTrees etc
|
6
|
+
class Node
|
7
|
+
end
|
8
|
+
|
9
|
+
# Leaves are Nodes with a parent but no children
|
10
|
+
class Leave < Node
|
11
|
+
end
|
12
|
+
|
13
|
+
# RootTrees have childrens but no parent
|
14
|
+
class RootTree < Node
|
15
|
+
end
|
16
|
+
|
17
|
+
# Tree's have Parent and children
|
18
|
+
class Tree < RootTree
|
19
|
+
end
|
20
|
+
|
21
|
+
# For functions... should have a single child---> the argument list
|
22
|
+
class FuncTree < Tree
|
23
|
+
end
|
24
|
+
|
25
|
+
# Indentity Func to use as "parent" func of parenthesis expressions
|
26
|
+
# --> allow to handle generically parenthesis always as argument of
|
27
|
+
# some function
|
28
|
+
class IdentityFuncTree < FuncTree
|
29
|
+
end
|
30
|
+
|
31
|
+
# unary op eg. NOT
|
32
|
+
class UnopTree < Tree
|
33
|
+
end
|
34
|
+
|
35
|
+
# Bin ops
|
36
|
+
class BinopTree < Tree
|
37
|
+
end
|
38
|
+
|
39
|
+
class BinopBool < BinopTree
|
40
|
+
end
|
41
|
+
|
42
|
+
class BinopArithm < BinopTree
|
43
|
+
end
|
44
|
+
|
45
|
+
# Arguments or lists
|
46
|
+
class ArgTree < Tree
|
47
|
+
end
|
48
|
+
|
49
|
+
# Numbers (floating point, ints, dec)
|
50
|
+
class FPNumber < Leave
|
51
|
+
end
|
52
|
+
|
53
|
+
# Literals are unquoted words without /
|
54
|
+
class Literal < Leave
|
55
|
+
end
|
56
|
+
|
57
|
+
# Qualit (qualified lits) are words separated by /
|
58
|
+
# path/path/path/attrib
|
59
|
+
class Qualit < Literal
|
60
|
+
end
|
61
|
+
|
62
|
+
# Quoted Strings
|
63
|
+
class QString < Leave
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/odata/filter/error.rb
CHANGED
@@ -1,5 +1,28 @@
|
|
1
|
+
require_relative '../error'
|
2
|
+
|
1
3
|
module OData
|
4
|
+
class SequelAdapterError < StandardError
|
5
|
+
attr_reader :inner
|
6
|
+
def initialize(err)
|
7
|
+
@inner = err
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# exception to OData error bridge
|
12
|
+
module ErrorBridge
|
13
|
+
# return an odata error object wrapping the exception
|
14
|
+
# the odata error object should respond to odata_get for output
|
15
|
+
def odata_error
|
16
|
+
self.class.const_get('ODATA_ERROR_KLASS').new(self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
2
20
|
module Filter
|
21
|
+
class FunctionNotImplemented < StandardError
|
22
|
+
ODATA_ERROR_KLASS = OData::FilterFunctionNotImplementedError
|
23
|
+
include ::OData::ErrorBridge
|
24
|
+
end
|
25
|
+
|
3
26
|
class Parser
|
4
27
|
# Parser errors
|
5
28
|
class Error < StandardError
|
@@ -29,6 +52,16 @@ module OData
|
|
29
52
|
class ErrorWrongColumnName < StandardError
|
30
53
|
end
|
31
54
|
|
55
|
+
# attempt to add a child to a Leave
|
56
|
+
class ErrorLeaveChild < StandardError
|
57
|
+
end
|
58
|
+
|
59
|
+
# invalid function error (literal attach to IdentityFuncTree)
|
60
|
+
class ErrorInvalidFunction < StandardError
|
61
|
+
ODATA_ERROR_KLASS = OData::FilterInvalidFunctionError
|
62
|
+
include ::OData::ErrorBridge
|
63
|
+
end
|
64
|
+
|
32
65
|
# Invalid function arity
|
33
66
|
class ErrorInvalidArity < Error
|
34
67
|
end
|
data/lib/odata/filter/parse.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative './token.rb'
|
2
4
|
require_relative './tree.rb'
|
3
5
|
require_relative './error.rb'
|
@@ -68,24 +70,18 @@ module OData
|
|
68
70
|
each_typed_token(@input) do |tok, typ|
|
69
71
|
case typ
|
70
72
|
when :FuncTree
|
71
|
-
with_accepted(tok, typ)
|
72
|
-
grow_at_cursor(FuncTree.new(tok))
|
73
|
-
end
|
73
|
+
with_accepted(tok, typ) { grow_at_cursor(FuncTree.new(tok)) }
|
74
74
|
when :Delimiter
|
75
75
|
case tok
|
76
76
|
when '('
|
77
77
|
with_accepted(tok, typ) do
|
78
|
-
unless @cursor.is_a? FuncTree
|
79
|
-
grow_at_cursor(IdentityFuncTree.new)
|
80
|
-
end
|
78
|
+
grow_at_cursor(IdentityFuncTree.new) unless @cursor.is_a? FuncTree
|
81
79
|
openarg = ArgTree.new('(')
|
82
80
|
@stack << openarg
|
83
81
|
grow_at_cursor(openarg)
|
84
82
|
end
|
85
83
|
when ')'
|
86
|
-
unless (@cursor = @stack.pop)
|
87
|
-
break invalid_closing_delimiter_error(tok, typ)
|
88
|
-
end
|
84
|
+
break invalid_closing_delimiter_error(tok, typ) unless (@cursor = @stack.pop)
|
89
85
|
|
90
86
|
with_accepted(tok, typ) do
|
91
87
|
@cursor.update_state(tok, typ)
|
@@ -94,9 +90,7 @@ module OData
|
|
94
90
|
end
|
95
91
|
|
96
92
|
when :Separator
|
97
|
-
unless (@cursor = @stack.last)
|
98
|
-
break invalid_separator_error(tok, typ)
|
99
|
-
end
|
93
|
+
break invalid_separator_error(tok, typ) unless (@cursor = @stack.last)
|
100
94
|
|
101
95
|
with_accepted(tok, typ) { @cursor.update_state(tok, typ) }
|
102
96
|
|
data/lib/odata/filter/sequel.rb
CHANGED
@@ -1,19 +1,23 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './base.rb'
|
4
|
+
require_relative './sequel_function_adapter.rb'
|
5
|
+
|
2
6
|
module OData
|
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
19
|
filtexpr = @children.first.leuqes(jh)
|
16
|
-
|
20
|
+
jh.dataset(dtcx).where(filtexpr).select_all(jh.start_model.table_name)
|
17
21
|
end
|
18
22
|
|
19
23
|
def sequel_expr(jh)
|
@@ -26,6 +30,8 @@ module OData
|
|
26
30
|
end
|
27
31
|
|
28
32
|
# For functions... should have a single child---> the argument list
|
33
|
+
# note: Adapter specific function helpers like year() or substringof_sig2()
|
34
|
+
# need to be mixed in on startup (eg. on publish finalize)
|
29
35
|
class FuncTree < Tree
|
30
36
|
def leuqes(jh)
|
31
37
|
case @value
|
@@ -38,28 +44,17 @@ module OData
|
|
38
44
|
when :substringof
|
39
45
|
|
40
46
|
# there are multiple possible argument types (but all should return edm.string)
|
41
|
-
if
|
47
|
+
if args[0].is_a?(QString)
|
42
48
|
# substringof('Rhum', name) -->
|
43
49
|
# name contains substr 'Rhum'
|
44
50
|
Sequel.like(args[1].leuqes(jh),
|
45
51
|
args[0].leuqes_substringof_sig1(jh))
|
46
52
|
# special non standard (ui5 client) case ?
|
47
|
-
elsif
|
53
|
+
elsif args[0].is_a?(Literal) && args[1].is_a?(Literal)
|
48
54
|
Sequel.like(args[1].leuqes(jh),
|
49
55
|
args[0].leuqes_substringof_sig1(jh))
|
50
|
-
elsif
|
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)
|
62
|
-
|
56
|
+
elsif args[1].is_a?(QString)
|
57
|
+
substringof_sig2(jh) # adapter specific
|
63
58
|
else
|
64
59
|
# TODO... actually not supported?
|
65
60
|
raise OData::Filter::Parser::ErrorFunctionArgumentType
|
@@ -75,6 +70,26 @@ module OData
|
|
75
70
|
Sequel.function(:upper, args.first.leuqes(jh))
|
76
71
|
when :tolower
|
77
72
|
Sequel.function(:lower, args.first.leuqes(jh))
|
73
|
+
# all datetime funcs are adapter specific (because sqlite does not have extract)
|
74
|
+
when :year
|
75
|
+
year(jh)
|
76
|
+
when :month
|
77
|
+
month(jh)
|
78
|
+
when :second
|
79
|
+
second(jh)
|
80
|
+
when :minute
|
81
|
+
minute(jh)
|
82
|
+
when :hour
|
83
|
+
hour(jh)
|
84
|
+
when :day
|
85
|
+
day(jh)
|
86
|
+
# math functions
|
87
|
+
when :round
|
88
|
+
Sequel.function(:round, args.first.leuqes(jh))
|
89
|
+
when :floor
|
90
|
+
floor(jh)
|
91
|
+
when :ceiling
|
92
|
+
ceiling(jh)
|
78
93
|
else
|
79
94
|
raise OData::FilterParseError
|
80
95
|
end
|
@@ -167,26 +182,24 @@ module OData
|
|
167
182
|
end
|
168
183
|
|
169
184
|
def leuqes_starts_like(_jh)
|
170
|
-
"#{@value
|
185
|
+
"#{@value}%"
|
171
186
|
end
|
172
187
|
|
173
188
|
def leuqes_ends_like(_jh)
|
174
|
-
"%#{@value
|
189
|
+
"%#{@value}"
|
175
190
|
end
|
176
191
|
|
177
192
|
def leuqes_substringof_sig1(_jh)
|
178
|
-
"%#{@value
|
193
|
+
"%#{@value}%"
|
179
194
|
end
|
180
195
|
end
|
181
196
|
|
182
197
|
# Literals are unquoted words
|
183
198
|
class Literal
|
184
199
|
def leuqes(jh)
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
raise OData::Filter::Parser::ErrorWrongColumnName
|
189
|
-
end
|
200
|
+
raise OData::Filter::Parser::ErrorWrongColumnName unless jh.start_model.db_schema.key?(@value.to_sym)
|
201
|
+
|
202
|
+
Sequel[jh.start_model.table_name][@value.to_sym]
|
190
203
|
end
|
191
204
|
|
192
205
|
# non stantard extensions to support things like
|