eezee 1.0.7

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: '091f04a358ff0034437fb934f267c82e74ee10109c3f9f9a89625e3cbcb464bf'
4
+ data.tar.gz: 413ad4f550a63cfebd4e688181369264a423f26bfa8fc2b3e1496b3c5552978c
5
+ SHA512:
6
+ metadata.gz: cb1b44f8219ba5bca6b40703c066f3c3da4ff681ac5cd005599866389d9a30bccdc7b52a2e708da8fd99d8adb0846280f3d71c07a30387671a9db2b98738e331
7
+ data.tar.gz: 89eef48f82b9a9cd284e2332cdfc2de54a187624c961ae7f8600bbed90e6b68f8f439f6930856e30d19298ed575969531987cb70fa1df513a65cd4e335e8c111
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2019 Linqueta
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,282 @@
1
+ # [Eezee][gem_page]
2
+
3
+ [![Gem Version][gem_version_image]][gem_version_page]
4
+ [![Build Status][travis_status_image]][travis_page]
5
+ [![Maintainability][code_climate_maintainability_image]][code_climate_maintainability_page]
6
+ [![Test Coverage][code_climate_test_coverage_image]][code_climate_test_coverage_page]
7
+
8
+ The easiest HTTP client for Ruby
9
+
10
+ With Eezee you can do these things:
11
+ * Define external services in an initializer file and use them through a simple method
12
+ * Take HTTP requests just extending a module and call the HTTP request method in your class/module
13
+ * Set before and after hooks to handle your requests, responses, and errors
14
+ * Handle all requests, responses, and errors in the same way
15
+ * Log the request, response, and errors
16
+ * Set general request timeout and open connection timeout
17
+ * Raise errors in failed requests
18
+ * Spend more time coding your API integrations instead defining and testing HTTP settings and clients
19
+
20
+ This gem is supported for Ruby 2.6+ applications
21
+
22
+ ## Table of Contents
23
+ - [Getting started](#getting-started)
24
+ - [Installation](#installation)
25
+ - [Supported HTTP Methods](#supported-http-methods)
26
+ - [How to take a request](#how-to-take-a-request)
27
+ - [Request options](#request-options)
28
+ - [Available Request options](#available-request-options)
29
+ - [Services](#services)
30
+ - [How a service works](#how-a-service-works)
31
+ - [Request](#response)
32
+ - [Response](#response)
33
+ - [Errors](#errors)
34
+ - [Examples](#examples)
35
+ - [Complete integrations](#complete-integrations)
36
+ - [Hooks](#hooks)
37
+ - [Timeout](#timeout)
38
+ - [Logging](#logging)
39
+ - [Why use Eezee instead Faraday](#why-use-eezee-instead-faraday)
40
+ - [Contributing](#contributing)
41
+ - [License](#license)
42
+
43
+ ## Getting started
44
+
45
+ ### Installation
46
+
47
+ Add this line to your application's Gemfile:
48
+
49
+ ```ruby
50
+ gem 'eezee'
51
+ ```
52
+
53
+ If you're on Rails you can run this line below to create the initializer:
54
+
55
+ ```shell
56
+ rails generator eezee:install
57
+ ```
58
+
59
+ ### Supported HTTP Methods
60
+
61
+ Eezee supports these HTTP methods:
62
+
63
+ - GET
64
+ - POST
65
+ - PATCH
66
+ - PUT
67
+ - DELETE
68
+
69
+ And here are the corresponding Eezee's HTTP methods:
70
+
71
+ - get(request_options)
72
+ - post(request_options)
73
+ - patch(request_options)
74
+ - put(request_options)
75
+ - delete(request_options)
76
+
77
+ OBS: The param `request_options` is optional.
78
+
79
+ ### How to take a request
80
+
81
+ To take a request using any of these methods you just have to call the HTTP method, and if you want, you can pass the options.
82
+
83
+ ```ruby
84
+ module RickMorty::Resource::Character
85
+ extend Eezee::Client
86
+
87
+ def self.index
88
+ get(url: 'rickandmortyapi.com/api', protocol: :https, path: 'character')
89
+ end
90
+
91
+ def self.find(id)
92
+ get(
93
+ url: 'rickandmortyapi.com/api',
94
+ protocol: :https,
95
+ path: 'character/:character_id',
96
+ params: { character_id: id }
97
+ )
98
+ end
99
+
100
+ def self.create(payload)
101
+ post(
102
+ url: 'rickandmortyapi.com/api',
103
+ protocol: :https,
104
+ path: 'character',
105
+ payload: payload
106
+ )
107
+ end
108
+
109
+ def self.update(id, payload)
110
+ post(
111
+ url: 'rickandmortyapi.com/api',
112
+ protocol: :https,
113
+ path: 'character/:character_id',
114
+ params: { character_id: id }
115
+ payload: payload
116
+ )
117
+ end
118
+
119
+ def self.destroy(id)
120
+ delete(
121
+ url: 'rickandmortyapi.com/api',
122
+ protocol: :https,
123
+ path: 'character/:character_id',
124
+ params: { character_id: id }
125
+ )
126
+ end
127
+ ```
128
+
129
+ ### Request options
130
+
131
+ Request options are the request settings. They can be used to define services, request options and as a param when you take the HTTP request. For example:
132
+
133
+ ```ruby
134
+ module RickMorty::Resource::Character
135
+ extend Eezee::Client
136
+
137
+ eezee_request_options protocol: :https,
138
+ url: 'rickandmortyapi.com/api'
139
+ path: 'character/:character_id'
140
+
141
+ def self.index
142
+ get
143
+ end
144
+
145
+ def self.find(id)
146
+ get(params: { character_id: id })
147
+ end
148
+
149
+ def self.update(id, payload)
150
+ put(
151
+ params: { character_id: id },
152
+ payload: payload,
153
+ after: ->(_req, res) { do_something!(res) }
154
+ )
155
+ end
156
+ end
157
+ ```
158
+
159
+ The method `eezee_request_options` can receive all of [Available Request options](#available-request-options).
160
+
161
+ When the HTTP methods were called, Eezee has created a Request setting with the options defined in the module and merge with the options passed as a param in the HTTP methods.
162
+
163
+ #### Available Request options
164
+
165
+ Here are the list of available options and about them:
166
+
167
+ | Option | Required | Default | What is it? | Example |
168
+ |--------|----------|---------|-------------|---------|
169
+ | `url` | Yes | `nil` | The request's url | `"rickandmortyapi.com/api"` |
170
+ | `protocol` | No | `nil` | The request's protocol | `:https` |
171
+ | `path` | No | `nil` | The resource's path | `"characters\:characted_id\addresses"` |
172
+ | `headers` | No | `{}` | The request's headers. | `{ Token: "Bearer 1a8then..." }` |
173
+ | `params` | No | `{}` | The query params. If the url or path has a nested param like `:character_id` and you pass it in the hash, this value will be replaced. In the opposite, the value will be concatenated in the url like `...?character_id=10&...`| `{ character_id: 10 }`|
174
+ | `payload` | No | `{}` | The request's payload | `{ name: "Linqueta", gender: "male" }` |
175
+ | `before` | No | `nil` | It's the before hook. You can pass Proc or Lambda to handle the request settings. See more in [Hooks](#hooks). | `->(req) { merge_new_headers! }` |
176
+ | `after` | No | `nil` | It's the after hook. You can pass Proc or Lambda to handle the request settings, response or error after the request. If it returns a valid value (different of false or `nil`) and the request raises an error, the error won't be raised to your application. See more in [Hooks](#hooks). | `->(req, res, err) { do_something! }` |
177
+ | `timeout` | No | `nil` | If it exceeds this timeout to make whole request Eezee will raise the error `Eezee::TimeoutError` | `5` |
178
+ | `open_timeout` | No | `nil` | If it exceed this timeout to open a connection Eezee will raise the error `Eezee::TimeoutError` | `2` |
179
+ | `raise_error` | No | `false` | If you want that Eezee raises an error if the request has wasn't successful. See more in [Errors](#errors) | `true` |
180
+ | `logger` | No | `false` | If you want to log the request, response, and error | `true` |
181
+
182
+ ### Services
183
+
184
+ It's common your app has integrations with many external services and this gem has a feature to organize in one file the settings of these external service integrations and it provides an easy way to get these settings.
185
+
186
+ For example, I will integrate with [Rick and Morty Api](https://rickandmortyapi.com/api/) using a service:
187
+
188
+ - I'll declare it in an initializer file:
189
+
190
+ ```ruby
191
+ Eezee.configure do |config|
192
+ config.add_service :rick_morty_api,
193
+ protocol: :https,
194
+ url: 'rickandmortyapi.com/api'
195
+ end
196
+ ```
197
+
198
+ - In my resource, I'll catch the service and pass other settings:
199
+
200
+ ```ruby
201
+ module RickMorty::Resource::Character
202
+ extend Eezee::Client
203
+
204
+ eezee_service :rick_morty_api
205
+ eezee_request_options path: 'character/:character_id'
206
+
207
+ def self.index
208
+ get
209
+ end
210
+
211
+ def self.find(id)
212
+ get(params: { character_id: id })
213
+ end
214
+ end
215
+ ```
216
+
217
+ #### How a service works
218
+
219
+ When Ruby loads a class/module and it has the method `eezee_service` declared with a service's name, by default, Eezee will try load the service and create a request base for the class/module, so, when the class/module takes a request, Eezee will create the final request instance based on request base to take the HTTP request. You can turn it lazy setting the option `lazy: true`, therefore, the final request will be created just in the HTTP request. If the service doesn't exist when Eezee search about it, it will be raised the error `Eezee::Client::UnknownService`.
220
+
221
+ About the method `add_service`, you can pass all of [Available Request options](#available-request-options). The meaning of this part is to organize in one way the external services integrations.
222
+
223
+ ### Request
224
+
225
+ Coming soon...
226
+
227
+ ### Response
228
+
229
+ Coming soon...
230
+
231
+ ### Errors
232
+
233
+ Coming soon...
234
+
235
+ ### Examples
236
+
237
+ Here are some examples:
238
+
239
+ #### Complete integrations
240
+
241
+ Coming soon...
242
+
243
+ #### Hooks
244
+
245
+ Coming soon...
246
+
247
+ #### Timeout
248
+
249
+ Coming soon...
250
+
251
+ #### Logging
252
+
253
+ Coming soon...
254
+
255
+ ## Why use Eezee instead Faraday
256
+
257
+ Coming soon...
258
+
259
+ ## Contributing
260
+
261
+ 1. Fork it
262
+ 2. Create your feature branch (git checkout -b my-new-feature)
263
+ 3. Commit your changes (git commit -am 'Add some feature')
264
+ 4. Push to the branch (git push origin my-new-feature)
265
+ 5. Create new Pull Request
266
+
267
+ ## License
268
+
269
+ The gem is available as open source under the terms of the [MIT License][mit_license_page].
270
+
271
+ [gem_page]: https://github.com/linqueta/eezee
272
+ [code_of_conduct_page]: https://github.com/linqueta/eezee/blob/master/CODE_OF_CONDUCT.md
273
+ [mit_license_page]: https://opensource.org/licenses/MIT
274
+ [contributor_convenant_page]: http://contributor-covenant.org
275
+ [travis_status_image]: https://travis-ci.org/linqueta/eezee.svg?branch=master
276
+ [travis_page]: https://travis-ci.org/linqueta/eezee
277
+ [code_climate_maintainability_image]: https://api.codeclimate.com/v1/badges/b3ae18295c290b6a92a9/maintainability
278
+ [code_climate_maintainability_page]: https://codeclimate.com/github/linqueta/eezee/maintainability
279
+ [code_climate_test_coverage_image]: https://api.codeclimate.com/v1/badges/b3ae18295c290b6a92a9/test_coverage
280
+ [code_climate_test_coverage_page]: https://codeclimate.com/github/linqueta/eezee/test_coverage
281
+ [gem_version_image]: https://badge.fury.io/rb/eezee.svg
282
+ [gem_version_page]: https://rubygems.org/gems/eezee
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eezee
4
+ module Client
5
+ class UnknownService < StandardError; end
6
+
7
+ module Builder
8
+ def self.extended(base)
9
+ base.send(:build_eezee_options)
10
+ base.send(:build_eezee_request_attributes)
11
+ base.eezee_options
12
+ end
13
+
14
+ def eezee_request_options(options)
15
+ build_eezee_request_options(options)
16
+ end
17
+
18
+ def eezee_service(name, options = {})
19
+ build_eezee_service(name, { lazy: false }.merge(options || {}))
20
+ end
21
+
22
+ private
23
+
24
+ def build_eezee_options
25
+ define_singleton_method(:eezee_options) { @eezee_options ||= {} }
26
+ end
27
+
28
+ def build_eezee_request_options(options = {})
29
+ eezee_options[:request_options] ||= options
30
+ build_eezee_request
31
+ end
32
+
33
+ def build_eezee_service(name = nil, options = {})
34
+ eezee_options[:service_name] ||= name
35
+ eezee_options[:service_options] ||= options
36
+ build_eezee_request
37
+ end
38
+
39
+ def build_eezee_request(force = false)
40
+ Eezee.configuration
41
+ .find_service(eezee_options[:service_name])
42
+ .then { |service| handle_unknown_service!(service, force) }
43
+ .then { |service| create_request(service, force) }
44
+ .then { |request| eezee_options[:request] = request }
45
+ .then { build_eezee_request_attributes }
46
+ end
47
+
48
+ def build_eezee_request_attributes
49
+ define_singleton_method(:eezee_request_attributes) { eezee_options&.[](:request)&.attributes || {} }
50
+ end
51
+
52
+ def handle_unknown_service!(service, force)
53
+ return unless take_request?(force)
54
+ raise UnknownService if !service && eezee_options[:service_name]
55
+
56
+ service
57
+ end
58
+
59
+ def take_request?(force)
60
+ !eezee_options.dig(:service_options, :lazy) || force
61
+ end
62
+
63
+ def create_request(service, force)
64
+ return unless take_request?(force)
65
+
66
+ Eezee.configuration.request_by(service, eezee_options[:request_options])
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'json'
5
+
6
+ module Eezee
7
+ module Client
8
+ module Requester
9
+ METHODS = %i[get post patch put delete].freeze
10
+
11
+ def self.extended(base)
12
+ METHODS.each do |method|
13
+ base.send(
14
+ :define_singleton_method,
15
+ method,
16
+ ->(options = {}) { eezee_client_request(options, method) }
17
+ )
18
+ end
19
+ end
20
+
21
+ def eezee_client_request(options, method)
22
+ request = build_final_request(options, method)
23
+
24
+ build_faraday_client(request)
25
+ .then { |client| build_faraday_request(request, client, method) }
26
+ .then { |response| Eezee::Response.new(response) }
27
+ .tap { |response| response.log if request.logger }
28
+ .tap { |response| request.after!(request, response, nil) }
29
+ rescue Faraday::Error => e
30
+ response = Eezee::Response.new(e)
31
+ error = Eezee::RequestErrorFactory.build(response)
32
+ error.log if request.logger
33
+ return response if rescue_faraday_error?(request, response, error)
34
+
35
+ raise error
36
+ end
37
+
38
+ def build_final_request(options, method)
39
+ build_eezee_request_lazy
40
+
41
+ Eezee.configuration
42
+ .request_by(eezee_options[:request], options)
43
+ .tap do |request|
44
+ request.before!(request)
45
+ request.method = method
46
+ request.log if request.logger
47
+ end
48
+ end
49
+
50
+ def build_eezee_request_lazy
51
+ return unless eezee_options.dig(:service_options, :lazy)
52
+
53
+ build_eezee_request(true)
54
+ end
55
+
56
+ def rescue_faraday_error?(req, res, err)
57
+ req.after!(req, res, err) || (err.is_a?(Eezee::TimeoutError) && !req.raise_error)
58
+ end
59
+
60
+ def build_faraday_request(req, client, method)
61
+ client.send(method) do |faraday_req|
62
+ faraday_req.body = req.payload.to_json if req.payload
63
+ end
64
+ end
65
+
66
+ def build_faraday_client(request)
67
+ Faraday.new(request.uri) do |config|
68
+ config.use(Faraday::Response::RaiseError) if request.raise_error
69
+ config.headers = request.headers if request.headers
70
+ config.options[:open_timeout] = request.open_timeout if request.open_timeout
71
+ config.options[:timeout] = request.timeout if request.timeout
72
+ config.adapter(Faraday.default_adapter)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'client/builder'
4
+ require_relative 'client/requester'
5
+
6
+ module Eezee
7
+ module Client
8
+ def self.extended(base)
9
+ base.send(:extend, Builder)
10
+ base.send(:extend, Requester)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eezee
4
+ class Configuration
5
+ attr_reader :services
6
+
7
+ def initialize
8
+ @services = {}
9
+ end
10
+
11
+ def add_service(name, options)
12
+ return unless name && options
13
+
14
+ @services[name] = Request.new(options)
15
+ end
16
+
17
+ def find_service(name)
18
+ @services[name]
19
+ end
20
+
21
+ def request_by(request, options)
22
+ Request.new((request&.attributes || {}).merge(options || {}))
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eezee
4
+ class Error < StandardError
5
+ end
6
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eezee
4
+ class RequestError < Error
5
+ attr_reader :response
6
+
7
+ def initialize(response)
8
+ @response = response
9
+ super(build_message)
10
+ end
11
+
12
+ def log
13
+ Eezee::Logger.error(self)
14
+ end
15
+
16
+ private
17
+
18
+ def build_message
19
+ "CODE: #{@response.code} - BODY: #{@response.body.to_json}"
20
+ end
21
+ end
22
+
23
+ class BadRequestError < RequestError; end
24
+ class UnauthorizedError < RequestError; end
25
+ class ForbiddenError < RequestError; end
26
+ class ResourceNotFoundError < RequestError; end
27
+ class UnprocessableEntityError < RequestError; end
28
+ class ClientError < RequestError; end
29
+ class InternalServerError < RequestError; end
30
+ class ServiceUnavailableError < RequestError; end
31
+ class ServerError < RequestError; end
32
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eezee
4
+ class RequiredFieldError < StandardError
5
+ attr_reader :origin, :field
6
+
7
+ def initialize(origin, field)
8
+ @origin = origin
9
+ @field = field
10
+ super(build_message)
11
+ end
12
+
13
+ private
14
+
15
+ def build_message
16
+ "The field #{@field} is required for #{@origin}"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eezee
4
+ class TimeoutError < Error
5
+ attr_reader :response
6
+
7
+ def initialize(response)
8
+ @response = response
9
+ super(response.original)
10
+ end
11
+
12
+ def log
13
+ Eezee::Logger.error(self)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'errors/error'
4
+ require_relative 'errors/timeout_error'
5
+ require_relative 'errors/required_field_error'
6
+ require_relative 'errors/request_error'
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eezee
4
+ module Logger
5
+ module_function
6
+
7
+ def request(req, method)
8
+ p log("request: #{method} #{req.uri}")
9
+ p log("request: HEADERS: #{req.headers&.to_json}") if req.headers
10
+ p log("request: PAYLOAD: #{req.payload&.to_json}") if req.payload
11
+ nil
12
+ end
13
+
14
+ def response(res)
15
+ p log("response: SUCCESS: #{res.success?}")
16
+ p log("response: TIMEOUT: #{res.timeout?}")
17
+ p log("response: CODE: #{res.code}")
18
+ p log("response: BODY: #{res.body&.to_json}")
19
+ end
20
+
21
+ def error(err)
22
+ p log("error: #{err.class}")
23
+ p log("error: SUCCESS: #{err.response.success?}")
24
+ p log("error: TIMEOUT: #{err.response.timeout?}")
25
+ p log("error: CODE: #{err.response.code}")
26
+ p log("error: BODY: #{err.response.body&.to_json}")
27
+ end
28
+
29
+ def log(message)
30
+ "INFO -- #{message}"
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eezee
4
+ class Request
5
+ ACCESSORS = %i[
6
+ after
7
+ before
8
+ headers
9
+ logger
10
+ open_timeout
11
+ params
12
+ path
13
+ payload
14
+ protocol
15
+ raise_error
16
+ timeout
17
+ url
18
+ ].freeze
19
+
20
+ DEFAULT = {
21
+ headers: {},
22
+ logger: false,
23
+ params: {},
24
+ payload: {},
25
+ raise_error: false
26
+ }.freeze
27
+
28
+ attr_accessor(*(ACCESSORS | %i[uri method]))
29
+
30
+ def initialize(options = {})
31
+ setup!(options)
32
+ end
33
+
34
+ def log
35
+ Eezee::Logger.request(self, @method.to_s.upcase)
36
+ end
37
+
38
+ def attributes
39
+ ACCESSORS.each_with_object({}) { |accessor, obj| obj[accessor] = send(accessor) }
40
+ end
41
+
42
+ def before!(*params)
43
+ hook!(:before, params)
44
+ end
45
+
46
+ def after!(*params)
47
+ hook!(:after, params)
48
+ end
49
+
50
+ private
51
+
52
+ def setup!(options = {})
53
+ accessors!(DEFAULT.merge(options || {}))
54
+ validate!
55
+ build_urn!
56
+ handle_query_params!
57
+ handle_urn_params!
58
+ end
59
+
60
+ def hook!(hook, params)
61
+ return unless send(hook)&.is_a?(Proc)
62
+
63
+ send(hook).call(*params[0..(send(hook).parameters.length - 1)])
64
+ end
65
+
66
+ def validate!
67
+ raise Eezee::RequiredFieldError.new(self.class, :url) unless @url
68
+ end
69
+
70
+ def accessors!(params)
71
+ params.slice(*ACCESSORS)
72
+ .each { |k, v| instance_variable_set("@#{k}", v) }
73
+ end
74
+
75
+ def build_urn!
76
+ @uri = [@protocol, [@url, @path].compact.join('/')].compact.join('://')
77
+ end
78
+
79
+ def handle_urn_params!
80
+ return unless @params.is_a?(Hash)
81
+
82
+ @params.filter { |k, _v| @uri.include?(":#{k}") }
83
+ .each { |k, v| @uri.gsub!(":#{k}", v.to_s) }
84
+ .then { @uri.gsub!(/:[a-z_-]+/, '') }
85
+ end
86
+
87
+ def handle_query_params!
88
+ return unless @params.is_a?(Hash)
89
+
90
+ @params.reject { |k, _v| @uri.include?(":#{k}") }
91
+ .map { |k, v| "#{k}=#{v}" }
92
+ .then { |array| array.join('&') }
93
+ .then { |query| query unless query.empty? }
94
+ .then { |query| @uri = [@uri, query].compact.join('?') }
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eezee
4
+ class RequestErrorFactory
5
+ class << self
6
+ def build(response)
7
+ return TimeoutError.new(response) if response.timeout?
8
+
9
+ find_by_code(response.code).new(response)
10
+ end
11
+
12
+ private
13
+
14
+ def find_by_code(code)
15
+ case code
16
+ when 400 then BadRequestError
17
+ when 401 then UnauthorizedError
18
+ when 403 then ForbiddenError
19
+ when 404 then ResourceNotFoundError
20
+ when 422 then UnprocessableEntityError
21
+ when 400..499 then ClientError
22
+ when 500 then InternalServerError
23
+ when 503 then ServiceUnavailableError
24
+ when 500..599 then ServerError
25
+ else
26
+ RequestError
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'faraday'
5
+
6
+ module Eezee
7
+ class Response
8
+ attr_reader :original
9
+
10
+ def initialize(original)
11
+ @original = original
12
+ end
13
+
14
+ def body
15
+ return {} if timeout?
16
+
17
+ @body ||= parsed_body
18
+ end
19
+
20
+ def success?
21
+ return false if timeout?
22
+
23
+ @success ||= success_response? && @original&.success?
24
+ end
25
+
26
+ def code
27
+ return if timeout?
28
+
29
+ @code ||= success_response? ? @original.status : @original.response[:status]
30
+ end
31
+
32
+ def timeout?
33
+ @original.is_a?(Faraday::TimeoutError) ||
34
+ @original.is_a?(Net::ReadTimeout) ||
35
+ @original.is_a?(Faraday::ConnectionFailed)
36
+ end
37
+
38
+ def log
39
+ Eezee::Logger.response(self)
40
+ end
41
+
42
+ private
43
+
44
+ def parsed_body
45
+ JSON.parse(handled_faraday_response, symbolize_names: true)
46
+ end
47
+
48
+ def success_response?
49
+ @original.is_a?(Faraday::Response)
50
+ end
51
+
52
+ def handled_faraday_response
53
+ faraday_response.nil? || faraday_response.empty? ? '{}' : faraday_response
54
+ end
55
+
56
+ def faraday_response
57
+ success_response? ? @original.body : @original.response[:body]
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Eezee
4
+ VERSION = '1.0.7'
5
+ end
data/lib/eezee.rb ADDED
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eezee/version'
4
+ require 'eezee/errors'
5
+ require 'eezee/request'
6
+ require 'eezee/response'
7
+ require 'eezee/request_error_factory'
8
+ require 'eezee/logger'
9
+ require 'eezee/configuration'
10
+ require 'eezee/client'
11
+
12
+ module Eezee
13
+ class << self
14
+ def configure
15
+ yield(configuration)
16
+ end
17
+
18
+ def configuration
19
+ @configuration ||= Configuration.new
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/base'
4
+
5
+ module Eezee
6
+ class InstallGenerator < Rails::Generators::Base
7
+ def create_initializer_file
8
+ create_file(
9
+ 'config/initializers/eezee.rb',
10
+ <<~KATINGUELE_INITIALIZER_TEXT
11
+ # frozen_string_literal: true
12
+
13
+ Eezee.configure do |config|
14
+ # You can add your service's configuration, like:
15
+
16
+ # config.add_service :external_service_1,
17
+ # raise_error: true,
18
+ # url: ENV['EXTERNAL_SERVICE_URL_1'],
19
+ # headers: { 'Content-Type' => 'application/json' }
20
+
21
+ # config.add_service :external_service_2,
22
+ # raise_error: false,
23
+ # url: ENV['EXTERNAL_SERVICE_URL_2'],
24
+ # headers: { 'Token' => "#Token {ENV['EXTERNAL_SERVICE_TOKEN']}"}
25
+
26
+ # All available options is on README
27
+ end
28
+ KATINGUELE_INITIALIZER_TEXT
29
+ )
30
+ end
31
+ end
32
+ end
metadata ADDED
@@ -0,0 +1,283 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eezee
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.7
5
+ platform: ruby
6
+ authors:
7
+ - linqueta
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-12-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.17.0
20
+ - - "~>"
21
+ - !ruby/object:Gem::Version
22
+ version: '0.17'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 0.17.0
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.17'
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '2.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: factory_bot
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '5.1'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 5.1.1
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '5.1'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 5.1.1
67
+ - !ruby/object:Gem::Dependency
68
+ name: pry-byebug
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 3.7.0
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '3.7'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 3.7.0
84
+ - - "~>"
85
+ - !ruby/object:Gem::Version
86
+ version: '3.7'
87
+ - !ruby/object:Gem::Dependency
88
+ name: rake
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: '10.0'
94
+ type: :development
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: '10.0'
101
+ - !ruby/object:Gem::Dependency
102
+ name: rspec
103
+ requirement: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - "~>"
106
+ - !ruby/object:Gem::Version
107
+ version: '3.0'
108
+ type: :development
109
+ prerelease: false
110
+ version_requirements: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - "~>"
113
+ - !ruby/object:Gem::Version
114
+ version: '3.0'
115
+ - !ruby/object:Gem::Dependency
116
+ name: rubocop
117
+ requirement: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: 0.74.0
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.74'
125
+ type: :development
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: 0.74.0
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: '0.74'
135
+ - !ruby/object:Gem::Dependency
136
+ name: rubocop-performance
137
+ requirement: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - "~>"
140
+ - !ruby/object:Gem::Version
141
+ version: '1.4'
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: 1.4.1
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '1.4'
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: 1.4.1
155
+ - !ruby/object:Gem::Dependency
156
+ name: simplecov
157
+ requirement: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: 0.17.0
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '0.17'
165
+ type: :development
166
+ prerelease: false
167
+ version_requirements: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: 0.17.0
172
+ - - "~>"
173
+ - !ruby/object:Gem::Version
174
+ version: '0.17'
175
+ - !ruby/object:Gem::Dependency
176
+ name: simplecov-console
177
+ requirement: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: 0.5.0
182
+ - - "~>"
183
+ - !ruby/object:Gem::Version
184
+ version: '0.5'
185
+ type: :development
186
+ prerelease: false
187
+ version_requirements: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - ">="
190
+ - !ruby/object:Gem::Version
191
+ version: 0.5.0
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '0.5'
195
+ - !ruby/object:Gem::Dependency
196
+ name: vcr
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: 5.0.0
202
+ - - "~>"
203
+ - !ruby/object:Gem::Version
204
+ version: '5.0'
205
+ type: :development
206
+ prerelease: false
207
+ version_requirements: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - ">="
210
+ - !ruby/object:Gem::Version
211
+ version: 5.0.0
212
+ - - "~>"
213
+ - !ruby/object:Gem::Version
214
+ version: '5.0'
215
+ - !ruby/object:Gem::Dependency
216
+ name: webmock
217
+ requirement: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - "~>"
220
+ - !ruby/object:Gem::Version
221
+ version: '3.7'
222
+ - - ">="
223
+ - !ruby/object:Gem::Version
224
+ version: 3.7.6
225
+ type: :development
226
+ prerelease: false
227
+ version_requirements: !ruby/object:Gem::Requirement
228
+ requirements:
229
+ - - "~>"
230
+ - !ruby/object:Gem::Version
231
+ version: '3.7'
232
+ - - ">="
233
+ - !ruby/object:Gem::Version
234
+ version: 3.7.6
235
+ description: A library to execute HTTP request in an easy way
236
+ email:
237
+ - lincolnrodrs@gmail.com
238
+ executables: []
239
+ extensions: []
240
+ extra_rdoc_files: []
241
+ files:
242
+ - MIT-LICENSE
243
+ - README.md
244
+ - lib/eezee.rb
245
+ - lib/eezee/client.rb
246
+ - lib/eezee/client/builder.rb
247
+ - lib/eezee/client/requester.rb
248
+ - lib/eezee/configuration.rb
249
+ - lib/eezee/errors.rb
250
+ - lib/eezee/errors/error.rb
251
+ - lib/eezee/errors/request_error.rb
252
+ - lib/eezee/errors/required_field_error.rb
253
+ - lib/eezee/errors/timeout_error.rb
254
+ - lib/eezee/logger.rb
255
+ - lib/eezee/request.rb
256
+ - lib/eezee/request_error_factory.rb
257
+ - lib/eezee/response.rb
258
+ - lib/eezee/version.rb
259
+ - lib/generators/katinguele/install_generator.rb
260
+ homepage: https://github.com/linqueta/eezee
261
+ licenses:
262
+ - MIT
263
+ metadata: {}
264
+ post_install_message:
265
+ rdoc_options: []
266
+ require_paths:
267
+ - lib
268
+ required_ruby_version: !ruby/object:Gem::Requirement
269
+ requirements:
270
+ - - ">="
271
+ - !ruby/object:Gem::Version
272
+ version: '0'
273
+ required_rubygems_version: !ruby/object:Gem::Requirement
274
+ requirements:
275
+ - - ">="
276
+ - !ruby/object:Gem::Version
277
+ version: '0'
278
+ requirements: []
279
+ rubygems_version: 3.0.3
280
+ signing_key:
281
+ specification_version: 4
282
+ summary: The easiest HTTP client for Ruby
283
+ test_files: []