rest-man 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/{multi-matrix-test.yml → ci.yml} +10 -1
  3. data/.github/workflows/single-matrix-test.yml +1 -0
  4. data/.gitignore +2 -0
  5. data/.rubocop-disables.yml +4 -29
  6. data/AUTHORS +5 -1
  7. data/CHANGELOG.md +14 -0
  8. data/Gemfile +3 -0
  9. data/README.md +30 -37
  10. data/Rakefile +1 -59
  11. data/_doc/lib/restman/abstract_response/_follow_redirection.rdoc +7 -0
  12. data/_doc/lib/restman/abstract_response/beautify_headers.rdoc +24 -0
  13. data/_doc/lib/restman/abstract_response/cookie_jar.rdoc +4 -0
  14. data/_doc/lib/restman/abstract_response/cookies.rdoc +12 -0
  15. data/_doc/lib/restman/abstract_response/follow_get_redirection.rdoc +2 -0
  16. data/_doc/lib/restman/abstract_response/follow_redirection.rdoc +2 -0
  17. data/_doc/lib/restman/abstract_response/headers.rdoc +2 -0
  18. data/_doc/lib/restman/abstract_response/response_set_vars.rdoc +5 -0
  19. data/_doc/lib/restman/abstract_response/return.rdoc +9 -0
  20. data/_doc/lib/restman/add_before_execution_proc.rdoc +2 -0
  21. data/_doc/lib/restman/create_log.rdoc +2 -0
  22. data/_doc/lib/restman/exception.rdoc +6 -0
  23. data/_doc/lib/restman/exceptions/timeout.rdoc +4 -0
  24. data/_doc/lib/restman/exceptions.rdoc +4 -0
  25. data/_doc/lib/restman/log=.rdoc +3 -0
  26. data/_doc/lib/restman/params_array/new.rdoc +20 -0
  27. data/_doc/lib/restman/params_array/process_pair.rdoc +4 -0
  28. data/_doc/lib/restman/params_array.rdoc +11 -0
  29. data/_doc/lib/restman/platform/jruby?.rdoc +4 -0
  30. data/_doc/lib/restman/platform/mac_mri?.rdoc +5 -0
  31. data/_doc/lib/restman/proxy.rdoc +2 -0
  32. data/_doc/lib/restman/proxy_set?.rdoc +5 -0
  33. data/_doc/lib/restman/raw_response/new.rdoc +6 -0
  34. data/_doc/lib/restman/raw_response.rdoc +10 -0
  35. data/_doc/lib/restman/request/cookie_jar.rdoc +3 -0
  36. data/_doc/lib/restman/request/cookies.rdoc +11 -0
  37. data/_doc/lib/restman/request/default_headers.rdoc +5 -0
  38. data/_doc/lib/restman/request/default_ssl_cert_store.rdoc +8 -0
  39. data/_doc/lib/restman/request/init/cookie_jar.rdoc +55 -0
  40. data/_doc/lib/restman/request/init/http_method.rdoc +15 -0
  41. data/_doc/lib/restman/request/make_cookie_header.rdoc +8 -0
  42. data/_doc/lib/restman/request/make_headers.rdoc +25 -0
  43. data/_doc/lib/restman/request/maybe_convert_extension.rdoc +18 -0
  44. data/_doc/lib/restman/request/process_result.rdoc +4 -0
  45. data/_doc/lib/restman/request/proxy_uri.rdoc +7 -0
  46. data/_doc/lib/restman/request/stringify_headers.rdoc +9 -0
  47. data/_doc/lib/restman/request/use_ssl.rdoc +4 -0
  48. data/_doc/lib/restman/request.rdoc +46 -0
  49. data/_doc/lib/restman/reset_before_execution_procs.rdoc +1 -0
  50. data/_doc/lib/restman/resource/[].rdoc +25 -0
  51. data/_doc/lib/restman/resource.rdoc +33 -0
  52. data/_doc/lib/restman/response/body.rdoc +7 -0
  53. data/_doc/lib/restman/response/create.rdoc +10 -0
  54. data/_doc/lib/restman/response/fix_encoding.rdoc +2 -0
  55. data/_doc/lib/restman/statuses.rdoc +11 -0
  56. data/_doc/lib/restman/utils/cgi_parse_header.rdoc +6 -0
  57. data/_doc/lib/restman/utils/encode_query_string.rdoc +90 -0
  58. data/_doc/lib/restman/utils/escape.rdoc +11 -0
  59. data/_doc/lib/restman/utils/flatten_params.rdoc +16 -0
  60. data/_doc/lib/restman/utils/get_encoding_from_headers.rdoc +24 -0
  61. data/_doc/lib/restman.rdoc +43 -0
  62. data/bin/console +15 -0
  63. data/lib/restman/abstract_response.rb +13 -60
  64. data/lib/restman/exception.rb +43 -0
  65. data/lib/restman/exceptions/exception_with_response.rb +7 -0
  66. data/lib/restman/exceptions/exceptions_map.rb +26 -0
  67. data/lib/restman/exceptions/request_failed.rb +15 -0
  68. data/lib/restman/exceptions/server_broke_connection.rb +13 -0
  69. data/lib/restman/exceptions/timeout.rb +37 -0
  70. data/lib/restman/params_array/process_pair.rb +39 -0
  71. data/lib/restman/params_array.rb +3 -48
  72. data/lib/restman/payload/base.rb +57 -0
  73. data/lib/restman/payload/multipart/write_content_disposition.rb +88 -0
  74. data/lib/restman/payload/multipart.rb +56 -0
  75. data/lib/restman/payload/streamed.rb +22 -0
  76. data/lib/restman/payload/url_encoded.rb +14 -0
  77. data/lib/restman/payload.rb +14 -196
  78. data/lib/restman/platform.rb +2 -18
  79. data/lib/restman/raw_response.rb +2 -14
  80. data/lib/restman/request/default_ssl_cert_store.rb +13 -0
  81. data/lib/restman/request/fetch_body_to_tempfile.rb +58 -0
  82. data/lib/restman/request/init/cookie_jar.rb +65 -0
  83. data/lib/restman/request/init/ssl_opts.rb +70 -0
  84. data/lib/restman/request/init/url/add_query_from_headers.rb +51 -0
  85. data/lib/restman/request/init/url/normalize_url.rb +19 -0
  86. data/lib/restman/request/init/url.rb +40 -0
  87. data/lib/restman/request/init.rb +106 -0
  88. data/lib/restman/request/log_request.rb +46 -0
  89. data/lib/restman/request/make_cookie_header.rb +16 -0
  90. data/lib/restman/request/make_headers.rb +39 -0
  91. data/lib/restman/request/maybe_convert_extension.rb +28 -0
  92. data/lib/restman/request/net_http_object.rb +25 -0
  93. data/lib/restman/request/process_result.rb +36 -0
  94. data/lib/restman/request/proxy_uri.rb +31 -0
  95. data/lib/restman/request/stringify_headers.rb +36 -0
  96. data/lib/restman/request/transmit.rb +152 -0
  97. data/lib/restman/request.rb +60 -745
  98. data/lib/restman/resource.rb +2 -60
  99. data/lib/restman/response.rb +3 -21
  100. data/lib/restman/statuses.rb +75 -0
  101. data/lib/restman/statuses_compatibility.rb +18 -0
  102. data/lib/restman/utils.rb +10 -206
  103. data/lib/restman/version.rb +1 -1
  104. data/lib/restman.rb +24 -62
  105. data/matrixeval.yml +19 -1
  106. data/rest-man.gemspec +4 -10
  107. data/spec/integration/capath_digicert/ce5e74ef.0 +1 -1
  108. data/spec/integration/request_spec.rb +13 -1
  109. data/spec/spec_helper.rb +11 -0
  110. data/spec/unit/abstract_response_spec.rb +14 -0
  111. data/spec/unit/exception_spec.rb +64 -0
  112. data/spec/unit/exceptions/backwards_campatibility_spec.rb +29 -0
  113. data/spec/unit/exceptions/exceptions_map_spec.rb +89 -0
  114. data/spec/unit/exceptions/request_failed_spec.rb +51 -0
  115. data/spec/unit/exceptions/server_broke_connection_spec.rb +8 -0
  116. data/spec/unit/exceptions/timeout_spec.rb +59 -0
  117. data/spec/unit/params_array/process_pair_spec.rb +59 -0
  118. data/spec/unit/params_array_spec.rb +15 -10
  119. data/spec/unit/payload/multipart_spec.rb +116 -0
  120. data/spec/unit/payload/streamed_spec.rb +48 -0
  121. data/spec/unit/payload/url_encoded_spec.rb +65 -0
  122. data/spec/unit/payload_spec.rb +0 -208
  123. data/spec/unit/request/init/url/add_query_from_headers_spec.rb +40 -0
  124. data/spec/unit/request/init/url/normalize_url_spec.rb +25 -0
  125. data/spec/unit/request/init_spec.rb +83 -0
  126. data/spec/unit/request_spec.rb +143 -151
  127. data/spec/unit/utils_spec.rb +96 -104
  128. metadata +132 -16
  129. data/lib/restman/exceptions.rb +0 -238
  130. data/lib/restman/windows/root_certs.rb +0 -105
  131. data/lib/restman/windows.rb +0 -8
  132. data/spec/unit/exceptions_spec.rb +0 -108
  133. data/spec/unit/windows/root_certs_spec.rb +0 -22
@@ -1,13 +1,4 @@
1
- require 'tempfile'
2
- require 'securerandom'
3
- require 'stringio'
4
-
5
- begin
6
- # Use mime/types/columnar if available, for reduced memory usage
7
- require 'mime/types/columnar'
8
- rescue LoadError
9
- require 'mime/types'
10
- end
1
+ require 'mime/types/columnar'
11
2
 
12
3
  module RestMan
13
4
  module Payload
@@ -15,220 +6,47 @@ module RestMan
15
6
 
16
7
  def generate(params)
17
8
  if params.is_a?(RestMan::Payload::Base)
18
- # pass through Payload objects unchanged
19
9
  params
10
+
20
11
  elsif params.is_a?(String)
21
12
  Base.new(params)
13
+
22
14
  elsif params.is_a?(Hash)
23
15
  if params.delete(:multipart) == true || has_file?(params)
24
16
  Multipart.new(params)
17
+
25
18
  else
26
19
  UrlEncoded.new(params)
27
20
  end
21
+
28
22
  elsif params.is_a?(ParamsArray)
29
- if _has_file?(params)
23
+ if has_file?(params)
30
24
  Multipart.new(params)
25
+
31
26
  else
32
27
  UrlEncoded.new(params)
28
+
33
29
  end
34
30
  elsif params.respond_to?(:read)
35
31
  Streamed.new(params)
32
+
36
33
  else
37
34
  nil
38
35
  end
39
36
  end
40
37
 
41
- def has_file?(params)
42
- unless params.is_a?(Hash)
43
- raise ArgumentError.new("Must pass Hash, not #{params.inspect}")
44
- end
45
- _has_file?(params)
46
- end
47
-
48
- def _has_file?(obj)
38
+ def has_file?(obj)
49
39
  case obj
50
40
  when Hash, ParamsArray
51
- obj.any? {|_, v| _has_file?(v) }
41
+ obj.any? {|_, v| has_file?(v) }
42
+
52
43
  when Array
53
- obj.any? {|v| _has_file?(v) }
44
+ obj.any? {|v| has_file?(v) }
45
+
54
46
  else
55
47
  obj.respond_to?(:path) && obj.respond_to?(:read)
56
48
  end
57
49
  end
58
50
 
59
- class Base
60
- def initialize(params)
61
- build_stream(params)
62
- end
63
-
64
- def build_stream(params)
65
- @stream = StringIO.new(params)
66
- @stream.seek(0)
67
- end
68
-
69
- def read(*args)
70
- @stream.read(*args)
71
- end
72
-
73
- def to_s
74
- result = read
75
- @stream.seek(0)
76
- result
77
- end
78
-
79
- def headers
80
- {'Content-Length' => size.to_s}
81
- end
82
-
83
- def size
84
- @stream.size
85
- end
86
-
87
- alias :length :size
88
-
89
- def close
90
- @stream.close unless @stream.closed?
91
- end
92
-
93
- def closed?
94
- @stream.closed?
95
- end
96
-
97
- def to_s_inspect
98
- to_s.inspect
99
- end
100
-
101
- def short_inspect
102
- if size && size > 500
103
- "#{size} byte(s) length"
104
- else
105
- to_s_inspect
106
- end
107
- end
108
-
109
- end
110
-
111
- class Streamed < Base
112
- def build_stream(params = nil)
113
- @stream = params
114
- end
115
-
116
- def size
117
- if @stream.respond_to?(:size)
118
- @stream.size
119
- elsif @stream.is_a?(IO)
120
- @stream.stat.size
121
- end
122
- end
123
-
124
- # TODO (breaks compatibility): ought to use mime_for() to autodetect the
125
- # Content-Type for stream objects that have a filename.
126
-
127
- alias :length :size
128
- end
129
-
130
- class UrlEncoded < Base
131
- def build_stream(params = nil)
132
- @stream = StringIO.new(Utils.encode_query_string(params))
133
- @stream.seek(0)
134
- end
135
-
136
- def headers
137
- super.merge({'Content-Type' => 'application/x-www-form-urlencoded'})
138
- end
139
- end
140
-
141
- class Multipart < Base
142
- EOL = "\r\n"
143
-
144
- def build_stream(params)
145
- b = '--' + boundary
146
-
147
- @stream = Tempfile.new('rest-man.multipart.')
148
- @stream.binmode
149
- @stream.write(b + EOL)
150
-
151
- case params
152
- when Hash, ParamsArray
153
- x = Utils.flatten_params(params)
154
- else
155
- x = params
156
- end
157
-
158
- last_index = x.length - 1
159
- x.each_with_index do |a, index|
160
- k, v = * a
161
- if v.respond_to?(:read) && v.respond_to?(:path)
162
- create_file_field(@stream, k, v)
163
- else
164
- create_regular_field(@stream, k, v)
165
- end
166
- @stream.write(EOL + b)
167
- @stream.write(EOL) unless last_index == index
168
- end
169
- @stream.write('--')
170
- @stream.write(EOL)
171
- @stream.seek(0)
172
- end
173
-
174
- def create_regular_field(s, k, v)
175
- s.write("Content-Disposition: form-data; name=\"#{k}\"")
176
- s.write(EOL)
177
- s.write(EOL)
178
- s.write(v)
179
- end
180
-
181
- def create_file_field(s, k, v)
182
- begin
183
- s.write("Content-Disposition: form-data;")
184
- s.write(" name=\"#{k}\";") unless (k.nil? || k=='')
185
- s.write(" filename=\"#{v.respond_to?(:original_filename) ? v.original_filename : File.basename(v.path)}\"#{EOL}")
186
- s.write("Content-Type: #{v.respond_to?(:content_type) ? v.content_type : mime_for(v.path)}#{EOL}")
187
- s.write(EOL)
188
- while (data = v.read(8124))
189
- s.write(data)
190
- end
191
- ensure
192
- v.close if v.respond_to?(:close)
193
- end
194
- end
195
-
196
- def mime_for(path)
197
- mime = MIME::Types.type_for path
198
- mime.empty? ? 'text/plain' : mime[0].content_type
199
- end
200
-
201
- def boundary
202
- return @boundary if defined?(@boundary) && @boundary
203
-
204
- # Use the same algorithm used by WebKit: generate 16 random
205
- # alphanumeric characters, replacing `+` `/` with `A` `B` (included in
206
- # the list twice) to round out the set of 64.
207
- s = SecureRandom.base64(12)
208
- s.tr!('+/', 'AB')
209
-
210
- @boundary = '----RubyFormBoundary' + s
211
- end
212
-
213
- # for Multipart do not escape the keys
214
- #
215
- # Ostensibly multipart keys MAY be percent encoded per RFC 7578, but in
216
- # practice no major browser that I'm aware of uses percent encoding.
217
- #
218
- # Further discussion of multipart encoding:
219
- # https://github.com/rest-man/rest-man/pull/403#issuecomment-156976930
220
- #
221
- def handle_key key
222
- key
223
- end
224
-
225
- def headers
226
- super.merge({'Content-Type' => %Q{multipart/form-data; boundary=#{boundary}}})
227
- end
228
-
229
- def close
230
- @stream.close!
231
- end
232
- end
233
51
  end
234
52
  end
@@ -2,28 +2,12 @@ require 'rbconfig'
2
2
 
3
3
  module RestMan
4
4
  module Platform
5
- # Return true if we are running on a darwin-based Ruby platform. This will
6
- # be false for jruby even on OS X.
7
- #
8
- # @return [Boolean]
5
+ # :include: _doc/lib/restman/platform/mac_mri?.rdoc
9
6
  def self.mac_mri?
10
7
  RUBY_PLATFORM.include?('darwin')
11
8
  end
12
9
 
13
- # Return true if we are running on Windows.
14
- #
15
- # @return [Boolean]
16
- #
17
- def self.windows?
18
- # Ruby only sets File::ALT_SEPARATOR on Windows, and the Ruby standard
19
- # library uses that to test what platform it's on.
20
- !!File::ALT_SEPARATOR
21
- end
22
-
23
- # Return true if we are running on jruby.
24
- #
25
- # @return [Boolean]
26
- #
10
+ # :include: _doc/lib/restman/platform/jruby?.rdoc
27
11
  def self.jruby?
28
12
  # defined on mri >= 1.9
29
13
  RUBY_ENGINE == 'jruby'
@@ -1,14 +1,5 @@
1
1
  module RestMan
2
- # The response from RestMan on a raw request looks like a string, but is
3
- # actually one of these. 99% of the time you're making a rest call all you
4
- # care about is the body, but on the occasion you want to fetch the
5
- # headers you can:
6
- #
7
- # RestMan.get('http://example.com').headers[:content_type]
8
- #
9
- # In addition, if you do not use the response as a string, you can access
10
- # a Tempfile object at res.file, which contains the path to the raw
11
- # downloaded request body.
2
+ # :include: _doc/lib/restman/raw_response.rdoc
12
3
  class RawResponse
13
4
 
14
5
  include AbstractResponse
@@ -19,10 +10,7 @@ module RestMan
19
10
  "<RestMan::RawResponse @code=#{code.inspect}, @file=#{file.inspect}, @request=#{request.inspect}>"
20
11
  end
21
12
 
22
- # @param [Tempfile] tempfile The temporary file containing the body
23
- # @param [Net::HTTPResponse] net_http_res
24
- # @param [RestMan::Request] request
25
- # @param [Time] start_time
13
+ # :include: _doc/lib/restman/raw_response/new.rdoc
26
14
  def initialize(tempfile, net_http_res, request, start_time=nil)
27
15
  @file = tempfile
28
16
 
@@ -0,0 +1,13 @@
1
+ module RestMan
2
+ class Request
3
+ class DefaultSSLCertStore < ActiveMethod::Base
4
+
5
+ def call
6
+ cert_store = OpenSSL::X509::Store.new
7
+ cert_store.set_default_paths
8
+ cert_store
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,58 @@
1
+ module RestMan
2
+ class Request
3
+ class FetchBodyToTempfile < ActiveMethod::Base
4
+
5
+ argument :http_response
6
+
7
+ def call
8
+ # Taken from Chef, which as in turn...
9
+ # Stolen from http://www.ruby-forum.com/topic/166423
10
+ # Kudos to _why!
11
+ tf = Tempfile.new('rest-man.')
12
+ tf.binmode
13
+
14
+ size = 0
15
+ total = http_response['Content-Length'].to_i
16
+ stream_log_bucket = nil
17
+
18
+ http_response.read_body do |chunk|
19
+ tf.write chunk
20
+ size += chunk.size
21
+ if log
22
+ if total == 0
23
+ log << "streaming %s %s (%d of unknown) [0 Content-Length]\n" % [method.upcase, url, size]
24
+ else
25
+ percent = (size * 100) / total
26
+ current_log_bucket, _ = percent.divmod(stream_log_percent)
27
+ if current_log_bucket != stream_log_bucket
28
+ stream_log_bucket = current_log_bucket
29
+ log << "streaming %s %s %d%% done (%d of %d)\n" % [method.upcase, url, (size * 100) / total, size, total]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ tf.close
35
+ tf
36
+ end
37
+
38
+ private
39
+
40
+ def log
41
+ request.log
42
+ end
43
+
44
+ def method
45
+ request.method
46
+ end
47
+
48
+ def url
49
+ request.url
50
+ end
51
+
52
+ def stream_log_percent
53
+ request.stream_log_percent
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,65 @@
1
+ module RestMan
2
+ class Request
3
+ module Init
4
+ # :include: _doc/lib/restman/request/init/cookie_jar.rdoc
5
+ class CookieJar < ActiveMethod::Base
6
+
7
+ argument :uri
8
+ argument :headers
9
+ argument :args
10
+
11
+ def call
12
+ duplicated_cookies_check
13
+ return cookies.dup if cookies.is_a?(HTTP::CookieJar)
14
+
15
+ cookies.each do |key, value|
16
+ cookie_jar.add cookie(key, value)
17
+ end
18
+
19
+ cookie_jar
20
+ end
21
+
22
+ private
23
+
24
+ # Avoid ambiguity in whether options from headers or options from
25
+ # Request#initialize should take precedence by raising ArgumentError when
26
+ # both are present. Prior versions of rest-man claimed to give
27
+ # precedence to init options, but actually gave precedence to headers.
28
+ # Avoid that mess by erroring out instead.
29
+ def duplicated_cookies_check
30
+ if headers[:cookies] && args[:cookies]
31
+ raise ArgumentError.new(
32
+ "Cannot pass :cookies in Request.new() and in headers hash at the same time")
33
+ end
34
+ end
35
+
36
+ def cookies
37
+ @cookies ||= headers.delete(:cookies) || args[:cookies] || []
38
+ end
39
+
40
+ # Support for Array<HTTP::Cookie> mode:
41
+ # If key is a cookie object, add it to the jar directly and assert that
42
+ # there is no separate val.
43
+ def cookie(key, value)
44
+ if key.is_a?(HTTP::Cookie)
45
+ raise ArgumentError.new("extra cookie val: #{value.inspect}") if value
46
+
47
+ key # cookie
48
+ else
49
+ HTTP::Cookie.new(
50
+ key.to_s, value,
51
+ domain: uri.hostname.downcase,
52
+ path: '/',
53
+ for_domain: true
54
+ )
55
+ end
56
+ end
57
+
58
+ def cookie_jar
59
+ @cookie_jar ||= HTTP::CookieJar.new
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,70 @@
1
+ module RestMan
2
+ class Request
3
+ module Init
4
+ class SSLOpts < ActiveMethod::Base
5
+
6
+ argument :args
7
+ argument :uri
8
+
9
+ def call
10
+ ssl_opts[:verify_ssl] = verify_ssl
11
+
12
+ Request::SSLOptionList.each do |key|
13
+ ssl_key = ('ssl_' + key).to_sym
14
+
15
+ if args.key?(ssl_key)
16
+ ssl_opts[key.to_sym] = args.fetch(ssl_key)
17
+ end
18
+ end
19
+
20
+ set_cert_store
21
+
22
+ ssl_opts
23
+ end
24
+
25
+ private
26
+
27
+ def verify_ssl
28
+ return default_ssl_verify unless args.key?(:verify_ssl)
29
+ return ssl_verify_none unless args[:verify_ssl]
30
+
31
+ if args[:verify_ssl] == true
32
+ default_ssl_verify
33
+ else
34
+ args[:verify_ssl]
35
+ end
36
+ end
37
+
38
+ def default_ssl_verify
39
+ ssl_verify_peer
40
+ end
41
+
42
+ def ssl_verify_peer
43
+ OpenSSL::SSL::VERIFY_PEER
44
+ end
45
+
46
+ def ssl_verify_none
47
+ OpenSSL::SSL::VERIFY_NONE
48
+ end
49
+
50
+ def set_cert_store
51
+ return unless use_ssl?
52
+
53
+ # If there's no CA file, CA path, or cert store provided, use default
54
+ if !ssl_opts[:ca_file] && !ssl_opts[:ca_path] && !ssl_opts.include?(:cert_store)
55
+ ssl_opts[:cert_store] = Request.default_ssl_cert_store
56
+ end
57
+ end
58
+
59
+ def use_ssl?
60
+ uri.is_a?(URI::HTTPS)
61
+ end
62
+
63
+ def ssl_opts
64
+ @ssl_opts ||= {}
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,51 @@
1
+ module RestMan
2
+ class Request
3
+ module Init
4
+ class Url
5
+ class AddQueryFromHeaders < ActiveMethod::Base
6
+
7
+ argument :url
8
+ argument :headers
9
+
10
+ def call
11
+ url_params = params_from_headers
12
+
13
+ if url_params && !url_params.empty?
14
+ query_string = RestMan::Utils.encode_query_string(url_params)
15
+
16
+ if url.include?('?')
17
+ url + "&#{query_string}"
18
+ else
19
+ url + "?#{query_string}"
20
+ end
21
+ else
22
+ url
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def params_from_headers
29
+ params = nil
30
+
31
+ # find and extract/remove "params" key if the value is a Hash/ParamsArray
32
+ headers.delete_if do |key, value|
33
+ if key.to_s.downcase == 'params' && (value.is_a?(Hash) || value.is_a?(RestMan::ParamsArray))
34
+ if params
35
+ raise ArgumentError.new("Multiple 'params' options passed")
36
+ end
37
+ params = value
38
+ true
39
+ else
40
+ false
41
+ end
42
+ end
43
+
44
+ params
45
+ end
46
+
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,19 @@
1
+ module RestMan
2
+ class Request
3
+ module Init
4
+ class Url
5
+ class NormalizeUrl < ActiveMethod::Base
6
+ argument :url
7
+
8
+ def call
9
+ if url.match(%r{\A[a-z][a-z0-9+.-]*://}i)
10
+ url
11
+ else
12
+ "http://#{url}"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,40 @@
1
+ module RestMan
2
+ class Request
3
+ module Init
4
+ class Url < ActiveMethod::Base
5
+
6
+ autoload :AddQueryFromHeaders, "restman/request/init/url/add_query_from_headers"
7
+ autoload :NormalizeUrl, "restman/request/init/url/normalize_url"
8
+
9
+ argument :args
10
+ argument :headers
11
+
12
+ attr_accessor :url
13
+
14
+ def call
15
+ raise ArgumentError, "must pass :url" unless url
16
+
17
+ add_http_scheme
18
+ add_query_from_headers
19
+
20
+ url
21
+ end
22
+
23
+ private
24
+
25
+ def add_http_scheme
26
+ self.url = NormalizeUrl.call(url)
27
+ end
28
+
29
+ def add_query_from_headers
30
+ self.url = AddQueryFromHeaders.call(url, headers)
31
+ end
32
+
33
+ def url
34
+ @url ||= args[:url].dup
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end