safrano 0.4.4 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/odata/attribute.rb +2 -2
- data/lib/odata/collection.rb +15 -10
- data/lib/odata/collection_filter.rb +5 -5
- data/lib/odata/collection_media.rb +63 -25
- data/lib/odata/complex_type.rb +185 -43
- data/lib/odata/entity.rb +31 -8
- data/lib/odata/error.rb +28 -0
- data/lib/odata/filter/base.rb +5 -0
- data/lib/odata/filter/parse.rb +6 -0
- data/lib/odata/filter/sequel.rb +15 -0
- data/lib/odata/filter/token.rb +13 -10
- data/lib/odata/filter/tree.rb +4 -4
- data/lib/odata/function_import.rb +35 -34
- data/lib/odata/model_ext.rb +28 -5
- data/lib/odata/transition.rb +22 -2
- data/lib/odata/walker.rb +23 -5
- data/lib/safrano/contract.rb +7 -9
- data/lib/safrano/core.rb +12 -12
- data/lib/safrano/deprecation.rb +5 -5
- data/lib/safrano/multipart.rb +4 -4
- data/lib/safrano/rack_app.rb +1 -1
- data/lib/safrano/request.rb +3 -2
- data/lib/safrano/service.rb +38 -21
- data/lib/safrano/version.rb +1 -1
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 00bf6cd3a561928000b8bf53bbc71f139b162f92adc229e625f010dc6f1ce3c7
|
4
|
+
data.tar.gz: bf154e1b55e4d32e4640d6c993566a45eae8a2bce44b23dd979a9946d3633dc7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b2fad0321b45bae50f3435b688b31f99d320cfa4f81f14867f64659faa0c4e8ece7bc4f1f7ecc2db2c7710c08f7726bb4d758bf5d09d487a54199afea50119a
|
7
|
+
data.tar.gz: b9a70b4a08fb7a2cc31df7e7637f635a42a85433660f5061f6897e1fb463496252c8d611fb1023e5f27202bbc478b58569d2ad42b1c3359db14e34c92f4ec35c
|
data/lib/odata/attribute.rb
CHANGED
@@ -53,7 +53,7 @@ module Safrano
|
|
53
53
|
# methods related to transitions to next state (cf. walker)
|
54
54
|
module Transitions
|
55
55
|
def transition_end(_match_result)
|
56
|
-
|
56
|
+
Transition::RESULT_END
|
57
57
|
end
|
58
58
|
|
59
59
|
def transition_value(_match_result)
|
@@ -64,7 +64,7 @@ module Safrano
|
|
64
64
|
Safrano::TransitionValue].freeze
|
65
65
|
|
66
66
|
def allowed_transitions
|
67
|
-
ALLOWED_TRANSITIONS
|
67
|
+
Transitions::ALLOWED_TRANSITIONS
|
68
68
|
end
|
69
69
|
end
|
70
70
|
include Transitions
|
data/lib/odata/collection.rb
CHANGED
@@ -60,10 +60,13 @@ module Safrano
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def initialize_dataset(dtset = nil)
|
63
|
-
@cx = dtset || @modelk
|
63
|
+
@cx = @cx || dtset || @modelk
|
64
|
+
end
|
65
|
+
|
66
|
+
def initialize_uparms
|
64
67
|
@uparms = UrlParameters4Coll.new(@cx, @params)
|
65
68
|
end
|
66
|
-
|
69
|
+
|
67
70
|
def odata_get_apply_params
|
68
71
|
@uparms.apply_to_dataset(@cx).map_result! do |dataset|
|
69
72
|
@cx = dataset
|
@@ -77,17 +80,20 @@ module Safrano
|
|
77
80
|
end
|
78
81
|
end
|
79
82
|
|
80
|
-
D = 'd'
|
81
|
-
DJ_OPEN = '{"d":'
|
82
|
-
DJ_CLOSE = '}'
|
83
|
+
D = 'd'
|
84
|
+
DJ_OPEN = '{"d":'
|
85
|
+
DJ_CLOSE = '}'
|
83
86
|
EMPTYH = {}.freeze
|
84
87
|
|
85
88
|
def to_odata_json(request:)
|
86
89
|
template = @modelk.output_template(expand_list: @uparms.expand.template,
|
87
90
|
select: @uparms.select)
|
91
|
+
# TODO: Error handling if database contains binary BLOB data that cant be
|
92
|
+
# interpreted as UTF-8 then JSON will fail here
|
88
93
|
innerj = request.service.get_coll_odata_h(array: @cx.all,
|
89
94
|
template: template,
|
90
95
|
icount: @inlinecount).to_json
|
96
|
+
|
91
97
|
"#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
|
92
98
|
end
|
93
99
|
|
@@ -128,9 +134,9 @@ module Safrano
|
|
128
134
|
|
129
135
|
# on model class level we return the collection
|
130
136
|
def odata_get(req)
|
131
|
-
@params = req.params
|
132
|
-
initialize_dataset
|
133
|
-
|
137
|
+
@params = @params || req.params
|
138
|
+
initialize_dataset
|
139
|
+
initialize_uparms
|
134
140
|
@uparms.check_all.if_valid { |_ret|
|
135
141
|
odata_get_apply_params.if_valid { |_ret|
|
136
142
|
odata_get_output(req)
|
@@ -167,8 +173,7 @@ module Safrano
|
|
167
173
|
end
|
168
174
|
|
169
175
|
def initialize_dataset(dtset = nil)
|
170
|
-
@cx = dtset || navigated_dataset
|
171
|
-
@uparms = UrlParameters4Coll.new(@cx, @params)
|
176
|
+
@cx = @cx || dtset || navigated_dataset
|
172
177
|
end
|
173
178
|
# redefinitions of the main methods for a navigated collection
|
174
179
|
# (eg. all Books of Author[2] is Author[2].Books.all )
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'odata/error
|
3
|
+
require 'odata/error'
|
4
4
|
|
5
|
-
require_relative 'filter/parse
|
6
|
-
require_relative 'filter/sequel
|
5
|
+
require_relative 'filter/parse'
|
6
|
+
require_relative 'filter/sequel'
|
7
7
|
|
8
8
|
# filter base class and subclass in our OData namespace
|
9
9
|
module Safrano
|
@@ -45,12 +45,12 @@ module Safrano
|
|
45
45
|
# the join-helper is shared by the order-by object and was potentially already
|
46
46
|
# partly built on order-by object creation.
|
47
47
|
def finalize(jh)
|
48
|
-
@filtexpr = @ast.if_valid
|
48
|
+
@filtexpr = @ast.if_valid { |ast| ast.sequel_expr(jh) }
|
49
49
|
end
|
50
50
|
|
51
51
|
def apply_to_dataset(dtcx)
|
52
52
|
# normally finalize is called before, and thus @filtexpr is set
|
53
|
-
@filtexpr.map_result!
|
53
|
+
@filtexpr.map_result! { |f| dtcx.where(f) }
|
54
54
|
end
|
55
55
|
|
56
56
|
# Note: this is really only *parse* error, ie the error encounterd while
|
@@ -2,12 +2,17 @@
|
|
2
2
|
|
3
3
|
require 'rack'
|
4
4
|
require 'fileutils'
|
5
|
-
require_relative './navigation_attribute
|
5
|
+
require_relative './navigation_attribute'
|
6
6
|
|
7
7
|
module Safrano
|
8
8
|
module Media
|
9
9
|
# base class for Media Handler
|
10
10
|
class Handler
|
11
|
+
def check_before_create(data:,
|
12
|
+
entity:,
|
13
|
+
filename:)
|
14
|
+
Contract::OK
|
15
|
+
end
|
11
16
|
end
|
12
17
|
|
13
18
|
# Simple static File/Directory based media store handler
|
@@ -24,9 +29,16 @@ module Safrano
|
|
24
29
|
|
25
30
|
def register
|
26
31
|
@abs_klass_dir = File.absolute_path(@media_dir_name, @root)
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_abs_class_dir
|
27
35
|
FileUtils.makedirs @abs_klass_dir unless Dir.exist?(@abs_klass_dir)
|
28
36
|
end
|
29
37
|
|
38
|
+
def finalize
|
39
|
+
create_abs_class_dir
|
40
|
+
end
|
41
|
+
|
30
42
|
# minimal working implementation...
|
31
43
|
# Note: @file_server works relative to @root directory
|
32
44
|
def odata_get(request:, entity:)
|
@@ -47,13 +59,13 @@ module Safrano
|
|
47
59
|
end
|
48
60
|
|
49
61
|
# relative to @root
|
50
|
-
# eg Photo/1/
|
62
|
+
# eg Photo/1/1
|
51
63
|
def filename(entity)
|
52
64
|
Dir.chdir(abs_path(entity)) do
|
53
65
|
# simple design: one file per directory, and the directory
|
54
66
|
# contains the media entity-id --> implicit link between the media
|
55
67
|
# entity
|
56
|
-
File.join(media_path(entity), Dir.glob('*').
|
68
|
+
File.join(media_path(entity), Dir.glob('*').max)
|
57
69
|
end
|
58
70
|
end
|
59
71
|
|
@@ -62,6 +74,11 @@ module Safrano
|
|
62
74
|
File.absolute_path(media_path(entity), @root)
|
63
75
|
end
|
64
76
|
|
77
|
+
# absolute filename
|
78
|
+
def abs_filename(entity)
|
79
|
+
File.absolute_path(filename(entity), @root)
|
80
|
+
end
|
81
|
+
|
65
82
|
# this is relative to abs_klass_dir(entity) eg to /@root/Photo
|
66
83
|
# simplest implementation is media_directory = entity.media_path_id
|
67
84
|
# --> we get a 1 level depth flat directory structure
|
@@ -101,7 +118,7 @@ module Safrano
|
|
101
118
|
def ressource_version(entity)
|
102
119
|
Dir.chdir(@abs_klass_dir) do
|
103
120
|
in_media_directory(entity) do
|
104
|
-
Dir.glob('*').
|
121
|
+
Dir.glob('*').max
|
105
122
|
end
|
106
123
|
end
|
107
124
|
end
|
@@ -135,15 +152,14 @@ module Safrano
|
|
135
152
|
|
136
153
|
# this is relative to abs_klass_dir(entity) eg to /@root/Photo
|
137
154
|
# tree-structure
|
138
|
-
# media_path_ids = 1 --> 1
|
139
|
-
# media_path_ids = 15 --> 1/5
|
140
|
-
# media_path_ids = 555 --> 5/5/5
|
141
|
-
# media_path_ids = 5,5,5 --> 5/00/5/00/5
|
142
|
-
# media_path_ids = 5,00,5 --> 5/00/0/0/00/5
|
143
|
-
# media_path_ids = 5,xyz,5 --> 5/00/x/y/z/00/5
|
155
|
+
# media_path_ids = 1 --> 1/v
|
156
|
+
# media_path_ids = 15 --> 1/5/v
|
157
|
+
# media_path_ids = 555 --> 5/5/5/v
|
158
|
+
# media_path_ids = 5,5,5 --> 5/00/5/00/5/v
|
159
|
+
# media_path_ids = 5,00,5 --> 5/00/0/0/00/5/v
|
160
|
+
# media_path_ids = 5,xyz,5 --> 5/00/x/y/z/00/5/v
|
144
161
|
def media_directory(entity)
|
145
162
|
StaticTree.path_builder(entity.media_path_ids)
|
146
|
-
# entity.media_path_ids.map{|id| id.to_s.chars.join('/')}.join(@sep)
|
147
163
|
end
|
148
164
|
|
149
165
|
def in_media_directory(entity)
|
@@ -201,9 +217,14 @@ module Safrano
|
|
201
217
|
@media_handler = Safrano::Media::Static.new(mediaklass: self)
|
202
218
|
end
|
203
219
|
|
220
|
+
def finalize_media
|
221
|
+
@media_handler.finalize
|
222
|
+
end
|
223
|
+
|
204
224
|
def use(klass, args)
|
205
225
|
args[:mediaklass] = self
|
206
226
|
@media_handler = klass.new(**args)
|
227
|
+
@media_handler.create_abs_class_dir
|
207
228
|
end
|
208
229
|
|
209
230
|
# API method for setting the model field mapped to SLUG on upload
|
@@ -250,22 +271,39 @@ module Safrano
|
|
250
271
|
missing: :skip)
|
251
272
|
end
|
252
273
|
|
253
|
-
#
|
254
|
-
if
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
274
|
+
# call before_create_entity media hook
|
275
|
+
new_entity.before_create_media_entity(data: data, mimetype: mimetype) if new_entity.respond_to? :before_create_media_entity
|
276
|
+
|
277
|
+
media_handler.check_before_create(data: data,
|
278
|
+
entity: new_entity,
|
279
|
+
filename: filename).if_valid { |_ret|
|
280
|
+
# to_one rels are create with FK data set on the parent entity
|
281
|
+
if parent
|
282
|
+
odata_create_save_entity_and_rel(req, new_entity, assoc, parent)
|
283
|
+
else
|
284
|
+
# in-changeset requests get their own transaction
|
285
|
+
new_entity.save(transaction: !req.in_changeset)
|
286
|
+
end
|
287
|
+
|
288
|
+
req.register_content_id_ref(new_entity)
|
289
|
+
new_entity.copy_request_infos(req)
|
290
|
+
|
291
|
+
# call before_create_media hook
|
292
|
+
new_entity.before_create_media if new_entity.respond_to? :before_create_media
|
293
|
+
|
294
|
+
media_handler.save_file(data: data,
|
295
|
+
entity: new_entity,
|
296
|
+
filename: filename)
|
297
|
+
|
298
|
+
# call after_create_media hook
|
299
|
+
new_entity.after_create_media if new_entity.respond_to? :after_create_media
|
260
300
|
|
261
|
-
|
262
|
-
|
301
|
+
# json is default content type so we dont need to specify it here again
|
302
|
+
# Contract.valid([201, EMPTY_HASH, new_entity.to_odata_post_json(service: req.service)])
|
303
|
+
# TODO quirks array mode !
|
304
|
+
Contract.valid([201, EMPTY_HASH, new_entity.to_odata_create_json(request: req)])
|
305
|
+
}.tap_error { |e| return e.odata_get(req) }.result
|
263
306
|
|
264
|
-
media_handler.save_file(data: data,
|
265
|
-
entity: new_entity,
|
266
|
-
filename: filename)
|
267
|
-
# json is default content type so we dont need to specify it here again
|
268
|
-
[201, EMPTY_HASH, new_entity.to_odata_post_json(service: req.service)]
|
269
307
|
else # TODO: other formats
|
270
308
|
415
|
271
309
|
end
|
data/lib/odata/complex_type.rb
CHANGED
@@ -2,76 +2,184 @@
|
|
2
2
|
|
3
3
|
module Safrano
|
4
4
|
module FunctionImport
|
5
|
+
EMPTY_HASH = {}.freeze
|
5
6
|
class ResultDefinition
|
6
|
-
D = 'd'
|
7
|
-
DJ_OPEN = '{"d":'
|
8
|
-
DJ_CLOSE = '}'
|
9
|
-
METAK = '__metadata'
|
10
|
-
TYPEK = 'type'
|
11
|
-
VALUEK = 'value'
|
12
|
-
RESULTSK = 'results'
|
13
|
-
COLLECTION = 'Collection'
|
14
|
-
|
15
|
-
def
|
16
|
-
|
7
|
+
D = 'd'
|
8
|
+
DJ_OPEN = '{"d":'
|
9
|
+
DJ_CLOSE = '}'
|
10
|
+
METAK = '__metadata'
|
11
|
+
TYPEK = 'type'
|
12
|
+
VALUEK = 'value'
|
13
|
+
RESULTSK = 'results'
|
14
|
+
COLLECTION = 'Collection'
|
15
|
+
|
16
|
+
def allowed_transitions
|
17
|
+
[Safrano::TransitionEnd]
|
17
18
|
end
|
18
|
-
|
19
|
-
def
|
20
|
-
|
19
|
+
|
20
|
+
def transition_end(_match_result)
|
21
|
+
Safrano::Transition::RESULT_END
|
21
22
|
end
|
22
|
-
|
23
|
-
|
23
|
+
|
24
|
+
# we will have this on class and instance level for making things simpler first
|
25
|
+
def self.klassmod
|
26
|
+
@klassmod
|
27
|
+
end
|
28
|
+
|
29
|
+
# return a subclass of ResultAsComplexType
|
30
|
+
def self.asComplexType(klassmod)
|
31
|
+
Class.new(ResultAsComplexType) do
|
32
|
+
@klassmod = klassmod
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# return a subclass of ResultAsComplexType
|
37
|
+
def self.asComplexTypeColl(klassmod)
|
38
|
+
Class.new(ResultAsComplexTypeColl) do
|
39
|
+
@klassmod = klassmod
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.asPrimitiveType(klassmod)
|
44
|
+
Class.new(ResultAsPrimitiveType) do
|
45
|
+
@klassmod = klassmod
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.asPrimitiveTypeColl(klassmod)
|
50
|
+
Class.new(ResultAsPrimitiveTypeColl) do
|
51
|
+
@klassmod = klassmod
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.asEntity(klassmod)
|
56
|
+
Class.new(ResultAsEntity) do
|
57
|
+
@klassmod = klassmod
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.asEntityColl(klassmod)
|
62
|
+
Class.new(ResultAsEntityColl) do
|
63
|
+
@klassmod = klassmod
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def initialize(value)
|
68
|
+
@value = value
|
69
|
+
end
|
70
|
+
|
71
|
+
def odata_get(req)
|
72
|
+
[200, EMPTY_HASH, [to_odata_json(req)]]
|
73
|
+
end
|
74
|
+
def self.type_metadata
|
24
75
|
@klassmod.type_name
|
25
76
|
end
|
77
|
+
def type_metadata
|
78
|
+
self.class.type_metadata
|
79
|
+
end
|
80
|
+
|
81
|
+
# needed for ComplexType result
|
82
|
+
def to_odata_json(_req)
|
83
|
+
"#{DJ_OPEN}#{@value.odata_h.to_json}#{DJ_CLOSE}"
|
84
|
+
end
|
85
|
+
|
86
|
+
# wrapper
|
87
|
+
# for OData Entity and Collections, return them directly
|
88
|
+
# for others, ie ComplexType, Prims etc, return the ResultDefinition-subclass wrapped result
|
89
|
+
def self.do_execute_func_result(result, _req, apply_query_params=false)
|
90
|
+
self.new(result)
|
91
|
+
end
|
92
|
+
|
26
93
|
end
|
94
|
+
|
27
95
|
class ResultAsComplexType < ResultDefinition
|
96
|
+
def self.type_metadata
|
97
|
+
@klassmod.type_name
|
98
|
+
end
|
28
99
|
end
|
100
|
+
|
29
101
|
class ResultAsComplexTypeColl < ResultDefinition
|
30
|
-
def type_metadata
|
102
|
+
def self.type_metadata
|
31
103
|
"Collection(#{@klassmod.type_name})"
|
32
104
|
end
|
33
105
|
|
34
|
-
def to_odata_json(
|
35
|
-
"#{DJ_OPEN}#{{ RESULTSK => coll.map { |c| c.odata_h } }.to_json}#{DJ_CLOSE}"
|
106
|
+
def to_odata_json(req)
|
107
|
+
# "#{DJ_OPEN}#{{ RESULTSK => coll.map { |c| c.odata_h } }.to_json}#{DJ_CLOSE}"
|
108
|
+
template = self.class.klassmod.output_template
|
109
|
+
# TODO: Error handling if database contains binary BLOB data that cant be
|
110
|
+
# interpreted as UTF-8 then JSON will fail here
|
111
|
+
|
112
|
+
innerh = req.service.get_coll_odata_h(array: @value,
|
113
|
+
template: template)
|
114
|
+
|
115
|
+
innerj = innerh.to_json
|
116
|
+
|
117
|
+
"#{DJ_OPEN}#{innerj}#{DJ_CLOSE}"
|
36
118
|
end
|
37
119
|
end
|
120
|
+
|
38
121
|
class ResultAsEntity < ResultDefinition
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
to_odata_json(request: req)
|
43
|
-
end
|
122
|
+
|
123
|
+
def self.type_metadata
|
124
|
+
@klassmod.type_name
|
44
125
|
end
|
126
|
+
|
127
|
+
|
128
|
+
# wrapper
|
129
|
+
# for OData Entity return them directly
|
130
|
+
def self.do_execute_func_result(result, _req, apply_query_params=false)
|
131
|
+
# note: Sequel entities instances seem to be thread safe, so we can
|
132
|
+
# safely add request-dependant data (eg. req.params) there
|
133
|
+
apply_query_params ? result : result.inactive_query_params
|
134
|
+
end
|
135
|
+
|
45
136
|
end
|
137
|
+
|
46
138
|
class ResultAsEntityColl < ResultDefinition
|
47
|
-
|
139
|
+
|
140
|
+
def self.type_metadata
|
48
141
|
"Collection(#{@klassmod.type_name})"
|
49
142
|
end
|
50
|
-
|
51
|
-
|
143
|
+
|
144
|
+
# wrapper
|
145
|
+
# for OData Entity Collection return them directly
|
146
|
+
def self.do_execute_func_result(result, req, apply_query_params=false)
|
52
147
|
coll = Safrano::OData::Collection.new(@klassmod)
|
148
|
+
# instance_exec has other instance variables; @values would be nil in the block below
|
149
|
+
# need to pass a local copy
|
150
|
+
dtset = result
|
53
151
|
coll.instance_exec do
|
54
|
-
|
55
|
-
|
152
|
+
|
153
|
+
@params = apply_query_params ? req.params : EMPTY_HASH
|
154
|
+
initialize_dataset(dtset)
|
155
|
+
initialize_uparms
|
56
156
|
end
|
57
|
-
coll
|
157
|
+
coll
|
58
158
|
end
|
159
|
+
|
59
160
|
end
|
161
|
+
|
60
162
|
class ResultAsPrimitiveType < ResultDefinition
|
61
|
-
def
|
163
|
+
def self.type_metadata
|
164
|
+
@klassmod.type_name
|
165
|
+
end
|
166
|
+
|
167
|
+
def to_odata_json(_req)
|
62
168
|
{ D => { METAK => { TYPEK => type_metadata },
|
63
|
-
VALUEK =>
|
169
|
+
VALUEK => self.class.klassmod.odata_value(@value) } }.to_json
|
64
170
|
end
|
65
171
|
end
|
172
|
+
|
66
173
|
class ResultAsPrimitiveTypeColl < ResultDefinition
|
67
|
-
def type_metadata
|
174
|
+
def self.type_metadata
|
68
175
|
"Collection(#{@klassmod.type_name})"
|
69
176
|
end
|
70
177
|
|
71
|
-
def to_odata_json(
|
72
|
-
{ D => { METAK => { TYPEK => type_metadata },
|
73
|
-
RESULTSK =>
|
178
|
+
def to_odata_json(_req)
|
179
|
+
{ D => { METAK => { TYPEK => self.class.type_metadata },
|
180
|
+
RESULTSK => self.class.klassmod.odata_collection(@value) } }.to_json
|
74
181
|
end
|
182
|
+
|
75
183
|
end
|
76
184
|
end
|
77
185
|
|
@@ -80,7 +188,8 @@ module Safrano
|
|
80
188
|
# with added OData functionality
|
81
189
|
class ComplexType
|
82
190
|
attr_reader :values
|
83
|
-
|
191
|
+
EMPTYH = {}.freeze
|
192
|
+
|
84
193
|
@namespace = nil
|
85
194
|
def self.namespace
|
86
195
|
@namespace
|
@@ -89,19 +198,52 @@ module Safrano
|
|
89
198
|
def self.props
|
90
199
|
@props
|
91
200
|
end
|
92
|
-
|
201
|
+
|
202
|
+
def type_name
|
203
|
+
self.class.type_name
|
204
|
+
end
|
205
|
+
|
206
|
+
def metadata_h
|
207
|
+
{ type: type_name }
|
208
|
+
end
|
209
|
+
|
210
|
+
def casted_values
|
211
|
+
# MVP... TODO: handle time mappings like in Entity models
|
212
|
+
values
|
213
|
+
end
|
214
|
+
|
215
|
+
# needed for nested json output
|
216
|
+
# this is a simpler version of model_ext#output_template
|
217
|
+
def self.default_template
|
218
|
+
template = {}
|
219
|
+
expand_e = {}
|
220
|
+
|
221
|
+
template[:all_values] = EMPTYH
|
222
|
+
@props.each { |prop, kl|
|
223
|
+
if kl.respond_to? :default_template
|
224
|
+
expand_e[prop] = kl.default_template
|
225
|
+
end
|
226
|
+
}
|
227
|
+
template[:expand_e] = expand_e
|
228
|
+
template
|
229
|
+
end
|
230
|
+
|
231
|
+
def self.output_template
|
232
|
+
default_template
|
233
|
+
end
|
93
234
|
def self.type_name
|
94
|
-
"#{@namespace}.#{self.to_s}"
|
235
|
+
@namespace ? "#{@namespace}.#{self.to_s}" : self.to_s
|
95
236
|
end
|
96
237
|
|
97
238
|
def initialize
|
98
239
|
@values = {}
|
99
240
|
end
|
100
|
-
METAK = '__metadata'
|
101
|
-
TYPEK = 'type'
|
241
|
+
METAK = '__metadata'
|
242
|
+
TYPEK = 'type'
|
102
243
|
|
103
244
|
def odata_h
|
104
245
|
ret = { METAK => { TYPEK => self.class.type_name } }
|
246
|
+
|
105
247
|
@values.each { |k, v|
|
106
248
|
ret[k] = if v.respond_to? :odata_h
|
107
249
|
v.odata_h
|
@@ -113,11 +255,11 @@ module Safrano
|
|
113
255
|
end
|
114
256
|
|
115
257
|
def self.return_as_collection_descriptor
|
116
|
-
FunctionImport::
|
258
|
+
FunctionImport::ResultDefinition.asComplexTypeColl(self)
|
117
259
|
end
|
118
260
|
|
119
261
|
def self.return_as_instance_descriptor
|
120
|
-
FunctionImport::
|
262
|
+
FunctionImport::ResultDefinition.asComplexType(self)
|
121
263
|
end
|
122
264
|
|
123
265
|
# add metadata xml to the passed REXML schema object
|