client-api-builder 0.2.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e5b76aafc223c8695b05460638fbd487b9ee2ad969e690f26de97d1cccf8293
4
- data.tar.gz: 4b31ea3ef759fd3cf553d32e6b474378137e6b0badc52139068f7db9d7f87b00
3
+ metadata.gz: fe20c0bab4626c805bc2b0c982cdd2bf27ed6b83d601a2b4be3377da74d34d65
4
+ data.tar.gz: c82184aadfac611f12f57060bc315d9790b4bab7238ba4c2942334058b5fdafd
5
5
  SHA512:
6
- metadata.gz: bb0d45fe8b4832a73b84a931f63ea3713a955c0d5b44a31159a54b0d454b4b63c0d29d80a379cbbcbd1c469b4b65a9e04ba238650d04e749f28918819987b65a
7
- data.tar.gz: 752dfdf1071668e456532b1d41c1adcdbd522e09bb6a05a5c904f678a71e62014247ecc6e66a0a51fe8474fc222dee05127e9988406b95e6c5887ee6682f7f23
6
+ metadata.gz: b3df586e352a828dc8423862f425ce0524c4e479c10f4e35b6a514b92412277dc4d0c095afbdaaeb024b5783c38745f3d1fe2bebb31b34f097611b0dca77d7ad
7
+ data.tar.gz: 42c359200e625705ce7960ae88d861d49cf5a83ad7e3dca71ee240f0df36addb88a7488e9da656a6d545b27cafe6075f09b8aab2242363896aa0321fb3399bf5
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'client-api-builder'
5
- s.version = '0.2.0'
5
+ s.version = '0.2.4'
6
6
  s.licenses = ['MIT']
7
7
  s.summary = 'Develop Client API libraries faster'
8
8
  s.description = 'Utility for constructing API clients'
@@ -0,0 +1,23 @@
1
+ require 'base64'
2
+
3
+ class BasicAuthExampleClient < Struct.new(
4
+ :username,
5
+ :password
6
+ )
7
+
8
+ include ClientApiBuilder::Router
9
+
10
+ base_url 'https://www.example.com'
11
+
12
+ header 'Authorization', :basic_authorization
13
+ query_param('cache_buster') { (Time.now.to_f * 1000).to_i }
14
+
15
+ route :get_apps, '/apps'
16
+ route :get_app, '/apps/:app_id'
17
+
18
+ private
19
+
20
+ def basic_authorization
21
+ 'basic ' + Base64.strict_encode64(username + ':' + password)
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ class IMDBDatesetsClient
2
+ include ClientApiBuilder::Router
3
+
4
+ base_url 'https://datasets.imdbws.com'
5
+
6
+ route :get_name_basics, '/name.basics.tsv.gz', stream: :file
7
+ route :get_title_akas, '/title.akas.tsv.gz', stream: :io
8
+ route :get_title_basics, '/title.basics.tsv.gz', stream: :block
9
+
10
+ def self.stream_to_file
11
+ new.get_name_basics(file: 'name.basics.tsv.gz')
12
+ end
13
+
14
+ def self.stream_to_io
15
+ File.open('title.akas.tsv.gz', 'wb') do |io|
16
+ new.get_title_akas(io: io)
17
+ end
18
+ end
19
+
20
+ def self.stream_with_block
21
+ File.open('title.basics.tsv.gz', 'wb') do |io|
22
+ total_read = 0.0
23
+ new.get_title_basics do |response, chunk|
24
+ total_read += chunk.bytesize
25
+ percentage_complete = ((total_read / response.content_length) * 100).to_i
26
+ puts "downloading title.basics.tsv.gz completed: #{percentage_complete}%"
27
+ io.write chunk
28
+ end
29
+ end
30
+ end
31
+ end
@@ -28,7 +28,30 @@ module ClientApiBuilder
28
28
  request.body = body if body
29
29
 
30
30
  Net::HTTP.start(uri.hostname, uri.port, connection_options.merge(use_ssl: uri.scheme == 'https')) do |http|
31
- http.request(request)
31
+ http.request(request) do |response|
32
+ yield response if block_given?
33
+ end
34
+ end
35
+ end
36
+
37
+ def stream(method:, uri:, body:, headers:, connection_options:)
38
+ request(method: method, uri: uri, body: body, headers: headers, connection_options: connection_options) do |response|
39
+ response.read_body do |chunk|
40
+ yield response, chunk
41
+ end
42
+ end
43
+ end
44
+
45
+ def stream_to_io(method:, uri:, body:, headers:, connection_options:, io:)
46
+ stream(method: method, uri: uri, body: body, headers: headers, connection_options: connection_options) do |_, chunk|
47
+ io.write chunk
48
+ end
49
+ end
50
+
51
+ def stream_to_file(method:, uri:, body:, headers:, connection_options:, file:)
52
+ mode = connection_options.delete(:file_mode) || 'wb'
53
+ File.open(file, mode) do |io|
54
+ stream_to_io(method: method, uri: uri, body: body, headers: headers, connection_options: connection_options, io: io)
32
55
  end
33
56
  end
34
57
  end
@@ -7,7 +7,7 @@ module ClientApiBuilder
7
7
  base.extend InheritanceHelper::Methods
8
8
  base.extend ClassMethods
9
9
  base.include ::ClientApiBuilder::NetHTTP::Request
10
- base.attr_reader :response
10
+ base.attr_reader :response, :request_options
11
11
  end
12
12
 
13
13
  module ClassMethods
@@ -22,6 +22,7 @@ module ClientApiBuilder
22
22
  base_url: nil,
23
23
  headers: {},
24
24
  connection_options: {},
25
+ query_params: {},
25
26
  query_builder: Hash.method_defined?(:to_query) ? :to_query : :query_params
26
27
  }.freeze
27
28
  end
@@ -50,6 +51,12 @@ module ClientApiBuilder
50
51
  add_value_to_class_method(:default_options, connection_options: connection_options)
51
52
  end
52
53
 
54
+ def query_param(name, value = nil, &block)
55
+ query_params = default_options[:query_params].dup
56
+ query_params[name] = value || block
57
+ add_value_to_class_method(:default_options, query_params: query_params)
58
+ end
59
+
53
60
  def headers
54
61
  default_options[:headers]
55
62
  end
@@ -58,6 +65,10 @@ module ClientApiBuilder
58
65
  default_options[:connection_options]
59
66
  end
60
67
 
68
+ def query_params
69
+ default_options[:query_params]
70
+ end
71
+
61
72
  def build_query(query)
62
73
  case query_builder
63
74
  when :to_query
@@ -181,8 +192,18 @@ module ClientApiBuilder
181
192
  end
182
193
  expected_response_codes.map!(&:to_s)
183
194
 
195
+ stream_param =
196
+ case options[:stream]
197
+ when true,
198
+ :file
199
+ :file
200
+ when :io
201
+ :io
202
+ end
203
+
184
204
  method_args = named_arguments.map { |arg_name| "#{arg_name}:" }
185
205
  method_args += ['body:'] if has_body_param
206
+ method_args += ["#{stream_param}:"] if stream_param
186
207
  method_args += ['**__options__', '&block']
187
208
 
188
209
  code = "def #{method_name}(" + method_args.join(', ') + ")\n"
@@ -194,9 +215,31 @@ module ClientApiBuilder
194
215
  code += " __body__ = build_body(__body__, __options__)\n"
195
216
  code += " __headers__ = build_headers(__options__)\n"
196
217
  code += " __connection_options__ = build_connection_options(__options__)\n"
197
- code += " @response = request(method: #{http_method.inspect}, uri: __uri__, body: __body__, headers: __headers__, connection_options: __connection_options__)\n"
218
+ code += " @request_options = {method: #{http_method.inspect}, uri: __uri__, body: __body__, headers: __headers__, connection_options: __connection_options__}\n"
219
+ code += " @request_options[:#{stream_param}] = #{stream_param}\n" if stream_param
220
+
221
+ case options[:stream]
222
+ when true,
223
+ :file
224
+ code += " @response = stream_to_file(**@request_options)\n"
225
+ when :io
226
+ code += " @response = stream_to_io(**@request_options)\n"
227
+ when :block
228
+ code += " @response = stream(**@request_options, &block)\n"
229
+ else
230
+ code += " @response = request(**@request_options)\n"
231
+ end
232
+
198
233
  code += " expected_response_code!(@response, __expected_response_codes__, __options__)\n"
199
- code += " handle_response(@response, __options__, &block)\n"
234
+
235
+ if options[:stream] || options[:return] == :response
236
+ code += " @response\n"
237
+ elsif options[:return] == :body
238
+ code += " @response.body\n"
239
+ else
240
+ code += " handle_response(@response, __options__, &block)\n"
241
+ end
242
+
200
243
  code += "end\n"
201
244
  code
202
245
  end
@@ -239,8 +282,23 @@ module ClientApiBuilder
239
282
  end
240
283
 
241
284
  def build_query(query, options)
242
- query.merge!(options[:query]) if options[:query]
243
- self.class.build_query(query)
285
+ query_params = {}
286
+
287
+ add_query_param_proc = proc do |name, value|
288
+ query_params[name] =
289
+ if value.is_a?(Proc)
290
+ instance_eval(&value)
291
+ elsif value.is_a?(Symbol)
292
+ send(value)
293
+ else
294
+ value
295
+ end
296
+ end
297
+
298
+ self.class.query_params.each(&add_query_param_proc)
299
+ query&.each(&add_query_param_proc)
300
+
301
+ self.class.build_query(query_params)
244
302
  end
245
303
 
246
304
  def build_body(body, options)
data/script/console CHANGED
@@ -2,6 +2,9 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  $LOAD_PATH << File.expand_path('../lib', __dir__)
5
+ $LOAD_PATH << File.expand_path('../examples', __dir__)
5
6
  require 'client-api-builder'
7
+ autoload :BasicAuthExampleClient, 'basic_auth_example_client'
8
+ autoload :IMDBDatesetsClient, 'imdb_datasets_client'
6
9
  require 'irb'
7
10
  IRB.start(__FILE__)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: client-api-builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Doug Youch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-05 00:00:00.000000000 Z
11
+ date: 2021-07-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Utility for constructing API clients
14
14
  email: dougyouch@gmail.com
@@ -24,6 +24,8 @@ files:
24
24
  - LICENSE
25
25
  - README.md
26
26
  - client-api-builder.gemspec
27
+ - examples/basic_auth_example_client.rb
28
+ - examples/imdb_datasets_client.rb
27
29
  - lib/client-api-builder.rb
28
30
  - lib/client_api_builder/net_http_request.rb
29
31
  - lib/client_api_builder/query_params.rb