safrano 0.5.5 → 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 (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