safrano 0.4.2 → 0.5.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 (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 +15 -10
  14. data/lib/odata/batch.rb +9 -7
  15. data/lib/odata/collection.rb +140 -591
  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 +152 -0
  21. data/lib/odata/edm/primitive_types.rb +184 -0
  22. data/lib/odata/entity.rb +123 -172
  23. data/lib/odata/error.rb +183 -32
  24. data/lib/odata/expand.rb +20 -17
  25. data/lib/odata/filter/base.rb +74 -0
  26. data/lib/odata/filter/error.rb +49 -6
  27. data/lib/odata/filter/parse.rb +41 -25
  28. data/lib/odata/filter/sequel.rb +133 -62
  29. data/lib/odata/filter/sequel_function_adapter.rb +148 -0
  30. data/lib/odata/filter/token.rb +26 -19
  31. data/lib/odata/filter/tree.rb +106 -52
  32. data/lib/odata/function_import.rb +168 -0
  33. data/lib/odata/model_ext.rb +639 -0
  34. data/lib/odata/navigation_attribute.rb +13 -26
  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 +20 -10
  40. data/lib/safrano.rb +18 -38
  41. data/lib/safrano/contract.rb +143 -0
  42. data/lib/safrano/core.rb +23 -107
  43. data/lib/safrano/core_ext.rb +13 -0
  44. data/lib/safrano/deprecation.rb +73 -0
  45. data/lib/safrano/multipart.rb +29 -33
  46. data/lib/safrano/rack_app.rb +66 -65
  47. data/lib/safrano/{odata_rack_builder.rb → rack_builder.rb} +18 -2
  48. data/lib/safrano/request.rb +96 -45
  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 +240 -130
  52. data/lib/safrano/version.rb +3 -1
  53. data/lib/sequel/plugins/join_by_paths.rb +6 -19
  54. metadata +32 -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,143 @@
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
+ def initialize(result)
66
+ @result = result
67
+ end
68
+
69
+ def tap_error
70
+ self # allow chaining
71
+ end
72
+
73
+ def tap_valid
74
+ yield @result
75
+ self # allow chaining
76
+ end
77
+
78
+ def if_valid
79
+ yield @result ## return this
80
+ end
81
+
82
+ def if_error
83
+ self # allow chaining
84
+ end
85
+
86
+ def if_valid_collect
87
+ yield(*@result) ## return this
88
+ end
89
+
90
+ def map_result!
91
+ @result = yield @result
92
+ self # allow chaining
93
+ end
94
+
95
+ def collect_result!
96
+ @result = yield(*@result)
97
+ self # allow chaining
98
+ end
99
+
100
+ def error
101
+ nil
102
+ end
103
+
104
+ def result
105
+ @result
106
+ end
107
+ end # class Valid
108
+
109
+ def self.valid(result)
110
+ Contract::Valid.new(result)
111
+ end
112
+
113
+ def self.and(*contracts)
114
+ # pick the first error if any
115
+ if (ff = contracts.find(&:error))
116
+ return ff
117
+ end
118
+
119
+ # return a new one with @result = list of the contracts's results
120
+ # usually this then be reduced again with #collect_result! or # #if_valid_collect methods
121
+ valid(contracts.map(&:result))
122
+ end
123
+
124
+ # shortcut for Contract.and(*contracts).collect_result!
125
+ def self.collect_result!(*contracts)
126
+ # pick the first error if any
127
+ if (ff = contracts.find(&:error))
128
+ return ff
129
+ end
130
+
131
+ # return a new one with @result = yield(*list of the contracts's results)
132
+ valid(yield(*contracts.map(&:result)))
133
+ end
134
+
135
+ # generic success return, when return value does not matter
136
+ # but usefull for control-flow
137
+ OK = Contract.valid(nil).freeze
138
+ # generic error return, when error value does not matter
139
+ # but usefull for control-flow
140
+ NOT_OK = Error.new.freeze
141
+ NOK = NOT_OK # it's shorter
142
+ end # Contract
143
+ end
data/lib/safrano/core.rb CHANGED
@@ -1,127 +1,43 @@
1
- #!/usr/bin/env ruby
1
+ # frozen_string_literal: true
2
2
 
3
3
  # our main namespace
4
- module OData
4
+ module Safrano
5
5
  # frozen empty Array/Hash to reduce unncecessary object creation
6
6
  EMPTY_ARRAY = [].freeze
7
7
  EMPTY_HASH = {}.freeze
8
8
  EMPTY_HASH_IN_ARY = [EMPTY_HASH].freeze
9
- EMPTY_STRING = ''.freeze
9
+ EMPTY_STRING = ''
10
10
  ARY_204_EMPTY_HASH_ARY = [204, EMPTY_HASH, EMPTY_ARRAY].freeze
11
- SPACE = ' '.freeze
12
- COMMA = ','.freeze
11
+ SPACE = ' '
12
+ COMMA = ','
13
13
 
14
14
  # some prominent constants... probably already defined elsewhere eg in Rack
15
15
  # but lets KISS
16
- CONTENT_TYPE = 'Content-Type'.freeze
17
- CTT_TYPE_LC = 'content-type'.freeze
18
- TEXTPLAIN_UTF8 = 'text/plain;charset=utf-8'.freeze
19
- APPJSON = 'application/json'.freeze
20
- APPXML = 'application/xml'.freeze
21
- MP_MIXED = 'multipart/mixed'.freeze
22
- APPXML_UTF8 = 'application/xml;charset=utf-8'.freeze
23
- APPATOMXML_UTF8 = 'application/atomsvc+xml;charset=utf-8'.freeze
24
- 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'
25
25
 
26
26
  CT_JSON = { CONTENT_TYPE => APPJSON_UTF8 }.freeze
27
27
  CT_TEXT = { CONTENT_TYPE => TEXTPLAIN_UTF8 }.freeze
28
28
  CT_ATOMXML = { CONTENT_TYPE => APPATOMXML_UTF8 }.freeze
29
29
  CT_APPXML = { CONTENT_TYPE => APPXML_UTF8 }.freeze
30
30
 
31
- # Type mapping DB --> Edm
32
- # TypeMap = {"INTEGER" => "Edm.Int32" , "TEXT" => "Edm.String",
33
- # "STRING" => "Edm.String"}
34
- # Todo: complete mapping... this is just for the most common ones
35
-
36
- # TODO: use Sequel GENERIC_TYPES: -->
37
- # Constants
38
- # GENERIC_TYPES = %w'String Integer Float Numeric BigDecimal Date DateTime
39
- # Time File TrueClass FalseClass'.freeze
40
- # Classes specifying generic types that Sequel will convert to
41
- # database-specific types.
42
- DB_TYPE_STRING_RGX = /\ACHAR\s*\(\d+\)\z/.freeze
43
-
44
- # TODO... complete; used in $metadata
45
- def self.get_edm_type(db_type:)
46
- case db_type.upcase
47
- when 'INTEGER'
48
- 'Edm.Int32'
49
- when 'TEXT', 'STRING'
50
- 'Edm.String'
51
- else
52
- 'Edm.String' if DB_TYPE_STRING_RGX =~ db_type.upcase
53
- end
54
- end
55
- end
56
-
57
- module REXML
58
- # some small extensions
59
- class Document
60
- def to_pretty_xml
61
- formatter = REXML::Formatters::Pretty.new(2)
62
- formatter.compact = true
63
- strio = ''
64
- formatter.write(root, strio)
65
- strio
66
- end
67
- end
68
- end
69
-
70
- # Core
71
- module Safrano
72
- # represents a state transition when navigating/parsing the url path
73
- # from left to right
74
- class Transition < Regexp
75
- attr_accessor :trans
76
- attr_accessor :match_result
77
- attr_accessor :rgx
78
- attr_reader :remain_idx
79
- def initialize(arg, trans: nil, remain_idx: 2)
80
- @rgx = if arg.respond_to? :each_char
81
- Regexp.new(arg)
82
- else
83
- arg
84
- end
85
- @trans = trans
86
- @remain_idx = remain_idx
87
- end
88
-
89
- def do_match(str)
90
- @match_result = @rgx.match(str)
91
- end
92
-
93
- # remain_idx is the index of the last match-data. ususally its 2
94
- # but can be overidden
95
- def path_remain
96
- @match_result[@remain_idx] if @match_result && @match_result[@remain_idx]
97
- end
31
+ module NavigationInfo
32
+ attr_reader :nav_parent
33
+ attr_reader :navattr_reflection
34
+ attr_reader :nav_name
98
35
 
99
- def path_done
100
- if @match_result
101
- @match_result[1] || ''
102
- else
103
- ''
104
- end
105
- end
106
-
107
- def do_transition(ctx)
108
- ctx.method(@trans).call(@match_result)
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
109
41
  end
110
42
  end
111
-
112
- TransitionEnd = Transition.new('\A(\/?)\z', trans: 'transition_end')
113
- TransitionMetadata = Transition.new('\A(\/\$metadata)(.*)',
114
- trans: 'transition_metadata')
115
- TransitionBatch = Transition.new('\A(\/\$batch)(.*)',
116
- trans: 'transition_batch')
117
- TransitionContentId = Transition.new('\A(\/\$(\d+))(.*)',
118
- trans: 'transition_content_id',
119
- remain_idx: 3)
120
- TransitionCount = Transition.new('(\A\/\$count)(.*)\z',
121
- trans: 'transition_count')
122
- TransitionValue = Transition.new('(\A\/\$value)(.*)\z',
123
- trans: 'transition_value')
124
- TransitionLinks = Transition.new('(\A\/\$links)(.*)\z',
125
- trans: 'transition_links')
126
- attr_accessor :allowed_transitions
127
43
  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
@@ -15,10 +17,9 @@ module MIME
15
17
  class Parser
16
18
  HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
17
19
 
18
- CRLF_LINE_RGX = /^#{CRLF}$/.freeze
19
-
20
20
  attr_accessor :lines
21
21
  attr_accessor :target
22
+
22
23
  def initialize
23
24
  @state = :h
24
25
  @lines = []
@@ -57,7 +58,6 @@ module MIME
57
58
  if (hmd = HMD_RGX.match(line))
58
59
  @target_hd[hmd[1].downcase] = hmd[2].strip
59
60
 
60
- # elsif CRLF_LINE_RGX =~ line
61
61
  elsif CRLF == line
62
62
  @target_ct = @target_hd[CTT_TYPE_LC] || TEXT_PLAIN
63
63
  @state = new_content
@@ -110,11 +110,11 @@ module MIME
110
110
  @target.ct = @target_ct
111
111
  @state = :bmp
112
112
  end
113
- MPS = 'multipart/'.freeze
114
- MP_RGX1 = %r{^(digest|mixed);\s*boundary=\"(.*)\"}.freeze
113
+ MPS = 'multipart/'
114
+ MP_RGX1 = %r{^(digest|mixed);\s*boundary="(.*)"}.freeze
115
115
  MP_RGX2 = %r{^(digest|mixed);\s*boundary=(.*)}.freeze
116
116
  # APP_HTTP_RGX = %r{^application/http}.freeze
117
- APP_HTTP = 'application/http'.freeze
117
+ APP_HTTP = 'application/http'
118
118
  def new_content
119
119
  @target =
120
120
  if @target_ct.start_with?(MPS) &&
@@ -199,7 +199,6 @@ module MIME
199
199
  # Parser for Text::Plain
200
200
  class Parser
201
201
  HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
202
- CRLF_LINE_RGX = /^#{CRLF}$/.freeze
203
202
  def initialize(target)
204
203
  @state = :h
205
204
  @lines = []
@@ -213,7 +212,6 @@ module MIME
213
212
  def parse_head(line)
214
213
  if (hmd = HMD_RGX.match(line))
215
214
  @target.hd[hmd[1].downcase] = hmd[2].strip
216
- # elsif CRLF_LINE_RGX =~ line
217
215
  elsif CRLF == line
218
216
  @state = :b
219
217
  else
@@ -242,7 +240,8 @@ module MIME
242
240
 
243
241
  def initialize
244
242
  @hd = {}
245
- @content = ''
243
+ # we need it unfrozen --> +
244
+ @content = +''
246
245
  # set default values. Can be overwritten by parser
247
246
  @hd[CTT_TYPE_LC] = TEXT_PLAIN
248
247
  @ct = TEXT_PLAIN
@@ -310,8 +309,10 @@ module MIME
310
309
  # to remove it from the end of the last body line
311
310
  return unless @body_lines
312
311
 
313
- # @body_lines.last.sub!(CRLF_ENDING_RGX, '')
314
- @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
315
316
  @parts << @body_lines
316
317
  end
317
318
 
@@ -365,7 +366,7 @@ module MIME
365
366
  end
366
367
 
367
368
  def set_multipart_header
368
- @hd[CTT_TYPE_LC] = "#{OData::MP_MIXED}; boundary=#{@boundary}"
369
+ @hd[CTT_TYPE_LC] = "#{Safrano::MP_MIXED}; boundary=#{@boundary}"
369
370
  end
370
371
 
371
372
  def get_http_resp(batcha)
@@ -404,15 +405,14 @@ module MIME
404
405
  @response.content = [{ 'odata.error' =>
405
406
  { 'message' =>
406
407
  'Bad Request: Failed changeset ' } }.to_json]
407
- @response.hd = OData::CT_JSON
408
+ @response.hd = Safrano::CT_JSON
408
409
  @response
409
410
  end
410
411
 
411
412
  def unparse(bodyonly = false)
412
- b = ''
413
+ b = +String.new
413
414
  unless bodyonly
414
- # b << OData::CONTENT_TYPE << ': ' << @hd[OData::CTT_TYPE_LC] << CRLF
415
- b << "#{OData::CONTENT_TYPE}: #{@hd[CTT_TYPE_LC]}#{CRLF}"
415
+ b << "#{Safrano::CONTENT_TYPE}: #{@hd[CTT_TYPE_LC]}#{CRLF}"
416
416
  end
417
417
 
418
418
  b << crbdcr = "#{CRLF}--#{@boundary}#{CRLF}"
@@ -436,15 +436,14 @@ module MIME
436
436
 
437
437
  def initialize
438
438
  @hd = {}
439
- @content = ''
439
+ @content = +String.new
440
440
  end
441
441
 
442
442
  def unparse
443
- b = "#{@http_method} #{@uri} HTTP/1.1#{CRLF}"
443
+ b = +"#{@http_method} #{@uri} HTTP/1.1#{CRLF}"
444
444
  @hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
445
- # @hd.each { |k, v| b << k.to_s << ': ' << v.to_s << CRLF }
446
445
  b << CRLF
447
- b << @content if @content != ''
446
+ b << @content unless content.empty?
448
447
  b
449
448
  end
450
449
  end
@@ -458,7 +457,7 @@ module MIME
458
457
  "Content-Transfer-Encoding: binary#{CRLF}",
459
458
  'HTTP/1.1 '].join(CRLF).freeze
460
459
 
461
- StatusMessage = ::WEBrick::HTTPStatus::StatusMessage.freeze
460
+ StatusMessage = ::Rack::Utils::HTTP_STATUS_CODES.freeze
462
461
 
463
462
  def initialize
464
463
  @hd = {}
@@ -470,10 +469,9 @@ module MIME
470
469
  end
471
470
 
472
471
  def unparse
473
- b = String.new(APPLICATION_HTTP_11)
472
+ b = +String.new(APPLICATION_HTTP_11)
474
473
  b << "#{@status} #{StatusMessage[@status]} #{CRLF}"
475
474
  @hd.each { |k, v| b << "#{k}: #{v}#{CRLF}" }
476
- # @hd.each { |k, v| b << k.to_s << ': ' << v.to_s << CRLF }
477
475
  b << CRLF
478
476
  b << @content.join if @content
479
477
  b
@@ -482,15 +480,14 @@ module MIME
482
480
 
483
481
  # For application/http . Content is either a Request or a Response
484
482
  class Http < Media
485
- HTTP_MTHS_RGX = 'POST|GET|PUT|MERGE|PATCH|DELETE'.freeze
483
+ HTTP_MTHS_RGX = 'POST|GET|PUT|MERGE|PATCH|DELETE'
486
484
  HTTP_R_RGX = %r{^(#{HTTP_MTHS_RGX})\s+(\S*)\s?(HTTP/1\.1)\s*$}.freeze
487
- 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
488
486
  HTTP_RESP_RGX = %r{^HTTP/1\.1\s(\d+)\s}.freeze
489
487
 
490
488
  # Parser for Http Media
491
489
  class Parser
492
490
  HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
493
- CRLF_LINE_RGX = /^#{CRLF}$/.freeze
494
491
 
495
492
  def initialize(target)
496
493
  @state = :http
@@ -523,7 +520,6 @@ module MIME
523
520
  if (hmd = HMD_RGX.match(line))
524
521
  @target.content.hd[hmd[1].downcase] = hmd[2].strip
525
522
  elsif CRLF == line
526
- # elsif CRLF_LINE_RGX =~ line
527
523
  @state = :b
528
524
  else
529
525
  @body_lines << line
@@ -575,7 +571,7 @@ module MIME
575
571
  end
576
572
 
577
573
  def unparse
578
- b = "Content-Type: #{@ct}#{CRLF}"
574
+ b = +"Content-Type: #{@ct}#{CRLF}"
579
575
  b << "Content-Transfer-Encoding: binary#{CRLF}#{CRLF}"
580
576
  b << @content.unparse
581
577
  b