safrano 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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