safrano 0.5.3 → 0.6.0

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/lib/core_ext/Date/format.rb +47 -0
  3. data/lib/core_ext/DateTime/format.rb +54 -0
  4. data/lib/core_ext/Dir/iter.rb +7 -5
  5. data/lib/core_ext/Hash/transform.rb +14 -6
  6. data/lib/core_ext/Numeric/convert.rb +25 -0
  7. data/lib/core_ext/Time/format.rb +71 -0
  8. data/lib/core_ext/date.rb +5 -0
  9. data/lib/core_ext/datetime.rb +5 -0
  10. data/lib/core_ext/numeric.rb +3 -0
  11. data/lib/core_ext/time.rb +5 -0
  12. data/lib/odata/attribute.rb +8 -6
  13. data/lib/odata/batch.rb +3 -3
  14. data/lib/odata/collection.rb +9 -9
  15. data/lib/odata/collection_filter.rb +3 -1
  16. data/lib/odata/collection_media.rb +4 -27
  17. data/lib/odata/collection_order.rb +1 -1
  18. data/lib/odata/common_logger.rb +5 -27
  19. data/lib/odata/complex_type.rb +61 -67
  20. data/lib/odata/edm/primitive_types.rb +110 -42
  21. data/lib/odata/entity.rb +14 -47
  22. data/lib/odata/error.rb +7 -7
  23. data/lib/odata/expand.rb +2 -2
  24. data/lib/odata/filter/base.rb +10 -1
  25. data/lib/odata/filter/error.rb +2 -2
  26. data/lib/odata/filter/parse.rb +16 -2
  27. data/lib/odata/filter/sequel.rb +31 -4
  28. data/lib/odata/filter/sequel_datetime_adapter.rb +21 -0
  29. data/lib/odata/filter/token.rb +18 -5
  30. data/lib/odata/filter/tree.rb +83 -9
  31. data/lib/odata/function_import.rb +19 -18
  32. data/lib/odata/model_ext.rb +96 -38
  33. data/lib/odata/request/json.rb +171 -0
  34. data/lib/odata/transition.rb +13 -9
  35. data/lib/odata/url_parameters.rb +3 -3
  36. data/lib/odata/walker.rb +9 -9
  37. data/lib/safrano/multipart.rb +1 -3
  38. data/lib/safrano/rack_app.rb +2 -14
  39. data/lib/safrano/rack_builder.rb +0 -15
  40. data/lib/safrano/request.rb +3 -3
  41. data/lib/safrano/response.rb +3 -3
  42. data/lib/safrano/service.rb +43 -12
  43. data/lib/safrano/type_mapping.rb +149 -0
  44. data/lib/safrano/version.rb +1 -2
  45. data/lib/safrano.rb +3 -0
  46. metadata +54 -15
@@ -5,6 +5,7 @@
5
5
  # Thus Below we have called that "EntityClass". It's meant as "Collection"
6
6
 
7
7
  require 'json'
8
+ require 'base64'
8
9
  require 'rexml/document'
9
10
  require 'bigdecimal'
10
11
  require_relative '../safrano/core'
@@ -32,9 +33,14 @@ module Safrano
32
33
  attr_reader :default_template
33
34
  attr_reader :uri
34
35
  attr_reader :odata_upk_parts
35
- attr_reader :time_cols
36
- attr_reader :decimal_cols
36
+ attr_reader :casted_cols
37
37
  attr_reader :namespace
38
+ # store cols metata here in the model (sub)-class. Initially we stored this infos (eg. edm_types etc)
39
+ # directly into sequels db_schema hash. But this hash is on the upper Sequel::Model(table) class
40
+ # and is shared by all subclasses.
41
+ # By storing it separately here we are less dependant from Sequel, and have less issues with
42
+ # testing with multiples models class derived from same Sequel::Model(table)
43
+ attr_reader :cols_metadata
38
44
 
39
45
  # initialising block of code to be executed at end of
40
46
  # ServerApp.publish_service after all model classes have been registered
@@ -75,7 +81,7 @@ module Safrano
75
81
  @uparms = nil
76
82
  @params = nil
77
83
  @cx = nil
78
- # @@time_cols = nil
84
+ @cols_metadata = {}
79
85
  end
80
86
 
81
87
  def build_uri(uribase)
@@ -96,11 +102,10 @@ module Safrano
96
102
 
97
103
  # Factory json-> Model Object instance
98
104
  def new_from_hson_h(hash)
99
- #enty = new
100
- #enty.set_fields(hash, data_fields, missing: :skip)
101
- enty = create(hash)
102
- #enty.set(hash)
103
- enty
105
+ # enty = new
106
+ # enty.set_fields(hash, data_fields, missing: :skip)
107
+ create(hash)
108
+ # enty.set(hash)
104
109
  end
105
110
 
106
111
  def attrib_path_valid?(path)
@@ -178,13 +183,14 @@ module Safrano
178
183
  end
179
184
  # with their properties
180
185
  db_schema.each do |pnam, prop|
186
+ metadata = @cols_metadata[pnam]
181
187
  if prop[:primary_key] == true
182
188
  enty.add_element('Key').add_element('PropertyRef',
183
189
  'Name' => pnam.to_s)
184
190
  end
185
191
  attrs = { 'Name' => pnam.to_s,
186
192
  # 'Type' => Safrano.get_edm_type(db_type: prop[:db_type]) }
187
- 'Type' => prop[:odata_edm_type] }
193
+ 'Type' => metadata[:edm_type] }
188
194
  attrs['Nullable'] = 'false' if prop[:allow_null] == false
189
195
  enty.add_element('Property', attrs)
190
196
  end
@@ -346,30 +352,82 @@ module Safrano
346
352
 
347
353
  def build_default_template
348
354
  @default_template = { all_values: EMPTYH }
349
- if @nav_entity_attribs || @nav_collection_attribs
350
- @default_template[:deferr] = (@nav_entity_attribs&.keys || []) + (@nav_collection_attribs&.keys || EMPTY_ARRAY)
355
+ @default_template[:deferr] = (@nav_entity_attribs&.keys || []) + (@nav_collection_attribs&.keys || EMPTY_ARRAY) if @nav_entity_attribs || @nav_collection_attribs
356
+ end
357
+
358
+ def build_casted_cols(service)
359
+ # cols needed catsting before final json output
360
+ @casted_cols = {}
361
+ db_schema.each do |col, props|
362
+ # first check if we have user-defined type mapping
363
+ usermap = nil
364
+ dbtyp = props[:db_type]
365
+ metadata = @cols_metadata[col]
366
+ if service.type_mappings.values.find { |map| usermap = map.match(dbtyp) }
367
+
368
+ metadata[:edm_type] = usermap.edm_type
369
+
370
+ @casted_cols[col] = usermap.castfunc
371
+ next # this will override our rules below !
372
+ end
373
+
374
+ if metadata[:edm_precision] && (metadata[:edm_type] =~ /\AEdm.Decimal\(/i)
375
+ # we save the precision and/or scale in the lambda (binding!)
376
+
377
+ @casted_cols[col] = if metadata[:edm_scale]
378
+ lambda { |x|
379
+ # not sure if these copies are really needed, but feels better that way
380
+ # output decimal with precision and scale
381
+ x&.toDecimalPrecisionScaleString(metadata[:edm_precision], metadata[:edm_scale])
382
+ }
383
+ else
384
+ lambda { |x|
385
+ # not sure if these copies are really needed, but feels better that way
386
+ # output decimal with precision only
387
+ x&.toDecimalPrecisionString(metadata[:edm_precision])
388
+ }
389
+ end
390
+
391
+ next
392
+ end
393
+ if metadata[:edm_type] == 'Edm.Decimal'
394
+ @casted_cols[col] = ->(x) { x&.toDecimalString }
395
+ next
396
+ end
397
+ # Odata V2 Spec:
398
+ # Edm.Binary Base64 encoded value of an EDM.Binary value represented as a JSON string
399
+ # See for example https://services.odata.org/V2/Northwind/Northwind.svc/Categories(1)?$format=json
400
+ if metadata[:edm_type] == 'Edm.Binary'
401
+ @casted_cols[col] = ->(x) { Base64.encode64(x) unless x.nil? } # Base64
402
+ next
403
+ end
404
+ # TODO: check this more in details
405
+ # NOTE: here we use :type which is the sequel defined ruby-type
406
+ if props[:type] == :datetime || props[:type] == :date
407
+ # @casted_cols[col] = ->(x) { x&.iso8601 }
408
+ @casted_cols[col] = ->(x) { x&.to_edm_json }
409
+ end
351
410
  end
352
411
  end
353
412
 
354
- def finalize_publishing
413
+ def finalize_publishing(service)
355
414
  build_type_name
356
415
 
357
416
  # build default output template structure
358
417
  build_default_template
359
418
 
360
- # Time columns
361
- @time_cols = db_schema.select { |_c, v| v[:type] == :datetime }.map { |c, _v| c }
362
-
363
-
364
-
365
- # add edm_types into schema
366
- db_schema.each do |_col, props|
367
- props[:odata_edm_type] = Safrano.default_edm_type(ruby_type: props[:type],
368
- db_type: props[:db_type])
419
+ # add edm_types into metadata store
420
+ @cols_metadata = {}
421
+ db_schema.each do |col, props|
422
+ metadata = @cols_metadata.key?(col) ? @cols_metadata[col] : (@cols_metadata[col] = {})
423
+ Safrano.add_edm_types(metadata, props)
369
424
  end
370
- # Edm.Decimal cols
371
- @decimal_cols = db_schema.select { |_c, v| v[:odata_edm_type] == 'Edm.Decimal' }.map { |c, _v| c }
372
-
425
+
426
+ build_casted_cols(service)
427
+ # unless @casted_cols.empty?
428
+ # require 'pry'
429
+ # binding.pry
430
+ # end
373
431
  # and finally build the path lists and allowed tr's
374
432
  build_attribute_path_list
375
433
  build_expand_path_list
@@ -379,7 +437,7 @@ module Safrano
379
437
  build_entity_allowed_transitions
380
438
 
381
439
  # for media
382
- finalize_media if self.respond_to? :finalize_media
440
+ finalize_media if respond_to? :finalize_media
383
441
  end
384
442
 
385
443
  KEYPRED_URL_REGEXP = /\A\(\s*'?([\w=,'\s]+)'?\s*\)(.*)/.freeze
@@ -388,7 +446,7 @@ module Safrano
388
446
  @pk_names = []
389
447
  @pk_cast_from_string = {}
390
448
  odata_upk_build = []
391
- primary_key.each { |pk|
449
+ primary_key.each do |pk|
392
450
  @pk_names << pk.to_s
393
451
  kvpredicate = case db_schema[pk][:type]
394
452
  when :integer
@@ -398,19 +456,19 @@ module Safrano
398
456
  "'?'"
399
457
  end
400
458
  odata_upk_build << "#{pk}=#{kvpredicate}"
401
- }
459
+ end
402
460
  @odata_upk_parts = odata_upk_build.join(',').split('?')
403
461
 
404
462
  # regex parts for unordered matching
405
- @iuk_rgx_parts = primary_key.map { |pk|
463
+ @iuk_rgx_parts = primary_key.map do |pk|
406
464
  kvpredicate = case db_schema[pk][:type]
407
465
  when :integer
408
- "(\\d+)"
466
+ '(\\d+)'
409
467
  else
410
468
  "'(\\w+)'"
411
469
  end
412
470
  [pk, "#{pk}=#{kvpredicate}"]
413
- }.to_h
471
+ end.to_h
414
472
 
415
473
  # single regex assuming the key fields are ordered !
416
474
  @iuk_rgx = /\A#{@iuk_rgx_parts.values.join(',\s*')}\z/
@@ -424,7 +482,7 @@ module Safrano
424
482
  kvpredicate = case db_schema[primary_key][:type]
425
483
  when :integer
426
484
  @pk_cast_from_string = ->(str) { Integer(str) }
427
- "(\\d+)"
485
+ '(\\d+)'
428
486
  else
429
487
  "'(\\w+)'"
430
488
  end
@@ -556,14 +614,14 @@ module Safrano
556
614
  scan_rgx_parts = @iuk_rgx_parts.dup
557
615
  mdch = {}
558
616
 
559
- mid.split(/\s*,\s*/).each { |midpart|
617
+ mid.split(/\s*,\s*/).each do |midpart|
560
618
  mval = nil
561
- mpk, mrgx = scan_rgx_parts.find { |pk, rgx|
619
+ mpk, mrgx = scan_rgx_parts.find do |_pk, rgx|
562
620
  if (md = rgx.match(midpart))
563
621
  mval = md[1]
564
622
  end
565
- }
566
- if mpk and mval
623
+ end
624
+ if mpk && mval
567
625
  mdch[mpk] = if (pk_cast = @pk_cast_from_string[mpk])
568
626
  pk_cast.call(mval)
569
627
  else
@@ -573,7 +631,7 @@ module Safrano
573
631
  else
574
632
  return Contract::NOK
575
633
  end
576
- }
634
+ end
577
635
  # normally arriving here we have mdch filled with key values pairs,
578
636
  # but not in the model key ordering. lets just re-order the values
579
637
  mdc = @iuk_rgx_parts.keys.map { |pk| mdch[pk] }
@@ -592,7 +650,7 @@ module Safrano
592
650
 
593
651
  def parse_odata_key(rawid)
594
652
  if (md = @iuk_rgx.match(rawid))
595
- if (@pk_cast_from_string)
653
+ if @pk_cast_from_string
596
654
  Contract.valid(@pk_cast_from_string.call(md[1]))
597
655
  else
598
656
  Contract.valid(md[1]) # no cast needed, eg for string
@@ -616,7 +674,7 @@ module Safrano
616
674
  # 2. Create relationship if needed
617
675
  def odata_create_entity_and_relation(req, assoc = nil, parent = nil)
618
676
  # TODO: this is for v2 only...
619
- req.with_parsed_data do |data|
677
+ req.with_parsed_data(self) do |data|
620
678
  data.delete('__metadata')
621
679
 
622
680
  # validate payload column names
@@ -0,0 +1,171 @@
1
+ require 'json'
2
+ require 'time'
3
+
4
+ # client parsing functionality to ease testing
5
+
6
+ module Safrano
7
+ module OData
8
+ # this is used to parse inbound json payload on POST / PUT & co
9
+ # it does not do symbolize but proper (hopefully) type casting when needed
10
+ module JSON
11
+ # def self.parse(*args)
12
+ # ::JSON.parse(*args)
13
+ # end
14
+
15
+ def self.cast_values_in(resd, typ)
16
+ typ.db_schema.each do |f, props|
17
+ metadata = typ.cols_metadata[f]
18
+
19
+ case props[:type]
20
+ when :datetime, :date
21
+ fstr = f.to_s
22
+ # resd[fstr] = Time.parse(resd[f]) if resd[fstr]
23
+ resd[fstr] = Sequel.datetime_class.from_edm_json(resd[fstr]) if resd[fstr]
24
+ end
25
+ end
26
+
27
+ resd
28
+ end
29
+
30
+ def self.parse_one(*args, type)
31
+ cast_values_in(::JSON.parse(*args), type)
32
+ end
33
+ end
34
+ end
35
+ # This is used from the Test-suite code !
36
+ # it does recursive / deep symbolize additionally to inbound casting
37
+ module XJSON
38
+ def self.get_class_from_meta(meta)
39
+ # type is normally namespaced ! --> split.last is the class
40
+ meta[:type].split('.').last.constantize
41
+ end
42
+
43
+ # symbolise keys and cast/parse values back to the proper ruby type;
44
+ # proper type meaning the one that Sequel chooses when loading data from the DB
45
+ # apply recursively to nested navigation attributes sub-structures
46
+ def self.cast_values_in(resd)
47
+ resd.symbolize_keys!
48
+
49
+ if (defered = resd[:__deferred])
50
+ defered.symbolize_keys!
51
+ elsif meta = resd[:__metadata]
52
+ meta.symbolize_keys!
53
+
54
+ # type is normally namespaced ! --> split.last is the class
55
+ typ = get_class_from_meta(meta)
56
+
57
+ typ.db_schema.each do |f, props|
58
+ metadata = typ.cols_metadata[f]
59
+ case props[:type]
60
+ when :datetime
61
+ # resd[f] = Time.parse(resd[f]) if resd[f]
62
+ # resd[f] = DateTime.strptime(resd[f], '/Date(%Q)').to_time if resd[f]
63
+ resd[f] = Sequel.datetime_class.from_edm_json(resd[f]) if resd[f]
64
+ when :decimal
65
+ resd[f] = BigDecimal(resd[f])
66
+ when :float
67
+ resd[f] = Float(resd[f])
68
+ else
69
+ # TODO: better typ-system
70
+ # Currently Sequel loads Numeric(x,y) as Float
71
+ resd[f] = Float(resd[f]) if metadata[:edm_type] =~ /\A\s*Edm.Decimal/
72
+ end
73
+ end
74
+
75
+ if typ.nav_entity_attribs
76
+ typ.nav_entity_attribs.each_key do |nattr|
77
+ cast_values_in(resd[nattr.to_sym]) if resd[nattr.to_sym]
78
+ end
79
+ end
80
+
81
+ if typ.nav_collection_attribs
82
+ typ.nav_collection_attribs.each_key do |ncattr|
83
+ if (resd_attr = resd[ncattr.to_sym])
84
+ if (defered = resd_attr['__deferred'])
85
+ defered.symbolize_keys! if defered
86
+ else
87
+ resd_attr.symbolize_keys! # 'results' --> :results
88
+ resd_attr[:results].each { |enty| cast_values_in(enty) }
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ resd
95
+ end
96
+
97
+ # symbolise attrib names (h-keys)
98
+ # apply recursively to nested navigation attributes sub-structures
99
+ def self.symbolize_attribs_in(resd)
100
+ resd.symbolize_keys!
101
+
102
+ if (defered = resd[:__deferred])
103
+ defered.symbolize_keys!
104
+ elsif meta = resd[:__metadata]
105
+ meta.symbolize_keys!
106
+
107
+ typ = get_class_from_meta(meta)
108
+
109
+ if typ.nav_entity_attribs
110
+ typ.nav_entity_attribs.each_key do |nattr|
111
+ symbolize_attribs_in(resd[nattr.to_sym])
112
+ end
113
+ end
114
+
115
+ if typ.nav_collection_attribs
116
+ typ.nav_collection_attribs.each_key do |ncattr|
117
+ if (defered = resd[ncattr.to_sym]['__deferred'])
118
+ defered.symbolize_keys!
119
+ else
120
+ resd[ncattr.to_sym].each { |enty| symbolize_attribs_in(enty) }
121
+ end
122
+ end
123
+ end
124
+
125
+ end
126
+ resd
127
+ end
128
+
129
+ def self.parse_one_nocast(*args)
130
+ resh = ::JSON.parse(*args)
131
+ symbolize_attribs_in(resh['d'])
132
+ end
133
+
134
+ def self.parse_one(*args)
135
+ resh = ::JSON.parse(*args)
136
+ cast_values_in(resh['d'])
137
+ end
138
+
139
+ def self.v1_parse_coll(*args)
140
+ resh = ::JSON.parse(*args)
141
+
142
+ resh['d'].map! { |currd| cast_values_in(currd) }
143
+
144
+ resh['d']
145
+ end
146
+
147
+ def self.parse_coll(*args)
148
+ resh = ::JSON.parse(*args)
149
+
150
+ resh['d']['results'].map! { |currd| cast_values_in(currd) }
151
+
152
+ resh['d']
153
+ end
154
+
155
+ def self.v1_parse_coll_nocast(*args)
156
+ resh = ::JSON.parse(*args)
157
+
158
+ resh['d'].map! { |currd| symbolize_attribs_in(currd) }
159
+
160
+ resh['d']
161
+ end
162
+
163
+ def self.parse_coll_nocast(*args)
164
+ resh = ::JSON.parse(*args)
165
+
166
+ resh['d']['results'].map! { |currd| symbolize_attribs_in(currd) }
167
+
168
+ resh['d']
169
+ end
170
+ end
171
+ end
@@ -6,14 +6,14 @@ require_relative 'error'
6
6
  module Safrano
7
7
  # represents a state transition when navigating/parsing the url path
8
8
  # from left to right
9
- class Transition
9
+ class Transition
10
10
  attr_accessor :trans
11
11
  attr_accessor :match_result
12
12
  attr_accessor :rgx
13
13
  attr_reader :remain_idx
14
14
 
15
- EMPTYSTR = ''.freeze
16
- SLASH = '/'.freeze
15
+ EMPTYSTR = ''
16
+ SLASH = '/'
17
17
 
18
18
  RESULT_BAD_REQ_ERR = [nil, :error, ::Safrano::BadRequestError].freeze
19
19
  RESULT_NOT_FOUND_ERR = [nil, :error, ::Safrano::ErrorNotFound].freeze
@@ -52,26 +52,30 @@ module Safrano
52
52
  ctx.method(@trans).call(@match_result)
53
53
  end
54
54
  end
55
-
56
- #Transition that does not move/change the input
57
- class InplaceTransition < Transition
58
- def initialize(trans: )
55
+
56
+ # Transition that does not move/change the input
57
+ class InplaceTransition < Transition
58
+ def initialize(trans:)
59
59
  @trans = trans
60
60
  end
61
+
61
62
  def do_match(str)
62
63
  @str = str
63
64
  end
65
+
64
66
  def path_remain
65
- @str
67
+ @str
66
68
  end
69
+
67
70
  def path_done
68
71
  EMPTYSTR
69
72
  end
73
+
70
74
  def do_transition(ctx)
71
75
  ctx.method(@trans).call(@str)
72
76
  end
73
77
  end
74
-
78
+
75
79
  TransitionEnd = Transition.new('\A(\/?)\z', trans: 'transition_end')
76
80
  TransitionExecuteFunc = InplaceTransition.new(trans: 'transition_execute_func')
77
81
  TransitionMetadata = Transition.new('\A(\/\$metadata)(.*)',
@@ -68,20 +68,20 @@ module Safrano
68
68
  return Contract::OK unless @params['$top']
69
69
 
70
70
  itop = number_or_nil(@params['$top'])
71
- (itop.nil? || itop.zero?) ? BadRequestError : Contract::OK
71
+ itop.nil? || itop.zero? ? BadRequestError : Contract::OK
72
72
  end
73
73
 
74
74
  def check_skip
75
75
  return Contract::OK unless @params['$skip']
76
76
 
77
77
  iskip = number_or_nil(@params['$skip'])
78
- (iskip.nil? || iskip.negative?) ? BadRequestError : Contract::OK
78
+ iskip.nil? || iskip.negative? ? BadRequestError : Contract::OK
79
79
  end
80
80
 
81
81
  def check_inlinecount
82
82
  return Contract::OK unless (icp = @params['$inlinecount'])
83
83
 
84
- ((icp == 'allpages') || (icp == 'none')) ? Contract::OK : BadRequestInlineCountParamError
84
+ (icp == 'allpages') || (icp == 'none') ? Contract::OK : BadRequestInlineCountParamError
85
85
  end
86
86
 
87
87
  def check_filter
data/lib/odata/walker.rb CHANGED
@@ -29,24 +29,24 @@ module Safrano
29
29
 
30
30
  # are $links requested ?
31
31
  attr_reader :do_links
32
-
33
- attr_reader :request
32
+
33
+ attr_reader :request
34
34
 
35
35
  NIL_SERVICE_FATAL = 'Walker is called with a nil service'
36
36
  EMPTYSTR = ''
37
37
  SLASH = '/'
38
38
 
39
- def initialize(service, path, request, content_id_refs = nil )
39
+ def initialize(service, path, request, content_id_refs = nil)
40
40
  raise NIL_SERVICE_FATAL unless service
41
41
 
42
42
  path = URI.decode_www_form_component(path)
43
43
  @context = service
44
44
  @content_id_refs = content_id_refs
45
-
45
+
46
46
  # needed because for function import we need access to the url parameters (req.params)
47
47
  # who contains the functions params
48
48
  @request = request
49
-
49
+
50
50
  @contexts = [@context]
51
51
 
52
52
  @path_start = @path_remain = if service
@@ -115,16 +115,16 @@ module Safrano
115
115
  end
116
116
  end
117
117
 
118
- # execute function import with request parameters
118
+ # execute function import with request parameters
119
119
  # input: @context containt the function to exectute,
120
120
  # @request.params should normally contain the params
121
- # result: validate the params for the given function, execute the function and
121
+ # result: validate the params for the given function, execute the function and
122
122
  # return it's result back into @context,
123
123
  # and finaly set status :end (or error if anyting went wrong )
124
124
  def do_run_with_execute_func
125
125
  @context, @status, @error = @context.do_execute_func(@request)
126
126
  end
127
-
127
+
128
128
  # little hacks... depending on returned state, set some attributes
129
129
  def state_mappings
130
130
  case @status
@@ -183,7 +183,7 @@ module Safrano
183
183
  end
184
184
 
185
185
  def finalize
186
- (@status == :end) ? Contract.valid(@end_context) : @error
186
+ @status == :end ? Contract.valid(@end_context) : @error
187
187
  end
188
188
  end
189
189
  end
@@ -411,9 +411,7 @@ module MIME
411
411
 
412
412
  def unparse(bodyonly = false)
413
413
  b = +String.new
414
- unless bodyonly
415
- b << "#{Safrano::CONTENT_TYPE}: #{@hd[CTT_TYPE_LC]}#{CRLF}"
416
- end
414
+ b << "#{Safrano::CONTENT_TYPE}: #{@hd[CTT_TYPE_LC]}#{CRLF}" unless bodyonly
417
415
 
418
416
  b << crbdcr = "#{CRLF}--#{@boundary}#{CRLF}"
419
417
  b << @content.map(&:unparse).join(crbdcr)
@@ -10,7 +10,7 @@ module Safrano
10
10
  module MethodHandlers
11
11
  def odata_options
12
12
  @walker.finalize.tap_error { |err| return err.odata_get(@request) }
13
- .if_valid do |context|
13
+ .if_valid do |_context|
14
14
  # cf. stackoverflow.com/questions/22924678/sinatra-delete-response-headers
15
15
  headers.delete('Content-Type')
16
16
  @response.headers.delete('Content-Type')
@@ -119,7 +119,7 @@ module Safrano
119
119
  @response = Safrano::Response.new
120
120
 
121
121
  before.tap_error { |err| dispatch_error(err) }
122
- .tap_valid { |res| dispatch }
122
+ .tap_valid { |_res| dispatch }
123
123
 
124
124
  # handle remaining Sequel errors that we couldnt prevent with our
125
125
  # own pre-checks
@@ -161,15 +161,3 @@ module Safrano
161
161
  end
162
162
  end
163
163
  end
164
-
165
- # deprecated
166
- # REMOVE 0.6
167
- module OData
168
- class ServerApp < Safrano::ServerApp
169
- def self.publish_service(&block)
170
- ::Safrano::Deprecation.deprecate('OData::ServerApp',
171
- 'Use Safrano::ServerApp instead')
172
- super
173
- end
174
- end
175
- end
@@ -17,18 +17,3 @@ module Rack
17
17
  end
18
18
  end
19
19
  end
20
-
21
- # deprecated
22
- # REMOVE 0.6
23
- module Rack
24
- module OData
25
- class Builder < ::Rack::Safrano::Builder
26
- def initialize(default_app = nil, &block)
27
- ::Safrano::Deprecation.deprecate('Rack::OData::Builder',
28
- 'Use Rack::Safrano::Builder instead')
29
-
30
- super
31
- end
32
- end
33
- end
34
- end
@@ -144,11 +144,11 @@ module Safrano
144
144
  end
145
145
  end
146
146
 
147
- def with_parsed_data
147
+ def with_parsed_data(type)
148
148
  if content_type == APPJSON
149
149
  # Parse json payload
150
150
  begin
151
- data = JSON.parse(body.read)
151
+ data = Safrano::OData::JSON.parse_one(body.read, type)
152
152
  rescue JSON::ParserError => e
153
153
  ON_CGST_ERROR.call(self)
154
154
  return [400, EMPTY_HASH, ['JSON Parser Error while parsing payload : ',
@@ -239,7 +239,7 @@ module Safrano
239
239
  get_minversion.if_valid do |minv|
240
240
  return MAX_LT_MIN_DTSV_ERROR if minv > maxv
241
241
 
242
- get_version.if_valid do |v|
242
+ get_version.if_valid do |_v|
243
243
  @service = nil
244
244
  @service = case maxv
245
245
  when '1'
@@ -18,8 +18,8 @@ module Safrano
18
18
  end
19
19
 
20
20
  def body=(value)
21
- value = value.body while ::Rack::Response === value
22
- @body = String === value ? [value.to_str] : value
21
+ value = value.body while value.is_a?(::Rack::Response)
22
+ @body = value.is_a?(String) ? [value.to_str] : value
23
23
  end
24
24
 
25
25
  def each
@@ -51,7 +51,7 @@ module Safrano
51
51
  private
52
52
 
53
53
  def calculate_content_length?
54
- headers['Content-Type'] && !headers['Content-Length'] && (Array === body)
54
+ headers['Content-Type'] && !headers['Content-Length'] && body.is_a?(Array)
55
55
  end
56
56
 
57
57
  def calculated_content_length