chimera_http_client 1.0.0 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a58fb6d4a20a8a2bef43186a46663abc494844ccc30cf6f2e467dd043519861
4
- data.tar.gz: '08c871044cb14bfbe9de820547ec7531a2719302f30d84e8aa9d658ed9ebf197'
3
+ metadata.gz: 01402cd235d413d16acc9adb72267b7a244898b5913d14d07e039ad6b1bd830b
4
+ data.tar.gz: 3909459cb620e544da67ee5864f39ab7ea51a6c33b6dd1b3fcddda5a56ec4dd5
5
5
  SHA512:
6
- metadata.gz: 28b48b1d2fe9b723a9b567b30be1dabdd2bdc76313f9392e4ebe30bf3a8568f729a00f7b0dbdc362099196dd5aea62ddfcceb09db15481b67ada604271a75546
7
- data.tar.gz: c211bc5978a7623c8f9ddaf1a069bd4f7d0aeb8bd680b2c8e61e6f9e4b32b281768da1b3bfa364b6ef69382763ae2f03324b37fa54d5277b1ae68e1468bc5b73
6
+ metadata.gz: 655127ba58b327eb2bc9f2ff6342ad3c93bd84c9cfa08fc262a4628005643e2562ae76ee68378331a92edb11cd9fb91fe0cf942f45d4ce911f176bfae8392c02
7
+ data.tar.gz: b3b282c08dc802b187ee95e974deb996ee833d3350f86ef4d6c89818ef1b182bedee2f043aaad50f392157829559b18b579480d7d50beef154617bb744e6cf9e
data/.travis.yml CHANGED
@@ -4,7 +4,6 @@ rvm:
4
4
  - ruby-head
5
5
  - 2.6
6
6
  - 2.5
7
- - 2.4
8
7
  - jruby
9
8
  - truffleruby
10
9
 
data/README.markdown CHANGED
@@ -14,7 +14,13 @@ The `chimera_http_client` gem is wrapping the **libcurl** wrapper [**Typhoeus**]
14
14
 
15
15
  The only other runtime dependency is Ruby's latest code loader [**zeitwerk**](https://github.com/fxn/zeitwerk) which is also part of Rails 6.
16
16
 
17
- All Ruby versions newer than **2.4** are supported.
17
+ ### Ruby version
18
+
19
+ | Chimera version | Ruby version |
20
+ |:----------------|:-------------|
21
+ | >= 1.1 | >= 2.5 |
22
+ | = 1.0 | >= 2.4 |
23
+ | <= 0.5 | >= 2.1 |
18
24
 
19
25
  ### ENV variables
20
26
 
@@ -81,6 +87,19 @@ The optional parameters are:
81
87
  * `user_agent` - if you would like your calls to identify with a specific user agent
82
88
  * `verbose` - the default is `false`, set it to true while debugging issues
83
89
  * `cache` - an instance of your cache solution, can be overwritten in any request
90
+ * `deserializers` - custom methods to deserialize the response body, below more details
91
+
92
+ ##### Custom deserializers
93
+
94
+ In case the API you are connecting to does not return JSON, you can pass custom deserializers to `Connection.new` or `Queue.new`:
95
+
96
+ deserializers: { error: your_error_deserializer, response: your_response_deserializer }
97
+
98
+ A Deserializer has to be an object on which the method `call` with the parameter `body` can be called:
99
+
100
+ custom_deserializer.call(body)
101
+
102
+ where `body` is the response body (in the default case a JSON object). The class `Deserializer` contains the default objects that are used. They might help you creating your own. Don't forget to make requests with another header than the default `"Content-Type" => "application/json"`, when the API you connect to does not support JSON.
84
103
 
85
104
  ### Request methods
86
105
 
@@ -263,10 +282,6 @@ Usually it does not have to be used directly. It is the class that executes the
263
282
 
264
283
  The `body` which it receives from the `Connection` class has to be in the in the (serialized) form in which the endpoint expects it. Usually this means you have to pass a JSON string to the `body` (it will **not** be serialized automatically).
265
284
 
266
- > Upcoming feature:
267
- >
268
- > It will be expanded by a `.queue` method, that will queue (sic) calls and run them in parallel and not run every call directly, like the `.run` method does.
269
-
270
285
  ## The Response class
271
286
 
272
287
  The `ChimeraHttpClient::Response` objects have the following interface:
@@ -276,9 +291,9 @@ The `ChimeraHttpClient::Response` objects have the following interface:
276
291
  * time (for monitoring)
277
292
  * response (the full response object, including the request)
278
293
  * error? (returns false)
279
- * parsed_body (returns the result of JSON.parse(body))
294
+ * parsed_body (returns the result of `deserializer[:response].call(body)`)
280
295
 
281
- If your API does not use JSON, but a different format e.g. XML, you can either monkey patch a `parsed_xml` method to the Response class, or let your wrapper handle the parsing of `body`.
296
+ If your API does not use JSON, but a different format e.g. XML, you can pass a custom deserializer to the Connection.
282
297
 
283
298
  ## Error classes
284
299
 
@@ -358,7 +373,7 @@ To inspect the requests waiting for execution, call `queue.queued_requests`.
358
373
 
359
374
  Add this line to your application's Gemfile:
360
375
 
361
- gem 'chimera_http_client', '~> 0.5'
376
+ gem 'chimera_http_client', '~> 1.1'
362
377
 
363
378
  And then execute:
364
379
 
data/TODO.markdown CHANGED
@@ -17,9 +17,9 @@ _none known_
17
17
 
18
18
  ### Custom De-serializer
19
19
 
20
- * [ ] allow to pass custom deserializer
21
- * [ ] use custom deserializer in #parsed_body instead of default JSON parsing
22
- * [ ] add example to README
20
+ * [x] ~~allow to pass custom deserializer~~
21
+ * [x] ~~use custom deserializer in #parsed_body instead of default JSON parsing~~
22
+ * [x] ~~add example to README~~
23
23
 
24
24
  ### Queueing / running in parallel
25
25
 
@@ -2,17 +2,18 @@ module ChimeraHttpClient
2
2
  class Base
3
3
  USER_AGENT = "ChimeraHttpClient (by mediafinger)".freeze
4
4
 
5
- def initialize(base_url:, logger: nil, timeout: nil, user_agent: USER_AGENT, verbose: false, cache: nil)
6
- fail(ChimeraHttpClient::ParameterMissingError, "base_url expected, but not given") if base_url.nil?
5
+ def initialize(options = {})
6
+ fail(ChimeraHttpClient::ParameterMissingError, "base_url expected, but not given") if options[:base_url].nil?
7
7
 
8
- @base_url = base_url
9
- @logger = logger
10
- @timeout = timeout
8
+ @base_url = options.fetch(:base_url)
9
+ @deserializer = default_deserializer.merge(options.fetch(:deserializer, {}))
10
+ @logger = options[:logger]
11
+ @timeout = options[:timeout]
11
12
 
12
- Typhoeus::Config.cache = cache
13
+ Typhoeus::Config.cache = options[:cache]
13
14
  Typhoeus::Config.memoize = false # hydra setting, prevents a possible memory leak
14
- Typhoeus::Config.user_agent = user_agent
15
- Typhoeus::Config.verbose = verbose
15
+ Typhoeus::Config.user_agent = options.fetch(:user_agent, USER_AGENT)
16
+ Typhoeus::Config.verbose = options.fetch(:verbose, false)
16
17
  end
17
18
 
18
19
  private
@@ -42,6 +43,10 @@ module ChimeraHttpClient
42
43
  { "Content-Type" => "application/json" }
43
44
  end
44
45
 
46
+ def default_deserializer
47
+ { error: ::ChimeraHttpClient::Deserializer.json_error, response: ::ChimeraHttpClient::Deserializer.json_response }
48
+ end
49
+
45
50
  # Build URL out of @base_url and endpoint given as String or Array, while trimming redundant "/"
46
51
  def url(endpoint)
47
52
  trimmed_endpoint = Array(endpoint).map { |e| trim(e) }
@@ -11,7 +11,12 @@ module ChimeraHttpClient
11
11
  end
12
12
 
13
13
  def request
14
- @request ||= Request.new(logger: @logger)
14
+ options = {
15
+ logger: @logger,
16
+ deserializer: @deserializer,
17
+ }
18
+
19
+ @request ||= Request.new(options)
15
20
  end
16
21
 
17
22
  private
@@ -0,0 +1,31 @@
1
+ # This two JSON deserializers are the default ones
2
+ #
3
+ # To use custom deserializers, pass them as param to Connection.new or Queue.new:
4
+ # `deserializers: { error: your_error_deserializer, response: your_response_deserializer }`
5
+ # (you might be able to use the same for both cases)
6
+ #
7
+ # a Deserializer has to be an object on which the method `call` with the parameter `body` can be called:
8
+ # `custom_deserializer.call(body)`
9
+ # where `body` is the response body (in the default case a JSON object)
10
+ #
11
+ module ChimeraHttpClient
12
+ class Deserializer
13
+ class << self
14
+ def json_error
15
+ proc do |body|
16
+ JSON.parse(body)
17
+ rescue JSON::ParserError
18
+ { "non_json_body" => body }
19
+ end
20
+ end
21
+
22
+ def json_response
23
+ proc do |body|
24
+ JSON.parse(body)
25
+ rescue JSON::ParserError => e
26
+ raise ::ChimeraHttpClient::JsonParserError, "Could not parse body as JSON: #{body}, error: #{e.message}"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -1,14 +1,15 @@
1
1
  module ChimeraHttpClient
2
2
  class Error < StandardError
3
- attr_reader :body, :code, :time, :response
3
+ attr_reader :body, :code, :time, :response, :deserializer
4
4
  alias message body
5
5
 
6
- def initialize(response)
6
+ def initialize(response, options = {})
7
7
  @body = response.body
8
8
  @code = response.code
9
9
  @time = response.options&.fetch(:total_time, nil)
10
10
  @response = response # contains the request
11
- super
11
+
12
+ @deserializer = options[:deserializer][:error]
12
13
  end
13
14
 
14
15
  def error?
@@ -16,9 +17,7 @@ module ChimeraHttpClient
16
17
  end
17
18
 
18
19
  def parsed_body
19
- JSON.parse(body)
20
- rescue JSON::ParserError
21
- { "non_json_body" => body }
20
+ deserializer.call(body)
22
21
  end
23
22
 
24
23
  def to_s
@@ -38,7 +38,12 @@ module ChimeraHttpClient
38
38
  private
39
39
 
40
40
  def create_request(method:, url:, body:, headers:, options:)
41
- Request.new(logger: @logger).create(
41
+ class_options = {
42
+ logger: @logger,
43
+ deserializer: @deserializer,
44
+ }
45
+
46
+ Request.new(class_options).create(
42
47
  method: method,
43
48
  url: url,
44
49
  body: body,
@@ -4,8 +4,9 @@ module ChimeraHttpClient
4
4
 
5
5
  attr_reader :request, :result
6
6
 
7
- def initialize(logger: nil)
8
- @logger = logger
7
+ def initialize(options = {})
8
+ @logger = options[:logger]
9
+ @options = options
9
10
  end
10
11
 
11
12
  def run(url:, method:, body: nil, options: {}, headers: {})
@@ -50,39 +51,39 @@ module ChimeraHttpClient
50
51
  private
51
52
 
52
53
  def on_complete_handler(response)
53
- return Response.new(response) if response.success?
54
+ return Response.new(response, @options) if response.success?
54
55
 
55
56
  exception_for(response)
56
57
  end
57
58
 
58
59
  def exception_for(response)
59
- return TimeoutError.new(response) if response.timed_out?
60
+ return TimeoutError.new(response, @options) if response.timed_out?
60
61
 
61
62
  case response.code.to_i
62
63
  when 301, 302, 303, 307
63
- RedirectionError.new(response)
64
+ RedirectionError.new(response, @options)
64
65
  when 200..399
65
66
  nil
66
67
  when 400
67
- BadRequestError.new(response)
68
+ BadRequestError.new(response, @options)
68
69
  when 401
69
- UnauthorizedError.new(response)
70
+ UnauthorizedError.new(response, @options)
70
71
  when 403
71
- ForbiddenError.new(response)
72
+ ForbiddenError.new(response, @options)
72
73
  when 404
73
- NotFoundError.new(response)
74
+ NotFoundError.new(response, @options)
74
75
  when 405
75
- MethodNotAllowedError.new(response)
76
+ MethodNotAllowedError.new(response, @options)
76
77
  when 409
77
- ResourceConflictError.new(response)
78
+ ResourceConflictError.new(response, @options)
78
79
  when 422
79
- UnprocessableEntityError.new(response)
80
+ UnprocessableEntityError.new(response, @options)
80
81
  when 400..499
81
- ClientError.new(response)
82
+ ClientError.new(response, @options)
82
83
  when 500..599
83
- ServerError.new(response)
84
+ ServerError.new(response, @options)
84
85
  else # response.code.zero?
85
- ConnectionError.new(response)
86
+ ConnectionError.new(response, @options)
86
87
  end
87
88
  end
88
89
  end
@@ -1,18 +1,18 @@
1
1
  module ChimeraHttpClient
2
2
  class Response
3
- attr_reader :body, :code, :time, :response
3
+ attr_reader :body, :code, :time, :response, :deserializer
4
4
 
5
- def initialize(response)
5
+ def initialize(response, options = {})
6
6
  @body = response.body
7
7
  @code = response.code
8
8
  @time = response.total_time
9
9
  @response = response # contains the request
10
+
11
+ @deserializer = options[:deserializer][:response]
10
12
  end
11
13
 
12
14
  def parsed_body
13
- JSON.parse(body)
14
- rescue JSON::ParserError => e
15
- raise ChimeraHttpClient::JsonParserError, "Could not parse body as JSON: #{body}, error: #{e.message}"
15
+ deserializer.call(body)
16
16
  end
17
17
 
18
18
  def error?
@@ -1,3 +1,3 @@
1
1
  module ChimeraHttpClient
2
- VERSION = "1.0.0".freeze
2
+ VERSION = "1.1.0".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chimera_http_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Finger
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-14 00:00:00.000000000 Z
11
+ date: 2019-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus
@@ -171,6 +171,7 @@ files:
171
171
  - lib/chimera_http_client.rb
172
172
  - lib/chimera_http_client/base.rb
173
173
  - lib/chimera_http_client/connection.rb
174
+ - lib/chimera_http_client/deserializer.rb
174
175
  - lib/chimera_http_client/error.rb
175
176
  - lib/chimera_http_client/queue.rb
176
177
  - lib/chimera_http_client/request.rb