safrano 0.8.0 → 0.8.1
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/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
|