safrano 0.8.0 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/core_ext/Date/format.rb +1 -0
- data/lib/core_ext/DateTime/format.rb +1 -0
- data/lib/core_ext/Hash/transform.rb +2 -2
- data/lib/core_ext/Time/format.rb +1 -1
- data/lib/odata/attribute.rb +4 -5
- data/lib/odata/batch.rb +3 -1
- data/lib/odata/collection.rb +3 -0
- data/lib/odata/collection_media.rb +5 -4
- data/lib/odata/complex_type.rb +3 -1
- data/lib/odata/entity.rb +7 -3
- data/lib/odata/error.rb +13 -5
- data/lib/odata/filter/base.rb +1 -0
- data/lib/odata/filter/error.rb +3 -0
- data/lib/odata/filter/sequel.rb +6 -10
- data/lib/odata/filter/sequel_datetime_adapter.rb +1 -0
- data/lib/odata/filter/sequel_function_adapter.rb +1 -0
- data/lib/odata/filter/tree.rb +28 -26
- data/lib/odata/function_import.rb +7 -0
- data/lib/odata/model_ext.rb +17 -14
- data/lib/odata/navigation_attribute.rb +6 -0
- data/lib/odata/request/json.rb +1 -0
- data/lib/odata/transition.rb +134 -27
- data/lib/odata/walker.rb +5 -25
- data/lib/safrano/rack_app.rb +3 -3
- data/lib/safrano/rack_builder.rb +37 -37
- data/lib/safrano/request.rb +5 -4
- data/lib/safrano/service.rb +7 -2
- data/lib/safrano/type_mapping.rb +6 -3
- data/lib/safrano/version.rb +1 -1
- data/lib/sequel/plugins/join_by_paths.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 351f486db0cd9cfa46ac9770e739dce4fcf9b4fe4a564799febc2d23eea2f713
|
4
|
+
data.tar.gz: b214e28350f97dc2a442950803773accb428f8316de8482884c56c81b0aab754
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e1a12730936b436c30c909970b8d31e90358b87f10242f76c7f8b238def70391fb20bc0568825495045a095db6d799b8da8c07c9f524914eecf0a072d083d40
|
7
|
+
data.tar.gz: 8a1fdd4ca3c8d4512bd6ef6890a653a1ad1e052daad13ee5355d51248c652a938cf75df16d42d97a42ee6149f111e69cce215102b69c414b58920cf4b62180a2
|
data/lib/core_ext/Date/format.rb
CHANGED
data/lib/core_ext/Time/format.rb
CHANGED
@@ -15,7 +15,7 @@ module Safrano
|
|
15
15
|
return unless (md = instr.match(REGEX))
|
16
16
|
|
17
17
|
sec, milli = md[1].to_i.divmod(1000)
|
18
|
-
secm = milli.zero? ? sec : sec + Float(milli) / 1000
|
18
|
+
secm = milli.zero? ? sec : sec + (Float(milli) / 1000)
|
19
19
|
if md[3].nil? # no offset
|
20
20
|
# ::Time.at(sec, milli, :millisecond) # not supported in ruby 2.4
|
21
21
|
::Time.gm(1970, 1, 1) + secm
|
data/lib/odata/attribute.rb
CHANGED
@@ -13,6 +13,7 @@ module Safrano
|
|
13
13
|
def initialize(entity, name)
|
14
14
|
@entity = entity
|
15
15
|
@name = name
|
16
|
+
@allowed_transitions = ALLOWED_TRANSITIONS
|
16
17
|
end
|
17
18
|
|
18
19
|
def value
|
@@ -20,11 +21,7 @@ module Safrano
|
|
20
21
|
# currently it is just set to make some minimal testcase work
|
21
22
|
# See also model_ext.rb
|
22
23
|
case (v = @entity.values[@name.to_sym])
|
23
|
-
when Date
|
24
|
-
v.to_edm_json
|
25
|
-
when Time
|
26
|
-
v.to_edm_json
|
27
|
-
when DateTime
|
24
|
+
when Date, Time, DateTime
|
28
25
|
v.to_edm_json
|
29
26
|
else
|
30
27
|
v
|
@@ -68,6 +65,8 @@ module Safrano
|
|
68
65
|
def allowed_transitions
|
69
66
|
Transitions::ALLOWED_TRANSITIONS
|
70
67
|
end
|
68
|
+
|
69
|
+
include Safrano::Transitions::GetNextTrans::BySimpleDetect
|
71
70
|
end
|
72
71
|
include Transitions
|
73
72
|
end
|
data/lib/odata/batch.rb
CHANGED
@@ -103,7 +103,7 @@ module Safrano
|
|
103
103
|
|
104
104
|
# $batch Handler
|
105
105
|
class HandlerBase
|
106
|
-
TREND = Safrano::Transition.new('', trans:
|
106
|
+
TREND = Safrano::Transition.new('', trans: :transition_end)
|
107
107
|
def allowed_transitions
|
108
108
|
@allowed_transitions = [TREND]
|
109
109
|
end
|
@@ -111,6 +111,8 @@ module Safrano
|
|
111
111
|
def transition_end(_match_result)
|
112
112
|
Safrano::Transition::RESULT_END
|
113
113
|
end
|
114
|
+
|
115
|
+
include Safrano::Transitions::GetNextTrans::ForJustTransitionEnd
|
114
116
|
end
|
115
117
|
|
116
118
|
# $batch disabled Handler
|
data/lib/odata/collection.rb
CHANGED
@@ -20,6 +20,7 @@ module Safrano
|
|
20
20
|
|
21
21
|
def initialize(modelk)
|
22
22
|
@modelk = modelk
|
23
|
+
@allowed_transitions = @modelk.allowed_transitions
|
23
24
|
end
|
24
25
|
|
25
26
|
def allowed_transitions
|
@@ -52,6 +53,8 @@ module Safrano
|
|
52
53
|
end
|
53
54
|
end
|
54
55
|
|
56
|
+
include Safrano::Transitions::GetNextTrans::ByLongestMatch
|
57
|
+
|
55
58
|
# pkid can be a single value for single-pk models, or an array.
|
56
59
|
# type checking/convertion is done in check_odata_key_type
|
57
60
|
def find_by_odata_key(pkid)
|
@@ -14,6 +14,7 @@ module Safrano
|
|
14
14
|
Contract::OK
|
15
15
|
end
|
16
16
|
end
|
17
|
+
|
17
18
|
# Simple static File/Directory based media store handler
|
18
19
|
# similar to Rack::Static
|
19
20
|
# with a flat directory structure
|
@@ -35,11 +36,11 @@ module Safrano
|
|
35
36
|
end
|
36
37
|
|
37
38
|
def create_abs_class_dir
|
38
|
-
FileUtils.makedirs @abs_klass_dir
|
39
|
+
FileUtils.makedirs @abs_klass_dir
|
39
40
|
end
|
40
41
|
|
41
42
|
def create_abs_temp_dir
|
42
|
-
FileUtils.makedirs @abs_temp_dir
|
43
|
+
FileUtils.makedirs @abs_temp_dir
|
43
44
|
end
|
44
45
|
|
45
46
|
def finalize
|
@@ -114,7 +115,7 @@ module Safrano
|
|
114
115
|
# and ensure the directory exists
|
115
116
|
def with_media_directory(entity)
|
116
117
|
mpi = abs_media_directory(entity)
|
117
|
-
|
118
|
+
FileUtils.mkdir_p mpi
|
118
119
|
yield Pathname(mpi)
|
119
120
|
end
|
120
121
|
|
@@ -200,7 +201,7 @@ module Safrano
|
|
200
201
|
def with_media_directory(entity)
|
201
202
|
mpi = abs_media_directory(entity)
|
202
203
|
|
203
|
-
FileUtils.makedirs mpi
|
204
|
+
FileUtils.makedirs mpi
|
204
205
|
yield Pathname(mpi)
|
205
206
|
end
|
206
207
|
|
data/lib/odata/complex_type.rb
CHANGED
@@ -21,6 +21,8 @@ module Safrano
|
|
21
21
|
Safrano::Transition::RESULT_END
|
22
22
|
end
|
23
23
|
|
24
|
+
include Safrano::Transitions::GetNextTrans::ForJustTransitionEnd
|
25
|
+
|
24
26
|
# we will have this on class and instance level for making things simpler first
|
25
27
|
class << self
|
26
28
|
attr_reader :klassmod
|
@@ -202,7 +204,7 @@ module Safrano
|
|
202
204
|
end
|
203
205
|
|
204
206
|
def metadata_h
|
205
|
-
{
|
207
|
+
{ type: type_name }
|
206
208
|
end
|
207
209
|
|
208
210
|
def casted_values
|
data/lib/odata/entity.rb
CHANGED
@@ -19,6 +19,8 @@ module Safrano
|
|
19
19
|
self.class.entity_allowed_transitions
|
20
20
|
end
|
21
21
|
|
22
|
+
include Safrano::Transitions::GetNextTrans::ByLongestMatchDyn
|
23
|
+
|
22
24
|
def transition_end(_match_result)
|
23
25
|
Safrano::Transition::RESULT_END
|
24
26
|
end
|
@@ -184,9 +186,9 @@ module Safrano
|
|
184
186
|
if req.walker.media_value
|
185
187
|
odata_media_value_put(req)
|
186
188
|
elsif req.accept?(APPJSON)
|
187
|
-
|
189
|
+
|
188
190
|
AlreadyExistsUnprocessableError.odata_get(req)
|
189
|
-
|
191
|
+
|
190
192
|
else # TODO: other formats
|
191
193
|
415
|
192
194
|
end
|
@@ -354,7 +356,7 @@ module Safrano
|
|
354
356
|
# real implementation for replacing $value for a media entity
|
355
357
|
def odata_media_value_put(req)
|
356
358
|
model = self.class
|
357
|
-
req.with_media_data do |data, mimetype,
|
359
|
+
req.with_media_data do |data, mimetype, _filename|
|
358
360
|
emdata = { content_type: mimetype }
|
359
361
|
if req.in_changeset
|
360
362
|
set_fields(emdata, model.data_fields, missing: :skip)
|
@@ -385,6 +387,7 @@ module Safrano
|
|
385
387
|
[pk_uri]
|
386
388
|
end
|
387
389
|
end
|
390
|
+
|
388
391
|
module PKUriWithoutFunc
|
389
392
|
def pk_uri
|
390
393
|
pk
|
@@ -398,6 +401,7 @@ module Safrano
|
|
398
401
|
[pk]
|
399
402
|
end
|
400
403
|
end
|
404
|
+
|
401
405
|
# for a single public key
|
402
406
|
module EntitySinglePK
|
403
407
|
include Entity
|
data/lib/odata/error.rb
CHANGED
@@ -142,11 +142,11 @@ module Safrano
|
|
142
142
|
@msg = reason
|
143
143
|
end
|
144
144
|
end
|
145
|
-
|
145
|
+
|
146
146
|
class AlreadyExistsUnprocessableError < UnprocessableEntityError
|
147
147
|
@msg = 'The ressource you are trying to create already exists'
|
148
148
|
end
|
149
|
-
|
149
|
+
|
150
150
|
# http Bad Req.
|
151
151
|
class BadRequestError
|
152
152
|
extend ErrorClass
|
@@ -165,6 +165,7 @@ module Safrano
|
|
165
165
|
@msg = "Bad Request: empty media file #{path}"
|
166
166
|
end
|
167
167
|
end
|
168
|
+
|
168
169
|
# Generic failed changeset
|
169
170
|
class BadRequestFailedChangeSet < BadRequestError
|
170
171
|
@msg = 'Bad Request: Failed changeset '
|
@@ -174,6 +175,7 @@ module Safrano
|
|
174
175
|
class BadRequestNonMediaValue < BadRequestError
|
175
176
|
@msg = 'Bad Request: $value request for a non-media entity'
|
176
177
|
end
|
178
|
+
|
177
179
|
class BadRequestSequelAdapterError < BadRequestError
|
178
180
|
include ErrorInstance
|
179
181
|
def initialize(err)
|
@@ -201,6 +203,7 @@ module Safrano
|
|
201
203
|
@msg = "Bad Request: the $expand path #{path} is invalid for entityset #{model.entity_set_name}"
|
202
204
|
end
|
203
205
|
end
|
206
|
+
|
204
207
|
# for invalid properti(es) in $select param
|
205
208
|
class BadRequestSelectInvalidProps < BadRequestError
|
206
209
|
include ErrorInstance
|
@@ -221,7 +224,7 @@ module Safrano
|
|
221
224
|
|
222
225
|
# http not found
|
223
226
|
class ErrorNotFound
|
224
|
-
extend
|
227
|
+
extend ErrorClass
|
225
228
|
HTTP_CODE = 404
|
226
229
|
@msg = 'The requested ressource was not found'
|
227
230
|
end
|
@@ -242,16 +245,17 @@ module Safrano
|
|
242
245
|
|
243
246
|
# not implemented (Safrano specific)
|
244
247
|
class NotImplementedError
|
245
|
-
extend
|
248
|
+
extend ErrorClass
|
246
249
|
HTTP_CODE = 501
|
247
250
|
end
|
248
251
|
|
249
252
|
# version not implemented (Safrano specific)
|
250
253
|
class VersionNotImplementedError
|
251
|
-
extend
|
254
|
+
extend ErrorClass
|
252
255
|
HTTP_CODE = 501
|
253
256
|
@msg = 'The requested OData version is not yet supported'
|
254
257
|
end
|
258
|
+
|
255
259
|
# batch not implemented (Safrano specific)
|
256
260
|
class BatchNotImplementedError < RuntimeError
|
257
261
|
@msg = 'Not implemented: OData batch'
|
@@ -270,22 +274,26 @@ module Safrano
|
|
270
274
|
@msg = xmsg
|
271
275
|
end
|
272
276
|
end
|
277
|
+
|
273
278
|
class FilterUnknownFunctionError < BadRequestError
|
274
279
|
include ErrorInstance
|
275
280
|
def initialize(badfuncname)
|
276
281
|
@msg = "Bad request: unknown function #{badfuncname} in $filter"
|
277
282
|
end
|
278
283
|
end
|
284
|
+
|
279
285
|
class FilterParseErrorWrongColumnName < BadRequestError
|
280
286
|
extend ErrorClass
|
281
287
|
@msg = 'Bad request: invalid property name in $filter'
|
282
288
|
end
|
289
|
+
|
283
290
|
class FilterParseWrappedError < BadRequestError
|
284
291
|
include ErrorInstance
|
285
292
|
def initialize(exception)
|
286
293
|
@msg = exception.to_s
|
287
294
|
end
|
288
295
|
end
|
296
|
+
|
289
297
|
class ServiceOperationParameterMissing < BadRequestError
|
290
298
|
include ErrorInstance
|
291
299
|
def initialize(missing:, sopname:)
|
data/lib/odata/filter/base.rb
CHANGED
data/lib/odata/filter/error.rb
CHANGED
@@ -35,6 +35,7 @@ module Safrano
|
|
35
35
|
@cur_typ = cur.class
|
36
36
|
end
|
37
37
|
end
|
38
|
+
|
38
39
|
# Invalid Tokens
|
39
40
|
class ErrorInvalidToken < Error
|
40
41
|
include ::Safrano::ErrorInstance
|
@@ -43,6 +44,7 @@ module Safrano
|
|
43
44
|
@msg = "Bad Request: invalid token #{tok} in $filter"
|
44
45
|
end
|
45
46
|
end
|
47
|
+
|
46
48
|
# Unmached closed
|
47
49
|
class ErrorUnmatchedClose < Error
|
48
50
|
include ::Safrano::ErrorInstance
|
@@ -73,6 +75,7 @@ module Safrano
|
|
73
75
|
@msg = "Bad Request: wrong number of parameters for function #{cur.parent.value} in $filter"
|
74
76
|
end
|
75
77
|
end
|
78
|
+
|
76
79
|
# Invalid separator in this context (missing parenthesis?)
|
77
80
|
class ErrorInvalidSeparator < Error
|
78
81
|
include ::Safrano::ErrorInstance
|
data/lib/odata/filter/sequel.rb
CHANGED
@@ -51,21 +51,17 @@ module Safrano
|
|
51
51
|
when :substringof
|
52
52
|
|
53
53
|
# there are multiple possible argument types (but all should return edm.string)
|
54
|
-
if args[0].is_a?(QString)
|
55
|
-
# substringof('Rhum', name) -->
|
56
|
-
# name contains substr 'Rhum'
|
57
|
-
Contract.collect_result!(args[1].leuqes(jh),
|
58
|
-
args[0].leuqes_substringof_sig1(jh)) do |l1, l0|
|
59
|
-
Sequel.like(l1, l0)
|
60
|
-
end
|
61
54
|
|
62
|
-
|
63
|
-
|
55
|
+
if args[0].is_a?(QString) ||
|
56
|
+
# substringof('Rhum', name) -->
|
57
|
+
# name contains substr 'Rhum'
|
58
|
+
|
59
|
+
# special non standard (ui5 client) case ?
|
60
|
+
(args[0].is_a?(Literal) && args[1].is_a?(Literal))
|
64
61
|
Contract.collect_result!(args[1].leuqes(jh),
|
65
62
|
args[0].leuqes_substringof_sig1(jh)) do |l1, l0|
|
66
63
|
Sequel.like(l1, l0)
|
67
64
|
end
|
68
|
-
|
69
65
|
elsif args[1].is_a?(QString)
|
70
66
|
substringof_sig2(jh) # adapter specific
|
71
67
|
else
|
@@ -64,6 +64,7 @@ module Safrano
|
|
64
64
|
Safrano::FilterFunctionNotImplementedError.new("$filter function 'ceiling' is not implemented in sqlite adapter")
|
65
65
|
end
|
66
66
|
end
|
67
|
+
|
67
68
|
# re-useable module with math floor/ceil functions for those adapters having these SQL funcs
|
68
69
|
module MathFloorCeilFuncTree
|
69
70
|
def floor(lq)
|
data/lib/odata/filter/tree.rb
CHANGED
@@ -355,14 +355,14 @@ module Safrano
|
|
355
355
|
|
356
356
|
# Numbers (floating point, ints, dec)
|
357
357
|
class FPNumber
|
358
|
-
def initialize(val)
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
end
|
358
|
+
# def initialize(val)
|
359
|
+
# 1.53f --> value 1.53
|
360
|
+
# 1.53d --> value 1.53
|
361
|
+
# 1.53 --> value 1.53
|
362
|
+
# Note: the tokenizer has already dropped the not usefull string parts
|
363
|
+
# Note : we dont differentiate between Float and Double here
|
364
|
+
# super(val)
|
365
|
+
# end
|
366
366
|
|
367
367
|
def accept?(tok, typ)
|
368
368
|
case typ
|
@@ -396,14 +396,13 @@ module Safrano
|
|
396
396
|
end
|
397
397
|
|
398
398
|
class DecimalLit
|
399
|
-
def initialize(val)
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
end
|
399
|
+
# def initialize(val)
|
400
|
+
# 1.53m --> value 1.53
|
401
|
+
# Warning, this assumes that the m|M part in the input is really not optional
|
402
|
+
# Note: the tokenizer has already dropped the not usefull string parts
|
403
|
+
# cf. DECIMALRGX in token.rb
|
404
|
+
# super(val)
|
405
|
+
# end
|
407
406
|
|
408
407
|
def accept?(tok, typ)
|
409
408
|
case typ
|
@@ -418,6 +417,7 @@ module Safrano
|
|
418
417
|
:decimal
|
419
418
|
end
|
420
419
|
end
|
420
|
+
|
421
421
|
# Literals are unquoted words without /
|
422
422
|
class Literal
|
423
423
|
def accept?(tok, typ)
|
@@ -465,11 +465,11 @@ module Safrano
|
|
465
465
|
|
466
466
|
# DateTimeLit
|
467
467
|
class DateTimeLit
|
468
|
-
def initialize(val)
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
end
|
468
|
+
# def initialize(val)
|
469
|
+
# datetime'2000-12-12T12:00:53' --> value 2000-12-12T12:00:53
|
470
|
+
# Note: the tokenizer has already dropped the not usefull string parts
|
471
|
+
# super(val)
|
472
|
+
# end
|
473
473
|
|
474
474
|
def accept?(tok, typ)
|
475
475
|
case typ
|
@@ -484,13 +484,14 @@ module Safrano
|
|
484
484
|
:datetime
|
485
485
|
end
|
486
486
|
end
|
487
|
+
|
487
488
|
# DateTimeOffsetLit
|
488
489
|
class DateTimeOffsetLit
|
489
|
-
def initialize(val)
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
end
|
490
|
+
# def initialize(val)
|
491
|
+
# datetimeoffset'2000-12-12T12:00:53+02:00' --> value 2000-12-12T12:00:53+02:00
|
492
|
+
# Note: the tokenizer has already dropped the not usefull string parts
|
493
|
+
# super(val)
|
494
|
+
# end
|
494
495
|
|
495
496
|
def accept?(tok, typ)
|
496
497
|
case typ
|
@@ -505,6 +506,7 @@ module Safrano
|
|
505
506
|
:datetimeoffset
|
506
507
|
end
|
507
508
|
end
|
509
|
+
|
508
510
|
# Quoted Strings
|
509
511
|
class QString
|
510
512
|
DBL_QO = "''"
|
@@ -17,6 +17,7 @@ module Safrano
|
|
17
17
|
super(msg)
|
18
18
|
end
|
19
19
|
end
|
20
|
+
|
20
21
|
class ProcRedefinition < StandardError
|
21
22
|
def initialize(fnam)
|
22
23
|
msg = "Function import #{fnam}: Block/lambda Redefinition . Provide definition either as a return code block or with .definition(lambda) but not both"
|
@@ -39,6 +40,12 @@ module Safrano
|
|
39
40
|
[Safrano::TransitionExecuteFunc]
|
40
41
|
end
|
41
42
|
|
43
|
+
include Safrano::Transitions::GetNextTrans::ForJustTransitionEnd
|
44
|
+
|
45
|
+
def get_next_transresult(path_remain)
|
46
|
+
Safrano::TransitionExecuteFunc.result(path_remain)
|
47
|
+
end
|
48
|
+
|
42
49
|
def input(**parmtypes)
|
43
50
|
@input = {}
|
44
51
|
parmtypes.each do |k, t|
|
data/lib/odata/model_ext.rb
CHANGED
@@ -445,17 +445,17 @@ module Safrano
|
|
445
445
|
|
446
446
|
# check if key needs casting. Important for later entity-uri generation !
|
447
447
|
return unless primary_key.is_a? Symbol # single key field
|
448
|
-
|
449
|
-
# guid key as guid'xxx-yyy-zzz'
|
448
|
+
|
449
|
+
# guid key as guid'xxx-yyy-zzz'
|
450
450
|
metadata = @cols_metadata[primary_key]
|
451
|
-
|
451
|
+
|
452
452
|
if metadata[:edm_type] == 'Edm.Guid'
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
453
|
+
props = db_schema[primary_key]
|
454
|
+
@pk_castfunc = if props[:type] == :blob # Edm.Guid but as 16 byte binary Blob on DB level, eg in Sqlite
|
455
|
+
lambda { |x| "guid'#{UUIDTools::UUID.parse_raw(x)}'" unless x.nil? }
|
456
|
+
else
|
457
|
+
lambda { |x| "guid'#{x}'" unless x.nil? }
|
458
|
+
end
|
459
459
|
else
|
460
460
|
@pk_castfunc = @casted_cols[primary_key]
|
461
461
|
end
|
@@ -607,12 +607,15 @@ module Safrano
|
|
607
607
|
end
|
608
608
|
end
|
609
609
|
end
|
610
|
+
|
610
611
|
# methods related to transitions to next state (cf. walker)
|
611
612
|
module Transitions
|
612
613
|
def allowed_transitions
|
613
614
|
@allowed_transitions
|
614
615
|
end
|
615
616
|
|
617
|
+
include Safrano::Transitions::GetNextTrans::BySimpleDetect
|
618
|
+
|
616
619
|
def entity_allowed_transitions
|
617
620
|
@entity_allowed_transitions
|
618
621
|
end
|
@@ -621,7 +624,7 @@ module Safrano
|
|
621
624
|
@allowed_transitions = [Safrano::TransitionEnd,
|
622
625
|
Safrano::TransitionCount,
|
623
626
|
Safrano::Transition.new(entity_id_url_regexp,
|
624
|
-
trans:
|
627
|
+
trans: :transition_id)].freeze
|
625
628
|
end
|
626
629
|
|
627
630
|
def build_entity_allowed_transitions
|
@@ -630,17 +633,17 @@ module Safrano
|
|
630
633
|
Safrano::TransitionCount,
|
631
634
|
Safrano::TransitionLinks,
|
632
635
|
Safrano::TransitionValue,
|
633
|
-
Safrano::Transition.new(transition_attribute_regexp, trans:
|
636
|
+
Safrano::Transition.new(transition_attribute_regexp, trans: :transition_attribute)
|
634
637
|
]
|
635
638
|
if (ncurgx = @nav_collection_url_regexp)
|
636
639
|
@entity_allowed_transitions <<
|
637
|
-
Safrano::Transition.new(%r{\A/(#{ncurgx})(.*)\z}, trans:
|
640
|
+
Safrano::Transition.new(%r{\A/(#{ncurgx})(.*)\z}, trans: :transition_nav_collection)
|
638
641
|
end
|
639
642
|
if (neurgx = @nav_entity_url_regexp)
|
640
643
|
@entity_allowed_transitions <<
|
641
|
-
Safrano::Transition.new(%r{\A/(#{neurgx})(.*)\z}, trans:
|
644
|
+
Safrano::Transition.new(%r{\A/(#{neurgx})(.*)\z}, trans: :transition_nav_entity)
|
642
645
|
end
|
643
|
-
@entity_allowed_transitions << Safrano::Transition.new(%r{\A/(\w+)(.*)\z}, trans:
|
646
|
+
@entity_allowed_transitions << Safrano::Transition.new(%r{\A/(\w+)(.*)\z}, trans: :transition_invalid_attribute)
|
644
647
|
@entity_allowed_transitions.freeze
|
645
648
|
@entity_allowed_transitions
|
646
649
|
end
|
@@ -78,6 +78,10 @@ module Safrano
|
|
78
78
|
# Represents a named but nil-valued navigation-attribute of an Entity
|
79
79
|
# (usually resulting from a NULL FK db value)
|
80
80
|
class NilNavigationAttribute
|
81
|
+
def initialize
|
82
|
+
@allowed_transitions = ALLOWED_TRANSITIONS
|
83
|
+
end
|
84
|
+
|
81
85
|
include Safrano::NavigationInfo
|
82
86
|
def odata_get(req)
|
83
87
|
if req.walker.media_value
|
@@ -131,6 +135,8 @@ module Safrano
|
|
131
135
|
def allowed_transitions
|
132
136
|
ALLOWED_TRANSITIONS
|
133
137
|
end
|
138
|
+
|
139
|
+
include Safrano::Transitions::GetNextTrans::BySimpleDetect
|
134
140
|
end
|
135
141
|
include Transitions
|
136
142
|
end
|
data/lib/odata/request/json.rb
CHANGED
data/lib/odata/transition.rb
CHANGED
@@ -4,16 +4,78 @@ require_relative 'error'
|
|
4
4
|
|
5
5
|
# our main namespace
|
6
6
|
module Safrano
|
7
|
+
module Transitions
|
8
|
+
module GetNextTrans
|
9
|
+
module BySimpleDetect
|
10
|
+
def get_next_transresult(path_remain)
|
11
|
+
# current url-parsing context
|
12
|
+
# has no ambiguous next match and we dont need to find longest match
|
13
|
+
# but it's sufficient to find the one matching (or nil)
|
14
|
+
tres_next = nil
|
15
|
+
@allowed_transitions.detect { |t| tres_next = t.result(path_remain) }
|
16
|
+
tres_next
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
module ForJustTransitionEnd
|
21
|
+
def get_next_transresult(path_remain)
|
22
|
+
Safrano::TransitionEnd.result(path_remain)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Transitions::GetNextTrans::ByLongestMatch
|
27
|
+
module ByLongestMatch
|
28
|
+
def get_next_transresult(path_remain)
|
29
|
+
# current url-parsing context
|
30
|
+
# has ambiguous next match and we need to find longest match
|
31
|
+
# example: current context is "the top level service" and we have
|
32
|
+
# entity types Race and RaceType
|
33
|
+
|
34
|
+
match_len = -1
|
35
|
+
tres_next = nil
|
36
|
+
|
37
|
+
@allowed_transitions.each { |t|
|
38
|
+
if (res = t.longer_match(path_remain, match_len))
|
39
|
+
tres_next = res
|
40
|
+
match_len = tres_next.match_length
|
41
|
+
end
|
42
|
+
}
|
43
|
+
tres_next
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# same as ByLongestMatch but use the getter method allowed_transitions instead of
|
48
|
+
# directly @allowed_transitions
|
49
|
+
module ByLongestMatchDyn
|
50
|
+
def get_next_transresult(path_remain)
|
51
|
+
# current url-parsing context
|
52
|
+
# has ambiguous next match and we need to find longest match
|
53
|
+
# example: current context is "the top level service" and we have
|
54
|
+
# entity types Race and RaceType
|
55
|
+
|
56
|
+
match_len = -1
|
57
|
+
tres_next = nil
|
58
|
+
|
59
|
+
allowed_transitions.each { |t|
|
60
|
+
if (res = t.longer_match(path_remain, match_len))
|
61
|
+
tres_next = res
|
62
|
+
match_len = tres_next.match_length
|
63
|
+
end
|
64
|
+
}
|
65
|
+
tres_next
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
7
71
|
# represents a state transition when navigating/parsing the url path
|
8
72
|
# from left to right
|
9
73
|
class Transition
|
10
|
-
attr_accessor :trans
|
11
|
-
attr_accessor :match_result
|
12
|
-
attr_accessor :
|
13
|
-
|
14
|
-
|
15
|
-
EMPTYSTR = ''
|
16
|
-
SLASH = '/'
|
74
|
+
# attr_accessor :trans
|
75
|
+
# attr_accessor :match_result
|
76
|
+
# attr_accessor :trans_result
|
77
|
+
# attr_accessor :rgx
|
78
|
+
# attr_reader :remain_idx
|
17
79
|
|
18
80
|
RESULT_BAD_REQ_ERR = [nil, :error, ::Safrano::BadRequestError].freeze
|
19
81
|
RESULT_NOT_FOUND_ERR = [nil, :error, ::Safrano::ErrorNotFound].freeze
|
@@ -25,31 +87,58 @@ module Safrano
|
|
25
87
|
Regexp.new(arg)
|
26
88
|
else
|
27
89
|
arg
|
28
|
-
end
|
90
|
+
end.freeze
|
91
|
+
@trans = trans.freeze
|
92
|
+
@remain_idx = remain_idx.freeze
|
93
|
+
end
|
94
|
+
|
95
|
+
def result(str)
|
96
|
+
return unless (mres = @rgx.match(str))
|
97
|
+
|
98
|
+
TransitionResult.new(trans: @trans, match_result: mres, remain_idx: @remain_idx)
|
99
|
+
end
|
100
|
+
|
101
|
+
# return match-result for str, if longer as min_match_length
|
102
|
+
def longer_match(str, min_match_length)
|
103
|
+
return unless (mres = @rgx.match(str))
|
104
|
+
return unless (mres.match_length(1) > min_match_length)
|
105
|
+
|
106
|
+
TransitionResult.new(trans: @trans, match_result: mres, remain_idx: @remain_idx)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class TransitionResult
|
111
|
+
attr_reader :match
|
112
|
+
|
113
|
+
EMPTYSTR = ''
|
114
|
+
SLASH = '/'
|
115
|
+
|
116
|
+
def initialize(trans:, match_result:, remain_idx:)
|
29
117
|
@trans = trans
|
118
|
+
@match = match_result
|
30
119
|
@remain_idx = remain_idx
|
31
120
|
end
|
32
121
|
|
33
|
-
def
|
34
|
-
@
|
122
|
+
def match_length
|
123
|
+
@match.match_length(1)
|
35
124
|
end
|
36
125
|
|
37
126
|
# remain_idx is the index of the last match-data. ususally its 2
|
38
127
|
# but can be overidden
|
39
128
|
def path_remain
|
40
|
-
@
|
129
|
+
@match[@remain_idx] if @match && @match[@remain_idx]
|
41
130
|
end
|
42
131
|
|
43
132
|
def path_done
|
44
|
-
if @
|
45
|
-
@
|
133
|
+
if @match
|
134
|
+
@match[1] || EMPTYSTR
|
46
135
|
else
|
47
136
|
EMPTYSTR
|
48
137
|
end
|
49
138
|
end
|
50
139
|
|
51
140
|
def do_transition(ctx)
|
52
|
-
ctx.
|
141
|
+
ctx.__send__(@trans, @match)
|
53
142
|
end
|
54
143
|
end
|
55
144
|
|
@@ -59,8 +148,30 @@ module Safrano
|
|
59
148
|
@trans = trans
|
60
149
|
end
|
61
150
|
|
62
|
-
def
|
151
|
+
def result(str)
|
152
|
+
@str = str
|
153
|
+
InplaceTransitionResult.new(trans: @trans, match_result: @str)
|
154
|
+
end
|
155
|
+
|
156
|
+
# return match-result for str, if longer as min_match_length
|
157
|
+
def longer_match(str, min_match_length)
|
63
158
|
@str = str
|
159
|
+
return unless (@str.size > min_match_length)
|
160
|
+
|
161
|
+
InplaceTransitionResult.new(trans: @trans, match_result: @str)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Transition that does not move/change the input
|
166
|
+
class InplaceTransitionResult < TransitionResult
|
167
|
+
def initialize(trans:, match_result:)
|
168
|
+
@trans = trans
|
169
|
+
@match = match_result
|
170
|
+
@str = match_result
|
171
|
+
end
|
172
|
+
|
173
|
+
def match_length
|
174
|
+
@str.size
|
64
175
|
end
|
65
176
|
|
66
177
|
def path_remain
|
@@ -70,26 +181,22 @@ module Safrano
|
|
70
181
|
def path_done
|
71
182
|
EMPTYSTR
|
72
183
|
end
|
73
|
-
|
74
|
-
def do_transition(ctx)
|
75
|
-
ctx.method(@trans).call(@str)
|
76
|
-
end
|
77
184
|
end
|
78
185
|
|
79
|
-
TransitionEnd = Transition.new('\A(\/?)\z', trans:
|
80
|
-
TransitionExecuteFunc = InplaceTransition.new(trans:
|
186
|
+
TransitionEnd = Transition.new('\A(\/?)\z', trans: :transition_end)
|
187
|
+
TransitionExecuteFunc = InplaceTransition.new(trans: :transition_execute_func)
|
81
188
|
TransitionMetadata = Transition.new('\A(\/\$metadata)(.*)',
|
82
|
-
trans:
|
189
|
+
trans: :transition_metadata)
|
83
190
|
TransitionBatch = Transition.new('\A(\/\$batch)(.*)',
|
84
|
-
trans:
|
191
|
+
trans: :transition_batch)
|
85
192
|
TransitionContentId = Transition.new('\A(\/\$(\d+))(.*)',
|
86
|
-
trans:
|
193
|
+
trans: :transition_content_id,
|
87
194
|
remain_idx: 3)
|
88
195
|
TransitionCount = Transition.new('(\A\/\$count)(.*)\z',
|
89
|
-
trans:
|
196
|
+
trans: :transition_count)
|
90
197
|
TransitionValue = Transition.new('(\A\/\$value)(.*)\z',
|
91
|
-
trans:
|
198
|
+
trans: :transition_value)
|
92
199
|
TransitionLinks = Transition.new('(\A\/\$links)(.*)\z',
|
93
|
-
trans:
|
200
|
+
trans: :transition_links)
|
94
201
|
attr_accessor :allowed_transitions
|
95
202
|
end
|
data/lib/odata/walker.rb
CHANGED
@@ -65,30 +65,10 @@ module Safrano
|
|
65
65
|
if (prefix == EMPTYSTR) || (prefix == SLASH)
|
66
66
|
path
|
67
67
|
else
|
68
|
-
# path.sub!(/\A#{prefix}/, '')
|
69
|
-
# TODO: check
|
70
68
|
path.sub(/\A#{prefix}/, EMPTYSTR)
|
71
69
|
end
|
72
70
|
end
|
73
71
|
|
74
|
-
def get_next_transition
|
75
|
-
# handle multiple valid transitions
|
76
|
-
# like when we have attributes that are substring of each other
|
77
|
-
# --> instead of using detect (ie take first transition)
|
78
|
-
# we need to use select and then find the longest match
|
79
|
-
|
80
|
-
valid_tr = @context.allowed_transitions.map(&:dup).select do |t|
|
81
|
-
t.do_match(@path_remain)
|
82
|
-
end
|
83
|
-
|
84
|
-
# HACK: (wanted: a better one) to make attributes that are substrings of each other
|
85
|
-
# work well
|
86
|
-
return unless valid_tr
|
87
|
-
return (@tr_next = nil) if valid_tr.empty?
|
88
|
-
|
89
|
-
@tr_next = valid_tr.size == 1 ? valid_tr.first : valid_tr.max_by { |t| t.match_result[1].size }
|
90
|
-
end
|
91
|
-
|
92
72
|
# perform a content-id ($batch changeset ref) transition
|
93
73
|
def do_run_with_content_id
|
94
74
|
if @content_id_refs.is_a? Hash
|
@@ -137,7 +117,7 @@ module Safrano
|
|
137
117
|
end
|
138
118
|
|
139
119
|
def do_next_transition
|
140
|
-
@context, @status, @error = @
|
120
|
+
@context, @status, @error = @tres_next.do_transition(@context)
|
141
121
|
# little hack's
|
142
122
|
case @status
|
143
123
|
# we dont have the content-id references data on service level
|
@@ -151,8 +131,8 @@ module Safrano
|
|
151
131
|
end
|
152
132
|
|
153
133
|
@contexts << @context
|
154
|
-
@path_remain = @
|
155
|
-
@path_done << @
|
134
|
+
@path_remain = @tres_next.path_remain
|
135
|
+
@path_done << @tres_next.path_done
|
156
136
|
|
157
137
|
# little hack's
|
158
138
|
state_mappings
|
@@ -160,8 +140,8 @@ module Safrano
|
|
160
140
|
|
161
141
|
def eo
|
162
142
|
while @context
|
163
|
-
|
164
|
-
if @
|
143
|
+
@tres_next = @context.get_next_transresult(@path_remain)
|
144
|
+
if @tres_next
|
165
145
|
do_next_transition
|
166
146
|
else
|
167
147
|
@context = nil
|
data/lib/safrano/rack_app.rb
CHANGED
@@ -36,12 +36,12 @@ module Safrano
|
|
36
36
|
def self.get_service_base
|
37
37
|
@service_base
|
38
38
|
end
|
39
|
-
|
40
|
-
# needed for safrano-rack_builder
|
39
|
+
|
40
|
+
# needed for safrano-rack_builder
|
41
41
|
def get_path_prefix
|
42
42
|
self.class.get_service_base.xpath_prefix
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
def self.set_servicebase(sbase)
|
46
46
|
@service_base = sbase
|
47
47
|
@service_base.enable_v1_service
|
data/lib/safrano/rack_builder.rb
CHANGED
@@ -7,9 +7,11 @@ module Rack
|
|
7
7
|
module Safrano
|
8
8
|
# just a Wrapper to ensure (force?) that mandatory middlewares are acutally
|
9
9
|
# used
|
10
|
-
LOCALHOST_ANY_PORT_RGX = /\A(?:https?:\/\/)?localhost(?::\d+)?\z
|
10
|
+
LOCALHOST_ANY_PORT_RGX = /\A(?:https?:\/\/)?localhost(?::\d+)?\z/.freeze
|
11
|
+
CORS_RO_METHODS = %i[get head options].freeze
|
12
|
+
CORS_BATCH_METHODS = %i[post head options].freeze
|
13
|
+
CORS_RW_METHODS = %i[get post put patch delete head options].freeze
|
11
14
|
class Builder < ::Rack::Builder
|
12
|
-
|
13
15
|
def initialize(default_app = nil, &block)
|
14
16
|
super(default_app) {}
|
15
17
|
@middlewares = []
|
@@ -17,63 +19,61 @@ module Rack
|
|
17
19
|
instance_eval(&block) if block_given?
|
18
20
|
prepend_rackcors_ifneeded
|
19
21
|
end
|
20
|
-
|
22
|
+
|
21
23
|
def use(middleware, *args, &block)
|
22
24
|
@middlewares << middleware
|
23
25
|
super(middleware, *args, &block)
|
24
26
|
end
|
25
|
-
|
27
|
+
|
26
28
|
def prepend_rackcors_ifneeded
|
27
29
|
return if stack_has_rackcors
|
28
|
-
|
30
|
+
|
29
31
|
# get the safrano app path prefix
|
30
32
|
# normally @run is a Safrano Server app
|
31
33
|
return unless @run.is_a? ::Safrano::ServerApp
|
32
|
-
|
33
|
-
|
34
|
+
|
34
35
|
service_path_prefix = @run.get_path_prefix.dup
|
35
|
-
|
36
|
-
# due to a bug in rack-cors
|
36
|
+
|
37
|
+
# due to a bug in rack-cors
|
37
38
|
# we cant use the batch ressource path as
|
38
39
|
# a string but need to pass it as a regexp
|
39
|
-
# ( bug fixed in rack-cors git / new release? but still need to workaround it for
|
40
|
-
# current/old releases ),
|
41
|
-
batch_path_regexp = if service_path_prefix.empty?
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
40
|
+
# ( bug fixed in rack-cors git / new release? but still need to workaround it for
|
41
|
+
# current/old releases ),
|
42
|
+
batch_path_regexp = if service_path_prefix.empty?
|
43
|
+
# Ressource /$batch
|
44
|
+
/\A\/\$batch\z/
|
45
|
+
else
|
46
|
+
# Ressource like /foo/bar/baz/$batch
|
47
|
+
service_path_prefix.sub!(::Safrano::TRAILING_SLASH_RGX, '')
|
48
|
+
service_path_prefix.sub!(::Safrano::LEADING_SLASH_RGX, '')
|
49
|
+
# now is foo/bar/baz
|
50
|
+
path_prefix_rgx = Regexp.escape("/#{service_path_prefix}/$batch")
|
51
|
+
# now escaped path regexp /foo/bar/baz/$batch
|
52
|
+
# finaly just add start / end anchors
|
53
|
+
/\A#{path_prefix_rgx}\z/
|
54
|
+
end
|
54
55
|
# this will append rack-cors mw
|
55
|
-
# per default allow GET * from everywhere
|
56
|
-
# allow POST $batch from localhost only
|
56
|
+
# per default allow GET * from everywhere
|
57
|
+
# allow POST $batch from localhost only
|
57
58
|
use ::Rack::Cors do
|
58
59
|
allow do
|
59
60
|
origins LOCALHOST_ANY_PORT_RGX
|
60
|
-
resource
|
61
|
-
resource '*', headers: :any, methods:
|
62
|
-
end
|
61
|
+
resource batch_path_regexp, headers: :any, methods: CORS_BATCH_METHODS
|
62
|
+
resource '*', headers: :any, methods: CORS_RW_METHODS
|
63
|
+
end
|
63
64
|
allow do
|
64
65
|
origins '*'
|
65
|
-
resource '*', headers: :any, methods:
|
66
|
-
end
|
67
|
-
|
66
|
+
resource '*', headers: :any, methods: CORS_RO_METHODS
|
67
|
+
end
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
# we need it in first place... move last element to beginin of mw stack
|
71
|
-
rackcors = @use.delete_at(-1)
|
72
|
-
@use.insert(0, rackcors)
|
71
|
+
rackcors = @use.delete_at(-1)
|
72
|
+
@use.insert(0, rackcors)
|
73
73
|
end
|
74
|
-
|
74
|
+
|
75
75
|
def stack_has_rackcors
|
76
|
-
@middlewares.find{|mw| mw == Rack::Cors }
|
76
|
+
@middlewares.find { |mw| mw == Rack::Cors }
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
data/lib/safrano/request.rb
CHANGED
@@ -11,12 +11,12 @@ module Safrano
|
|
11
11
|
headers.delete('Content-Type')
|
12
12
|
@response.headers.delete('Content-Type')
|
13
13
|
@response.headers['Content-Type'] = ''
|
14
|
-
|
14
|
+
|
15
15
|
# we only let rack-cors handle Cors OPTIONS .
|
16
16
|
# otherwise dont do it...
|
17
|
-
# see https://www.mnot.net/blog/2012/10/29/NO_OPTIONS
|
17
|
+
# see https://www.mnot.net/blog/2012/10/29/NO_OPTIONS
|
18
18
|
# 501 not implemented
|
19
|
-
[501, EMPTY_HASH, '']
|
19
|
+
[501, EMPTY_HASH, '']
|
20
20
|
end
|
21
21
|
|
22
22
|
def odata_delete
|
@@ -41,7 +41,7 @@ module Safrano
|
|
41
41
|
|
42
42
|
def odata_post
|
43
43
|
@walker.finalize.tap_error { |err| return err.odata_get(self) }
|
44
|
-
|
44
|
+
.if_valid { |context| context.odata_post(self) }
|
45
45
|
end
|
46
46
|
|
47
47
|
def odata_head
|
@@ -99,6 +99,7 @@ module Safrano
|
|
99
99
|
@type
|
100
100
|
end
|
101
101
|
end
|
102
|
+
|
102
103
|
## original coding:
|
103
104
|
# # The set of media-types. Requests that do not indicate
|
104
105
|
# # one of the media types presents in this list will not be eligible
|
data/lib/safrano/service.rb
CHANGED
@@ -110,6 +110,7 @@ module Safrano
|
|
110
110
|
TRAILING_SLASH_RGX = %r{/\z}.freeze
|
111
111
|
LEADING_SLASH_RGX = %r{\A/}.freeze
|
112
112
|
include XMLNS
|
113
|
+
GENERIC_415_RESP = [415, {}, ['']].freeze
|
113
114
|
# Base class for service. Subclass will be for V1, V2 etc...
|
114
115
|
class ServiceBase
|
115
116
|
include Safrano
|
@@ -620,6 +621,8 @@ module Safrano
|
|
620
621
|
Safrano::TransitionContentId
|
621
622
|
].freeze
|
622
623
|
|
624
|
+
include Safrano::Transitions::GetNextTrans::ByLongestMatch
|
625
|
+
|
623
626
|
def build_allowed_transitions
|
624
627
|
@allowed_transitions = if @function_imports.empty?
|
625
628
|
(ALLOWED_TRANSITIONS_FIXED + [
|
@@ -679,7 +682,7 @@ module Safrano
|
|
679
682
|
[200, CT_APPXML, [service_xml(req)]]
|
680
683
|
else
|
681
684
|
# this is returned by http://services.odata.org/V2/OData/Safrano.svc
|
682
|
-
|
685
|
+
GENERIC_415_RESP
|
683
686
|
end
|
684
687
|
end
|
685
688
|
end
|
@@ -748,11 +751,13 @@ module Safrano
|
|
748
751
|
Safrano::Transition::RESULT_END
|
749
752
|
end
|
750
753
|
|
754
|
+
include Safrano::Transitions::GetNextTrans::ForJustTransitionEnd
|
755
|
+
|
751
756
|
def odata_get(req)
|
752
757
|
if req.accept?(APPXML)
|
753
758
|
[200, CT_APPXML, [@service.metadata_xml(req)]]
|
754
759
|
else
|
755
|
-
|
760
|
+
GENERIC_415_RESP
|
756
761
|
end
|
757
762
|
end
|
758
763
|
end
|
data/lib/safrano/type_mapping.rb
CHANGED
@@ -12,6 +12,7 @@ module Safrano
|
|
12
12
|
attr_reader :castfunc
|
13
13
|
attr_reader :edm_type
|
14
14
|
end
|
15
|
+
|
15
16
|
# Model attribute (column) specific mapping
|
16
17
|
class AttributeTypeMapping < TypeMapping
|
17
18
|
attr_reader :attr_name
|
@@ -20,6 +21,7 @@ module Safrano
|
|
20
21
|
@edm_type = builder.xedm_type
|
21
22
|
@castfunc = builder.castfunc
|
22
23
|
end
|
24
|
+
|
23
25
|
# wrapper to handle API
|
24
26
|
class Builder
|
25
27
|
attr_reader :xedm_type
|
@@ -82,9 +84,8 @@ module Safrano
|
|
82
84
|
end
|
83
85
|
|
84
86
|
def match(curtyp)
|
85
|
-
if @bui2 && (m = @bui2.match(curtyp))
|
86
|
-
|
87
|
-
elsif @bui1 && (m = @bui1.match(curtyp))
|
87
|
+
if (@bui2 && (m = @bui2.match(curtyp))) ||
|
88
|
+
(@bui1 && (m = @bui1.match(curtyp)))
|
88
89
|
m
|
89
90
|
elsif @rgx.match(curtyp)
|
90
91
|
type_mapping
|
@@ -134,6 +135,7 @@ module Safrano
|
|
134
135
|
RgxTypeMapping1Par.new(self)
|
135
136
|
end
|
136
137
|
end
|
138
|
+
|
137
139
|
class Builder2Par < Builder
|
138
140
|
def initialize(db_ty_rgx, proc)
|
139
141
|
@db_types_rgx = db_ty_rgx
|
@@ -177,6 +179,7 @@ module Safrano
|
|
177
179
|
@castfunc = builder.castfunc
|
178
180
|
end
|
179
181
|
end
|
182
|
+
|
180
183
|
class RgxTypeMapping2Par < RgxTypeMapping
|
181
184
|
def initialize(builder)
|
182
185
|
@edm_type = builder.xedm_type
|
data/lib/safrano/version.rb
CHANGED
@@ -130,7 +130,7 @@ class JoinByPathsHelper < Set
|
|
130
130
|
# associated model's primary key. Each current model object can be
|
131
131
|
# associated with many associated model objects, and each associated
|
132
132
|
# model object can be associated with many current model objects.
|
133
|
-
# TODO: testcase for :one_through_one
|
133
|
+
# TODO: testcase for :one_through_one
|
134
134
|
# when # :one_through_one :: Similar to many_to_many in terms of foreign keys, but only one object
|
135
135
|
# is associated to the current object through the association.
|
136
136
|
# Provides only getter methods, no setter or modification methods.
|
@@ -173,7 +173,6 @@ class JoinByPathsHelper < Set
|
|
173
173
|
|
174
174
|
next result_
|
175
175
|
|
176
|
-
|
177
176
|
end
|
178
177
|
|
179
178
|
lks.map! { |k| Sequel[seg.first.alias_sym][k] } unless seg.first.empty?
|
@@ -229,6 +228,7 @@ module Sequel
|
|
229
228
|
@alias_cnt = 0
|
230
229
|
end
|
231
230
|
end
|
231
|
+
|
232
232
|
module ClassMethods
|
233
233
|
attr_reader :aliases_sym
|
234
234
|
attr_reader :alias_cnt
|
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.8.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- oz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|