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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/core_ext/Dir/iter.rb +18 -0
  3. data/lib/core_ext/Hash/transform.rb +21 -0
  4. data/lib/core_ext/Integer/edm.rb +13 -0
  5. data/lib/core_ext/REXML/Document/output.rb +16 -0
  6. data/lib/core_ext/String/convert.rb +25 -0
  7. data/lib/core_ext/String/edm.rb +13 -0
  8. data/lib/core_ext/dir.rb +3 -0
  9. data/lib/core_ext/hash.rb +3 -0
  10. data/lib/core_ext/integer.rb +3 -0
  11. data/lib/core_ext/rexml.rb +3 -0
  12. data/lib/core_ext/string.rb +5 -0
  13. data/lib/odata/attribute.rb +15 -10
  14. data/lib/odata/batch.rb +9 -7
  15. data/lib/odata/collection.rb +140 -591
  16. data/lib/odata/collection_filter.rb +18 -42
  17. data/lib/odata/collection_media.rb +111 -54
  18. data/lib/odata/collection_order.rb +5 -2
  19. data/lib/odata/common_logger.rb +2 -0
  20. data/lib/odata/complex_type.rb +152 -0
  21. data/lib/odata/edm/primitive_types.rb +184 -0
  22. data/lib/odata/entity.rb +123 -172
  23. data/lib/odata/error.rb +183 -32
  24. data/lib/odata/expand.rb +20 -17
  25. data/lib/odata/filter/base.rb +74 -0
  26. data/lib/odata/filter/error.rb +49 -6
  27. data/lib/odata/filter/parse.rb +41 -25
  28. data/lib/odata/filter/sequel.rb +133 -62
  29. data/lib/odata/filter/sequel_function_adapter.rb +148 -0
  30. data/lib/odata/filter/token.rb +26 -19
  31. data/lib/odata/filter/tree.rb +106 -52
  32. data/lib/odata/function_import.rb +168 -0
  33. data/lib/odata/model_ext.rb +639 -0
  34. data/lib/odata/navigation_attribute.rb +13 -26
  35. data/lib/odata/relations.rb +5 -5
  36. data/lib/odata/select.rb +17 -5
  37. data/lib/odata/transition.rb +71 -0
  38. data/lib/odata/url_parameters.rb +100 -24
  39. data/lib/odata/walker.rb +20 -10
  40. data/lib/safrano.rb +18 -38
  41. data/lib/safrano/contract.rb +143 -0
  42. data/lib/safrano/core.rb +23 -107
  43. data/lib/safrano/core_ext.rb +13 -0
  44. data/lib/safrano/deprecation.rb +73 -0
  45. data/lib/safrano/multipart.rb +29 -33
  46. data/lib/safrano/rack_app.rb +66 -65
  47. data/lib/safrano/{odata_rack_builder.rb → rack_builder.rb} +18 -2
  48. data/lib/safrano/request.rb +96 -45
  49. data/lib/safrano/response.rb +4 -2
  50. data/lib/safrano/sequel_join_by_paths.rb +2 -2
  51. data/lib/safrano/service.rb +240 -130
  52. data/lib/safrano/version.rb +3 -1
  53. data/lib/sequel/plugins/join_by_paths.rb +6 -19
  54. 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
@@ -1,19 +1,24 @@
1
- # top level OData namespace
2
- module OData
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].freeze
9
- FUNCRGX = FUNCNAMES.join('|').freeze
10
- QSTRINGRGX = /'((?:[^']|(?:\'{2}))*)'/.freeze
11
- BINOBOOL = '[eE][qQ]|[LlgGNn][eETt]|[aA][nN][dD]|[oO][rR]'.freeze
12
- BINOARITHM = '[aA][dD][dD]|[sS][uU][bB]|[mM][uU][lL]|[dD][iI][vV]|[mM][oO][dD]'.freeze
13
- NOTRGX = 'not|NOT'.freeze
14
- FPRGX = '\d+(?:\.\d+)?(?:e[+-]?\d+)?'.freeze
15
- QUALITRGX = '\w+(?:\/\w+)+'.freeze
16
- RGX = /(#{FUNCRGX})|([\(\),])|(#{BINOBOOL})|(#{BINOARITHM})|(#{NOTRGX})|#{QSTRINGRGX}|(#{FPRGX})|(#{QUALITRGX})|(\w+)|(')/.freeze
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
- :BinopArithm
47
+ :BinopBool
43
48
  when 4
44
- :UnopTree
49
+ :BinopArithm
45
50
  when 5
46
- :QString
51
+ :UnopTree
47
52
  when 6
48
- :FPNumber
53
+ :QString
49
54
  when 7
50
- :Qualit
55
+ :FPNumber
51
56
  when 8
52
- :Literal
57
+ :Qualit
53
58
  when 9
59
+ :Literal
60
+ when 10
54
61
  :unmatchedQuote
55
62
  end
56
63
  yield found, typ
@@ -1,10 +1,14 @@
1
- require_relative './error.rb'
1
+ # frozen_string_literal: true
2
2
 
3
- module OData
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 < Node
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
- [false, Parser::ErrorInvalidToken(tok, typ)]
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 < Node
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, :UnopTree, :FPNumber
56
- true
68
+ when :Literal, :NullLiteral, :Qualit, :QString, :FuncTree, :ArgTree,
69
+ :UnopTree, :FPNumber
70
+ nil
57
71
  when :Delimiter
58
72
  if tok == '('
59
- true
73
+ nil
60
74
  else
61
- [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
75
+ Parser::ErrorInvalidToken.new(tok, typ, self)
62
76
  end
63
77
  else
64
- [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
78
+ Parser::ErrorInvalidToken.new(tok, typ, self)
65
79
  end
66
80
  end
67
81
 
68
82
  def check_types
69
- @children.each(&:check_types)
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 < RootTree
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 < Tree
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
- true
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
- raise Parser::ErrorInvalidArgumentType.new(self,
135
- expected: :string,
136
- actual: argtyp)
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 < FuncTree
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 < Tree
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 < Tree
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 < BinopTree
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 < BinopTree
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 < Tree
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.arity_full?(@children.size)
272
- true
310
+ if (@parent.class == IdentityFuncTree) or
311
+ (@parent.arity_full?(@children.size))
312
+
313
+ nil
273
314
  else
274
- [false, Parser::ErrorInvalidArity.new(tok, typ, self)]
315
+ Parser::ErrorInvalidArity.new(tok, typ, self)
275
316
  end
276
317
  else
277
- [false, Parser::ErrorUnmatchedClose.new(tok, typ, self)]
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
- true
326
+ nil
282
327
  elsif @state == :sep
283
- [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
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
- [false, Parser::ErrorInvalidArity.new(tok, typ, self)]
291
- else
292
- true
333
+ Parser::ErrorInvalidArity.new(tok, typ, self)
293
334
  end
294
335
  else
295
- [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
336
+ Parser::ErrorInvalidToken.new(tok, typ, self)
296
337
  end
297
338
  when :BinopBool, :BinopArithm
298
- true
339
+ nil
299
340
  else
300
- [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
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 < Leave
351
+ class FPNumber
311
352
  def accept?(tok, typ)
312
353
  case typ
313
354
  when :Delimiter, :Separator, :BinopBool, :BinopArithm
314
- true
355
+ nil
315
356
  else
316
- [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
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 < Leave
368
+ class Literal
328
369
  def accept?(tok, typ)
329
370
  case typ
330
371
  when :Delimiter, :Separator, :BinopBool, :BinopArithm
331
- true
372
+ nil
332
373
  else
333
- [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
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 < Literal
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 < Leave
360
- DBL_QO = "''".freeze
361
- SI_QO = "'".freeze
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
- true
424
+ nil
371
425
  else
372
- [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
426
+ Parser::ErrorInvalidToken.new(tok, typ, self)
373
427
  end
374
428
  end
375
429