petail 0.3.0 → 0.4.0

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: e974c64e0259b3561130ac6d4ea4a7bae80fa381e9ab7ee7cb3ab0338d9fe2c4
4
- data.tar.gz: 6e99b146485fd8da57711ed81512ee599b10797fbc5cfb2072941927f78bd640
3
+ metadata.gz: 90ccb34a36bfc17201ce4b4881e8f37acbbc3714f076c5de0e7cf1d5ad373346
4
+ data.tar.gz: 0a2ee99b2cd3191aa9ec08473a218436bd259576f3b0e6e31c8779eddcbfe5b0
5
5
  SHA512:
6
- metadata.gz: 4f6aa6a580106976c75f5f7db20362ffad7be1c6d25c1c55ed19dafe8c7ea7b50387213dcac51395d8cb9f6fe9aa5160bd1879fb20b9cbde9b48dedfdbe63822
7
- data.tar.gz: a51c4609448bc36218c226c4f71a1b3fd99d46d4d16c666c4b009d76f1020ec2ee2634f156fbbaecd6012c9f4149409d45d44d00e378e5ecbc1eef1037db9bb9
6
+ metadata.gz: b663a7f90af61259298cd14a8f23c63afbd0d0e6aab80c3c38c0242e7cc240418563021623bcf95e7af88e3368788bdad8ac30178431788f4cc3e552219815b7
7
+ data.tar.gz: 6efc822522e3e828df9c2d81953a8b2a2344e6ec9aec462ebe063cb32feffcc10552b4f874b67888d3e347875a805621df317b429d2a57227ecb55e061530611
checksums.yaml.gz.sig CHANGED
Binary file
data/README.adoc CHANGED
@@ -86,6 +86,17 @@ payload.to_xml
86
86
 
87
87
  💡 You can also use `Petail.new` to create instances if you don't like `Petail.[]`, as shown above, but `.[]` is preferred.
88
88
 
89
+ === Members
90
+
91
+ As briefly shown above, the minimum members (attributes) that make up problem details are:
92
+
93
+ * `type` (optional): The full (or relative) URI that links to additional documentation. Default: `"about:blank"`.
94
+ * `status` (optional): The link:https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status[HTTP status code] (or symbol) that must match your HTTP status code. Default: `nil`.
95
+ * `title` (optional): The HTTP status label that must match your HTTP status code label. Default: HTTP status label (dynamically computed based on code unless overwritten).
96
+ * `detail` (optional): The human readable reason for the error (should not include debugging information). Default: `nil`.
97
+ * `instance` (optional): The full (or relative) URI that represents the cause of the error. Default: `nil`.
98
+ * `extensions` (optional): A free form hash of additional details. Default: `{}`.
99
+
89
100
  === Media Types
90
101
 
91
102
  For convenience, you can obtain the necessary media types for your HTTP headers as follows:
@@ -187,7 +198,7 @@ This means you can serialize as follows:
187
198
  [source,ruby]
188
199
  ----
189
200
  payload.to_json
190
- # {"type":"https://test.io/problem_details/out_of_credit","title":"You do not have enough credit.","status":403,"detail":"Your current balance is 30, but that costs 50.","instance":"/accounts/1","extensions":{"balance":30,"accounts":["/accounts/1","/accounts/10"]}}
201
+ # "{\"type\":\"https://test.io/problem_details/out_of_credit\",\"title\":\"You do not have enough credit.\",\"status\":403,\"detail\":\"Your current balance is 30, but that costs 50.\",\"instance\":\"/accounts/1\",\"balance\":30,\"accounts\":[\"/accounts/1\",\"/accounts/10\"]}"
191
202
 
192
203
  payload.to_json indent: " ", space: " ", object_nl: "\n", array_nl: "\n"
193
204
  # {
@@ -196,13 +207,11 @@ payload.to_json indent: " ", space: " ", object_nl: "\n", array_nl: "\n"
196
207
  # "status": 403,
197
208
  # "detail": "Your current balance is 30, but that costs 50.",
198
209
  # "instance": "/accounts/1",
199
- # "extensions": {
200
- # "balance": 30,
201
- # "accounts": [
202
- # "/accounts/1",
203
- # "/accounts/10"
204
- # ]
205
- # }
210
+ # "balance": 30,
211
+ # "accounts": [
212
+ # "/accounts/1",
213
+ # "/accounts/10"
214
+ # ]
206
215
  # }
207
216
  ----
208
217
 
@@ -212,7 +221,7 @@ You can also deserialize by taking the result of the above and turning the raw J
212
221
 
213
222
  [source,ruby]
214
223
  ----
215
- Petail.from_json "{\"type\":\"https://test.io/problem_details/out_of_credit\",\"title\":\"You do not have enough credit.\",\"status\":403,\"detail\":\"Your current balance is 30, but that costs 50.\",\"instance\":\"/accounts/1\",\"extensions\":{\"balance\":30,\"accounts\":[\"/accounts/1\",\"/accounts/10\"]}}"
224
+ Petail.from_json "{\"type\":\"https://test.io/problem_details/out_of_credit\",\"title\":\"You do not have enough credit.\",\"status\":403,\"detail\":\"Your current balance is 30, but that costs 50.\",\"instance\":\"/accounts/1\",\"balance\":30,\"accounts\":[\"/accounts/1\",\"/accounts/10\"]}"
216
225
 
217
226
  # #<Struct:Petail::Payload:0x00007670
218
227
  # detail = "Your current balance is 30, but that costs 50.",
@@ -353,6 +362,13 @@ To test, run:
353
362
  bin/rake
354
363
  ----
355
364
 
365
+ == Resources
366
+
367
+ You can find additional resources here:
368
+
369
+ * link:https://www.iana.org/assignments/http-problem-types/http-problem-types.xhtml[IANA Hypertext Transfer Protocol (HTTP) Problem Types]: A registered list of problem types you can use.
370
+ * link:https://github.com/protocol-registries/http-problem-types[HTTP Problem Type Registration Requests]: Where you can register new problem types.
371
+
356
372
  == link:https://alchemists.io/policies/license[License]
357
373
 
358
374
  == link:https://alchemists.io/policies/security[Security]
@@ -5,8 +5,10 @@ require "rack/utils"
5
5
  require "rexml"
6
6
 
7
7
  module Petail
8
+ PRIMARY_KEYS = %i[type title status detail instance].freeze
9
+
8
10
  # Models the problem details response payload.
9
- Payload = Struct.new :type, :title, :status, :detail, :instance, :extensions do
11
+ Payload = Struct.new(*PRIMARY_KEYS, :extensions) do
10
12
  def self.for(**attributes)
11
13
  status = attributes.delete(:status).then { Rack::Utils.status_code it if it }
12
14
  title = attributes.delete(:title).then { it || Rack::Utils::HTTP_STATUS_CODES[status] }
@@ -14,18 +16,23 @@ module Petail
14
16
  new title:, status:, **attributes
15
17
  end
16
18
 
17
- def self.from_json(body) = self.for(**JSON(body, symbolize_names: true))
19
+ def self.from_json body
20
+ attributes = JSON body, symbolize_names: true
21
+ extensions = attributes.reject { |key| PRIMARY_KEYS.include? key }
22
+
23
+ self.for(**attributes.slice(*PRIMARY_KEYS), extensions:)
24
+ end
18
25
 
19
26
  # :reek:TooManyStatements
20
27
  def self.from_xml body, deserializer: XML::Deserializer
21
28
  elements = REXML::Document.new(body).root.elements
22
29
 
23
30
  attributes = elements.each_with_object({extensions: {}}) do |element, collection|
24
- name = element.name
31
+ name = element.name.to_sym
25
32
  text = element.text
26
33
 
27
34
  case name
28
- when "type", "title", "detail", "instance", "status" then collection[name.to_sym] = text
35
+ when *PRIMARY_KEYS then collection[name] = text
29
36
  else collection[:extensions].merge! deserializer.call(element)
30
37
  end
31
38
  end
@@ -48,11 +55,12 @@ module Petail
48
55
 
49
56
  def extension?(name) = extensions.key? name
50
57
 
51
- def to_h = super.compact.tap { it.delete :extensions if extensions.empty? }
58
+ def to_h = {type:, title:, status:, detail:, instance:, **extensions}.compact
52
59
 
53
60
  def to_json(*) = to_h.to_json(*)
54
61
 
55
62
  # :reek:TooManyStatements
63
+ # :reek:FeatureEnvy
56
64
  def to_xml(serializer: XML::Serializer, **options)
57
65
  document = REXML::Document.new
58
66
  document.add REXML::XMLDecl.new("1.0", "UTF-8")
@@ -60,9 +68,7 @@ module Petail
60
68
  problem = REXML::Element.new("problem").add_namespace("urn:ietf:rfc:7807")
61
69
  document.add problem
62
70
 
63
- attributes = to_h
64
- attributes.merge! attributes.delete :extensions if extensions.any?
65
- attributes.each { |name, value| serializer.call name, value, problem }
71
+ to_h.each { |name, value| serializer.call name, value, problem }
66
72
 
67
73
  "".dup.tap { document.write(**options, output: it) }
68
74
  end
data/petail.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "petail"
5
- spec.version = "0.3.0"
5
+ spec.version = "0.4.0"
6
6
  spec.authors = ["Brooke Kuhlmann"]
7
7
  spec.email = ["brooke@alchemists.io"]
8
8
  spec.homepage = "https://alchemists.io/projects/petail"
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: petail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brooke Kuhlmann
metadata.gz.sig CHANGED
Binary file