client-api-builder 0.2.4 → 0.2.8

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: fe20c0bab4626c805bc2b0c982cdd2bf27ed6b83d601a2b4be3377da74d34d65
4
- data.tar.gz: c82184aadfac611f12f57060bc315d9790b4bab7238ba4c2942334058b5fdafd
3
+ metadata.gz: b9853c5806a999fe126ad1b1ce5fdeb6ea6bdb985142e36a1cbdcbbbac6434ae
4
+ data.tar.gz: 9bfb059e5adcbf18c55882d1fcca7d04d310e04dce915cd3d46e88f770e36650
5
5
  SHA512:
6
- metadata.gz: b3df586e352a828dc8423862f425ce0524c4e479c10f4e35b6a514b92412277dc4d0c095afbdaaeb024b5783c38745f3d1fe2bebb31b34f097611b0dca77d7ad
7
- data.tar.gz: 42c359200e625705ce7960ae88d861d49cf5a83ad7e3dca71ee240f0df36addb88a7488e9da656a6d545b27cafe6075f09b8aab2242363896aa0321fb3399bf5
6
+ metadata.gz: 1896be3318d8f5362c8b367d9836854d8567f9e7a394cac645c126a866afd2eb53507721228ae78f7bc94d5625b5fdfe2abe2273320de04c844e94cbe1b8df5e
7
+ data.tar.gz: 90aaaf4e7a73452397c699f095b383397c9a9d715b91b20431f0faf798b00e716d387eef9280f8687e118538c6e9c1e56f894979559b96b3671f04cf75f5ea6e
data/Gemfile.lock CHANGED
@@ -9,7 +9,7 @@ GEM
9
9
  diff-lcs (1.4.4)
10
10
  docile (1.4.0)
11
11
  hashdiff (1.0.1)
12
- inheritance-helper (0.1.5)
12
+ inheritance-helper (0.2.5)
13
13
  parallel (1.20.1)
14
14
  parser (3.0.1.1)
15
15
  ast (~> 2.4.1)
@@ -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.4'
5
+ s.version = '0.2.8'
6
6
  s.licenses = ['MIT']
7
7
  s.summary = 'Develop Client API libraries faster'
8
8
  s.description = 'Utility for constructing API clients'
@@ -10,4 +10,6 @@ Gem::Specification.new do |s|
10
10
  s.email = 'dougyouch@gmail.com'
11
11
  s.homepage = 'https://github.com/dougyouch/client-api-builder'
12
12
  s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
13
+
14
+ s.add_runtime_dependency 'inheritance-helper', '>= 0.2.5'
13
15
  end
@@ -1,4 +1,5 @@
1
1
  require 'base64'
2
+ require 'securerandom'
2
3
 
3
4
  class BasicAuthExampleClient < Struct.new(
4
5
  :username,
@@ -6,6 +7,7 @@ class BasicAuthExampleClient < Struct.new(
6
7
  )
7
8
 
8
9
  include ClientApiBuilder::Router
10
+ include ClientApiBuilder::Section
9
11
 
10
12
  base_url 'https://www.example.com'
11
13
 
@@ -15,9 +17,27 @@ class BasicAuthExampleClient < Struct.new(
15
17
  route :get_apps, '/apps'
16
18
  route :get_app, '/apps/:app_id'
17
19
 
20
+ section :users do
21
+ header 'Authorization', :bearer_authorization
22
+
23
+ route :create_user, '/users?z={cache_buster}'
24
+ end
25
+
26
+ def cache_buster
27
+ (Time.now.to_f * 1000).to_i
28
+ end
29
+
18
30
  private
19
31
 
32
+ def auth_token
33
+ @auth_token ||= SecureRandom.uuid
34
+ end
35
+
20
36
  def basic_authorization
21
37
  'basic ' + Base64.strict_encode64(username + ':' + password)
22
38
  end
39
+
40
+ def bearer_authorization
41
+ 'bearer ' + auth_token
42
+ end
23
43
  end
@@ -11,8 +11,10 @@ module ClientApiBuilder
11
11
  end
12
12
  end
13
13
 
14
- autoload :Router, 'client_api_builder/router'
14
+ autoload :NestedRouter, 'client_api_builder/nested_router'
15
15
  autoload :QueryParams, 'client_api_builder/query_params'
16
+ autoload :Router, 'client_api_builder/router'
17
+ autoload :Section, 'client_api_builder/section'
16
18
 
17
19
  module NetHTTP
18
20
  autoload :Request, 'client_api_builder/net_http_request'
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Purpose: to nest routers, which are sub sections of APIs
4
+ # for example if you had an entire section of your API dedicatd to user management.
5
+ # you may want to nest all calls to those routes under the user section
6
+ # ex: client.users.get_user(id: 1) # where users is a nested router
7
+ module ClientApiBuilder
8
+ class NestedRouter
9
+ include ::ClientApiBuilder::Router
10
+
11
+ attr_reader :root_router
12
+
13
+ def initialize(root_router)
14
+ @root_router = root_router
15
+ end
16
+
17
+ def self.get_instance_method(var)
18
+ "\#{root_router.#{var}\}"
19
+ end
20
+
21
+ def request(**options, &block)
22
+ root_router.request(**options, &block)
23
+ end
24
+
25
+ def stream(**options, &block)
26
+ root_router.stream(**options, &block)
27
+ end
28
+
29
+ def stream_to_io(**options, &block)
30
+ root_router.stream_to_io(**options, &block)
31
+ end
32
+
33
+ def stream_to_file(**options, &block)
34
+ root_router.stream_to_file(**options, &block)
35
+ end
36
+
37
+ def base_url(options)
38
+ self.class.base_url || root_router.base_url(options)
39
+ end
40
+
41
+ def build_headers(options)
42
+ headers = root_router.build_headers(options)
43
+
44
+ add_header_proc = proc do |name, value|
45
+ headers[name] =
46
+ if value.is_a?(Proc)
47
+ root_router.instance_eval(&value)
48
+ elsif value.is_a?(Symbol)
49
+ root_router.send(value)
50
+ else
51
+ value
52
+ end
53
+ end
54
+
55
+ self.class.headers.each(&add_header_proc)
56
+
57
+ headers
58
+ end
59
+
60
+ def build_connection_options(options)
61
+ root_router.build_connection_options(options)
62
+ end
63
+
64
+ def build_query(query, options)
65
+ return nil if query.nil? && root_router.class.query_params.empty? && self.class.query_params.empty?
66
+
67
+ query_params = {}
68
+
69
+ add_query_param_proc = proc do |name, value|
70
+ query_params[name] =
71
+ if value.is_a?(Proc)
72
+ root_router.instance_eval(&value)
73
+ elsif value.is_a?(Symbol)
74
+ root_router.send(value)
75
+ else
76
+ value
77
+ end
78
+ end
79
+
80
+ root_router.class.query_params.each(&add_query_param_proc)
81
+ self.class.query_params.each(&add_query_param_proc)
82
+ query && query.each(&add_query_param_proc)
83
+ options[:query] && options[:query].each(&add_query_param_proc)
84
+
85
+ self.class.build_query(query_params)
86
+ end
87
+
88
+ def build_body(body, options)
89
+ root_router.build_body(body, options)
90
+ end
91
+
92
+ def expected_response_code!(response, expected_response_codes, options)
93
+ root_router.expected_response_code!(response, expected_response_codes, options)
94
+ end
95
+
96
+ def handle_response(response, options, &block)
97
+ root_router.handle_response(response, options, &block)
98
+ end
99
+ end
100
+ end
@@ -6,8 +6,9 @@ module ClientApiBuilder
6
6
  def self.included(base)
7
7
  base.extend InheritanceHelper::Methods
8
8
  base.extend ClassMethods
9
+ base.include ::ClientApiBuilder::Section
9
10
  base.include ::ClientApiBuilder::NetHTTP::Request
10
- base.attr_reader :response, :request_options
11
+ base.send(:attr_reader, :response, :request_options)
11
12
  end
12
13
 
13
14
  module ClassMethods
@@ -20,19 +21,39 @@ module ClientApiBuilder
20
21
  def default_options
21
22
  {
22
23
  base_url: nil,
23
- headers: {},
24
+ body_builder: :to_json,
24
25
  connection_options: {},
26
+ headers: {},
27
+ query_builder: Hash.method_defined?(:to_query) ? :to_query : :query_params,
25
28
  query_params: {},
26
- query_builder: Hash.method_defined?(:to_query) ? :to_query : :query_params
29
+ response_procs: {}
27
30
  }.freeze
28
31
  end
29
32
 
33
+
34
+ def add_response_procs(method_name, proc)
35
+ response_procs = default_options[:response_procs].dup
36
+ response_procs[method_name] = proc
37
+ add_value_to_class_method(:default_options, response_procs: response_procs)
38
+ end
39
+
40
+ def response_proc(method_name)
41
+ proc = default_options[:response_procs][method_name]
42
+ proc
43
+ end
44
+
30
45
  def base_url(url = nil)
31
46
  return default_options[:base_url] unless url
32
47
 
33
48
  add_value_to_class_method(:default_options, base_url: url)
34
49
  end
35
50
 
51
+ def body_builder(builder = nil)
52
+ return default_options[:body_builder] unless builder
53
+
54
+ add_value_to_class_method(:default_options, body_builder: builder)
55
+ end
56
+
36
57
  def query_builder(builder = nil)
37
58
  return default_options[:query_builder] unless builder
38
59
 
@@ -69,6 +90,21 @@ module ClientApiBuilder
69
90
  default_options[:query_params]
70
91
  end
71
92
 
93
+ def build_body(router, body, options)
94
+ builder = options[:body_builder] || body_builder
95
+
96
+ case builder
97
+ when :to_json
98
+ body.to_json
99
+ when :to_query
100
+ body.to_query
101
+ when :query_params
102
+ ClientApiBuilder::QueryParams.to_query(body)
103
+ else
104
+ router.instance_exec(body, &builder)
105
+ end
106
+ end
107
+
72
108
  def build_query(query)
73
109
  case query_builder
74
110
  when :to_query
@@ -111,6 +147,8 @@ module ClientApiBuilder
111
147
  arguments += get_hash_arguments(v)
112
148
  when Array
113
149
  arguments += get_array_arguments(v)
150
+ when String
151
+ hsh[k] = "__||#{$1}||__" if v =~ /\{([a-z0-9_]+)\}/i
114
152
  end
115
153
  end
116
154
  arguments
@@ -127,6 +165,8 @@ module ClientApiBuilder
127
165
  arguments += get_hash_arguments(v)
128
166
  when Array
129
167
  arguments += get_array_arguments(v)
168
+ when String
169
+ list[idx] = "__||#{$1}||__" if v =~ /\{([a-z0-9_]+)\}/i
130
170
  end
131
171
  end
132
172
  arguments
@@ -143,9 +183,31 @@ module ClientApiBuilder
143
183
  end
144
184
  end
145
185
 
186
+ def get_instance_method(var)
187
+ "#\{#{var}\}"
188
+ end
189
+
190
+ @@namespaces = []
191
+ def namespaces
192
+ @@namespaces
193
+ end
194
+
195
+ def namespace(name)
196
+ namespaces << name
197
+ yield
198
+ namespaces.pop
199
+ end
200
+
146
201
  def generate_route_code(method_name, path, options = {})
147
202
  http_method = options[:method] || http_method(method_name)
148
203
 
204
+ path = namespaces.join + path
205
+
206
+ # instance method
207
+ path.gsub!(/\{([a-z0-9_]+)\}/i) do |_|
208
+ get_instance_method($1)
209
+ end
210
+
149
211
  path_arguments = []
150
212
  path.gsub!(/:([a-z0-9_]+)/i) do |_|
151
213
  path_arguments << $1
@@ -207,6 +269,7 @@ module ClientApiBuilder
207
269
  method_args += ['**__options__', '&block']
208
270
 
209
271
  code = "def #{method_name}(" + method_args.join(', ') + ")\n"
272
+ code += " block ||= self.class.response_proc(#{method_name.inspect})\n"
210
273
  code += " __path__ = \"#{path}\"\n"
211
274
  code += " __query__ = #{query}\n"
212
275
  code += " __body__ = #{body}\n"
@@ -244,13 +307,15 @@ module ClientApiBuilder
244
307
  code
245
308
  end
246
309
 
247
- def route(method_name, path, options = {})
310
+ def route(method_name, path, options = {}, &block)
311
+ add_response_procs(method_name, block) if block
312
+
248
313
  self.class_eval generate_route_code(method_name, path, options), __FILE__, __LINE__
249
314
  end
250
315
  end
251
316
 
252
317
  def base_url(options)
253
- self.class.base_url
318
+ options[:base_url] || self.class.base_url
254
319
  end
255
320
 
256
321
  def build_headers(options)
@@ -268,7 +333,7 @@ module ClientApiBuilder
268
333
  end
269
334
 
270
335
  self.class.headers.each(&add_header_proc)
271
- options[:headers]&.each(&add_header_proc)
336
+ options[:headers] && options[:headers].each(&add_header_proc)
272
337
 
273
338
  headers
274
339
  end
@@ -282,6 +347,8 @@ module ClientApiBuilder
282
347
  end
283
348
 
284
349
  def build_query(query, options)
350
+ return nil if query.nil? && self.class.query_params.empty?
351
+
285
352
  query_params = {}
286
353
 
287
354
  add_query_param_proc = proc do |name, value|
@@ -296,22 +363,24 @@ module ClientApiBuilder
296
363
  end
297
364
 
298
365
  self.class.query_params.each(&add_query_param_proc)
299
- query&.each(&add_query_param_proc)
366
+ query && query.each(&add_query_param_proc)
367
+ options[:query] && options[:query].each(&add_query_param_proc)
300
368
 
301
369
  self.class.build_query(query_params)
302
370
  end
303
371
 
304
372
  def build_body(body, options)
305
- return unless body
373
+ body = options[:body] if options.key?(:body)
374
+
375
+ return nil unless body
306
376
  return body if body.is_a?(String)
307
377
 
308
- body.merge!(options[:body]) if options[:body]
309
- body.to_json
378
+ self.class.build_body(self, body, options)
310
379
  end
311
380
 
312
381
  def build_uri(path, query, options)
313
382
  uri = URI(base_url(options) + path)
314
- uri.query = build_query(query, options) if query
383
+ uri.query = build_query(query, options)
315
384
  uri
316
385
  end
317
386
 
@@ -343,5 +412,9 @@ module ClientApiBuilder
343
412
  data
344
413
  end
345
414
  end
415
+
416
+ def root_router
417
+ self
418
+ end
346
419
  end
347
420
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Purpose is to encapsulate adding nested routers
4
+ module ClientApiBuilder
5
+ module Section
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ def section(name, &block)
12
+ kls = InheritanceHelper::ClassBuilder::Utils.create_class(
13
+ self,
14
+ name,
15
+ ::ClientApiBuilder::NestedRouter,
16
+ nil,
17
+ 'NestedRouter',
18
+ &block
19
+ )
20
+
21
+ code = <<CODE
22
+ def self.#{name}_router
23
+ #{kls.name}
24
+ end
25
+
26
+ def #{name}
27
+ @#{name} ||= self.class.#{name}_router.new(self.root_router)
28
+ end
29
+ CODE
30
+ self.class_eval code, __FILE__, __LINE__
31
+ end
32
+ end
33
+ end
34
+ end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: client-api-builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.8
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-17 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2021-08-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: inheritance-helper
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 0.2.5
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.2.5
13
27
  description: Utility for constructing API clients
14
28
  email: dougyouch@gmail.com
15
29
  executables: []
@@ -27,9 +41,11 @@ files:
27
41
  - examples/basic_auth_example_client.rb
28
42
  - examples/imdb_datasets_client.rb
29
43
  - lib/client-api-builder.rb
44
+ - lib/client_api_builder/nested_router.rb
30
45
  - lib/client_api_builder/net_http_request.rb
31
46
  - lib/client_api_builder/query_params.rb
32
47
  - lib/client_api_builder/router.rb
48
+ - lib/client_api_builder/section.rb
33
49
  - script/console
34
50
  homepage: https://github.com/dougyouch/client-api-builder
35
51
  licenses: