safrano 0.3.4 → 0.4.4

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 (57) 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 +17 -15
  15. data/lib/odata/collection.rb +141 -500
  16. data/lib/odata/collection_filter.rb +44 -37
  17. data/lib/odata/collection_media.rb +193 -43
  18. data/lib/odata/collection_order.rb +50 -37
  19. data/lib/odata/common_logger.rb +39 -12
  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 +201 -176
  23. data/lib/odata/error.rb +186 -33
  24. data/lib/odata/expand.rb +126 -0
  25. data/lib/odata/filter/base.rb +69 -0
  26. data/lib/odata/filter/error.rb +55 -6
  27. data/lib/odata/filter/parse.rb +38 -36
  28. data/lib/odata/filter/sequel.rb +121 -67
  29. data/lib/odata/filter/sequel_function_adapter.rb +148 -0
  30. data/lib/odata/filter/token.rb +15 -11
  31. data/lib/odata/filter/tree.rb +110 -60
  32. data/lib/odata/function_import.rb +166 -0
  33. data/lib/odata/model_ext.rb +618 -0
  34. data/lib/odata/navigation_attribute.rb +50 -32
  35. data/lib/odata/relations.rb +7 -7
  36. data/lib/odata/select.rb +54 -0
  37. data/lib/{safrano_core.rb → odata/transition.rb} +14 -60
  38. data/lib/odata/url_parameters.rb +128 -37
  39. data/lib/odata/walker.rb +19 -11
  40. data/lib/safrano.rb +18 -28
  41. data/lib/safrano/contract.rb +143 -0
  42. data/lib/safrano/core.rb +43 -0
  43. data/lib/safrano/core_ext.rb +13 -0
  44. data/lib/safrano/deprecation.rb +73 -0
  45. data/lib/{multipart.rb → safrano/multipart.rb} +37 -41
  46. data/lib/safrano/rack_app.rb +175 -0
  47. data/lib/{odata_rack_builder.rb → safrano/rack_builder.rb} +18 -2
  48. data/lib/{request.rb → safrano/request.rb} +102 -50
  49. data/lib/{response.rb → safrano/response.rb} +5 -4
  50. data/lib/safrano/sequel_join_by_paths.rb +5 -0
  51. data/lib/{service.rb → safrano/service.rb} +257 -188
  52. data/lib/safrano/version.rb +5 -0
  53. data/lib/sequel/plugins/join_by_paths.rb +17 -29
  54. metadata +53 -17
  55. data/lib/rack_app.rb +0 -174
  56. data/lib/sequel_join_by_paths.rb +0 -5
  57. data/lib/version.rb +0 -4
@@ -1,10 +1,10 @@
1
- #!/usr/bin/env ruby
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
4
  require 'rexml/document'
5
- require 'safrano.rb'
5
+ require 'safrano'
6
6
 
7
- module OData
7
+ module Safrano
8
8
  # handle navigation in the Datamodel tree of entities/attributes
9
9
  # input is the url path. Url parameters ($filter etc...) are NOT handled here
10
10
  # This uses a state transition algorithm
@@ -30,8 +30,12 @@ module OData
30
30
  # are $links requested ?
31
31
  attr_reader :do_links
32
32
 
33
+ NIL_SERVICE_FATAL = 'Walker is called with a nil service'.freeze
34
+ EMPTYSTR = ''.freeze
35
+ SLASH = '/'.freeze
36
+
33
37
  def initialize(service, path, content_id_refs = nil)
34
- raise 'Walker is called with a nil service' unless service
38
+ raise NIL_SERVICE_FATAL unless service
35
39
 
36
40
  path = URI.decode_www_form_component(path)
37
41
  @context = service
@@ -42,10 +46,9 @@ module OData
42
46
  @path_start = @path_remain = if service
43
47
  unprefixed(service.xpath_prefix, path)
44
48
  else # This is for batch function
45
-
46
49
  path
47
50
  end
48
- @path_done = ''
51
+ @path_done = String.new
49
52
  @status = :start
50
53
  @end_context = nil
51
54
  @do_count = nil
@@ -53,12 +56,12 @@ module OData
53
56
  end
54
57
 
55
58
  def unprefixed(prefix, path)
56
- if (prefix == '') || (prefix == '/')
59
+ if (prefix == EMPTYSTR) || (prefix == SLASH)
57
60
  path
58
61
  else
59
62
  # path.sub!(/\A#{prefix}/, '')
60
63
  # TODO check
61
- path.sub(/\A#{prefix}/, '')
64
+ path.sub(/\A#{prefix}/, EMPTYSTR)
62
65
  end
63
66
  end
64
67
 
@@ -74,6 +77,7 @@ module OData
74
77
  valid_tr = @context.allowed_transitions.select do |t|
75
78
  t.do_match(@path_remain)
76
79
  end
80
+
77
81
  # this is a very fragile and obscure but required hack (wanted: a
78
82
  # better one) to make attributes that are substrings of each other
79
83
  # work well
@@ -95,13 +99,13 @@ module OData
95
99
  @context = nil
96
100
  @status = :error
97
101
  # TODO: more appropriate error handling
98
- @error = OData::ErrorNotFound
102
+ @error = Safrano::ErrorNotFound
99
103
  end
100
104
  else
101
105
  @context = nil
102
106
  @status = :error
103
107
  # TODO: more appropriate error handling
104
- @error = OData::ErrorNotFound
108
+ @error = Safrano::ErrorNotFound
105
109
  end
106
110
  end
107
111
 
@@ -151,7 +155,7 @@ module OData
151
155
  else
152
156
  @context = nil
153
157
  @status = :error
154
- @error = OData::ErrorNotFound
158
+ @error = Safrano::ErrorNotFoundSegment.new(@path_remain)
155
159
  end
156
160
  end
157
161
  # TODO: shouldnt we raise an error here if @status != :end ?
@@ -159,5 +163,9 @@ module OData
159
163
 
160
164
  @end_context = @contexts.size >= 2 ? @contexts[-2] : @contexts[1]
161
165
  end
166
+
167
+ def finalize
168
+ (@status == :end) ? Contract.valid(@end_context) : @error
169
+ end
162
170
  end
163
171
  end
@@ -1,32 +1,22 @@
1
- #!/usr/bin/env ruby
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'json'
4
4
  require 'rexml/document'
5
- require_relative './multipart.rb'
6
- require 'safrano_core.rb'
7
- require 'odata/entity.rb'
8
- require 'odata/attribute.rb'
9
- require 'odata/navigation_attribute.rb'
10
- require 'odata/collection.rb'
11
- require 'service.rb'
12
- require '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'
13
19
  require 'sequel'
14
- require_relative './sequel_join_by_paths.rb'
15
- require 'rack_app'
16
- require 'odata_rack_builder'
17
-
18
- # picked from activsupport; needed for ruby < 2.5
19
- # Destructively converts all keys using the +block+ operations.
20
- # Same as +transform_keys+ but modifies +self+.
21
- class Hash
22
- def transform_keys!
23
- keys.each do |key|
24
- self[yield(key)] = delete(key)
25
- end
26
- self
27
- end unless method_defined? :transform_keys!
28
-
29
- def symbolize_keys!
30
- transform_keys! { |key| key.to_sym rescue key }
31
- end
32
- end
20
+ require_relative 'safrano/sequel_join_by_paths'
21
+ require_relative 'safrano/rack_app'
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 Contract.valid(result)
110
+ Contract::Valid.new(result)
111
+ end
112
+
113
+ def Contract.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
+ Contract.valid(contracts.map(&:result))
122
+ end
123
+
124
+ # shortcut for Contract.and(*contracts).collect_result!
125
+ def Contract.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
+ Contract.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
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # our main namespace
4
+ module Safrano
5
+ # frozen empty Array/Hash to reduce unncecessary object creation
6
+ EMPTY_ARRAY = [].freeze
7
+ EMPTY_HASH = {}.freeze
8
+ EMPTY_HASH_IN_ARY = [EMPTY_HASH].freeze
9
+ EMPTY_STRING = ''.freeze
10
+ ARY_204_EMPTY_HASH_ARY = [204, EMPTY_HASH, EMPTY_ARRAY].freeze
11
+ SPACE = ' '.freeze
12
+ COMMA = ','.freeze
13
+
14
+ # some prominent constants... probably already defined elsewhere eg in Rack
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
25
+
26
+ CT_JSON = { CONTENT_TYPE => APPJSON_UTF8 }.freeze
27
+ CT_TEXT = { CONTENT_TYPE => TEXTPLAIN_UTF8 }.freeze
28
+ CT_ATOMXML = { CONTENT_TYPE => APPATOMXML_UTF8 }.freeze
29
+ CT_APPXML = { CONTENT_TYPE => APPXML_UTF8 }.freeze
30
+
31
+ module NavigationInfo
32
+ attr_reader :nav_parent
33
+ attr_reader :navattr_reflection
34
+ attr_reader :nav_name
35
+
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
41
+ end
42
+ end
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: ".freeze
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
+ if RUBY_VERSION > '2.3'
68
+ # :nocov:
69
+ mod.deprecate_constant(constant)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -1,21 +1,25 @@
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
7
  require 'webrick/httpstatus'
6
8
 
7
9
  # Simple multipart support for OData $batch purpose
8
10
  module MIME
11
+ CTT_TYPE_LC = 'content-type'
12
+ TEXT_PLAIN = 'text/plain'
13
+
9
14
  # a mime object has a header(with content-type etc) and a content(aka body)
10
15
  class Media
11
16
  # Parser for MIME::Media
12
17
  class Parser
13
18
  HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
14
19
 
15
- CRLF_LINE_RGX = /^#{CRLF}$/.freeze
16
-
17
20
  attr_accessor :lines
18
21
  attr_accessor :target
22
+
19
23
  def initialize
20
24
  @state = :h
21
25
  @lines = []
@@ -54,9 +58,8 @@ module MIME
54
58
  if (hmd = HMD_RGX.match(line))
55
59
  @target_hd[hmd[1].downcase] = hmd[2].strip
56
60
 
57
- # elsif CRLF_LINE_RGX =~ line
58
61
  elsif CRLF == line
59
- @target_ct = @target_hd['content-type'] || 'text/plain'
62
+ @target_ct = @target_hd[CTT_TYPE_LC] || TEXT_PLAIN
60
63
  @state = new_content
61
64
 
62
65
  end
@@ -100,24 +103,22 @@ module MIME
100
103
  end
101
104
 
102
105
  def hook_multipart(content_type, boundary)
103
- @target_hd['content-type'] = content_type
104
- @target_ct = @target_hd['content-type']
106
+ @target_hd[CTT_TYPE_LC] = content_type
107
+ @target_ct = @target_hd[CTT_TYPE_LC]
105
108
  @target = multipart_content(boundary)
106
109
  @target.hd = @target_hd
107
110
  @target.ct = @target_ct
108
111
  @state = :bmp
109
112
  end
110
113
  MPS = 'multipart/'.freeze
111
- MP_RGX1 = %r{^(digest|mixed);\s*boundary=\"(.*)\"}.freeze
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
117
  APP_HTTP = 'application/http'.freeze
115
118
  def new_content
116
119
  @target =
117
- if @target_ct.start_with?(MPS) and
118
- (md = ((MP_RGX1.match(@target_ct[10..-1])) ||
119
- (MP_RGX2.match(@target_ct[10..-1])))
120
- )
120
+ if @target_ct.start_with?(MPS) &&
121
+ (md = (MP_RGX1.match(@target_ct[10..-1]) || MP_RGX2.match(@target_ct[10..-1])))
121
122
  multipart_content(md[2].strip)
122
123
  elsif @target_ct.start_with?(APP_HTTP)
123
124
  MIME::Content::Application::Http.new
@@ -198,7 +199,6 @@ module MIME
198
199
  # Parser for Text::Plain
199
200
  class Parser
200
201
  HMD_RGX = /^([\w-]+)\s*:\s*(.*)/.freeze
201
- CRLF_LINE_RGX = /^#{CRLF}$/.freeze
202
202
  def initialize(target)
203
203
  @state = :h
204
204
  @lines = []
@@ -212,7 +212,6 @@ module MIME
212
212
  def parse_head(line)
213
213
  if (hmd = HMD_RGX.match(line))
214
214
  @target.hd[hmd[1].downcase] = hmd[2].strip
215
- # elsif CRLF_LINE_RGX =~ line
216
215
  elsif CRLF == line
217
216
  @state = :b
218
217
  else
@@ -241,10 +240,11 @@ module MIME
241
240
 
242
241
  def initialize
243
242
  @hd = {}
244
- @content = ''
243
+ # we need it unfrozen --> +
244
+ @content = +''
245
245
  # set default values. Can be overwritten by parser
246
- @hd['content-type'] = 'text/plain'
247
- @ct = 'text/plain'
246
+ @hd[CTT_TYPE_LC] = TEXT_PLAIN
247
+ @ct = TEXT_PLAIN
248
248
  @parser = Parser.new(self)
249
249
  end
250
250
 
@@ -309,8 +309,10 @@ module MIME
309
309
  # to remove it from the end of the last body line
310
310
  return unless @body_lines
311
311
 
312
- # @body_lines.last.sub!(CRLF_ENDING_RGX, '')
313
- @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
314
316
  @parts << @body_lines
315
317
  end
316
318
 
@@ -364,7 +366,7 @@ module MIME
364
366
  end
365
367
 
366
368
  def set_multipart_header
367
- @hd['content-type'] = "#{OData::MP_MIXED}; boundary=#{@boundary}"
369
+ @hd[CTT_TYPE_LC] = "#{Safrano::MP_MIXED}; boundary=#{@boundary}"
368
370
  end
369
371
 
370
372
  def get_http_resp(batcha)
@@ -382,9 +384,7 @@ module MIME
382
384
  # of the changes
383
385
  batcha.db.transaction do
384
386
  begin
385
- @response.content = @content.map { |part|
386
- part.get_response(batcha)
387
- }
387
+ @response.content = @content.map { |part| part.get_response(batcha) }
388
388
  rescue Sequel::Rollback => e
389
389
  # one of the changes of the changeset has failed
390
390
  # --> provide a dummy empty response for the change-parts
@@ -405,18 +405,18 @@ module MIME
405
405
  @response.content = [{ 'odata.error' =>
406
406
  { 'message' =>
407
407
  'Bad Request: Failed changeset ' } }.to_json]
408
- @response.hd = OData::CT_JSON
408
+ @response.hd = Safrano::CT_JSON
409
409
  @response
410
410
  end
411
411
 
412
412
  def unparse(bodyonly = false)
413
- b = ''
413
+ b = +String.new
414
414
  unless bodyonly
415
- # b << OData::CONTENT_TYPE << ': ' << @hd[OData::CTT_TYPE_LC] << CRLF
416
- b << "#{OData::CONTENT_TYPE}: #{@hd[OData::CTT_TYPE_LC]}#{CRLF}"
415
+ b << "#{Safrano::CONTENT_TYPE}: #{@hd[CTT_TYPE_LC]}#{CRLF}"
417
416
  end
418
- b << "#{CRLF}--#{@boundary}#{CRLF}"
419
- b << @content.map(&:unparse).join("#{CRLF}--#{@boundary}#{CRLF}")
417
+
418
+ b << crbdcr = "#{CRLF}--#{@boundary}#{CRLF}"
419
+ b << @content.map(&:unparse).join(crbdcr)
420
420
  b << "#{CRLF}--#{@boundary}--#{CRLF}"
421
421
  b
422
422
  end
@@ -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
@@ -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