grac 4.2.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/grac/client.rb +128 -120
  3. data/lib/grac/version.rb +1 -1
  4. metadata +10 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bc29285a1603b9ee7559be0edc7949a950611dd04aac4399400816381def0a72
4
- data.tar.gz: b39fa9ab54323755a4fb4f80b932f26f21b0232d12b9b790c308b7960e03239f
3
+ metadata.gz: 8460d522d154cc9553f4a67210e6509fd1e1e5d9b956cccf4e3e4701eccd6135
4
+ data.tar.gz: a8c70bc97eadf86324176c301b4bc5bd856f4d20e402ba111236860734d8e0b6
5
5
  SHA512:
6
- metadata.gz: d8764509b72746e4b692f0cad8f06f8ecf23b41f0a0e8dc8635507da57f2567af6e7ac008207242398029a26298ce401d10fde3fa2c3c80d621c09a3ffb9013e
7
- data.tar.gz: 112f99d47388faff6743122ebd3044d5466cd46ca7857363cb4da6740bb16445b3de545ca48f3ba8c5de1247718f09900a32766139d5bcc5fac93ec448bb9479
6
+ metadata.gz: 2db352fde7d23f9e42d578b50c08c702232c096d0de59d4ed1075bf4db63946fbff16335de25bdbac7d076e9f176a8b18b78f506bd7c5b3fe8bb81e4d9d8ba57
7
+ data.tar.gz: 146317de75aa8ab0a276016b90cd3aac920594424edc2f601b650bc64746ff531c360b34997be1e6d1af73676826948ddb573ac4be2b11e004a24aed4250d779
data/lib/grac/client.rb CHANGED
@@ -8,6 +8,7 @@ require_relative './response'
8
8
 
9
9
  module Grac
10
10
  class Client
11
+
11
12
  attr_reader :uri
12
13
 
13
14
  def initialize(uri, options = {})
@@ -15,21 +16,22 @@ module Grac
15
16
 
16
17
  @uri = uri
17
18
  @options = {
18
- :connecttimeout => options[:connecttimeout] || 0.1,
19
- :timeout => options[:timeout] || 15,
20
- :params => options[:params] || {},
21
- :headers => {
22
- "User-Agent" => "Grac v#{Grac::VERSION}",
23
- "Content-Type" => "application/json;charset=utf-8"
19
+ connecttimeout: options[:connecttimeout] || 0.1,
20
+ timeout: options[:timeout] || 15,
21
+ params: options[:params] || {},
22
+ headers: {
23
+ 'User-Agent' => "Grac v#{Grac::VERSION}",
24
+ 'Content-Type' => 'application/json;charset=utf-8'
24
25
  }.merge(options[:headers] || {}),
25
- :postprocessing => {},
26
- :middleware => options[:middleware] || []
26
+ postprocessing: {},
27
+ middleware: options[:middleware] || [],
28
+ retry_get_head: options.fetch(:retry_get_head, true),
27
29
  }
28
30
 
29
31
  if options[:postprocessing]
30
32
  options[:postprocessing]
31
33
  .each_with_object(postprocessing = {}) do |(pattern, transformation), obj|
32
- if pattern.kind_of?(Regexp)
34
+ if pattern.is_a?(Regexp)
33
35
  obj[pattern] = transformation
34
36
  else
35
37
  obj[Regexp.new(pattern)] = transformation
@@ -49,10 +51,12 @@ module Grac
49
51
  end
50
52
 
51
53
  def set(options = {})
52
- options = options.merge({
53
- headers: @options[:headers].merge(options[:headers] || {}),
54
- middleware: @options[:middleware] + (options[:middleware] || [])
55
- })
54
+ options = options.merge(
55
+ {
56
+ headers: @options[:headers].merge(options[:headers] || {}),
57
+ middleware: @options[:middleware] + (options[:middleware] || []),
58
+ },
59
+ )
56
60
 
57
61
  self.class.new(@uri, @options.merge(options))
58
62
  end
@@ -64,35 +68,38 @@ module Grac
64
68
  self.class.new("#{@uri}#{path}", @options)
65
69
  end
66
70
 
67
- %w{post put patch}.each do |method|
71
+ ['post', 'put', 'patch'].each do |method|
68
72
  define_method method do |body = {}, params = {}|
69
- response = build_and_run(method, { :body => body, :params => params })
73
+ response = build_and_run(method, { body: body, params: params })
70
74
  check_response(method, response)
71
75
  end
72
76
  end
73
77
 
74
- %w{get delete}.each do |method|
78
+ ['get', 'delete'].each do |method|
75
79
  define_method method do |params = {}|
76
- response = build_and_run(method, { :params => params })
80
+ response = build_and_run(method, { params: params })
77
81
  check_response(method, response)
78
82
  end
79
83
  end
80
84
 
81
85
  def call(opts, request_uri, method, params, body)
82
86
  request_hash = {
83
- :method => method,
84
- :params => params, # Query params are escaped by Typhoeus
85
- :body => body,
86
- :connecttimeout => opts[:connecttimeout],
87
- :timeout => opts[:timeout],
88
- :headers => opts[:headers]
87
+ method: method,
88
+ params: params, # Query params are escaped by Typhoeus
89
+ body: body,
90
+ connecttimeout: opts[:connecttimeout],
91
+ timeout: opts[:timeout],
92
+ headers: opts[:headers],
89
93
  }
90
94
 
91
95
  request = ::Typhoeus::Request.new(request_uri, request_hash)
92
96
  response = request.run
93
97
 
94
98
  # Retry GET and HEAD requests - modifying requests might not be idempotent
95
- response = request.run if response.timed_out? && ['get', 'head'].include?(method)
99
+ # Only retry those requests if the feature is enabled
100
+ if response.timed_out? && ['get', 'head'].include?(method) && @options[:retry_get_head]
101
+ response = request.run
102
+ end
96
103
 
97
104
  # A request can time out while receiving data. In this case response.code might indicate
98
105
  # success although data hasn't been fully transferred. Thus rely on Typhoeus for
@@ -119,122 +126,123 @@ module Grac
119
126
 
120
127
  private
121
128
 
122
- def build_and_run(method, options = {})
123
- body = prepare_body_by_content_type(options[:body])
124
- params = @options[:params].merge(options[:params] || {})
125
- return middleware_chain.call(@options, uri, method, params, body)
126
- end
127
-
128
- def headers
129
- @options[:headers] || {}
130
- end
131
-
132
- def prepare_body_by_content_type(body)
133
- return nil if body.nil? || body.empty?
134
-
135
- case headers['Content-Type']
136
- when /\Aapplication\/json/
137
- return body.to_json
138
- when /\Aapplication\/x-www-form-urlencoded/
139
- # Typhoeus will take care of the encoding when receiving a hash
140
- return body
141
- else
142
- # Do not encode other unknown Content-Types either.
143
- # The default is JSON through the Content-Type header which is set by default.
144
- return body
129
+ def build_and_run(method, options = {})
130
+ body = prepare_body_by_content_type(options[:body])
131
+ params = @options[:params].merge(options[:params] || {})
132
+ return middleware_chain.call(@options, uri, method, params, body)
145
133
  end
146
- end
147
134
 
148
- def middleware_chain
149
- callee = self
135
+ def headers
136
+ @options[:headers] || {}
137
+ end
150
138
 
151
- @options[:middleware].reverse.each do |mw|
152
- if mw.kind_of?(Array)
153
- middleware_class = mw[0]
154
- params = mw[1..-1]
139
+ def prepare_body_by_content_type(body)
140
+ return nil if body.nil? || body.empty?
155
141
 
156
- callee = middleware_class.new(callee, *params)
142
+ case headers['Content-Type']
143
+ when /\Aapplication\/json/
144
+ return body.to_json
145
+ when /\Aapplication\/x-www-form-urlencoded/
146
+ # Typhoeus will take care of the encoding when receiving a hash
147
+ return body
157
148
  else
158
- callee = mw.new(callee)
149
+ # Do not encode other unknown Content-Types either.
150
+ # The default is JSON through the Content-Type header which is set by default.
151
+ return body
159
152
  end
160
153
  end
161
154
 
162
- return callee
163
- end
155
+ def middleware_chain
156
+ callee = self
164
157
 
165
- def check_response(method, response)
166
- case response.code
167
- when 200..203, 206..299
168
- # unknown status codes must be treated as the x00 of their class, so 200
169
- if response.json_content?
170
- return postprocessing(response.parsed_json)
171
- end
158
+ @options[:middleware].reverse.each do |mw|
159
+ if mw.kind_of?(Array)
160
+ middleware_class = mw[0]
161
+ params = mw[1..-1]
172
162
 
173
- return response.body
174
- when 204, 205
175
- return true
176
- when 0
177
- raise Exception::RequestFailed.new(method, response.effective_url, response.return_message)
178
- else
179
- begin
180
- # The Response class doesn't have enough information to create a proper exception, so
181
- # catch its exception and raise a proper one.
182
- parsed_body = response.parsed_json
183
- rescue Exception::InvalidContent
184
- raise Exception::ErrorWithInvalidContent.new(
185
- method,
186
- response.effective_url,
187
- response.code,
188
- response.body,
189
- 'json'
190
- )
191
- end
192
- case response.code
193
- when 400
194
- raise Exception::BadRequest.new(method, response.effective_url, parsed_body)
195
- when 403
196
- raise Exception::Forbidden.new(method, response.effective_url, parsed_body)
197
- when 404
198
- raise Exception::NotFound.new(method, response.effective_url, parsed_body)
199
- when 409
200
- raise Exception::Conflict.new(method, response.effective_url, parsed_body)
163
+ callee = middleware_class.new(callee, *params)
201
164
  else
202
- raise Exception::ServiceError.new(method, response.effective_url, parsed_body)
165
+ callee = mw.new(callee)
203
166
  end
167
+ end
168
+
169
+ return callee
204
170
  end
205
- end
206
171
 
207
- def postprocessing(data, processing = nil)
208
- return data if @options[:postprocessing].nil? || @options[:postprocessing].empty?
172
+ def check_response(method, response)
173
+ case response.code
174
+ when 200..203, 206..299
175
+ # unknown status codes must be treated as the x00 of their class, so 200
176
+ if response.json_content?
177
+ return postprocessing(response.parsed_json)
178
+ end
179
+
180
+ return response.body
181
+ when 204, 205
182
+ return true
183
+ when 0
184
+ raise Exception::RequestFailed.new(method, response.effective_url, response.return_message)
185
+ else
186
+ begin
187
+ # The Response class doesn't have enough information to create a proper exception, so
188
+ # catch its exception and raise a proper one.
189
+ parsed_body = response.parsed_json
190
+ rescue Exception::InvalidContent
191
+ raise Exception::ErrorWithInvalidContent.new(
192
+ method,
193
+ response.effective_url,
194
+ response.code,
195
+ response.body,
196
+ 'json'
197
+ )
198
+ end
199
+ case response.code
200
+ when 400
201
+ raise Exception::BadRequest.new(method, response.effective_url, parsed_body)
202
+ when 403
203
+ raise Exception::Forbidden.new(method, response.effective_url, parsed_body)
204
+ when 404
205
+ raise Exception::NotFound.new(method, response.effective_url, parsed_body)
206
+ when 409
207
+ raise Exception::Conflict.new(method, response.effective_url, parsed_body)
208
+ else
209
+ raise Exception::ServiceError.new(method, response.effective_url, parsed_body)
210
+ end
211
+ end
212
+ end
213
+
214
+ def postprocessing(data, processing = nil)
215
+ return data if @options[:postprocessing].nil? || @options[:postprocessing].empty?
209
216
 
210
- if data.kind_of?(Hash)
211
- data.each do |key, value|
212
- processing = nil
213
- regexp = @options[:postprocessing].keys.detect { |pattern| pattern.match?(key) }
217
+ if data.kind_of?(Hash)
218
+ data.each do |key, value|
219
+ processing = nil
220
+ regexp = @options[:postprocessing].keys.detect { |pattern| pattern.match?(key) }
214
221
 
215
- if !regexp.nil?
216
- processing = @options[:postprocessing][regexp]
222
+ if !regexp.nil?
223
+ processing = @options[:postprocessing][regexp]
224
+ end
225
+ data[key] = postprocessing(value, processing)
217
226
  end
218
- data[key] = postprocessing(value, processing)
219
- end
220
- elsif data.kind_of?(Array)
221
- data.each_with_index do |value, index|
222
- data[index] = postprocessing(value, processing)
227
+ elsif data.kind_of?(Array)
228
+ data.each_with_index do |value, index|
229
+ data[index] = postprocessing(value, processing)
230
+ end
231
+ else
232
+ data = processing.nil? ? data : processing.call(data)
223
233
  end
224
- else
225
- data = processing.nil? ? data : processing.call(data)
234
+
235
+ return data
226
236
  end
227
237
 
228
- return data
229
- end
238
+ def escape_url_param(value)
239
+ # We don't want spaces to be encoded as plus sign - a plus sign can be ambiguous in a URL and
240
+ # either represent a plus sign or a space.
241
+ # CGI::escape replaces all plus signs with their percent-encoding representation, so all
242
+ # remaining plus signs are spaces. Replacing these with a space's percent encoding makes the
243
+ # encoding unambiguous.
244
+ CGI::escape(value).gsub('+', '%20')
245
+ end
230
246
 
231
- def escape_url_param(value)
232
- # We don't want spaces to be encoded as plus sign - a plus sign can be ambiguous in a URL and
233
- # either represent a plus sign or a space.
234
- # CGI::escape replaces all plus signs with their percent-encoding representation, so all
235
- # remaining plus signs are spaces. Replacing these with a space's percent encoding makes the
236
- # encoding unambiguous.
237
- CGI::escape(value).gsub('+', '%20')
238
- end
239
247
  end
240
248
  end
data/lib/grac/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Grac
2
- VERSION = "4.2.0"
2
+ VERSION = "4.3.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grac
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.0
4
+ version: 4.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Schoknecht
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-15 00:00:00.000000000 Z
11
+ date: 2024-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -72,28 +72,28 @@ dependencies:
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 3.0.1
75
+ version: '3.1'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 3.0.1
82
+ version: '3.1'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: rack-test
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 2.0.2
89
+ version: '2.1'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 2.0.2
96
+ version: '2.1'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: oj
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -138,7 +138,7 @@ homepage: https://github.com/Barzahlen/grac
138
138
  licenses:
139
139
  - MIT
140
140
  metadata: {}
141
- post_install_message:
141
+ post_install_message:
142
142
  rdoc_options: []
143
143
  require_paths:
144
144
  - lib
@@ -153,8 +153,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
153
  - !ruby/object:Gem::Version
154
154
  version: '0'
155
155
  requirements: []
156
- rubygems_version: 3.1.6
157
- signing_key:
156
+ rubygems_version: 3.5.3
157
+ signing_key:
158
158
  specification_version: 4
159
159
  summary: Very generic client for REST API with basic error handling
160
160
  test_files: []