safrano 0.4.2 → 0.5.0
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 +9 -7
- data/lib/odata/collection.rb +140 -591
- data/lib/odata/collection_filter.rb +18 -42
- data/lib/odata/collection_media.rb +111 -54
- data/lib/odata/collection_order.rb +5 -2
- data/lib/odata/common_logger.rb +2 -0
- data/lib/odata/complex_type.rb +152 -0
- data/lib/odata/edm/primitive_types.rb +184 -0
- data/lib/odata/entity.rb +123 -172
- data/lib/odata/error.rb +183 -32
- data/lib/odata/expand.rb +20 -17
- data/lib/odata/filter/base.rb +74 -0
- data/lib/odata/filter/error.rb +49 -6
- data/lib/odata/filter/parse.rb +41 -25
- data/lib/odata/filter/sequel.rb +133 -62
- data/lib/odata/filter/sequel_function_adapter.rb +148 -0
- data/lib/odata/filter/token.rb +26 -19
- data/lib/odata/filter/tree.rb +106 -52
- data/lib/odata/function_import.rb +168 -0
- data/lib/odata/model_ext.rb +639 -0
- data/lib/odata/navigation_attribute.rb +13 -26
- data/lib/odata/relations.rb +5 -5
- data/lib/odata/select.rb +17 -5
- data/lib/odata/transition.rb +71 -0
- data/lib/odata/url_parameters.rb +100 -24
- data/lib/odata/walker.rb +20 -10
- data/lib/safrano.rb +18 -38
- data/lib/safrano/contract.rb +143 -0
- data/lib/safrano/core.rb +23 -107
- data/lib/safrano/core_ext.rb +13 -0
- data/lib/safrano/deprecation.rb +73 -0
- data/lib/safrano/multipart.rb +29 -33
- data/lib/safrano/rack_app.rb +66 -65
- data/lib/safrano/{odata_rack_builder.rb → rack_builder.rb} +18 -2
- data/lib/safrano/request.rb +96 -45
- data/lib/safrano/response.rb +4 -2
- data/lib/safrano/sequel_join_by_paths.rb +2 -2
- data/lib/safrano/service.rb +240 -130
- data/lib/safrano/version.rb +3 -1
- data/lib/sequel/plugins/join_by_paths.rb +6 -19
- metadata +32 -11
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative './tree'
|
4
|
+
require_relative './sequel'
|
5
|
+
|
6
|
+
module Safrano
|
7
|
+
module Filter
|
8
|
+
# sqlite adapter specific function handler
|
9
|
+
module FuncTreeSqlite
|
10
|
+
def substringof_sig2(jh)
|
11
|
+
# substringof(name, '__Route du Rhum__') -->
|
12
|
+
# '__Route du Rhum__' contains name as a substring
|
13
|
+
# sqlite uses instr()
|
14
|
+
Contract.collect_result!(args[1].leuqes(jh),
|
15
|
+
args[0].leuqes(jh)) do |l1, l0|
|
16
|
+
substr_func = Sequel.function(:instr, l1, l0)
|
17
|
+
Sequel::SQL::BooleanExpression.new(:>, substr_func, 0)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
# %d day of month: 00
|
21
|
+
# %f fractional seconds: SS.SSS
|
22
|
+
# %H hour: 00-24
|
23
|
+
# %j day of year: 001-366
|
24
|
+
# %J Julian day number
|
25
|
+
# %m month: 01-12
|
26
|
+
# %M minute: 00-59
|
27
|
+
# %s seconds since 1970-01-01
|
28
|
+
# %S seconds: 00-59
|
29
|
+
# %w day of week 0-6 with Sunday==0
|
30
|
+
# %W week of year: 00-53
|
31
|
+
# %Y year: 0000-9999
|
32
|
+
# %% %
|
33
|
+
|
34
|
+
# sqlite does not have extract but recommends to use strftime
|
35
|
+
def year(lq)
|
36
|
+
Sequel.function(:strftime, '%Y', lq).cast(:integer)
|
37
|
+
end
|
38
|
+
|
39
|
+
def month(lq)
|
40
|
+
Sequel.function(:strftime, '%m', lq).cast(:integer)
|
41
|
+
end
|
42
|
+
|
43
|
+
def second(lq)
|
44
|
+
Sequel.function(:strftime, '%S', lq).cast(:integer)
|
45
|
+
end
|
46
|
+
|
47
|
+
def minute(lq)
|
48
|
+
Sequel.function(:strftime, '%M', lq).cast(:integer)
|
49
|
+
end
|
50
|
+
|
51
|
+
def hour(lq)
|
52
|
+
Sequel.function(:strftime, '%H', lq).cast(:integer)
|
53
|
+
end
|
54
|
+
|
55
|
+
def day(lq)
|
56
|
+
Sequel.function(:strftime, '%d', lq).cast(:integer)
|
57
|
+
end
|
58
|
+
|
59
|
+
def floor(_lq)
|
60
|
+
Safrano::FilterFunctionNotImplementedError.new("$filter function 'floor' is not implemented in sqlite adapter")
|
61
|
+
end
|
62
|
+
|
63
|
+
def ceiling(_lq)
|
64
|
+
Safrano::FilterFunctionNotImplementedError.new("$filter function 'ceiling' is not implemented in sqlite adapter")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
# re-useable module with math floor/ceil functions for those adapters having these SQL funcs
|
68
|
+
module MathFloorCeilFuncTree
|
69
|
+
def floor(lq)
|
70
|
+
success Sequel.function(:floor, lq)
|
71
|
+
end
|
72
|
+
|
73
|
+
def ceiling(lq)
|
74
|
+
success Sequel.function(:ceil, lq)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# re-useable module with Datetime functions with extract()
|
79
|
+
module DateTimeFuncTreeExtract
|
80
|
+
def year(lq)
|
81
|
+
lq.extract(:year)
|
82
|
+
end
|
83
|
+
|
84
|
+
def month(lq)
|
85
|
+
lq.extract(:month)
|
86
|
+
end
|
87
|
+
|
88
|
+
def second(lq)
|
89
|
+
lq.extract(:second)
|
90
|
+
end
|
91
|
+
|
92
|
+
def minute(lq)
|
93
|
+
lq.extract(:minute)
|
94
|
+
end
|
95
|
+
|
96
|
+
def hour(lq)
|
97
|
+
lq.extract(:hour)
|
98
|
+
end
|
99
|
+
|
100
|
+
def day(lq)
|
101
|
+
lq.extract(:day)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# postgresql adapter specific function handler
|
106
|
+
module FuncTreePostgres
|
107
|
+
def substringof_sig2(jh)
|
108
|
+
# substringof(name, '__Route du Rhum__') -->
|
109
|
+
# '__Route du Rhum__' contains name as a substring
|
110
|
+
# postgres does not know instr() but has strpos
|
111
|
+
Contract.collect_result!(args[1].leuqes(jh),
|
112
|
+
args[0].leuqes(jh)) do |l1, l0|
|
113
|
+
substr_func = Sequel.function(:strpos, l1, l0)
|
114
|
+
Sequel::SQL::BooleanExpression.new(:>, substr_func, 0)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# postgres uses extract()
|
119
|
+
include DateTimeFuncTreeExtract
|
120
|
+
|
121
|
+
# postgres has floor/ceil funcs
|
122
|
+
include MathFloorCeilFuncTree
|
123
|
+
end
|
124
|
+
|
125
|
+
# default adapter function handler for all others... try to use the most common version
|
126
|
+
# :substring --> instr because here is seems Postgres is special
|
127
|
+
# datetime funcs --> exctract, here sqlite is special(uses format)
|
128
|
+
# note: we dont test this, provided as an example/template, might work eg for mysql
|
129
|
+
module FuncTreeDefault
|
130
|
+
def substringof_sig2(jh)
|
131
|
+
# substringof(name, '__Route du Rhum__') -->
|
132
|
+
# '__Route du Rhum__' contains name as a substring
|
133
|
+
# instr() seems to be the most common substring func
|
134
|
+
Contract.collect_result!(args[1].leuqes(jh),
|
135
|
+
args[0].leuqes(jh)) do |l1, l0|
|
136
|
+
substr_func = Sequel.function(:instr, l1, l0)
|
137
|
+
Sequel::SQL::BooleanExpression.new(:>, substr_func, 0)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# XYZ uses extract() ?
|
142
|
+
include DateTimeFuncTreeExtract
|
143
|
+
|
144
|
+
# ... assume SQL
|
145
|
+
include MathFloorCeilFuncTree
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/odata/filter/token.rb
CHANGED
@@ -1,19 +1,24 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Safrano
|
3
4
|
module Filter
|
4
5
|
class Parser
|
5
6
|
# Input tokenizer
|
6
7
|
module Token
|
7
8
|
FUNCNAMES = %w[concat substringof endswith startswith length indexof
|
8
|
-
replace substring trim toupper tolower
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
9
|
+
replace substring trim toupper tolower
|
10
|
+
day hour minute month second year
|
11
|
+
round floor ceiling].freeze
|
12
|
+
FUNCRGX = FUNCNAMES.join('|')
|
13
|
+
NULLRGX = 'null|NULL|Null'
|
14
|
+
QSTRINGRGX = /'((?:[^']|(?:'{2}))*)'/.freeze
|
15
|
+
BINOBOOL = '[eE][qQ]|[LlgGNn][eETt]|[aA][nN][dD]|[oO][rR]'
|
16
|
+
BINOARITHM = '[aA][dD][dD]|[sS][uU][bB]|[mM][uU][lL]|[dD][iI][vV]|[mM][oO][dD]'
|
17
|
+
NOTRGX = 'not|NOT|Not'
|
18
|
+
FPRGX = '\d+(?:\.\d+)?(?:e[+-]?\d+)?'
|
19
|
+
QUALITRGX = '\w+(?:\/\w+)+'
|
20
|
+
RGX = /(#{FUNCRGX})|(#{NULLRGX})|([\(\),])|(#{BINOBOOL})\s+|(#{BINOARITHM})|(#{NOTRGX})|#{QSTRINGRGX}|(#{FPRGX})|(#{QUALITRGX})|(\w+)|(')/.freeze
|
21
|
+
|
17
22
|
def each_typed_token(inp)
|
18
23
|
typ = nil
|
19
24
|
|
@@ -30,27 +35,29 @@ module OData
|
|
30
35
|
when 0
|
31
36
|
:FuncTree
|
32
37
|
when 1
|
38
|
+
:NullLiteral
|
39
|
+
when 2
|
33
40
|
case found
|
34
41
|
when '(', ')'
|
35
42
|
:Delimiter
|
36
43
|
when ','
|
37
44
|
:Separator
|
38
45
|
end
|
39
|
-
when 2
|
40
|
-
:BinopBool
|
41
46
|
when 3
|
42
|
-
:
|
47
|
+
:BinopBool
|
43
48
|
when 4
|
44
|
-
:
|
49
|
+
:BinopArithm
|
45
50
|
when 5
|
46
|
-
:
|
51
|
+
:UnopTree
|
47
52
|
when 6
|
48
|
-
:
|
53
|
+
:QString
|
49
54
|
when 7
|
50
|
-
:
|
55
|
+
:FPNumber
|
51
56
|
when 8
|
52
|
-
:
|
57
|
+
:Qualit
|
53
58
|
when 9
|
59
|
+
:Literal
|
60
|
+
when 10
|
54
61
|
:unmatchedQuote
|
55
62
|
end
|
56
63
|
yield found, typ
|
data/lib/odata/filter/tree.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative './base'
|
4
|
+
require_relative './error'
|
5
|
+
|
6
|
+
module Safrano
|
4
7
|
module Filter
|
5
8
|
# Base class for Leaves, Trees, RootTrees etc
|
6
9
|
class Node
|
7
10
|
attr_reader :value
|
11
|
+
|
8
12
|
def initialize(val, &block)
|
9
13
|
@value = val
|
10
14
|
instance_eval(&block) if block_given?
|
@@ -16,19 +20,26 @@ module OData
|
|
16
20
|
end
|
17
21
|
|
18
22
|
# Leaves are Nodes with a parent but no children
|
19
|
-
class Leave
|
23
|
+
class Leave
|
20
24
|
attr_accessor :parent
|
25
|
+
|
26
|
+
# nil is considered as accepted, otherwise non-nil=the error
|
21
27
|
def accept?(tok, typ)
|
22
|
-
|
28
|
+
Parser::ErrorInvalidToken(tok, typ)
|
23
29
|
end
|
24
30
|
|
25
31
|
def check_types; end
|
32
|
+
|
33
|
+
def attach(_child)
|
34
|
+
Safrano::Filter::Parser::ErrorLeaveChild
|
35
|
+
end
|
26
36
|
end
|
27
37
|
|
28
38
|
# RootTrees have childrens but no parent
|
29
|
-
class RootTree
|
39
|
+
class RootTree
|
30
40
|
attr_reader :children
|
31
41
|
attr_accessor :state
|
42
|
+
|
32
43
|
def initialize(val: :root, &block)
|
33
44
|
@children = []
|
34
45
|
super(val, &block)
|
@@ -37,6 +48,7 @@ module OData
|
|
37
48
|
def attach(child)
|
38
49
|
child.parent = self
|
39
50
|
@children << child
|
51
|
+
Contract::OK
|
40
52
|
end
|
41
53
|
|
42
54
|
def detach(child)
|
@@ -50,28 +62,32 @@ module OData
|
|
50
62
|
|
51
63
|
def update_state(tok, typ) end
|
52
64
|
|
65
|
+
# nil is considered as accepted, otherwise non-nil=the error
|
53
66
|
def accept?(tok, typ)
|
54
67
|
case typ
|
55
|
-
when :Literal, :Qualit, :QString, :FuncTree, :ArgTree,
|
56
|
-
|
68
|
+
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :ArgTree,
|
69
|
+
:UnopTree, :FPNumber
|
70
|
+
nil
|
57
71
|
when :Delimiter
|
58
72
|
if tok == '('
|
59
|
-
|
73
|
+
nil
|
60
74
|
else
|
61
|
-
|
75
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
62
76
|
end
|
63
77
|
else
|
64
|
-
|
78
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
65
79
|
end
|
66
80
|
end
|
67
81
|
|
68
82
|
def check_types
|
69
|
-
|
83
|
+
err = nil
|
84
|
+
@children.find { |c| (err = c.check_types) }
|
85
|
+
err
|
70
86
|
end
|
71
87
|
end
|
72
88
|
|
73
89
|
# Tree's have Parent and children
|
74
|
-
class Tree
|
90
|
+
class Tree
|
75
91
|
attr_accessor :parent
|
76
92
|
|
77
93
|
def initialize(val)
|
@@ -80,7 +96,7 @@ module OData
|
|
80
96
|
end
|
81
97
|
|
82
98
|
# For functions... should have a single child---> the argument list
|
83
|
-
class FuncTree
|
99
|
+
class FuncTree
|
84
100
|
def initialize(val)
|
85
101
|
super(val.downcase.to_sym)
|
86
102
|
end
|
@@ -117,10 +133,11 @@ module OData
|
|
117
133
|
end
|
118
134
|
end
|
119
135
|
|
136
|
+
# nil is considered as accepted, otherwise non-nil=the error
|
120
137
|
def accept?(tok, typ)
|
121
138
|
case typ
|
122
139
|
when :BinopBool, :BinopArithm
|
123
|
-
|
140
|
+
nil
|
124
141
|
else
|
125
142
|
super(tok, typ)
|
126
143
|
end
|
@@ -131,9 +148,9 @@ module OData
|
|
131
148
|
when :length
|
132
149
|
argtyp = args.first.edm_type
|
133
150
|
if (argtyp != :any) && (argtyp != :string)
|
134
|
-
|
135
|
-
|
136
|
-
|
151
|
+
return Parser::ErrorInvalidArgumentType.new(self,
|
152
|
+
expected: :string,
|
153
|
+
actual: argtyp)
|
137
154
|
end
|
138
155
|
end
|
139
156
|
super
|
@@ -143,24 +160,44 @@ module OData
|
|
143
160
|
# Indentity Func to use as "parent" func of parenthesis expressions
|
144
161
|
# --> allow to handle generically parenthesis always as argument of
|
145
162
|
# some function
|
146
|
-
class IdentityFuncTree
|
163
|
+
class IdentityFuncTree
|
147
164
|
def initialize
|
148
165
|
super(:__indentity)
|
149
166
|
end
|
150
167
|
|
151
168
|
# we can have parenthesis with one expression inside everywhere
|
152
169
|
# only in FuncTree this is redefined for the function's arity
|
170
|
+
# Note: if you change this method, please also update arity_full_monkey?
|
171
|
+
# see below
|
153
172
|
def arity_full?(cursize)
|
154
173
|
cursize >= 1
|
155
174
|
end
|
156
175
|
|
176
|
+
# this is for testing only.
|
177
|
+
# see 99_threadsafe_tc.rb
|
178
|
+
# there we will monkey patch arity_full? by adding some sleeping
|
179
|
+
# to easily slow down a given test-thread (while the other one runs normaly)
|
180
|
+
#
|
181
|
+
# The rule is to keep this method here exactly same as the original
|
182
|
+
# "productive" one
|
183
|
+
#
|
184
|
+
# With this trick we can test threadsafeness without touching
|
185
|
+
# "productive" code
|
186
|
+
def arity_full_monkey?(cursize)
|
187
|
+
cursize >= 1
|
188
|
+
end
|
189
|
+
|
157
190
|
def edm_type
|
158
191
|
@children.first.edm_type
|
159
192
|
end
|
193
|
+
|
194
|
+
def ==(other)
|
195
|
+
@children == other.children
|
196
|
+
end
|
160
197
|
end
|
161
198
|
|
162
199
|
# unary op eg. NOT
|
163
|
-
class UnopTree
|
200
|
+
class UnopTree
|
164
201
|
def initialize(val)
|
165
202
|
super(val.downcase.to_sym)
|
166
203
|
end
|
@@ -187,7 +224,7 @@ module OData
|
|
187
224
|
end
|
188
225
|
|
189
226
|
# Bin ops
|
190
|
-
class BinopTree
|
227
|
+
class BinopTree
|
191
228
|
def initialize(val)
|
192
229
|
@state = :open
|
193
230
|
super(val.downcase.to_sym)
|
@@ -195,13 +232,13 @@ module OData
|
|
195
232
|
|
196
233
|
def update_state(_tok, typ)
|
197
234
|
case typ
|
198
|
-
when :Literal, :Qualit, :QString, :FuncTree, :BinopBool, :BinopArithm, :UnopTree, :FPNumber
|
235
|
+
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :BinopBool, :BinopArithm, :UnopTree, :FPNumber
|
199
236
|
@state = :closed
|
200
237
|
end
|
201
238
|
end
|
202
239
|
end
|
203
240
|
|
204
|
-
class BinopBool
|
241
|
+
class BinopBool
|
205
242
|
# reference:
|
206
243
|
# OData v4 par 5.1.1.9 Operator Precedence
|
207
244
|
def precedence
|
@@ -224,7 +261,7 @@ module OData
|
|
224
261
|
end
|
225
262
|
end
|
226
263
|
|
227
|
-
class BinopArithm
|
264
|
+
class BinopArithm
|
228
265
|
# reference:
|
229
266
|
# OData v4 par 5.1.1.9 Operator Precedence
|
230
267
|
def precedence
|
@@ -245,8 +282,9 @@ module OData
|
|
245
282
|
end
|
246
283
|
|
247
284
|
# Arguments or lists
|
248
|
-
class ArgTree
|
285
|
+
class ArgTree
|
249
286
|
attr_reader :type
|
287
|
+
|
250
288
|
def initialize(val)
|
251
289
|
@type = :expression
|
252
290
|
@state = :open
|
@@ -259,45 +297,48 @@ module OData
|
|
259
297
|
@state = :closed
|
260
298
|
when :Separator
|
261
299
|
@state = :sep
|
262
|
-
when :Literal, :Qualit, :QString, :FuncTree, :FPNumber
|
300
|
+
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber
|
263
301
|
@state = :val
|
264
302
|
end
|
265
303
|
end
|
266
304
|
|
305
|
+
# nil is considered as accepted, otherwise non-nil=the error
|
267
306
|
def accept?(tok, typ)
|
268
307
|
case typ
|
269
308
|
when :Delimiter
|
270
309
|
if @value == '(' && tok == ')' && @state != :closed
|
271
|
-
if @parent.
|
272
|
-
|
310
|
+
if (@parent.class == IdentityFuncTree) or
|
311
|
+
(@parent.arity_full?(@children.size))
|
312
|
+
|
313
|
+
nil
|
273
314
|
else
|
274
|
-
|
315
|
+
Parser::ErrorInvalidArity.new(tok, typ, self)
|
275
316
|
end
|
276
317
|
else
|
277
|
-
|
318
|
+
if @value == '(' && tok == '(' && @state == :open
|
319
|
+
nil
|
320
|
+
else
|
321
|
+
Parser::ErrorUnmatchedClose.new(tok, typ, self)
|
322
|
+
end
|
278
323
|
end
|
279
324
|
when :Separator
|
280
325
|
if @value == '(' && tok == ',' && @state == :val
|
281
|
-
|
326
|
+
nil
|
282
327
|
elsif @state == :sep
|
283
|
-
|
284
|
-
else
|
285
|
-
true
|
328
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
286
329
|
end
|
287
|
-
when :Literal, :Qualit, :QString, :FuncTree, :FPNumber
|
330
|
+
when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :FPNumber
|
288
331
|
if (@state == :open) || (@state == :sep)
|
289
332
|
if @parent.arity_full?(@children.size)
|
290
|
-
|
291
|
-
else
|
292
|
-
true
|
333
|
+
Parser::ErrorInvalidArity.new(tok, typ, self)
|
293
334
|
end
|
294
335
|
else
|
295
|
-
|
336
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
296
337
|
end
|
297
338
|
when :BinopBool, :BinopArithm
|
298
|
-
|
339
|
+
nil
|
299
340
|
else
|
300
|
-
|
341
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
301
342
|
end
|
302
343
|
end
|
303
344
|
|
@@ -307,13 +348,13 @@ module OData
|
|
307
348
|
end
|
308
349
|
|
309
350
|
# Numbers (floating point, ints, dec)
|
310
|
-
class FPNumber
|
351
|
+
class FPNumber
|
311
352
|
def accept?(tok, typ)
|
312
353
|
case typ
|
313
354
|
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
314
|
-
|
355
|
+
nil
|
315
356
|
else
|
316
|
-
|
357
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
317
358
|
end
|
318
359
|
end
|
319
360
|
|
@@ -324,27 +365,40 @@ module OData
|
|
324
365
|
end
|
325
366
|
|
326
367
|
# Literals are unquoted words without /
|
327
|
-
class Literal
|
368
|
+
class Literal
|
328
369
|
def accept?(tok, typ)
|
329
370
|
case typ
|
330
371
|
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
331
|
-
|
372
|
+
nil
|
332
373
|
else
|
333
|
-
|
374
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
334
375
|
end
|
335
376
|
end
|
336
377
|
|
337
378
|
def edm_type
|
338
379
|
:any
|
339
380
|
end
|
381
|
+
|
382
|
+
# error, Literal are leaves
|
383
|
+
# when the child is a IdentityFuncTree then this looks like
|
384
|
+
# an attempt to use a unknown function, eg. ceil(Total)
|
385
|
+
# instead of ceiling(Total)
|
386
|
+
def attach(child)
|
387
|
+
if child.is_a? Safrano::Filter::IdentityFuncTree
|
388
|
+
Safrano::FilterUnknownFunctionError.new(value)
|
389
|
+
else
|
390
|
+
super
|
391
|
+
end
|
392
|
+
end
|
340
393
|
end
|
341
394
|
|
342
395
|
# Qualit (qualified lits) are words separated by /
|
343
396
|
# path/path/path/attrib
|
344
|
-
class Qualit
|
397
|
+
class Qualit
|
345
398
|
REGEXP = /((?:\w+\/)+)(\w+)/.freeze
|
346
399
|
attr_reader :path
|
347
400
|
attr_reader :attrib
|
401
|
+
|
348
402
|
def initialize(val)
|
349
403
|
super(val)
|
350
404
|
# split into path + attrib
|
@@ -356,9 +410,9 @@ module OData
|
|
356
410
|
end
|
357
411
|
|
358
412
|
# Quoted Strings
|
359
|
-
class QString
|
360
|
-
DBL_QO = "''"
|
361
|
-
SI_QO = "'"
|
413
|
+
class QString
|
414
|
+
DBL_QO = "''"
|
415
|
+
SI_QO = "'"
|
362
416
|
def initialize(val)
|
363
417
|
# unescape double quotes
|
364
418
|
super(val.gsub(DBL_QO, SI_QO))
|
@@ -367,9 +421,9 @@ module OData
|
|
367
421
|
def accept?(tok, typ)
|
368
422
|
case typ
|
369
423
|
when :Delimiter, :Separator, :BinopBool, :BinopArithm
|
370
|
-
|
424
|
+
nil
|
371
425
|
else
|
372
|
-
|
426
|
+
Parser::ErrorInvalidToken.new(tok, typ, self)
|
373
427
|
end
|
374
428
|
end
|
375
429
|
|