reynard 0.3.0 → 0.5.0

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: bb3f70f5fd68cbe19747d08687715165595d29a6b81da3329a975cac30bd3cbe
4
- data.tar.gz: 73020e57c19507af7ac13b9bc509d3a4b7ac3ae9b74866d7f783cc816436039f
3
+ metadata.gz: d85681365ddb46b06c062b39bdea10bf46810d80d813c9737df0ee3aeb38e843
4
+ data.tar.gz: '007908692ed0a3c437befba30a29d0f694429033097b7ae47ca2aaf7050c05ea'
5
5
  SHA512:
6
- metadata.gz: d5289ec587b945a0a858537569a4543e9bbfb3daa6dcb8e11cc409d620b89c1d85406d4a1fa0d20dcccc4c00eddcc6ff4b03f5ae24feff865f4cb62661f09638
7
- data.tar.gz: af30bf905849a2589e091b0b9655e52375db08ea00f0b28365029900b4101c5d4b94822b30a17b3a1b8e35329886b61cf857fa6d8de1ff6a16fd56ca0dc2a904
6
+ metadata.gz: 1e23bf2702660623a61039bb8444ea3e0f4bdd7eaceab42d22dbca2ea157e705a54af69558ce84812d4ef2f13c1330071781e8da61d3ed44c8b0d2a944816752
7
+ data.tar.gz: 9e973c551d30a6b7e6ab8ef494d270b5dac05dfd96de86c49448c5889b070a07ee8d8aa486de1f80c3d372c73ed7b11e37da49625fdeb64d21e84352130e55f3
data/README.md CHANGED
@@ -81,6 +81,22 @@ response['Content-Type'] #=> 'application/json'
81
81
  response.body #=> '{"name":"Sam Seven"}'
82
82
  ```
83
83
 
84
+ ## Logging
85
+
86
+ When you want to know what the Reynard client is doing you can enable logging.
87
+
88
+ ```ruby
89
+ logger = Logger.new($stdout)
90
+ logger.level = Logger::INFO
91
+ reynard.logger(logger).execute
92
+ ```
93
+
94
+ The logging should be compatible with the Ruby on Rails logger.
95
+
96
+ ```ruby
97
+ reynard.logger(Rails.logger).execute
98
+ ```
99
+
84
100
  ## Mocking
85
101
 
86
102
  You can mock Reynard requests by changing the HTTP implementation. The class **must** implement a single `request` method that accepts an URI and net/http request object. It **must** return a net/http response object or an object with the exact same interface.
@@ -97,4 +113,4 @@ end
97
113
 
98
114
  ## Copyright and other legal
99
115
 
100
- See LICENCE.
116
+ See LICENCE.
@@ -42,6 +42,10 @@ class Reynard
42
42
  copy(headers: @request_context.headers.merge(headers))
43
43
  end
44
44
 
45
+ def logger(logger)
46
+ copy(logger: logger)
47
+ end
48
+
45
49
  def execute
46
50
  build_response(build_request.perform)
47
51
  end
@@ -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,50 +15,23 @@ class Reynard
15
15
  end
16
16
 
17
17
  def perform
18
+ @request_context.logger&.info { "#{@request_context.verb.upcase} #{uri}" }
18
19
  Reynard.http.request(uri, build_request)
19
20
  end
20
21
 
21
22
  private
22
23
 
23
- def build_request
24
- case @request_context.verb
25
- when 'get'
26
- build_http_get
27
- when 'post'
28
- build_http_post
29
- when 'put'
30
- build_http_put
31
- when 'patch'
32
- build_http_patch
33
- when 'delete'
34
- build_http_delete
35
- end
36
- end
37
-
38
- def build_http_get
39
- Net::HTTP::Get.new(uri, @request_context.headers)
40
- end
41
-
42
- def build_http_post
43
- post = Net::HTTP::Post.new(uri, @request_context.headers)
44
- post.body = @request_context.body
45
- post
24
+ def request_class
25
+ Net::HTTP.const_get(@request_context.verb.capitalize)
46
26
  end
47
27
 
48
- def build_http_put
49
- put = Net::HTTP::Put.new(uri, @request_context.headers)
50
- put.body = @request_context.body
51
- put
52
- end
53
-
54
- def build_http_patch
55
- patch = Net::HTTP::Patch.new(uri, @request_context.headers)
56
- patch.body = @request_context.body
57
- patch
58
- end
59
-
60
- def build_http_delete
61
- Net::HTTP::Delete.new(uri, @request_context.headers)
28
+ def build_request
29
+ request = request_class.new(uri, @request_context.headers)
30
+ if @request_context.body
31
+ @request_context.logger&.debug { @request_context.body }
32
+ request.body = @request_context.body
33
+ end
34
+ request
62
35
  end
63
36
  end
64
37
  end
@@ -46,7 +46,7 @@ class Reynard
46
46
  end
47
47
 
48
48
  def self.model_class_get(name)
49
- Reynard::Models.const_get(name)
49
+ Kernel.const_get("::Reynard::Models::#{name}")
50
50
  rescue NameError
51
51
  nil
52
52
  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,
@@ -102,11 +102,14 @@ 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 }
110
113
  end
111
114
 
112
115
  private
@@ -117,6 +120,9 @@ class Reynard
117
120
  end
118
121
  end
119
122
 
123
+ # rubocop:disable Metrics/AbcSize
124
+ # rubocop:disable Metrics/CyclomaticComplexity
125
+ # rubocop:disable Metrics/MethodLength
120
126
  def dig_into(data, cursor, path)
121
127
  while path.length.positive?
122
128
  cursor = cursor[path.first]
@@ -125,12 +131,23 @@ class Reynard
125
131
  path.shift
126
132
  next unless cursor.respond_to?(:key?) && cursor&.key?('$ref')
127
133
 
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
134
+ case cursor['$ref']
135
+ # References another element in the current specification.
136
+ when %r{\A#/}
137
+ path = Rack::Utils.unescape_path(cursor['$ref'][2..]).split('/') + path
138
+ cursor = data
139
+ # References another file, with an optional anchor to an element in the data.
140
+ when %r{\A\./}
141
+ external = External.new(path: File.dirname(@filename), ref: cursor['$ref'])
142
+ path = external.path + path
143
+ cursor = external.data
144
+ end
131
145
  end
132
146
  cursor
133
147
  end
148
+ # rubocop:enable Metrics/AbcSize
149
+ # rubocop:enable Metrics/CyclomaticComplexity
150
+ # rubocop:enable Metrics/MethodLength
134
151
 
135
152
  def schema_name(response)
136
153
  ref = response.dig('schema', '$ref')
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Reynard
4
- VERSION = '0.3.0'
4
+ VERSION = '0.5.0'
5
5
  end
data/lib/reynard.rb CHANGED
@@ -11,12 +11,14 @@ 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'
21
+ autoload :Logger, 'reynard/logger'
20
22
  autoload :MediaType, 'reynard/media_type'
21
23
  autoload :Model, 'reynard/model'
22
24
  autoload :Models, 'reynard/models'
@@ -34,9 +36,9 @@ class Reynard
34
36
  @specification = Specification.new(filename: filename)
35
37
  end
36
38
 
37
- # Assign an object that follows Reynard's internal request interface to mock requests or use a
38
- # different HTTP client.
39
39
  class << self
40
+ # Assign an object that follows Reynard's internal request interface to mock requests or use a
41
+ # different HTTP client.
40
42
  attr_writer :http
41
43
  end
42
44
 
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.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manfred Stienstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-17 00:00:00.000000000 Z
11
+ date: 2022-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -121,6 +121,7 @@ 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