safrano 0.4.3 → 0.5.1

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/core_ext/Dir/iter.rb +18 -0
  3. data/lib/core_ext/Hash/transform.rb +21 -0
  4. data/lib/core_ext/Integer/edm.rb +13 -0
  5. data/lib/core_ext/REXML/Document/output.rb +16 -0
  6. data/lib/core_ext/String/convert.rb +25 -0
  7. data/lib/core_ext/String/edm.rb +13 -0
  8. data/lib/core_ext/dir.rb +3 -0
  9. data/lib/core_ext/hash.rb +3 -0
  10. data/lib/core_ext/integer.rb +3 -0
  11. data/lib/core_ext/rexml.rb +3 -0
  12. data/lib/core_ext/string.rb +5 -0
  13. data/lib/odata/attribute.rb +8 -4
  14. data/lib/odata/batch.rb +9 -7
  15. data/lib/odata/collection.rb +139 -642
  16. data/lib/odata/collection_filter.rb +18 -42
  17. data/lib/odata/collection_media.rb +111 -54
  18. data/lib/odata/collection_order.rb +5 -2
  19. data/lib/odata/common_logger.rb +2 -0
  20. data/lib/odata/complex_type.rb +196 -0
  21. data/lib/odata/edm/primitive_types.rb +184 -0
  22. data/lib/odata/entity.rb +78 -123
  23. data/lib/odata/error.rb +170 -37
  24. data/lib/odata/expand.rb +20 -17
  25. data/lib/odata/filter/base.rb +9 -1
  26. data/lib/odata/filter/error.rb +43 -27
  27. data/lib/odata/filter/parse.rb +39 -25
  28. data/lib/odata/filter/sequel.rb +112 -56
  29. data/lib/odata/filter/sequel_function_adapter.rb +50 -49
  30. data/lib/odata/filter/token.rb +21 -18
  31. data/lib/odata/filter/tree.rb +78 -44
  32. data/lib/odata/function_import.rb +168 -0
  33. data/lib/odata/model_ext.rb +641 -0
  34. data/lib/odata/navigation_attribute.rb +9 -24
  35. data/lib/odata/relations.rb +5 -5
  36. data/lib/odata/select.rb +17 -5
  37. data/lib/odata/transition.rb +71 -0
  38. data/lib/odata/url_parameters.rb +100 -24
  39. data/lib/odata/walker.rb +18 -10
  40. data/lib/safrano.rb +18 -38
  41. data/lib/safrano/contract.rb +141 -0
  42. data/lib/safrano/core.rb +24 -106
  43. data/lib/safrano/core_ext.rb +13 -0
  44. data/lib/safrano/deprecation.rb +73 -0
  45. data/lib/safrano/multipart.rb +29 -24
  46. data/lib/safrano/rack_app.rb +62 -63
  47. data/lib/safrano/{odata_rack_builder.rb → rack_builder.rb} +18 -1
  48. data/lib/safrano/request.rb +96 -38
  49. data/lib/safrano/response.rb +4 -2
  50. data/lib/safrano/sequel_join_by_paths.rb +2 -2
  51. data/lib/safrano/service.rb +156 -110
  52. data/lib/safrano/version.rb +3 -1
  53. data/lib/sequel/plugins/join_by_paths.rb +6 -19
  54. metadata +30 -11
data/lib/safrano.rb CHANGED
@@ -1,42 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
  require 'rexml/document'
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'
5
+ require_relative 'safrano/version'
6
+ require_relative 'safrano/deprecation'
7
+ require_relative 'safrano/core_ext'
8
+ require_relative 'safrano/contract'
9
+ require_relative 'safrano/multipart'
10
+ require_relative 'safrano/core'
11
+ require_relative 'odata/transition'
12
+ require_relative 'odata/edm/primitive_types'
13
+ require_relative 'odata/entity'
14
+ require_relative 'odata/attribute'
15
+ require_relative 'odata/navigation_attribute'
16
+ require_relative 'odata/model_ext'
17
+ require_relative 'safrano/service'
18
+ require_relative 'odata/walker'
11
19
  require 'sequel'
12
- require_relative 'safrano/sequel_join_by_paths.rb'
20
+ require_relative 'safrano/sequel_join_by_paths'
13
21
  require_relative 'safrano/rack_app'
14
- require_relative 'safrano/odata_rack_builder'
15
- require_relative 'safrano/version'
16
-
17
- # picked from activsupport; needed for ruby < 2.5
18
- # Destructively converts all keys using the +block+ operations.
19
- # Same as +transform_keys+ but modifies +self+.
20
- class Hash
21
- def transform_keys!
22
- keys.each do |key|
23
- self[yield(key)] = delete(key)
24
- end
25
- self
26
- end unless method_defined? :transform_keys!
27
-
28
- def symbolize_keys!
29
- transform_keys! { |key| key.to_sym rescue key }
30
- end
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
22
+ require_relative 'safrano/rack_builder'
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Safrano
4
+ # Design: the Invalid module and the Valid class share exactly the same
5
+ # methods (ie. interface) so methods returning such objects can be
6
+ # post-processed in a declarative way
7
+ # Example:
8
+ # something.do_stuff(param).tap_valid{|valid_result| ... }
9
+ # .tap_error{|error| ... }
10
+
11
+ module Contract
12
+ # represents a invalid result, ie. an error
13
+ # this shall be included/extended to our Error classes thus
14
+ # automagically making them contract/flow enabled
15
+
16
+ # All tap_valid* handlers are not executed
17
+ # tap_error* handlers are executed
18
+ module Invalid
19
+ def tap_error
20
+ yield self
21
+ self # allow chaining
22
+ end
23
+
24
+ def tap_valid
25
+ self # allow chaining
26
+ end
27
+
28
+ def if_valid
29
+ self
30
+ end
31
+
32
+ def if_error
33
+ yield self ## return this
34
+ end
35
+
36
+ def if_valid_collect
37
+ self
38
+ end
39
+
40
+ def map_result!
41
+ self # allow chaining
42
+ end
43
+
44
+ def collect_result!
45
+ self # allow chaining
46
+ end
47
+
48
+ def error
49
+ self
50
+ end
51
+
52
+ def result
53
+ nil
54
+ end
55
+ end
56
+
57
+ class Error
58
+ include Invalid
59
+ end
60
+
61
+ # represents a valid result.
62
+ # All tap_valid* handlers are executed
63
+ # tap_error* handlers are not executed
64
+ class Valid
65
+ attr_reader :result
66
+
67
+ def initialize(result)
68
+ @result = result
69
+ end
70
+
71
+ def tap_error
72
+ self # allow chaining
73
+ end
74
+
75
+ def tap_valid
76
+ yield @result
77
+ self # allow chaining
78
+ end
79
+
80
+ def if_valid
81
+ yield @result ## return this
82
+ end
83
+
84
+ def if_error
85
+ self # allow chaining
86
+ end
87
+
88
+ def if_valid_collect
89
+ yield(*@result) ## return this
90
+ end
91
+
92
+ def map_result!
93
+ @result = yield @result
94
+ self # allow chaining
95
+ end
96
+
97
+ def collect_result!
98
+ @result = yield(*@result)
99
+ self # allow chaining
100
+ end
101
+
102
+ def error
103
+ nil
104
+ end
105
+ end # class Valid
106
+
107
+ def self.valid(result)
108
+ Contract::Valid.new(result)
109
+ end
110
+
111
+ def self.and(*contracts)
112
+ # pick the first error if any
113
+ if (ff = contracts.find(&:error))
114
+ return ff
115
+ end
116
+
117
+ # return a new one with @result = list of the contracts's results
118
+ # usually this then be reduced again with #collect_result! or # #if_valid_collect methods
119
+ valid(contracts.map(&:result))
120
+ end
121
+
122
+ # shortcut for Contract.and(*contracts).collect_result!
123
+ def self.collect_result!(*contracts)
124
+ # pick the first error if any
125
+ if (ff = contracts.find(&:error))
126
+ return ff
127
+ end
128
+
129
+ # return a new one with @result = yield(*list of the contracts's results)
130
+ valid(yield(*contracts.map(&:result)))
131
+ end
132
+
133
+ # generic success return, when return value does not matter
134
+ # but usefull for control-flow
135
+ OK = Contract.valid(nil).freeze
136
+ # generic error return, when error value does not matter
137
+ # but usefull for control-flow
138
+ NOT_OK = Error.new.freeze
139
+ NOK = NOT_OK # it's shorter
140
+ end # Contract
141
+ end
data/lib/safrano/core.rb CHANGED
@@ -1,125 +1,43 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # our main namespace
2
- module OData
4
+ module Safrano
3
5
  # frozen empty Array/Hash to reduce unncecessary object creation
4
6
  EMPTY_ARRAY = [].freeze
5
7
  EMPTY_HASH = {}.freeze
6
8
  EMPTY_HASH_IN_ARY = [EMPTY_HASH].freeze
7
- EMPTY_STRING = ''.freeze
9
+ EMPTY_STRING = ''
8
10
  ARY_204_EMPTY_HASH_ARY = [204, EMPTY_HASH, EMPTY_ARRAY].freeze
9
- SPACE = ' '.freeze
10
- COMMA = ','.freeze
11
+ SPACE = ' '
12
+ COMMA = ','
11
13
 
12
14
  # some prominent constants... probably already defined elsewhere eg in Rack
13
15
  # but lets KISS
14
- CONTENT_TYPE = 'Content-Type'.freeze
15
- CTT_TYPE_LC = 'content-type'.freeze
16
- TEXTPLAIN_UTF8 = 'text/plain;charset=utf-8'.freeze
17
- APPJSON = 'application/json'.freeze
18
- APPXML = 'application/xml'.freeze
19
- MP_MIXED = 'multipart/mixed'.freeze
20
- APPXML_UTF8 = 'application/xml;charset=utf-8'.freeze
21
- APPATOMXML_UTF8 = 'application/atomsvc+xml;charset=utf-8'.freeze
22
- APPJSON_UTF8 = 'application/json;charset=utf-8'.freeze
16
+ CONTENT_TYPE = 'Content-Type'
17
+ CTT_TYPE_LC = 'content-type'
18
+ TEXTPLAIN_UTF8 = 'text/plain;charset=utf-8'
19
+ APPJSON = 'application/json'
20
+ APPXML = 'application/xml'
21
+ MP_MIXED = 'multipart/mixed'
22
+ APPXML_UTF8 = 'application/xml;charset=utf-8'
23
+ APPATOMXML_UTF8 = 'application/atomsvc+xml;charset=utf-8'
24
+ APPJSON_UTF8 = 'application/json;charset=utf-8'
23
25
 
24
26
  CT_JSON = { CONTENT_TYPE => APPJSON_UTF8 }.freeze
25
27
  CT_TEXT = { CONTENT_TYPE => TEXTPLAIN_UTF8 }.freeze
26
28
  CT_ATOMXML = { CONTENT_TYPE => APPATOMXML_UTF8 }.freeze
27
29
  CT_APPXML = { CONTENT_TYPE => APPXML_UTF8 }.freeze
28
30
 
29
- # Type mapping DB --> Edm
30
- # TypeMap = {"INTEGER" => "Edm.Int32" , "TEXT" => "Edm.String",
31
- # "STRING" => "Edm.String"}
32
- # Todo: complete mapping... this is just for the most common ones
33
-
34
- # TODO: use Sequel GENERIC_TYPES: -->
35
- # Constants
36
- # GENERIC_TYPES = %w'String Integer Float Numeric BigDecimal Date DateTime
37
- # Time File TrueClass FalseClass'.freeze
38
- # Classes specifying generic types that Sequel will convert to
39
- # database-specific types.
40
- DB_TYPE_STRING_RGX = /\ACHAR\s*\(\d+\)\z/.freeze
31
+ module NavigationInfo
32
+ attr_reader :nav_parent
33
+ attr_reader :navattr_reflection
34
+ attr_reader :nav_name
41
35
 
42
- # TODO... complete; used in $metadata
43
- def self.get_edm_type(db_type:)
44
- case db_type.upcase
45
- when 'INTEGER'
46
- 'Edm.Int32'
47
- when 'TEXT', 'STRING'
48
- 'Edm.String'
49
- else
50
- 'Edm.String' if DB_TYPE_STRING_RGX =~ db_type.upcase
36
+ def set_relation_info(parent, name)
37
+ @nav_parent = parent
38
+ @nav_name = name
39
+ @navattr_reflection = parent.class.association_reflections[name.to_sym]
40
+ @nav_klass = @navattr_reflection[:class_name].constantize
51
41
  end
52
42
  end
53
43
  end
54
-
55
- module REXML
56
- # some small extensions
57
- class Document
58
- def to_pretty_xml
59
- formatter = REXML::Formatters::Pretty.new(2)
60
- formatter.compact = true
61
- strio = ''
62
- formatter.write(root, strio)
63
- strio
64
- end
65
- end
66
- end
67
-
68
- # Core
69
- module Safrano
70
- # represents a state transition when navigating/parsing the url path
71
- # from left to right
72
- class Transition < Regexp
73
- attr_accessor :trans
74
- attr_accessor :match_result
75
- attr_accessor :rgx
76
- attr_reader :remain_idx
77
- def initialize(arg, trans: nil, remain_idx: 2)
78
- @rgx = if arg.respond_to? :each_char
79
- Regexp.new(arg)
80
- else
81
- arg
82
- end
83
- @trans = trans
84
- @remain_idx = remain_idx
85
- end
86
-
87
- def do_match(str)
88
- @match_result = @rgx.match(str)
89
- end
90
-
91
- # remain_idx is the index of the last match-data. ususally its 2
92
- # but can be overidden
93
- def path_remain
94
- @match_result[@remain_idx] if @match_result && @match_result[@remain_idx]
95
- end
96
-
97
- def path_done
98
- if @match_result
99
- @match_result[1] || ''
100
- else
101
- ''
102
- end
103
- end
104
-
105
- def do_transition(ctx)
106
- ctx.method(@trans).call(@match_result)
107
- end
108
- end
109
-
110
- TransitionEnd = Transition.new('\A(\/?)\z', trans: 'transition_end')
111
- TransitionMetadata = Transition.new('\A(\/\$metadata)(.*)',
112
- trans: 'transition_metadata')
113
- TransitionBatch = Transition.new('\A(\/\$batch)(.*)',
114
- trans: 'transition_batch')
115
- TransitionContentId = Transition.new('\A(\/\$(\d+))(.*)',
116
- trans: 'transition_content_id',
117
- remain_idx: 3)
118
- TransitionCount = Transition.new('(\A\/\$count)(.*)\z',
119
- trans: 'transition_count')
120
- TransitionValue = Transition.new('(\A\/\$value)(.*)\z',
121
- trans: 'transition_value')
122
- TransitionLinks = Transition.new('(\A\/\$links)(.*)\z',
123
- trans: 'transition_links')
124
- attr_accessor :allowed_transitions
125
- end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir.glob(File.expand_path('../core_ext/*.rb', __dir__)).sort.each do |path|
4
+ require path
5
+ end
6
+
7
+ # small helper method
8
+ # http://stackoverflow.com/
9
+ # questions/24980295/strictly-convert-string-to-integer-or-nil
10
+ def number_or_nil(str)
11
+ num = str.to_i
12
+ num if num.to_s == str
13
+ end
@@ -0,0 +1,73 @@
1
+ # frozen-string-literal: true
2
+
3
+ # shamelessly copied from Sequel...
4
+
5
+ module Safrano
6
+ # This module makes it easy to print deprecation warnings with optional backtraces to a given stream.
7
+ # There are a two accessors you can use to change how/where the deprecation methods are printed
8
+ # and whether/how backtraces should be included:
9
+ #
10
+ # Safrano::Deprecation.output = $stderr # print deprecation messages to standard error (default)
11
+ # Safrano::Deprecation.output = File.open('deprecated_calls.txt', 'wb') # use a file instead
12
+ # Safrano::Deprecation.output = false # do not output deprecation messages
13
+ #
14
+ # Safrano::Deprecation.prefix = "SAFRANO DEPRECATION WARNING: " # prefix deprecation messages with a given string (default)
15
+ # Safrano::Deprecation.prefix = false # do not prefix deprecation messages
16
+ #
17
+ # Safrano::Deprecation.backtrace_filter = false # don't include backtraces
18
+ # Safrano::Deprecation.backtrace_filter = true # include full backtraces
19
+ # Safrano::Deprecation.backtrace_filter = 2 # include 2 backtrace lines (default)
20
+ # Safrano::Deprecation.backtrace_filter = 1 # include 1 backtrace line
21
+ # Safrano::Deprecation.backtrace_filter = lambda{|line, line_no| line_no < 3 || line =~ /my_app/} # select backtrace lines to output
22
+ module Deprecation
23
+ @backtrace_filter = false
24
+ @output = $stderr
25
+ @prefix = 'SAFRANO DEPRECATION WARNING: '
26
+
27
+ class << self
28
+ # How to filter backtraces. +false+ does not include backtraces, +true+ includes
29
+ # full backtraces, an Integer includes that number of backtrace lines, and
30
+ # a proc is called with the backtrace line and line number to select the backtrace
31
+ # lines to include. The default is no backtrace .
32
+ attr_accessor :backtrace_filter
33
+
34
+ # Where deprecation messages should be output, must respond to puts. $stderr by default.
35
+ attr_accessor :output
36
+
37
+ # Where deprecation messages should be prefixed with ("SEQUEL DEPRECATION WARNING: " by default).
38
+ attr_accessor :prefix
39
+ end
40
+
41
+ # Print the message and possibly backtrace to the output.
42
+ def self.deprecate(method, instead = nil)
43
+ return unless output
44
+
45
+ message = instead ? "#{method} is deprecated and will be removed in Safrano 0.6. #{instead}." : method
46
+ message = "#{prefix}#{message}" if prefix
47
+ output.puts(message)
48
+ case b = backtrace_filter
49
+ when Integer
50
+ caller.each do |c|
51
+ b -= 1
52
+ output.puts(c)
53
+ break if b <= 0
54
+ end
55
+ when true
56
+ caller.each { |c| output.puts(c) }
57
+ when Proc
58
+ caller.each_with_index { |line, line_no| output.puts(line) if b.call(line, line_no) }
59
+ end
60
+ nil
61
+ end
62
+
63
+ # If using ruby 2.3+, use Module#deprecate_constant to deprecate the constant,
64
+ # otherwise do nothing as the ruby implementation does not support constant deprecation.
65
+ def self.deprecate_constant(mod, constant)
66
+ # :nocov:
67
+ return unless RUBY_VERSION > '2.3'
68
+
69
+ # :nocov:
70
+ mod.deprecate_constant(constant)
71
+ end
72
+ end
73
+ end
@@ -1,13 +1,15 @@
1
- CRLF = "\r\n".freeze
2
- LF = "\n".freeze
1
+ # frozen_string_literal: true
2
+
3
+ CRLF = "\r\n"
4
+ LF = "\n"
3
5
 
4
6
  require 'securerandom'
5
- require 'webrick/httpstatus'
7
+ require 'rack/utils'
6
8
 
7
9
  # Simple multipart support for OData $batch purpose
8
10
  module MIME
9
- CTT_TYPE_LC = 'content-type'.freeze
10
- TEXT_PLAIN = 'text/plain'.freeze
11
+ CTT_TYPE_LC = 'content-type'
12
+ TEXT_PLAIN = 'text/plain'
11
13
 
12
14
  # a mime object has a header(with content-type etc) and a content(aka body)
13
15
  class Media
@@ -17,6 +19,7 @@ module MIME
17
19
 
18
20
  attr_accessor :lines
19
21
  attr_accessor :target
22
+
20
23
  def initialize
21
24
  @state = :h
22
25
  @lines = []
@@ -107,11 +110,11 @@ module MIME
107
110
  @target.ct = @target_ct
108
111
  @state = :bmp
109
112
  end
110
- MPS = 'multipart/'.freeze
111
- MP_RGX1 = %r{^(digest|mixed);\s*boundary=\"(.*)\"}.freeze
113
+ MPS = 'multipart/'
114
+ MP_RGX1 = %r{^(digest|mixed);\s*boundary="(.*)"}.freeze
112
115
  MP_RGX2 = %r{^(digest|mixed);\s*boundary=(.*)}.freeze
113
116
  # APP_HTTP_RGX = %r{^application/http}.freeze
114
- APP_HTTP = 'application/http'.freeze
117
+ APP_HTTP = 'application/http'
115
118
  def new_content
116
119
  @target =
117
120
  if @target_ct.start_with?(MPS) &&
@@ -237,7 +240,8 @@ module MIME
237
240
 
238
241
  def initialize
239
242
  @hd = {}
240
- @content = ''
243
+ # we need it unfrozen --> +
244
+ @content = +''
241
245
  # set default values. Can be overwritten by parser
242
246
  @hd[CTT_TYPE_LC] = TEXT_PLAIN
243
247
  @ct = TEXT_PLAIN
@@ -305,8 +309,10 @@ module MIME
305
309
  # to remove it from the end of the last body line
306
310
  return unless @body_lines
307
311
 
308
- # @body_lines.last.sub!(CRLF_ENDING_RGX, '')
309
- @body_lines.last.chomp!(CRLF)
312
+ # the last line ends up frozen --> chomp! fails
313
+ # @body_lines.last.chomp!(CRLF)
314
+ last_line = @body_lines.pop.chomp(CRLF)
315
+ @body_lines.push last_line
310
316
  @parts << @body_lines
311
317
  end
312
318
 
@@ -360,7 +366,7 @@ module MIME
360
366
  end
361
367
 
362
368
  def set_multipart_header
363
- @hd[CTT_TYPE_LC] = "#{OData::MP_MIXED}; boundary=#{@boundary}"
369
+ @hd[CTT_TYPE_LC] = "#{Safrano::MP_MIXED}; boundary=#{@boundary}"
364
370
  end
365
371
 
366
372
  def get_http_resp(batcha)
@@ -399,15 +405,14 @@ module MIME
399
405
  @response.content = [{ 'odata.error' =>
400
406
  { 'message' =>
401
407
  'Bad Request: Failed changeset ' } }.to_json]
402
- @response.hd = OData::CT_JSON
408
+ @response.hd = Safrano::CT_JSON
403
409
  @response
404
410
  end
405
411
 
406
412
  def unparse(bodyonly = false)
407
- b = ''
413
+ b = +String.new
408
414
  unless bodyonly
409
- # b << OData::CONTENT_TYPE << ': ' << @hd[OData::CTT_TYPE_LC] << CRLF
410
- b << "#{OData::CONTENT_TYPE}: #{@hd[CTT_TYPE_LC]}#{CRLF}"
415
+ b << "#{Safrano::CONTENT_TYPE}: #{@hd[CTT_TYPE_LC]}#{CRLF}"
411
416
  end
412
417
 
413
418
  b << crbdcr = "#{CRLF}--#{@boundary}#{CRLF}"
@@ -431,14 +436,14 @@ module MIME
431
436
 
432
437
  def initialize
433
438
  @hd = {}
434
- @content = ''
439
+ @content = +String.new
435
440
  end
436
441
 
437
442
  def unparse
438
- b = "#{@http_method} #{@uri} HTTP/1.1#{CRLF}"
443
+ b = +"#{@http_method} #{@uri} HTTP/1.1#{CRLF}"
439
444
  @hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
440
445
  b << CRLF
441
- b << @content if @content != ''
446
+ b << @content unless content.empty?
442
447
  b
443
448
  end
444
449
  end
@@ -452,7 +457,7 @@ module MIME
452
457
  "Content-Transfer-Encoding: binary#{CRLF}",
453
458
  'HTTP/1.1 '].join(CRLF).freeze
454
459
 
455
- StatusMessage = ::WEBrick::HTTPStatus::StatusMessage.freeze
460
+ StatusMessage = ::Rack::Utils::HTTP_STATUS_CODES.freeze
456
461
 
457
462
  def initialize
458
463
  @hd = {}
@@ -464,7 +469,7 @@ module MIME
464
469
  end
465
470
 
466
471
  def unparse
467
- b = String.new(APPLICATION_HTTP_11)
472
+ b = +String.new(APPLICATION_HTTP_11)
468
473
  b << "#{@status} #{StatusMessage[@status]} #{CRLF}"
469
474
  @hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
470
475
  b << CRLF
@@ -475,9 +480,9 @@ module MIME
475
480
 
476
481
  # For application/http . Content is either a Request or a Response
477
482
  class Http < Media
478
- HTTP_MTHS_RGX = 'POST|GET|PUT|MERGE|PATCH|DELETE'.freeze
483
+ HTTP_MTHS_RGX = 'POST|GET|PUT|MERGE|PATCH|DELETE'
479
484
  HTTP_R_RGX = %r{^(#{HTTP_MTHS_RGX})\s+(\S*)\s?(HTTP/1\.1)\s*$}.freeze
480
- HEADER_RGX = %r{^([a-zA-Z\-]+):\s*([0-9a-zA-Z\-\/,\s]+;?\S*)\s*$}.freeze
485
+ HEADER_RGX = %r{^([a-zA-Z\-]+):\s*([0-9a-zA-Z\-/,\s]+;?\S*)\s*$}.freeze
481
486
  HTTP_RESP_RGX = %r{^HTTP/1\.1\s(\d+)\s}.freeze
482
487
 
483
488
  # Parser for Http Media
@@ -566,7 +571,7 @@ module MIME
566
571
  end
567
572
 
568
573
  def unparse
569
- b = "Content-Type: #{@ct}#{CRLF}"
574
+ b = +"Content-Type: #{@ct}#{CRLF}"
570
575
  b << "Content-Transfer-Encoding: binary#{CRLF}#{CRLF}"
571
576
  b << @content.unparse
572
577
  b