safrano 0.8.0 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -35,6 +35,7 @@ module Safrano
35
35
  end
36
36
  end
37
37
  end
38
+
38
39
  # This is used from the Test-suite code !
39
40
  # it does recursive / deep symbolize additionally to inbound casting
40
41
  module XJSON
@@ -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 :rgx
13
- attr_reader :remain_idx
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 do_match(str)
34
- @match_result = @rgx.match(str)
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
- @match_result[@remain_idx] if @match_result && @match_result[@remain_idx]
129
+ @match[@remain_idx] if @match && @match[@remain_idx]
41
130
  end
42
131
 
43
132
  def path_done
44
- if @match_result
45
- @match_result[1] || EMPTYSTR
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.method(@trans).call(@match_result)
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 do_match(str)
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: 'transition_end')
80
- TransitionExecuteFunc = InplaceTransition.new(trans: 'transition_execute_func')
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: 'transition_metadata')
189
+ trans: :transition_metadata)
83
190
  TransitionBatch = Transition.new('\A(\/\$batch)(.*)',
84
- trans: 'transition_batch')
191
+ trans: :transition_batch)
85
192
  TransitionContentId = Transition.new('\A(\/\$(\d+))(.*)',
86
- trans: 'transition_content_id',
193
+ trans: :transition_content_id,
87
194
  remain_idx: 3)
88
195
  TransitionCount = Transition.new('(\A\/\$count)(.*)\z',
89
- trans: 'transition_count')
196
+ trans: :transition_count)
90
197
  TransitionValue = Transition.new('(\A\/\$value)(.*)\z',
91
- trans: 'transition_value')
198
+ trans: :transition_value)
92
199
  TransitionLinks = Transition.new('(\A\/\$links)(.*)\z',
93
- trans: 'transition_links')
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 = @tr_next.do_transition(@context)
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 = @tr_next.path_remain
155
- @path_done << @tr_next.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
- get_next_transition
164
- if @tr_next
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
@@ -22,6 +22,13 @@ module Safrano
22
22
  Safrano::Request.new(env, @service_base).process
23
23
  end
24
24
 
25
+ # needed for testing only ? try to remove this
26
+ def self.copy(other)
27
+ copy = Class.new(Safrano::ServerApp) # <---- !!!
28
+ copy.set_servicebase(other.get_service_base.dup)
29
+ copy
30
+ end
31
+
25
32
  # needed for testing only ? try to remove this
26
33
  def self.enable_batch
27
34
  @service_base.enable_batch
@@ -32,16 +39,22 @@ module Safrano
32
39
  @service_base.path_prefix path_pr
33
40
  end
34
41
 
42
+ # needed for testing only ? try to remove this
43
+
44
+ def self.response_format_options(*args)
45
+ @service_base.response_format_options(*args)
46
+ end
47
+
35
48
  # needed for testing only ? try to remove this
36
49
  def self.get_service_base
37
50
  @service_base
38
51
  end
39
-
40
- # needed for safrano-rack_builder
52
+
53
+ # needed for safrano-rack_builder
41
54
  def get_path_prefix
42
55
  self.class.get_service_base.xpath_prefix
43
56
  end
44
-
57
+
45
58
  def self.set_servicebase(sbase)
46
59
  @service_base = sbase
47
60
  @service_base.enable_v1_service
@@ -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
- # Ressource /$batch
43
- /\A\/\$batch\z/
44
- else
45
- # Ressource like /foo/bar/baz/$batch
46
- service_path_prefix.sub!(::Safrano::TRAILING_SLASH_RGX, '')
47
- service_path_prefix.sub!(::Safrano::LEADING_SLASH_RGX, '')
48
- # now is foo/bar/baz
49
- path_prefix_rgx = Regexp.escape("/#{service_path_prefix}/$batch")
50
- # now escaped path regexp /foo/bar/baz/$batch
51
- # finaly just add start / end anchors
52
- /\A#{path_prefix_rgx}\z/
53
- end
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 (batch_path_regexp), headers: :any, methods: [:post, :head, :options]
61
- resource '*', headers: :any, methods: [:get, :post, :put, :patch, :head, :options]
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: [:get, :head, :options]
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
@@ -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
- .if_valid { |context| context.odata_post(self) }
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
@@ -12,8 +12,12 @@ require 'set'
12
12
  require 'odata/collection'
13
13
 
14
14
  module Safrano
15
- # this module has all methods related to expand/defered output preparation
15
+ # these modules have all methods related to expand/defered output preparation
16
16
  # and will be included in Service class
17
+
18
+ METADATA_K = '__metadata'
19
+ EMPTYH = {}.freeze
20
+
17
21
  module ExpandHandler
18
22
  PATH_SPLITTER = %r{\A(\w+)/?(.*)\z}.freeze
19
23
  DEFERRED = '__deferred'
@@ -40,14 +44,17 @@ module Safrano
40
44
  { uri: entity.uri }
41
45
  end
42
46
 
43
- EMPTYH = {}.freeze
44
- METADATA_K = '__metadata'
45
47
  def get_entity_odata_h(entity:, template:)
46
- # start with metadata
47
- hres = { METADATA_K => entity.metadata_h }
48
+ hres = {}
49
+ # finalise the template according to options
50
+ # (eg. skip metadata and or deferred...)
51
+ @final_template_func.call(template)
48
52
 
49
53
  template.each do |elmt, arg|
50
54
  case elmt
55
+ when :meta
56
+ hres[METADATA_K] = entity.metadata_h
57
+
51
58
  when :all_values
52
59
  hres.merge! entity.casted_values
53
60
 
@@ -110,6 +117,7 @@ module Safrano
110
117
  TRAILING_SLASH_RGX = %r{/\z}.freeze
111
118
  LEADING_SLASH_RGX = %r{\A/}.freeze
112
119
  include XMLNS
120
+ GENERIC_415_RESP = [415, {}, ['']].freeze
113
121
  # Base class for service. Subclass will be for V1, V2 etc...
114
122
  class ServiceBase
115
123
  include Safrano
@@ -142,6 +150,8 @@ module Safrano
142
150
  attr_accessor :function_imports
143
151
  attr_accessor :function_import_keys
144
152
  attr_accessor :type_mappings
153
+ attr_accessor :final_template_func
154
+ attr_accessor :response_format_options
145
155
 
146
156
  # Instance attributes for specialized Version specific Instances
147
157
  attr_accessor :v1
@@ -150,6 +160,12 @@ module Safrano
150
160
  # TODO: more elegant design
151
161
  attr_reader :data_service_version
152
162
 
163
+ # for response format options
164
+ FINAL_TEMPLATE_FUNC_DEFAULT = ->(_template) {}
165
+ FINAL_TEMPLATE_FUNC_SKIP_META = ->(template) { template.delete(:meta) }
166
+ FINAL_TEMPLATE_FUNC_SKIP_DEFERR = ->(template) { template.delete(:deferr) }
167
+ FINAL_TEMPLATE_FUNC_SKIP_META_DEFERR = ->(template) { template.delete(:meta); template.delete(:deferr) }
168
+
153
169
  def initialize(&block)
154
170
  # Warning: if you add attributes here, you shall need add them
155
171
  # in copy_attribs_to as well
@@ -163,6 +179,8 @@ module Safrano
163
179
  @function_import_keys = []
164
180
  @cmap = {}
165
181
  @type_mappings = {}
182
+ @response_format_options = []
183
+ @final_template_func = FINAL_TEMPLATE_FUNC_DEFAULT
166
184
  # enabled per default starting from 0.6
167
185
  @bugfix_create_response = true
168
186
  instance_eval(&block) if block_given?
@@ -213,12 +231,42 @@ module Safrano
213
231
  (@v2.xserver_url = @xserver_url) if @v2
214
232
  end
215
233
 
234
+ VALID_RESP_FORMAT_OPTS = [:skip_deferred, :skip_metadata]
235
+ def valid_resp_format_options(*args)
236
+ args.map!(&:to_sym)
237
+ args.each { |arg|
238
+ raise API::InvalidRespFormatOption.new(arg) unless VALID_RESP_FORMAT_OPTS.include?(arg)
239
+ }
240
+ yield(args)
241
+ end
242
+
243
+ def response_format_options(*args)
244
+ valid_resp_format_options(*args) do |vargs|
245
+ @response_format_options = vargs.dup
246
+ process_response_format_options
247
+ @v1.response_format_options(*vargs) if @v1
248
+ @v2.response_format_options(*vargs) if @v2
249
+ end
250
+ end
251
+
216
252
  # keep the bug active for now, but allow to de-activate the fix
217
253
  def bugfix_create_response(bool)
218
254
  @bugfix_create_response = bool
219
255
  end
220
256
 
221
257
  # end public API
258
+ def process_response_format_options
259
+ @final_template_func = if (@response_format_options.include?(:skip_metadata) &&
260
+ @response_format_options.include?(:skip_deferred))
261
+ FINAL_TEMPLATE_FUNC_SKIP_META_DEFERR
262
+ elsif @response_format_options.include?(:skip_metadata)
263
+ FINAL_TEMPLATE_FUNC_SKIP_META
264
+ elsif @response_format_options.include?(:skip_deferred)
265
+ FINAL_TEMPLATE_FUNC_SKIP_DEFERR
266
+ else
267
+ FINAL_TEMPLATE_FUNC_DEFAULT
268
+ end
269
+ end
222
270
 
223
271
  def set_uribase
224
272
  @uribase = if @xpath_prefix.empty?
@@ -249,6 +297,8 @@ module Safrano
249
297
  other.function_imports = @function_imports
250
298
  other.function_import_keys = @function_import_keys
251
299
  other.type_mappings = @type_mappings
300
+ other.response_format_options = @response_format_options
301
+ other.final_template_func = @final_template_func
252
302
  other.bugfix_create_response(@bugfix_create_response)
253
303
  other
254
304
  end
@@ -620,6 +670,8 @@ module Safrano
620
670
  Safrano::TransitionContentId
621
671
  ].freeze
622
672
 
673
+ include Safrano::Transitions::GetNextTrans::ByLongestMatch
674
+
623
675
  def build_allowed_transitions
624
676
  @allowed_transitions = if @function_imports.empty?
625
677
  (ALLOWED_TRANSITIONS_FIXED + [
@@ -679,7 +731,7 @@ module Safrano
679
731
  [200, CT_APPXML, [service_xml(req)]]
680
732
  else
681
733
  # this is returned by http://services.odata.org/V2/OData/Safrano.svc
682
- 415
734
+ GENERIC_415_RESP
683
735
  end
684
736
  end
685
737
  end
@@ -748,11 +800,13 @@ module Safrano
748
800
  Safrano::Transition::RESULT_END
749
801
  end
750
802
 
803
+ include Safrano::Transitions::GetNextTrans::ForJustTransitionEnd
804
+
751
805
  def odata_get(req)
752
806
  if req.accept?(APPXML)
753
807
  [200, CT_APPXML, [@service.metadata_xml(req)]]
754
808
  else
755
- 415
809
+ GENERIC_415_RESP
756
810
  end
757
811
  end
758
812
  end
@@ -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
- m
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