safrano 0.3.3 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/odata/attribute.rb +9 -8
- data/lib/odata/batch.rb +8 -8
- data/lib/odata/collection.rb +239 -92
- data/lib/odata/collection_filter.rb +40 -9
- data/lib/odata/collection_media.rb +159 -28
- data/lib/odata/collection_order.rb +46 -36
- data/lib/odata/common_logger.rb +37 -12
- data/lib/odata/entity.rb +188 -99
- data/lib/odata/error.rb +60 -12
- data/lib/odata/expand.rb +123 -0
- data/lib/odata/filter/base.rb +66 -0
- data/lib/odata/filter/error.rb +33 -0
- data/lib/odata/filter/parse.rb +6 -12
- data/lib/odata/filter/sequel.rb +42 -29
- data/lib/odata/filter/sequel_function_adapter.rb +147 -0
- data/lib/odata/filter/token.rb +5 -1
- data/lib/odata/filter/tree.rb +45 -29
- data/lib/odata/navigation_attribute.rb +60 -27
- data/lib/odata/relations.rb +2 -2
- data/lib/odata/select.rb +42 -0
- data/lib/odata/url_parameters.rb +51 -36
- data/lib/odata/walker.rb +6 -6
- data/lib/safrano.rb +23 -13
- data/lib/{safrano_core.rb → safrano/core.rb} +12 -4
- data/lib/{multipart.rb → safrano/multipart.rb} +17 -26
- data/lib/{odata_rack_builder.rb → safrano/odata_rack_builder.rb} +0 -1
- data/lib/{rack_app.rb → safrano/rack_app.rb} +12 -10
- data/lib/{request.rb → safrano/request.rb} +8 -14
- data/lib/{response.rb → safrano/response.rb} +1 -2
- data/lib/{sequel_join_by_paths.rb → safrano/sequel_join_by_paths.rb} +1 -1
- data/lib/{service.rb → safrano/service.rb} +162 -131
- data/lib/safrano/version.rb +3 -0
- data/lib/sequel/plugins/join_by_paths.rb +11 -10
- metadata +33 -16
- data/lib/version.rb +0 -4
data/lib/odata/relations.rb
CHANGED
@@ -85,7 +85,7 @@ module OData
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
-
def get_metadata_xml_attribs(from, to, assoc_type, xnamespace)
|
88
|
+
def get_metadata_xml_attribs(from, to, assoc_type, xnamespace, attrname)
|
89
89
|
rel = get([from, to])
|
90
90
|
# use Sequel reflection to get multiplicity (will be used later
|
91
91
|
# in 2. Associations below)
|
@@ -107,7 +107,7 @@ module OData
|
|
107
107
|
# <NavigationProperty Name="Supplier"
|
108
108
|
# Relationship="ODataDemo.Product_Supplier_Supplier_Products"
|
109
109
|
# FromRole="Product_Supplier" ToRole="Supplier_Products"/>
|
110
|
-
{ 'Name' =>
|
110
|
+
{ 'Name' => attrname, 'Relationship' => "#{xnamespace}.#{rel.name}",
|
111
111
|
'FromRole' => from, 'ToRole' => to }
|
112
112
|
end
|
113
113
|
end
|
data/lib/odata/select.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'odata/error.rb'
|
2
|
+
|
3
|
+
# all dataset selecting related classes in our OData module
|
4
|
+
# ie do eager loading
|
5
|
+
module OData
|
6
|
+
# base class for selecting. We have to distinguish between
|
7
|
+
# fields of the current entity, and the navigation properties
|
8
|
+
# we can have one special case
|
9
|
+
# empty, ie no $select specified --> return all fields and all nav props
|
10
|
+
# ==> SelectAll
|
11
|
+
|
12
|
+
class SelectBase
|
13
|
+
ALL = new # re-useable selecting-all handler
|
14
|
+
|
15
|
+
def self.factory(selectstr)
|
16
|
+
case selectstr&.strip
|
17
|
+
when nil, '', '*'
|
18
|
+
ALL
|
19
|
+
else
|
20
|
+
Select.new(selectstr)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def all_props?
|
25
|
+
false
|
26
|
+
end
|
27
|
+
|
28
|
+
def ALL.all_props?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# single select
|
34
|
+
class Select < SelectBase
|
35
|
+
COMASPLIT = /\s*,\s*/.freeze
|
36
|
+
attr_reader :props
|
37
|
+
def initialize(selstr)
|
38
|
+
@selectp = selstr.strip
|
39
|
+
@props = @selectp.split(COMASPLIT)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/odata/url_parameters.rb
CHANGED
@@ -3,57 +3,72 @@ require 'odata/error.rb'
|
|
3
3
|
# url parameters processing . Mostly delegates to specialised classes
|
4
4
|
# (filter, order...) to convert into Sequel exprs.
|
5
5
|
module OData
|
6
|
-
class
|
6
|
+
class UrlParametersBase
|
7
|
+
attr_reader :expand
|
8
|
+
attr_reader :select
|
9
|
+
|
10
|
+
def check_expand
|
11
|
+
return BadRequestExpandParseError if @expand.parse_error?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# url parameters for a single entity expand/select
|
16
|
+
class UrlParameters4Single < UrlParametersBase
|
17
|
+
def initialize(params)
|
18
|
+
@params = params
|
19
|
+
@expand = ExpandBase.factory(@params['$expand'])
|
20
|
+
@select = SelectBase.factory(@params['$select'])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# url parameters for a collection expand/select + filter/order
|
25
|
+
class UrlParameters4Coll < UrlParametersBase
|
7
26
|
attr_reader :filt
|
8
27
|
attr_reader :ordby
|
9
|
-
|
10
|
-
|
28
|
+
|
29
|
+
def initialize(model, params)
|
30
|
+
# join helper is only needed for odering or filtering
|
31
|
+
@jh = model.join_by_paths_helper if params['$orderby'] || params['$filter']
|
11
32
|
@params = params
|
33
|
+
@ordby = OrderBase.factory(@params['$orderby'], @jh)
|
34
|
+
@filt = FilterBase.factory(@params['$filter'])
|
35
|
+
@expand = ExpandBase.factory(@params['$expand'])
|
36
|
+
@select = SelectBase.factory(@params['$select'])
|
12
37
|
end
|
13
38
|
|
14
39
|
def check_filter
|
15
|
-
return unless @params['$filter']
|
16
|
-
|
17
|
-
@filt = FilterByParse.new(@params['$filter'], @jh)
|
18
40
|
return BadRequestFilterParseError if @filt.parse_error?
|
19
|
-
|
20
|
-
# nil is the expected return for no errors
|
21
|
-
nil
|
22
41
|
end
|
23
42
|
|
24
43
|
def check_order
|
25
|
-
return
|
44
|
+
return BadRequestOrderParseError if @ordby.parse_error?
|
45
|
+
end
|
26
46
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
qualfn.strip!
|
32
|
-
dir.strip! if dir
|
33
|
-
return BadRequestError unless @jh.start_model.attrib_path_valid? qualfn
|
34
|
-
return BadRequestError unless [nil, 'asc', 'desc'].include? dir
|
35
|
-
end
|
47
|
+
def apply_to_dataset(dtcx)
|
48
|
+
dtcx = apply_expand_to_dataset(dtcx)
|
49
|
+
apply_filter_order_to_dataset(dtcx)
|
50
|
+
end
|
36
51
|
|
37
|
-
|
52
|
+
def apply_expand_to_dataset(dtcx)
|
53
|
+
return dtcx if @expand.empty?
|
38
54
|
|
39
|
-
|
40
|
-
nil
|
55
|
+
@expand.apply_to_dataset(dtcx)
|
41
56
|
end
|
42
57
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
58
|
+
# Warning, the @ordby and @filt objects are coupled by way of the join helper
|
59
|
+
def apply_filter_order_to_dataset(dtcx)
|
60
|
+
return dtcx if @filt.empty? && @ordby.empty?
|
61
|
+
|
62
|
+
# filter object and join-helper need to be finalized after filter has been parsed and checked
|
63
|
+
@filt.finalize(@jh)
|
64
|
+
|
65
|
+
# start with the join
|
66
|
+
dtcx = @jh.dataset(dtcx)
|
67
|
+
|
68
|
+
dtcx = @filt.apply_to_dataset(dtcx)
|
69
|
+
dtcx = @ordby.apply_to_dataset(dtcx)
|
70
|
+
|
71
|
+
dtcx.select_all(@jh.start_model.table_name)
|
57
72
|
end
|
58
73
|
end
|
59
74
|
end
|
data/lib/odata/walker.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
require 'json'
|
4
2
|
require 'rexml/document'
|
5
3
|
require 'safrano.rb'
|
@@ -29,9 +27,11 @@ module OData
|
|
29
27
|
|
30
28
|
# are $links requested ?
|
31
29
|
attr_reader :do_links
|
32
|
-
|
30
|
+
NIL_SERVICE_FATAL = 'Walker is called with a nil service'.freeze
|
31
|
+
EMPTYSTR = ''.freeze
|
32
|
+
SLASH = '/'.freeze
|
33
33
|
def initialize(service, path, content_id_refs = nil)
|
34
|
-
raise
|
34
|
+
raise NIL_SERVICE_FATAL unless service
|
35
35
|
|
36
36
|
path = URI.decode_www_form_component(path)
|
37
37
|
@context = service
|
@@ -53,12 +53,12 @@ module OData
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def unprefixed(prefix, path)
|
56
|
-
if (prefix ==
|
56
|
+
if (prefix == EMPTYSTR) || (prefix == SLASH)
|
57
57
|
path
|
58
58
|
else
|
59
59
|
# path.sub!(/\A#{prefix}/, '')
|
60
60
|
# TODO check
|
61
|
-
path.sub(/\A#{prefix}/,
|
61
|
+
path.sub(/\A#{prefix}/, EMPTYSTR)
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
data/lib/safrano.rb
CHANGED
@@ -1,19 +1,18 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
require 'json'
|
4
2
|
require 'rexml/document'
|
5
|
-
require_relative '
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
3
|
+
require_relative 'safrano/multipart.rb'
|
4
|
+
require_relative 'safrano/core.rb'
|
5
|
+
require_relative 'odata/entity.rb'
|
6
|
+
require_relative 'odata/attribute.rb'
|
7
|
+
require_relative 'odata/navigation_attribute.rb'
|
8
|
+
require_relative 'odata/collection.rb'
|
9
|
+
require_relative 'safrano/service.rb'
|
10
|
+
require_relative 'odata/walker.rb'
|
13
11
|
require 'sequel'
|
14
|
-
require_relative '
|
15
|
-
|
16
|
-
|
12
|
+
require_relative 'safrano/sequel_join_by_paths.rb'
|
13
|
+
require_relative 'safrano/rack_app'
|
14
|
+
require_relative 'safrano/odata_rack_builder'
|
15
|
+
require_relative 'safrano/version'
|
17
16
|
|
18
17
|
# picked from activsupport; needed for ruby < 2.5
|
19
18
|
# Destructively converts all keys using the +block+ operations.
|
@@ -30,3 +29,14 @@ class Hash
|
|
30
29
|
transform_keys! { |key| key.to_sym rescue key }
|
31
30
|
end
|
32
31
|
end
|
32
|
+
|
33
|
+
# needed for ruby < 2.5
|
34
|
+
class Dir
|
35
|
+
def self.each_child(dir)
|
36
|
+
Dir.foreach(dir) do |x|
|
37
|
+
next if (x == '.') || (x == '..')
|
38
|
+
|
39
|
+
yield x
|
40
|
+
end
|
41
|
+
end unless respond_to? :each_child
|
42
|
+
end
|
@@ -1,7 +1,14 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
1
|
# our main namespace
|
4
2
|
module OData
|
3
|
+
# frozen empty Array/Hash to reduce unncecessary object creation
|
4
|
+
EMPTY_ARRAY = [].freeze
|
5
|
+
EMPTY_HASH = {}.freeze
|
6
|
+
EMPTY_HASH_IN_ARY = [EMPTY_HASH].freeze
|
7
|
+
EMPTY_STRING = ''.freeze
|
8
|
+
ARY_204_EMPTY_HASH_ARY = [204, EMPTY_HASH, EMPTY_ARRAY].freeze
|
9
|
+
SPACE = ' '.freeze
|
10
|
+
COMMA = ','.freeze
|
11
|
+
|
5
12
|
# some prominent constants... probably already defined elsewhere eg in Rack
|
6
13
|
# but lets KISS
|
7
14
|
CONTENT_TYPE = 'Content-Type'.freeze
|
@@ -32,14 +39,15 @@ module OData
|
|
32
39
|
# database-specific types.
|
33
40
|
DB_TYPE_STRING_RGX = /\ACHAR\s*\(\d+\)\z/.freeze
|
34
41
|
|
42
|
+
# TODO... complete; used in $metadata
|
35
43
|
def self.get_edm_type(db_type:)
|
36
|
-
case db_type
|
44
|
+
case db_type.upcase
|
37
45
|
when 'INTEGER'
|
38
46
|
'Edm.Int32'
|
39
47
|
when 'TEXT', 'STRING'
|
40
48
|
'Edm.String'
|
41
49
|
else
|
42
|
-
'Edm.String' if DB_TYPE_STRING_RGX =~ db_type
|
50
|
+
'Edm.String' if DB_TYPE_STRING_RGX =~ db_type.upcase
|
43
51
|
end
|
44
52
|
end
|
45
53
|
end
|
@@ -6,14 +6,15 @@ require 'webrick/httpstatus'
|
|
6
6
|
|
7
7
|
# Simple multipart support for OData $batch purpose
|
8
8
|
module MIME
|
9
|
+
CTT_TYPE_LC = 'content-type'.freeze
|
10
|
+
TEXT_PLAIN = 'text/plain'.freeze
|
11
|
+
|
9
12
|
# a mime object has a header(with content-type etc) and a content(aka body)
|
10
13
|
class Media
|
11
14
|
# Parser for MIME::Media
|
12
15
|
class Parser
|
13
16
|
HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
|
14
17
|
|
15
|
-
CRLF_LINE_RGX = /^#{CRLF}$/.freeze
|
16
|
-
|
17
18
|
attr_accessor :lines
|
18
19
|
attr_accessor :target
|
19
20
|
def initialize
|
@@ -54,9 +55,8 @@ module MIME
|
|
54
55
|
if (hmd = HMD_RGX.match(line))
|
55
56
|
@target_hd[hmd[1].downcase] = hmd[2].strip
|
56
57
|
|
57
|
-
# elsif CRLF_LINE_RGX =~ line
|
58
58
|
elsif CRLF == line
|
59
|
-
@target_ct = @target_hd[
|
59
|
+
@target_ct = @target_hd[CTT_TYPE_LC] || TEXT_PLAIN
|
60
60
|
@state = new_content
|
61
61
|
|
62
62
|
end
|
@@ -100,8 +100,8 @@ module MIME
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def hook_multipart(content_type, boundary)
|
103
|
-
@target_hd[
|
104
|
-
@target_ct = @target_hd[
|
103
|
+
@target_hd[CTT_TYPE_LC] = content_type
|
104
|
+
@target_ct = @target_hd[CTT_TYPE_LC]
|
105
105
|
@target = multipart_content(boundary)
|
106
106
|
@target.hd = @target_hd
|
107
107
|
@target.ct = @target_ct
|
@@ -114,10 +114,8 @@ module MIME
|
|
114
114
|
APP_HTTP = 'application/http'.freeze
|
115
115
|
def new_content
|
116
116
|
@target =
|
117
|
-
if @target_ct.start_with?(MPS)
|
118
|
-
(md = (
|
119
|
-
(MP_RGX2.match(@target_ct[10..-1])))
|
120
|
-
)
|
117
|
+
if @target_ct.start_with?(MPS) &&
|
118
|
+
(md = (MP_RGX1.match(@target_ct[10..-1]) || MP_RGX2.match(@target_ct[10..-1])))
|
121
119
|
multipart_content(md[2].strip)
|
122
120
|
elsif @target_ct.start_with?(APP_HTTP)
|
123
121
|
MIME::Content::Application::Http.new
|
@@ -198,7 +196,6 @@ module MIME
|
|
198
196
|
# Parser for Text::Plain
|
199
197
|
class Parser
|
200
198
|
HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
|
201
|
-
CRLF_LINE_RGX = /^#{CRLF}$/.freeze
|
202
199
|
def initialize(target)
|
203
200
|
@state = :h
|
204
201
|
@lines = []
|
@@ -212,7 +209,6 @@ module MIME
|
|
212
209
|
def parse_head(line)
|
213
210
|
if (hmd = HMD_RGX.match(line))
|
214
211
|
@target.hd[hmd[1].downcase] = hmd[2].strip
|
215
|
-
# elsif CRLF_LINE_RGX =~ line
|
216
212
|
elsif CRLF == line
|
217
213
|
@state = :b
|
218
214
|
else
|
@@ -243,8 +239,8 @@ module MIME
|
|
243
239
|
@hd = {}
|
244
240
|
@content = ''
|
245
241
|
# set default values. Can be overwritten by parser
|
246
|
-
@hd[
|
247
|
-
@ct =
|
242
|
+
@hd[CTT_TYPE_LC] = TEXT_PLAIN
|
243
|
+
@ct = TEXT_PLAIN
|
248
244
|
@parser = Parser.new(self)
|
249
245
|
end
|
250
246
|
|
@@ -364,7 +360,7 @@ module MIME
|
|
364
360
|
end
|
365
361
|
|
366
362
|
def set_multipart_header
|
367
|
-
@hd[
|
363
|
+
@hd[CTT_TYPE_LC] = "#{OData::MP_MIXED}; boundary=#{@boundary}"
|
368
364
|
end
|
369
365
|
|
370
366
|
def get_http_resp(batcha)
|
@@ -382,9 +378,7 @@ module MIME
|
|
382
378
|
# of the changes
|
383
379
|
batcha.db.transaction do
|
384
380
|
begin
|
385
|
-
@response.content = @content.map { |part|
|
386
|
-
part.get_response(batcha)
|
387
|
-
}
|
381
|
+
@response.content = @content.map { |part| part.get_response(batcha) }
|
388
382
|
rescue Sequel::Rollback => e
|
389
383
|
# one of the changes of the changeset has failed
|
390
384
|
# --> provide a dummy empty response for the change-parts
|
@@ -413,10 +407,11 @@ module MIME
|
|
413
407
|
b = ''
|
414
408
|
unless bodyonly
|
415
409
|
# b << OData::CONTENT_TYPE << ': ' << @hd[OData::CTT_TYPE_LC] << CRLF
|
416
|
-
b << "#{OData::CONTENT_TYPE}: #{@hd[
|
410
|
+
b << "#{OData::CONTENT_TYPE}: #{@hd[CTT_TYPE_LC]}#{CRLF}"
|
417
411
|
end
|
418
|
-
|
419
|
-
b <<
|
412
|
+
|
413
|
+
b << crbdcr = "#{CRLF}--#{@boundary}#{CRLF}"
|
414
|
+
b << @content.map(&:unparse).join(crbdcr)
|
420
415
|
b << "#{CRLF}--#{@boundary}--#{CRLF}"
|
421
416
|
b
|
422
417
|
end
|
@@ -442,7 +437,6 @@ module MIME
|
|
442
437
|
def unparse
|
443
438
|
b = "#{@http_method} #{@uri} HTTP/1.1#{CRLF}"
|
444
439
|
@hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
|
445
|
-
# @hd.each { |k, v| b << k.to_s << ': ' << v.to_s << CRLF }
|
446
440
|
b << CRLF
|
447
441
|
b << @content if @content != ''
|
448
442
|
b
|
@@ -473,7 +467,6 @@ module MIME
|
|
473
467
|
b = String.new(APPLICATION_HTTP_11)
|
474
468
|
b << "#{@status} #{StatusMessage[@status]} #{CRLF}"
|
475
469
|
@hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
|
476
|
-
# @hd.each { |k, v| b << k.to_s << ': ' << v.to_s << CRLF }
|
477
470
|
b << CRLF
|
478
471
|
b << @content.join if @content
|
479
472
|
b
|
@@ -482,7 +475,7 @@ module MIME
|
|
482
475
|
|
483
476
|
# For application/http . Content is either a Request or a Response
|
484
477
|
class Http < Media
|
485
|
-
HTTP_MTHS_RGX = 'POST|GET|PUT|MERGE|PATCH'.freeze
|
478
|
+
HTTP_MTHS_RGX = 'POST|GET|PUT|MERGE|PATCH|DELETE'.freeze
|
486
479
|
HTTP_R_RGX = %r{^(#{HTTP_MTHS_RGX})\s+(\S*)\s?(HTTP/1\.1)\s*$}.freeze
|
487
480
|
HEADER_RGX = %r{^([a-zA-Z\-]+):\s*([0-9a-zA-Z\-\/,\s]+;?\S*)\s*$}.freeze
|
488
481
|
HTTP_RESP_RGX = %r{^HTTP/1\.1\s(\d+)\s}.freeze
|
@@ -490,7 +483,6 @@ module MIME
|
|
490
483
|
# Parser for Http Media
|
491
484
|
class Parser
|
492
485
|
HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
|
493
|
-
CRLF_LINE_RGX = /^#{CRLF}$/.freeze
|
494
486
|
|
495
487
|
def initialize(target)
|
496
488
|
@state = :http
|
@@ -523,7 +515,6 @@ module MIME
|
|
523
515
|
if (hmd = HMD_RGX.match(line))
|
524
516
|
@target.content.hd[hmd[1].downcase] = hmd[2].strip
|
525
517
|
elsif CRLF == line
|
526
|
-
# elsif CRLF_LINE_RGX =~ line
|
527
518
|
@state = :b
|
528
519
|
else
|
529
520
|
@body_lines << line
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'rack'
|
4
|
-
require_relative 'odata/walker.rb'
|
4
|
+
require_relative '../odata/walker.rb'
|
5
5
|
require_relative 'request.rb'
|
6
6
|
require_relative 'response.rb'
|
7
7
|
|
@@ -14,7 +14,7 @@ module OData
|
|
14
14
|
x = if @walker.status == :end
|
15
15
|
headers.delete('Content-Type')
|
16
16
|
@response.headers.delete('Content-Type')
|
17
|
-
[200,
|
17
|
+
[200, EMPTY_HASH, '']
|
18
18
|
else
|
19
19
|
odata_error
|
20
20
|
end
|
@@ -70,20 +70,20 @@ module OData
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def odata_head
|
73
|
-
[200,
|
73
|
+
[200, EMPTY_HASH, [EMPTY_STRING]]
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
77
|
# the main Rack server app. Source: the Rack docu/examples and partly
|
78
78
|
# inspired from Sinatra
|
79
79
|
class ServerApp
|
80
|
-
METHODS_REGEXP = Regexp.new('HEAD|OPTIONS|GET|POST|PATCH|MERGE|PUT|DELETE')
|
80
|
+
METHODS_REGEXP = Regexp.new('HEAD|OPTIONS|GET|POST|PATCH|MERGE|PUT|DELETE').freeze
|
81
|
+
NOCACHE_HDRS = { 'Cache-Control' => 'no-cache',
|
82
|
+
'Expires' => '-1',
|
83
|
+
'Pragma' => 'no-cache' }.freeze
|
84
|
+
DATASERVICEVERSION = 'DataServiceVersion'.freeze
|
81
85
|
include MethodHandlers
|
82
86
|
def before
|
83
|
-
headers 'Cache-Control' => 'no-cache'
|
84
|
-
headers 'Expires' => '-1'
|
85
|
-
headers 'Pragma' => 'no-cache'
|
86
|
-
|
87
87
|
@request.service_base = self.class.get_service_base
|
88
88
|
|
89
89
|
neg_error = @request.negotiate_service_version
|
@@ -92,7 +92,9 @@ module OData
|
|
92
92
|
|
93
93
|
return false unless @request.service
|
94
94
|
|
95
|
-
|
95
|
+
myhdrs = NOCACHE_HDRS.dup
|
96
|
+
myhdrs[DATASERVICEVERSION] = @request.service.data_service_version
|
97
|
+
headers myhdrs
|
96
98
|
end
|
97
99
|
|
98
100
|
# dispatch for all methods requiring parsing of the path
|
@@ -119,7 +121,7 @@ module OData
|
|
119
121
|
|
120
122
|
def dispatch
|
121
123
|
req_ret = if @request.request_method !~ METHODS_REGEXP
|
122
|
-
[404,
|
124
|
+
[404, EMPTY_HASH, ['Did you get lost?']]
|
123
125
|
elsif @request.request_method == 'HEAD'
|
124
126
|
odata_head
|
125
127
|
else
|