safrano 0.3.3 → 0.4.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/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
|