safrano 0.5.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) 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 +1 -1
  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/time.rb +5 -0
  11. data/lib/odata/attribute.rb +8 -6
  12. data/lib/odata/batch.rb +3 -3
  13. data/lib/odata/collection.rb +5 -5
  14. data/lib/odata/collection_filter.rb +3 -1
  15. data/lib/odata/collection_media.rb +4 -27
  16. data/lib/odata/collection_order.rb +1 -1
  17. data/lib/odata/common_logger.rb +5 -27
  18. data/lib/odata/complex_type.rb +19 -21
  19. data/lib/odata/edm/primitive_types.rb +14 -19
  20. data/lib/odata/entity.rb +12 -12
  21. data/lib/odata/error.rb +7 -7
  22. data/lib/odata/expand.rb +2 -2
  23. data/lib/odata/filter/base.rb +10 -1
  24. data/lib/odata/filter/error.rb +2 -2
  25. data/lib/odata/filter/parse.rb +16 -2
  26. data/lib/odata/filter/sequel.rb +31 -4
  27. data/lib/odata/filter/sequel_datetime_adapter.rb +21 -0
  28. data/lib/odata/filter/token.rb +18 -5
  29. data/lib/odata/filter/tree.rb +83 -9
  30. data/lib/odata/function_import.rb +11 -9
  31. data/lib/odata/model_ext.rb +26 -29
  32. data/lib/odata/request/json.rb +171 -0
  33. data/lib/odata/transition.rb +2 -2
  34. data/lib/odata/url_parameters.rb +3 -3
  35. data/lib/odata/walker.rb +1 -1
  36. data/lib/safrano/multipart.rb +1 -3
  37. data/lib/safrano/rack_app.rb +2 -14
  38. data/lib/safrano/rack_builder.rb +0 -15
  39. data/lib/safrano/request.rb +3 -3
  40. data/lib/safrano/response.rb +3 -3
  41. data/lib/safrano/service.rb +13 -5
  42. data/lib/safrano/type_mapping.rb +4 -4
  43. data/lib/safrano/version.rb +1 -2
  44. data/lib/safrano.rb +3 -0
  45. metadata +51 -15
@@ -104,9 +104,8 @@ module Safrano
104
104
  def new_from_hson_h(hash)
105
105
  # enty = new
106
106
  # enty.set_fields(hash, data_fields, missing: :skip)
107
- enty = create(hash)
107
+ create(hash)
108
108
  # enty.set(hash)
109
- enty
110
109
  end
111
110
 
112
111
  def attrib_path_valid?(path)
@@ -353,20 +352,18 @@ module Safrano
353
352
 
354
353
  def build_default_template
355
354
  @default_template = { all_values: EMPTYH }
356
- if @nav_entity_attribs || @nav_collection_attribs
357
- @default_template[:deferr] = (@nav_entity_attribs&.keys || []) + (@nav_collection_attribs&.keys || EMPTY_ARRAY)
358
- end
355
+ @default_template[:deferr] = (@nav_entity_attribs&.keys || []) + (@nav_collection_attribs&.keys || EMPTY_ARRAY) if @nav_entity_attribs || @nav_collection_attribs
359
356
  end
360
357
 
361
358
  def build_casted_cols(service)
362
359
  # cols needed catsting before final json output
363
360
  @casted_cols = {}
364
- db_schema.each { |col, props|
361
+ db_schema.each do |col, props|
365
362
  # first check if we have user-defined type mapping
366
363
  usermap = nil
367
364
  dbtyp = props[:db_type]
368
365
  metadata = @cols_metadata[col]
369
- if (service.type_mappings.values.find { |map| usermap = map.match(dbtyp) })
366
+ if service.type_mappings.values.find { |map| usermap = map.match(dbtyp) }
370
367
 
371
368
  metadata[:edm_type] = usermap.edm_type
372
369
 
@@ -374,17 +371,17 @@ module Safrano
374
371
  next # this will override our rules below !
375
372
  end
376
373
 
377
- if (metadata[:edm_precision] && (metadata[:edm_type] =~ /\AEdm.Decimal\(/i))
374
+ if metadata[:edm_precision] && (metadata[:edm_type] =~ /\AEdm.Decimal\(/i)
378
375
  # we save the precision and/or scale in the lambda (binding!)
379
376
 
380
377
  @casted_cols[col] = if metadata[:edm_scale]
381
- ->(x) {
378
+ lambda { |x|
382
379
  # not sure if these copies are really needed, but feels better that way
383
380
  # output decimal with precision and scale
384
381
  x&.toDecimalPrecisionScaleString(metadata[:edm_precision], metadata[:edm_scale])
385
382
  }
386
383
  else
387
- ->(x) {
384
+ lambda { |x|
388
385
  # not sure if these copies are really needed, but feels better that way
389
386
  # output decimal with precision only
390
387
  x&.toDecimalPrecisionString(metadata[:edm_precision])
@@ -404,13 +401,13 @@ module Safrano
404
401
  @casted_cols[col] = ->(x) { Base64.encode64(x) unless x.nil? } # Base64
405
402
  next
406
403
  end
407
- # TODO check this more in details
404
+ # TODO: check this more in details
408
405
  # NOTE: here we use :type which is the sequel defined ruby-type
409
- if props[:type] == :datetime
410
- @casted_cols[col] = ->(x) { x&.iso8601 }
411
-
406
+ if props[:type] == :datetime || props[:type] == :date
407
+ # @casted_cols[col] = ->(x) { x&.iso8601 }
408
+ @casted_cols[col] = ->(x) { x&.to_edm_json }
412
409
  end
413
- }
410
+ end
414
411
  end
415
412
 
416
413
  def finalize_publishing(service)
@@ -440,7 +437,7 @@ module Safrano
440
437
  build_entity_allowed_transitions
441
438
 
442
439
  # for media
443
- finalize_media if self.respond_to? :finalize_media
440
+ finalize_media if respond_to? :finalize_media
444
441
  end
445
442
 
446
443
  KEYPRED_URL_REGEXP = /\A\(\s*'?([\w=,'\s]+)'?\s*\)(.*)/.freeze
@@ -449,7 +446,7 @@ module Safrano
449
446
  @pk_names = []
450
447
  @pk_cast_from_string = {}
451
448
  odata_upk_build = []
452
- primary_key.each { |pk|
449
+ primary_key.each do |pk|
453
450
  @pk_names << pk.to_s
454
451
  kvpredicate = case db_schema[pk][:type]
455
452
  when :integer
@@ -459,19 +456,19 @@ module Safrano
459
456
  "'?'"
460
457
  end
461
458
  odata_upk_build << "#{pk}=#{kvpredicate}"
462
- }
459
+ end
463
460
  @odata_upk_parts = odata_upk_build.join(',').split('?')
464
461
 
465
462
  # regex parts for unordered matching
466
- @iuk_rgx_parts = primary_key.map { |pk|
463
+ @iuk_rgx_parts = primary_key.map do |pk|
467
464
  kvpredicate = case db_schema[pk][:type]
468
465
  when :integer
469
- "(\\d+)"
466
+ '(\\d+)'
470
467
  else
471
468
  "'(\\w+)'"
472
469
  end
473
470
  [pk, "#{pk}=#{kvpredicate}"]
474
- }.to_h
471
+ end.to_h
475
472
 
476
473
  # single regex assuming the key fields are ordered !
477
474
  @iuk_rgx = /\A#{@iuk_rgx_parts.values.join(',\s*')}\z/
@@ -485,7 +482,7 @@ module Safrano
485
482
  kvpredicate = case db_schema[primary_key][:type]
486
483
  when :integer
487
484
  @pk_cast_from_string = ->(str) { Integer(str) }
488
- "(\\d+)"
485
+ '(\\d+)'
489
486
  else
490
487
  "'(\\w+)'"
491
488
  end
@@ -617,14 +614,14 @@ module Safrano
617
614
  scan_rgx_parts = @iuk_rgx_parts.dup
618
615
  mdch = {}
619
616
 
620
- mid.split(/\s*,\s*/).each { |midpart|
617
+ mid.split(/\s*,\s*/).each do |midpart|
621
618
  mval = nil
622
- mpk, mrgx = scan_rgx_parts.find { |pk, rgx|
619
+ mpk, mrgx = scan_rgx_parts.find do |_pk, rgx|
623
620
  if (md = rgx.match(midpart))
624
621
  mval = md[1]
625
622
  end
626
- }
627
- if mpk and mval
623
+ end
624
+ if mpk && mval
628
625
  mdch[mpk] = if (pk_cast = @pk_cast_from_string[mpk])
629
626
  pk_cast.call(mval)
630
627
  else
@@ -634,7 +631,7 @@ module Safrano
634
631
  else
635
632
  return Contract::NOK
636
633
  end
637
- }
634
+ end
638
635
  # normally arriving here we have mdch filled with key values pairs,
639
636
  # but not in the model key ordering. lets just re-order the values
640
637
  mdc = @iuk_rgx_parts.keys.map { |pk| mdch[pk] }
@@ -653,7 +650,7 @@ module Safrano
653
650
 
654
651
  def parse_odata_key(rawid)
655
652
  if (md = @iuk_rgx.match(rawid))
656
- if (@pk_cast_from_string)
653
+ if @pk_cast_from_string
657
654
  Contract.valid(@pk_cast_from_string.call(md[1]))
658
655
  else
659
656
  Contract.valid(md[1]) # no cast needed, eg for string
@@ -677,7 +674,7 @@ module Safrano
677
674
  # 2. Create relationship if needed
678
675
  def odata_create_entity_and_relation(req, assoc = nil, parent = nil)
679
676
  # TODO: this is for v2 only...
680
- req.with_parsed_data do |data|
677
+ req.with_parsed_data(self) do |data|
681
678
  data.delete('__metadata')
682
679
 
683
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
@@ -12,8 +12,8 @@ module Safrano
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
@@ -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
@@ -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
@@ -161,6 +161,8 @@ module Safrano
161
161
  @function_import_keys = []
162
162
  @cmap = {}
163
163
  @type_mappings = {}
164
+ # enabled per default starting from 0.6
165
+ @bugfix_create_response = true
164
166
  instance_eval(&block) if block_given?
165
167
  end
166
168
 
@@ -209,9 +211,8 @@ module Safrano
209
211
  (@v2.xserver_url = @xserver_url) if @v2
210
212
  end
211
213
 
212
- # keep the bug active for now, but allow to activate the fix,
213
- # later we will change the default to be fixed
214
- def bugfix_create_response(bool = false)
214
+ # keep the bug active for now, but allow to de-activate the fix
215
+ def bugfix_create_response(bool)
215
216
  @bugfix_create_response = bool
216
217
  end
217
218
 
@@ -246,6 +247,7 @@ module Safrano
246
247
  other.function_imports = @function_imports
247
248
  other.function_import_keys = @function_import_keys
248
249
  other.type_mappings = @type_mappings
250
+ other.bugfix_create_response(@bugfix_create_response)
249
251
  other
250
252
  end
251
253
 
@@ -386,10 +388,10 @@ module Safrano
386
388
  klass.build_uri(@uribase)
387
389
 
388
390
  # Output create (POST) as single entity (Standard) or as array (non-standard buggy)
389
- klass.include ( @bugfix_create_response ? Safrano::EntityCreateStandardOutput : Safrano::EntityCreateArrayOutput)
391
+ klass.include(@bugfix_create_response ? Safrano::EntityCreateStandardOutput : Safrano::EntityCreateArrayOutput)
390
392
 
391
393
  # define the most optimal casted_values method for the given model(klass)
392
- if (klass.casted_cols.empty?)
394
+ if klass.casted_cols.empty?
393
395
  klass.send(:define_method, :casted_values) do |cols = nil|
394
396
  cols ? selected_values_for_odata(cols) : values_for_odata
395
397
  end
@@ -413,10 +415,16 @@ module Safrano
413
415
  case Sequel::Model.db.adapter_scheme
414
416
  when :postgres
415
417
  Safrano::Filter::FuncTree.include Safrano::Filter::FuncTreePostgres
418
+ Safrano::Filter::DateTimeLit.include Safrano::Filter::DateTimeDefault
419
+ Safrano::Filter::DateTimeOffsetLit.include Safrano::Filter::DateTimeDefault
416
420
  when :sqlite
417
421
  Safrano::Filter::FuncTree.include Safrano::Filter::FuncTreeSqlite
422
+ Safrano::Filter::DateTimeLit.include Safrano::Filter::DateTimeSqlite
423
+ Safrano::Filter::DateTimeOffsetLit.include Safrano::Filter::DateTimeSqlite
418
424
  else
419
425
  Safrano::Filter::FuncTree.include Safrano::Filter::FuncTreeDefault
426
+ Safrano::Filter::DateTimeLit.include Safrano::Filter::DateTimeDefault
427
+ Safrano::Filter::DateTimeOffsetLit.include Safrano::Filter::DateTimeDefault
420
428
  end
421
429
  end
422
430
 
@@ -45,17 +45,17 @@ module Safrano
45
45
  end
46
46
 
47
47
  def match(curtyp)
48
- if (@bui2 && (m = @bui2.match(curtyp)))
48
+ if @bui2 && (m = @bui2.match(curtyp))
49
49
  m
50
- elsif (@bui1 && (m = @bui1.match(curtyp)))
50
+ elsif @bui1 && (m = @bui1.match(curtyp))
51
51
  m
52
- elsif @rgx.match(curtyp)
52
+ elsif @rgx.match(curtyp)
53
53
  type_mapping
54
54
  end
55
55
  end
56
56
 
57
57
  def type_mapping
58
- # TODO perf; return always same object when called multiple times
58
+ # TODO: perf; return always same object when called multiple times
59
59
  FixedTypeMapping.new(self)
60
60
  end
61
61
  end # Builder
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  module Safrano
4
- VERSION = '0.5.5'
3
+ VERSION = '0.6.0'
5
4
  end
data/lib/safrano.rb CHANGED
@@ -14,9 +14,12 @@ require_relative 'odata/entity'
14
14
  require_relative 'odata/attribute'
15
15
  require_relative 'odata/navigation_attribute'
16
16
  require_relative 'odata/model_ext'
17
+ require_relative 'odata/request/json'
17
18
  require_relative 'safrano/service'
18
19
  require_relative 'odata/walker'
19
20
  require 'sequel'
20
21
  require_relative 'safrano/sequel_join_by_paths'
21
22
  require_relative 'safrano/rack_app'
22
23
  require_relative 'safrano/rack_builder'
24
+
25
+ Sequel.extension :named_timezones