safrano 0.3.1 → 0.3.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '068df72ed0e610de674a7f8771f1ff4e5a8e1d4217c5755c08bab78e3c735588'
4
- data.tar.gz: e45fc20c09b7717c1354e80c9e7afd6b4e71a0b27cc97c99b2fd51b99abcb083
3
+ metadata.gz: '048c3efb69194f00a6ed5593c23300ae7b9d46f7d98a546d443bd5d9c5d2c6c8'
4
+ data.tar.gz: 825698055233240072faee6e038e531884e23d938fc1ed9708e98d27cf0db3c6
5
5
  SHA512:
6
- metadata.gz: 709fe03f7939265381c276192a1db812a26a55a14914de89ae220f37b99efe21ed057f05c0e071bb388eef21c01df9a8408557aca6bc19e30a2767752f7d39f4
7
- data.tar.gz: b4fb2033ec0fd0568c23762cbf660f36e879574367c61617c022847fee64747762943139eb91849f8ae762182688c7e424db5545539e1f7896e10c3e6db62113
6
+ metadata.gz: d2697db38029b2701a418a620b74e7b8080d368cd34461073de46065d7057cb3b852cc62795cabb1bbbad82d10607bff96b899beb483a3c0f03e028cf8cb8a31
7
+ data.tar.gz: 12a8674563e67e2f3c4e475d5867f8965e0095a0ff0afea80079f6e7e59c56921abc506d33b07ca483f5f2c16024176a6e3679db26b09e979a9f9fb6fac8d755
@@ -41,7 +41,8 @@ module OData
41
41
  # class methods. They Make heavy use of Sequel::Model functionality
42
42
  # we will add this to our Model classes with "extend" --> self is the Class
43
43
  module EntityClassBase
44
- SINGLE_PK_URL_REGEXP = /\A\('?([\w\s]+)'?\)(.*)/.freeze
44
+ SINGLE_PK_URL_REGEXP = /\A\(\s*'?([\w\s]+)'?\s*\)(.*)/.freeze
45
+ ONLY_INTEGER_RGX = /\A[+-]?\d+\z/
45
46
 
46
47
  attr_reader :nav_collection_url_regexp
47
48
  attr_reader :nav_entity_url_regexp
@@ -406,6 +407,32 @@ module OData
406
407
  %r{\A/(#{@columns.join('|')})(.*)\z}
407
408
  end
408
409
 
410
+ # pkid can be a single value for single-pk models, or an array.
411
+ # type checking/convertion is done in check_odata_key_type
412
+ def find_by_odata_key(pkid)
413
+ # amazingly this works as expected from an Entity.get_related(...) anonymous class
414
+ # without need to redefine primary_key_lookup (returns nil for valid but unrelated keys)
415
+ primary_key_lookup(pkid)
416
+ end
417
+
418
+ # super-minimal type check, but better as nothing
419
+ def check_odata_val_type(val, type)
420
+ case type
421
+ when :integer
422
+ if (val =~ ONLY_INTEGER_RGX)
423
+ [true, Integer(val)]
424
+ else
425
+ [false, val]
426
+ end
427
+ when :string
428
+ [true, val]
429
+ else
430
+ [true, val] # todo...
431
+ end
432
+ rescue StandardError
433
+ [false, val]
434
+ end
435
+
409
436
  # methods related to transitions to next state (cf. walker)
410
437
  module Transitions
411
438
  def transition_end(_match_result)
@@ -418,10 +445,17 @@ module OData
418
445
 
419
446
  def transition_id(match_result)
420
447
  if (id = match_result[1])
421
- if (y = find_by_odata_key(id))
422
- [y, :run]
448
+
449
+ ck, casted_id = check_odata_key(id)
450
+
451
+ if ck
452
+ if (y = find_by_odata_key(casted_id))
453
+ [y, :run]
454
+ else
455
+ [nil, :error, ErrorNotFound]
456
+ end
423
457
  else
424
- [nil, :error, ErrorNotFound]
458
+ [nil, :error, BadRequestError]
425
459
  end
426
460
  else
427
461
  [nil, :error, ServerTransitionError]
@@ -440,24 +474,38 @@ module OData
440
474
  # special handling for composite key
441
475
  module EntityClassMultiPK
442
476
  include EntityClassBase
443
- # id is for composite key, something like fx='aas',fy_w='0001'
444
- def find_by_odata_key(mid)
477
+ # input fx='aas',fy_w='0001'
478
+ # output true, ['aas', '0001'] ... or false when typ-error
479
+ def check_odata_key(mid)
445
480
  # @iuk_rgx is (needs to be) built on start with
446
481
  # collklass.prepare_pk
447
482
  md = @iuk_rgx.match(mid).to_a
448
483
  md.shift # remove first element which is the whole match
449
- # amazingly this works as expected from an Entity.get_related(...) anonymous class
450
- # without need to redefine primary_key_lookup (returns nil for valid but unrelated keys)
451
- primary_key_lookup(md)
484
+ mdc = []
485
+ error = false
486
+ primary_key.each_with_index { |pk, i|
487
+ ck, casted = check_odata_val_type(md[i], db_schema[pk][:type])
488
+ if ck
489
+ mdc << casted
490
+ else
491
+ error = true
492
+ break
493
+ end
494
+ }
495
+ if error
496
+ [false, md]
497
+ else
498
+ [true, mdc]
499
+ end
452
500
  end
453
501
  end
454
502
 
455
503
  # special handling for single key
456
504
  module EntityClassSinglePK
457
505
  include EntityClassBase
458
- # id is really just the value of single pk
459
- def find_by_odata_key(id)
460
- primary_key_lookup(id)
506
+
507
+ def check_odata_key(id)
508
+ check_odata_val_type(id, db_schema[primary_key][:type])
461
509
  end
462
510
  end
463
511
  end
@@ -114,9 +114,9 @@ module OData
114
114
  end
115
115
  grow_at_cursor(unoptr)
116
116
 
117
- when :BinopTree
117
+ when :BinopBool
118
118
  with_accepted(tok, typ) do
119
- binoptr = BinopTree.new(tok)
119
+ binoptr = BinopBool.new(tok)
120
120
  if (prev = @binop_stack.last)
121
121
  # handling of lower precedence binding vs the other
122
122
  # ones(le,gt,eq...)
@@ -129,6 +129,22 @@ module OData
129
129
  end
130
130
  insert_before_cursor(binoptr)
131
131
  end
132
+ when :BinopArithm
133
+ with_accepted(tok, typ) do
134
+ binoptr = BinopArithm.new(tok)
135
+ if (prev = @binop_stack.last)
136
+ # handling of lower precedence binding vs the other
137
+ # ones(le,gt,eq...)
138
+ unless prev.precedence < binoptr.precedence
139
+ @cursor = @binop_stack.pop
140
+ @binop_stack << binoptr
141
+ end
142
+ else
143
+ @binop_stack << binoptr
144
+ end
145
+ insert_before_cursor(binoptr)
146
+ end
147
+
132
148
  when :Literal
133
149
  with_accepted(tok, typ) do
134
150
  @cursor.update_state(tok, typ)
@@ -36,27 +36,30 @@ module OData
36
36
  Sequel.like(args[0].leuqes(jh),
37
37
  args[1].leuqes_ends_like(jh))
38
38
  when :substringof
39
- # there are two possible signatures
40
- if (args[0].is_a?(QString) && args[1].is_a?(Literal))
39
+
40
+ # there are multiple possible argument types (but all should return edm.string)
41
+ if (args[0].is_a?(QString))
41
42
  # substringof('Rhum', name) -->
42
43
  # name contains substr 'Rhum'
43
44
  Sequel.like(args[1].leuqes(jh),
44
- args[0].leuqes_substringof_like(jh))
45
- # according to the spec it should be like that?
46
- # elsif args[1].is_a? QString
47
- ## substringof(name, '__Route du Rhum__') -->
48
- ## '__Route du Rhum__' contains name as a substring
49
- # Sequel.like(args[1].leuqes_substringof_like(dtcx),
50
- # args[0].leuqes(dtcx) )
51
- # or like that?
52
- # elsif args[1].is_a? QString
53
- # # substringof(name, 'Route du Rhum') -->
54
- # # name contains substr 'Rhum' ??
55
- # Sequel.like(args[0].leuqes(dtcx) ,
56
- # args[1].leuqes_substringof_like(dtcx) )
45
+ args[0].leuqes_substringof_sig1(jh))
46
+ # special non standard (ui5 client) case ?
57
47
  elsif (args[0].is_a?(Literal) && args[1].is_a?(Literal))
58
48
  Sequel.like(args[1].leuqes(jh),
59
- args[0].leuqes_substringof_like(jh))
49
+ args[0].leuqes_substringof_sig1(jh))
50
+ elsif (args[1].is_a?(QString))
51
+ # substringof(name, '__Route du Rhum__') -->
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
+
60
63
  else
61
64
  # TODO... actually not supported?
62
65
  raise OData::Filter::Parser::ErrorFunctionArgumentType
@@ -99,8 +102,8 @@ module OData
99
102
  end
100
103
  end
101
104
 
102
- # Bin ops
103
- class BinopTree < Tree
105
+ # logical Bin ops
106
+ class BinopBool
104
107
  def leuqes(jh)
105
108
  leuqes_op = case @value
106
109
  when :eq
@@ -129,6 +132,30 @@ module OData
129
132
  end
130
133
  end
131
134
 
135
+ # Arithmetic Bin ops
136
+ class BinopArithm
137
+ def leuqes(jh)
138
+ leuqes_op = case @value
139
+ when :add
140
+ :+
141
+ when :sub
142
+ :-
143
+ when :mul
144
+ :*
145
+ when :div
146
+ :/
147
+ when :mod
148
+ :%
149
+ else
150
+ raise OData::FilterParseError
151
+ end
152
+
153
+ Sequel::SQL::NumericExpression.new(leuqes_op,
154
+ @children[0].leuqes(jh),
155
+ @children[1].leuqes(jh))
156
+ end
157
+ end
158
+
132
159
  # Arguments or lists
133
160
  class ArgTree
134
161
  end
@@ -147,7 +174,7 @@ module OData
147
174
  "%#{@value.to_s}"
148
175
  end
149
176
 
150
- def leuqes_substringof_like(_jh)
177
+ def leuqes_substringof_sig1(_jh)
151
178
  "%#{@value.to_s}%"
152
179
  end
153
180
  end
@@ -172,9 +199,13 @@ module OData
172
199
  "%#{@value}"
173
200
  end
174
201
 
175
- def leuqes_substringof_like(_jh)
202
+ def leuqes_substringof_sig1(_jh)
176
203
  "%#{@value}%"
177
204
  end
205
+
206
+ def as_string
207
+ @value
208
+ end
178
209
  end
179
210
 
180
211
  # Qualit (qualified lits) are words separated by /
@@ -200,7 +231,7 @@ module OData
200
231
  "%#{@value}"
201
232
  end
202
233
 
203
- def leuqes_substringof_like(_jh)
234
+ def leuqes_substringof_sig1(_jh)
204
235
  "%#{@value}%"
205
236
  end
206
237
  end
@@ -8,11 +8,12 @@ module OData
8
8
  replace substring trim toupper tolower].freeze
9
9
  FUNCRGX = FUNCNAMES.join('|').freeze
10
10
  QSTRINGRGX = /'((?:[^']|(?:\'{2}))*)'/.freeze
11
- BINOPSRGX = '[eE][qQ]|[LlgGNn][eETt]|and|AND|or|OR'.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
12
13
  NOTRGX = 'not|NOT'.freeze
13
14
  FPRGX = '\d+(?:\.\d+)?(?:e[+-]?\d+)?'.freeze
14
15
  QUALITRGX = '\w+(?:\/\w+)+'.freeze
15
- RGX = /(#{FUNCRGX})|([\(\),])|(#{BINOPSRGX})|(#{NOTRGX})|#{QSTRINGRGX}|(#{FPRGX})|(#{QUALITRGX})|(\w+)|(')/.freeze
16
+ RGX = /(#{FUNCRGX})|([\(\),])|(#{BINOBOOL})|(#{BINOARITHM})|(#{NOTRGX})|#{QSTRINGRGX}|(#{FPRGX})|(#{QUALITRGX})|(\w+)|(')/.freeze
16
17
  def each_typed_token(inp)
17
18
  typ = nil
18
19
 
@@ -36,18 +37,20 @@ module OData
36
37
  :Separator
37
38
  end
38
39
  when 2
39
- :BinopTree
40
+ :BinopBool
40
41
  when 3
41
- :UnopTree
42
+ :BinopArithm
42
43
  when 4
43
- :QString
44
+ :UnopTree
44
45
  when 5
45
- :FPNumber
46
+ :QString
46
47
  when 6
47
- :Qualit
48
+ :FPNumber
48
49
  when 7
49
- :Literal
50
+ :Qualit
50
51
  when 8
52
+ :Literal
53
+ when 9
51
54
  :unmatchedQuote
52
55
  end
53
56
  yield found, typ
@@ -119,7 +119,7 @@ module OData
119
119
 
120
120
  def accept?(tok, typ)
121
121
  case typ
122
- when :BinopTree
122
+ when :BinopBool, :BinopArithm
123
123
  true
124
124
  else
125
125
  super(tok, typ)
@@ -193,6 +193,15 @@ module OData
193
193
  super(val.downcase.to_sym)
194
194
  end
195
195
 
196
+ def update_state(_tok, typ)
197
+ case typ
198
+ when :Literal, :Qualit, :QString, :FuncTree, :BinopBool, :BinopArithm, :UnopTree, :FPNumber
199
+ @state = :closed
200
+ end
201
+ end
202
+ end
203
+
204
+ class BinopBool < BinopTree
196
205
  # reference:
197
206
  # OData v4 par 5.1.1.9 Operator Precedence
198
207
  def precedence
@@ -203,28 +212,36 @@ module OData
203
212
  2
204
213
  when :eq, :ne
205
214
  3
206
- when :gt, :ge, :lt, :le
215
+ when :gt, :ge, :lt, :le, :isof
207
216
  4
208
217
  else
209
218
  999
210
219
  end
211
220
  end
212
221
 
213
- def update_state(_tok, typ)
214
- case typ
215
- when :Literal, :Qualit, :QString, :FuncTree, :BinopTree, :UnopTree, :FPNumber
216
- @state = :closed
217
- end
222
+ def edm_type
223
+ :bool
218
224
  end
225
+ end
219
226
 
220
- def edm_type
227
+ class BinopArithm < BinopTree
228
+ # reference:
229
+ # OData v4 par 5.1.1.9 Operator Precedence
230
+ def precedence
221
231
  case @value
222
- when :or, :and, :eq, :ne, :gt, :ge, :lt, :le
223
- :bool
232
+ when :add, :sub
233
+ 5
234
+ when :mul, :div, :mod
235
+ 6
224
236
  else
225
- :any
237
+ 999
226
238
  end
227
239
  end
240
+
241
+ # TODO different num types?
242
+ def edm_type
243
+ :any
244
+ end
228
245
  end
229
246
 
230
247
  # Arguments or lists
@@ -279,7 +296,7 @@ module OData
279
296
  else
280
297
  [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
281
298
  end
282
- when :BinopTree
299
+ when :BinopBool, :BinopArithm
283
300
  true
284
301
  else
285
302
  [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
@@ -295,7 +312,7 @@ module OData
295
312
  class FPNumber < Leave
296
313
  def accept?(tok, typ)
297
314
  case typ
298
- when :Delimiter, :Separator, :BinopTree
315
+ when :Delimiter, :Separator, :BinopBool, :BinopArithm
299
316
  true
300
317
  else
301
318
  [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
@@ -312,7 +329,7 @@ module OData
312
329
  class Literal < Leave
313
330
  def accept?(tok, typ)
314
331
  case typ
315
- when :Delimiter, :Separator, :BinopTree
332
+ when :Delimiter, :Separator, :BinopBool, :BinopArithm
316
333
  true
317
334
  else
318
335
  [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
@@ -353,7 +370,7 @@ module OData
353
370
 
354
371
  def accept?(tok, typ)
355
372
  case typ
356
- when :Delimiter, :Separator, :BinopTree
373
+ when :Delimiter, :Separator, :BinopBool, :BinopArithm
357
374
  true
358
375
  else
359
376
  [false, Parser::ErrorInvalidToken.new(tok, typ, self)]
@@ -145,13 +145,16 @@ module OData
145
145
  @service_base.enable_batch
146
146
  end
147
147
 
148
+ def self.path_prefix(path_pr)
149
+ @service_base.path_prefix path_pr
150
+ end
151
+
148
152
  def self.get_service_base
149
153
  @service_base
150
154
  end
151
155
 
152
- def self.set_servicebase(sbase, p_prefix = '')
156
+ def self.set_servicebase(sbase)
153
157
  @service_base = sbase
154
- @service_base.path_prefix p_prefix
155
158
  @service_base.enable_v1_service
156
159
  @service_base.enable_v2_service
157
160
  end
@@ -242,6 +242,8 @@ module OData
242
242
 
243
243
  def path_prefix(path_pr)
244
244
  @xpath_prefix = path_pr.sub(TRAILING_SLASH, '')
245
+ (@v1.xpath_prefix = @xpath_prefix) if @v1
246
+ (@v2.xpath_prefix = @xpath_prefix) if @v2
245
247
  end
246
248
  # end public API
247
249
 
@@ -251,7 +253,6 @@ module OData
251
253
  other.xtitle = @xtitle
252
254
  other.xname = @xname
253
255
  other.xnamespace = @xnamespace
254
- # other.xuribase = @xuribase
255
256
  other.xpath_prefix = @xpath_prefix
256
257
  other.meta = ServiceMeta.new(other) # hum ... #todo: versions as well ?
257
258
  other.relman = @relman
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safrano
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - D.M.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-30 00:00:00.000000000 Z
11
+ date: 2019-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack