reynard 0.4.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee7b44612f3b8e0fec7a0a3d1e18bc30366d1bea3470a8b3eb46a8d6180c85cd
4
- data.tar.gz: e6c9629439a36c50ff214f932d5997ce004b21d12a1c71a6c727ab7ef38d722a
3
+ metadata.gz: 891b142e2ca908078c960db4b36040b407974da43395d02b7ee3f768a1c758b4
4
+ data.tar.gz: 5deefbad34c0364cfd3c3a04910b882499c3b96d7c4d642fdb10fbdcc3e9898f
5
5
  SHA512:
6
- metadata.gz: 159960b7bb4e38673c00baa531e3586135dcabedadd50dbd12da55eee31b77ea4d4d7d182974144adf602128b664e5d28f3c15c9e6d376f96774e78a45cdba2c
7
- data.tar.gz: 5a7cbcc61c772858283855289f9b3a119b716c78994d16a4de6d5f34bc6921c633f023d362417657253885e6c35f157cb98d2771228e2790a0dff4df69bcc7f2
6
+ metadata.gz: 393b2cab7867c45c53b761f42b03e609e653600981aa1283cf3f7c0213379adb9a1eeb1530b3a0f826e07d1ceb5c3fab0d8afc114a2001ec773f71d2484bc109
7
+ data.tar.gz: c14ebcc55481c202c0c3c7b7dd36e68eb5b72f0297c0339867e6c298d4a3605284dff2e041d33b074c8c9e3b8064f450fdf481fa686e56c1e764af59a1050593
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rack'
4
+
5
+ class Reynard
6
+ # Loads external references.
7
+ class External
8
+ def initialize(path:, ref:)
9
+ @path = path
10
+ @relative_path, @anchor = ref.split('#', 2)
11
+ end
12
+
13
+ def path
14
+ return [] unless @anchor
15
+
16
+ @anchor.split('/')[1..]
17
+ end
18
+
19
+ def data
20
+ File.open(filename, encoding: 'UTF-8') do |file|
21
+ YAML.safe_load(file, aliases: true)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def filename
28
+ filename = File.expand_path(@relative_path, @path)
29
+ return filename if filename.start_with?(@path)
30
+
31
+ raise 'You are only allowed to reference files relative to the specification file.'
32
+ end
33
+ end
34
+ end
@@ -15,7 +15,7 @@ class Reynard
15
15
  end
16
16
 
17
17
  def perform
18
- @request_context.logger.info { "#{@request_context.verb.upcase} #{uri}" }
18
+ @request_context.logger&.info { "#{@request_context.verb.upcase} #{uri}" }
19
19
  Reynard.http.request(uri, build_request)
20
20
  end
21
21
 
@@ -28,7 +28,7 @@ class Reynard
28
28
  def build_request
29
29
  request = request_class.new(uri, @request_context.headers)
30
30
  if @request_context.body
31
- @request_context.logger.debug { @request_context.body }
31
+ @request_context.logger&.debug { @request_context.body }
32
32
  request.body = @request_context.body
33
33
  end
34
34
  request
@@ -14,6 +14,8 @@ class Reynard
14
14
  def object_class
15
15
  if @media_type.schema_name
16
16
  self.class.model_class(@media_type.schema_name, @schema.object_type)
17
+ elsif @schema.object_type == 'array'
18
+ Array
17
19
  else
18
20
  Reynard::Model
19
21
  end
@@ -46,7 +48,7 @@ class Reynard
46
48
  end
47
49
 
48
50
  def self.model_class_get(name)
49
- Reynard::Models.const_get(name)
51
+ Kernel.const_get("::Reynard::Models::#{name}")
50
52
  rescue NameError
51
53
  nil
52
54
  end
@@ -3,6 +3,7 @@
3
3
  class Reynard
4
4
  # Value class for details about the request.
5
5
  RequestContext = Struct.new(
6
+ :logger,
6
7
  :base_url,
7
8
  :operation,
8
9
  :headers,
@@ -10,14 +11,6 @@ class Reynard
10
11
  :body,
11
12
  keyword_init: true
12
13
  ) do
13
- attr_writer :logger
14
-
15
- def logger
16
- @logger = nil unless defined?(@logger)
17
-
18
- Reynard::Logger.new(@logger)
19
- end
20
-
21
14
  def verb
22
15
  operation&.verb
23
16
  end
@@ -102,11 +102,22 @@ class Reynard
102
102
 
103
103
  def self.normalize_model_name(name)
104
104
  # 1. Unescape encoded characters to create an UTF-8 string
105
- # 2. Replace all non-alphabetic characters with a space (not allowed in Ruby constant)
106
- # 3. Camelcase
105
+ # 2. Remove extensions for regularly used external schema files
106
+ # 3. Replace all non-alphabetic characters with a space (not allowed in Ruby constant)
107
+ # 4. Camelcase
107
108
  Rack::Utils.unescape_path(name)
109
+ .gsub(/(.yml|.yaml|.json)\Z/, '')
108
110
  .gsub(/[^[:alpha:]]/, ' ')
109
111
  .gsub(/(\s+)([[:alpha:]])/) { Regexp.last_match(2).upcase }
112
+ .gsub(/\A(.)/) { Regexp.last_match(1).upcase }
113
+ end
114
+
115
+ def self.normalize_model_title(title)
116
+ title
117
+ .gsub(/[^[:alpha:]]/, ' ')
118
+ .gsub(/\s{2,}/, ' ')
119
+ .gsub(/(\s+)([[:alpha:]])/) { Regexp.last_match(2).upcase }
120
+ .strip
110
121
  end
111
122
 
112
123
  private
@@ -117,6 +128,9 @@ class Reynard
117
128
  end
118
129
  end
119
130
 
131
+ # rubocop:disable Metrics/AbcSize
132
+ # rubocop:disable Metrics/CyclomaticComplexity
133
+ # rubocop:disable Metrics/MethodLength
120
134
  def dig_into(data, cursor, path)
121
135
  while path.length.positive?
122
136
  cursor = cursor[path.first]
@@ -125,25 +139,44 @@ class Reynard
125
139
  path.shift
126
140
  next unless cursor.respond_to?(:key?) && cursor&.key?('$ref')
127
141
 
128
- # We currenly only supply references inside the document starting with #/.
129
- path = Rack::Utils.unescape_path(cursor['$ref'][2..]).split('/') + path
130
- cursor = data
142
+ case cursor['$ref']
143
+ # References another element in the current specification.
144
+ when %r{\A#/}
145
+ path = Rack::Utils.unescape_path(cursor['$ref'][2..]).split('/') + path
146
+ cursor = data
147
+ # References another file, with an optional anchor to an element in the data.
148
+ when %r{\A\./}
149
+ external = External.new(path: File.dirname(@filename), ref: cursor['$ref'])
150
+ path = external.path + path
151
+ cursor = external.data
152
+ end
131
153
  end
132
154
  cursor
133
155
  end
156
+ # rubocop:enable Metrics/AbcSize
157
+ # rubocop:enable Metrics/CyclomaticComplexity
158
+ # rubocop:enable Metrics/MethodLength
134
159
 
135
160
  def schema_name(response)
136
- ref = response.dig('schema', '$ref')
137
- return unless ref
138
-
139
- self.class.normalize_model_name(ref&.split('/')&.last)
161
+ extract_schema_name(response['schema'])
140
162
  end
141
163
 
142
164
  def item_schema_name(schema)
143
- ref = schema.dig('items', '$ref')
144
- return unless ref
165
+ if schema['type'] == 'array'
166
+ extract_schema_name(schema['items'])
167
+ else
168
+ extract_schema_name(schema)
169
+ end
170
+ end
171
+
172
+ def extract_schema_name(definition)
173
+ ref = definition['$ref']
174
+ return self.class.normalize_model_name(ref&.split('/')&.last) if ref
175
+
176
+ title = definition['title']
177
+ return unless title
145
178
 
146
- self.class.normalize_model_name(ref&.split('/')&.last)
179
+ self.class.normalize_model_title(title)
147
180
  end
148
181
  end
149
182
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Reynard
4
- VERSION = '0.4.0'
4
+ VERSION = '0.5.1'
5
5
  end
data/lib/reynard.rb CHANGED
@@ -11,10 +11,11 @@ require 'uri'
11
11
  # OpenAPI specification.
12
12
  class Reynard
13
13
  extend Forwardable
14
- def_delegators :build_context, :base_url, :operation, :headers, :params
14
+ def_delegators :build_context, :logger, :base_url, :operation, :headers, :params
15
15
  def_delegators :@specification, :servers
16
16
 
17
17
  autoload :Context, 'reynard/context'
18
+ autoload :External, 'reynard/external'
18
19
  autoload :GroupedParameters, 'reynard/grouped_parameters'
19
20
  autoload :Http, 'reynard/http'
20
21
  autoload :Logger, 'reynard/logger'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: reynard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manfred Stienstra
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-04-05 00:00:00.000000000 Z
11
+ date: 2022-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -121,11 +121,11 @@ files:
121
121
  - README.md
122
122
  - lib/reynard.rb
123
123
  - lib/reynard/context.rb
124
+ - lib/reynard/external.rb
124
125
  - lib/reynard/grouped_parameters.rb
125
126
  - lib/reynard/http.rb
126
127
  - lib/reynard/http/request.rb
127
128
  - lib/reynard/http/response.rb
128
- - lib/reynard/logger.rb
129
129
  - lib/reynard/media_type.rb
130
130
  - lib/reynard/model.rb
131
131
  - lib/reynard/models.rb
@@ -143,7 +143,7 @@ licenses:
143
143
  - MIT
144
144
  metadata:
145
145
  rubygems_mfa_required: 'true'
146
- post_install_message:
146
+ post_install_message:
147
147
  rdoc_options: []
148
148
  require_paths:
149
149
  - lib
@@ -159,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
159
  version: '0'
160
160
  requirements: []
161
161
  rubygems_version: 3.1.6
162
- signing_key:
162
+ signing_key:
163
163
  specification_version: 4
164
164
  summary: Minimal OpenAPI client.
165
165
  test_files: []
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'logger'
4
-
5
- class Reynard
6
- # Proxy class for a Logger object. Makes irrelevant logging actions a no-op.
7
- class Logger
8
- def initialize(logger)
9
- @logger = logger
10
- end
11
-
12
- def debug(&block)
13
- return unless @logger
14
- return if @logger.level > ::Logger::DEBUG
15
-
16
- @logger.debug(block.call)
17
- end
18
-
19
- def info(&block)
20
- return unless @logger
21
- return if @logger.level > ::Logger::INFO
22
-
23
- @logger.info(block.call)
24
- end
25
- end
26
- end