rfc-reader 1.0.1 → 1.1.1

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: f626967d7919c641d14227967a9e863a2c4eaedfd5f97dc5ecde994349725cd5
4
- data.tar.gz: 303d9bf3449ac2122f27f36f3053dfdbb7350b1f8900662517055aadfe8a6c42
3
+ metadata.gz: cc7f0a4142e7e5cb5b1ed4b8a5ecda4728177df15e6f9c8d7e84dcc557b660e4
4
+ data.tar.gz: 89e7ac7472c84e84d87feed86e66f35f14dbc65a7af39e6a55fe65e158b78e89
5
5
  SHA512:
6
- metadata.gz: 1c29a80e008410ed0924921850c70ad84b8a8bf99e341725a6164757204db8469e7f4eab9c725e04557098251e9d22c23861eb1fbe9a0a9c1cd75603392d9aff
7
- data.tar.gz: 3351a9b4447a38eabf3eb9ba5f0fd50415e9502af6db5fadd236ee1ab848ff57bde6b959b0a6ceaafb337387310f7ca656b9214b7ec063879df51daa275415f7
6
+ metadata.gz: 0b0588ecb6c225fa8d2ef6c1c27506dda70d59ae2af050797f6ed71536a379423848570022223b04f0911ca3d987431d2b28ab4c019c8c74c31ece1fed3e037a
7
+ data.tar.gz: 51e9100f05269801e8fcbbe879405181ae0d3910e42593d5a9ad5933d6d8f3f45214cc8a8827d809b75e920029ccfd7584139c707e6a9e3fe0ec12ca750c4f77
data/exe/rfc-reader CHANGED
@@ -4,4 +4,4 @@
4
4
  $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
5
5
  require "rfc_reader"
6
6
 
7
- exit RfcReader::Command.start
7
+ exit RfcReader.start
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+
5
+ module RfcReader
6
+ module ErrorContext
7
+ class ContextError < StandardError
8
+ extend Forwardable
9
+
10
+ attr_reader :context
11
+
12
+ def_delegators :@cause, :message, :to_s, :backtrace, :backtrace_locations
13
+
14
+ # @param cause [StandardError]
15
+ # @param context [String] (Optional)
16
+ def initialize(cause:, context: nil)
17
+ @cause = cause
18
+ @context = context || default_context
19
+ super
20
+ end
21
+
22
+ def short_message
23
+ "Error: #{context}"
24
+ end
25
+
26
+ def full_message
27
+ <<~MESSAGE
28
+ Context:
29
+ #{indent(@context)}
30
+ Error:
31
+ #{@cause.class}
32
+ Message:
33
+ #{indent(message)}
34
+ Backtrace:
35
+ #{indent(backtrace)}
36
+ MESSAGE
37
+ end
38
+
39
+ private
40
+
41
+ def default_context
42
+ "#{@cause.class}: #{message.lines.first}"
43
+ end
44
+
45
+ def indent(string_or_array)
46
+ strings = case string_or_array
47
+ when Array then string_or_array
48
+ when String then string_or_array.lines
49
+ else raise ArgumentError, "Expected string or array instead of #{string_or_array.class}"
50
+ end
51
+
52
+ strings
53
+ .map { _1.prepend(" ") }
54
+ .join("\n")
55
+ end
56
+ end
57
+
58
+ # Yields an error context. Any `StandardError` that gets raised in this block
59
+ # gets wrapped by `ContextError` automatically and re-raised.
60
+ #
61
+ # If no error has been raised, it returns the result of the block.
62
+ #
63
+ # @param context [String]
64
+ # @return yielded block value
65
+ def self.wrap(context)
66
+ yield
67
+ rescue StandardError => e
68
+ raise ContextError.new(cause: e, context: context)
69
+ end
70
+
71
+ # Yields a handler context where any `StandardError` is caught and the error message is
72
+ # printed to stderr along with the context. It prints only a short message by default
73
+ # and prints the full error message if the `DEBUG` error message is set. It then exits
74
+ # with a non-zero error code.
75
+ #
76
+ # If no error has been raised, it returns the result of the block as an integer.
77
+ # If the result of block cannot be turned into an integer, it returns zero.
78
+ #
79
+ # @return [Integer] exit code
80
+ def self.handler
81
+ error = nil
82
+
83
+ begin
84
+ result = yield
85
+ return result.respond_to?(:to_i) ? result.to_i : 0
86
+ rescue ContextError => e
87
+ error = e
88
+ rescue StandardError => e
89
+ error = ContextError.new(cause: e)
90
+ end
91
+
92
+ if ENV["DEBUG"]
93
+ warn error.full_message
94
+ else
95
+ warn error.short_message
96
+ warn "Note: Set the `DEBUG` environment variable to see the full error context"
97
+ end
98
+
99
+ 1
100
+ end
101
+ end
102
+ end
@@ -16,7 +16,10 @@ module RfcReader
16
16
  file_name = File.basename(url)
17
17
  file_path = File.join(library_cache_dir, file_name)
18
18
 
19
- content = Net::HTTP.get(URI(url))
19
+ content = ErrorContext.wrap("Downloading RFC document") do
20
+ Net::HTTP.get(URI(url))
21
+ end
22
+
20
23
  FileUtils.mkdir_p(library_cache_dir)
21
24
  File.write(file_path, content)
22
25
  add_to_catalog(title: title, url: url, path: file_path)
@@ -16,7 +16,9 @@ module RfcReader
16
16
 
17
17
  # @return [String] the raw XML from the recent RFCs RSS feed
18
18
  def self.fetch
19
- Net::HTTP.get(RECENT_RFCS_RSS_URI)
19
+ ErrorContext.wrap("Fetching the recent RFCs list") do
20
+ Net::HTTP.get(RECENT_RFCS_RSS_URI)
21
+ end
20
22
  end
21
23
 
22
24
  # Example: XML fragment we're trying to parse title and link data from.
@@ -36,18 +38,20 @@ module RfcReader
36
38
  # @param xml [String] the XML of the recent RFCs RSS endpoint
37
39
  # @return [Hash<String, String>] from RFC title to text file url
38
40
  def self.parse(xml)
39
- Nokogiri::XML(xml).xpath("//item").to_h do |item|
40
- item_hash = item.elements.to_h do |elem|
41
- [elem.name, elem.text.strip]
42
- end
41
+ ErrorContext.wrap("Parsing the recent RFCs list") do
42
+ Nokogiri::XML(xml).xpath("//item").to_h do |item|
43
+ item_hash = item.elements.to_h do |elem|
44
+ [elem.name, elem.text.strip]
45
+ end
43
46
 
44
- # The link is to the webpage and not the plaintext document so we must convert it.
45
- file_name = File.basename(item_hash.fetch("link"))
47
+ # The link is to the webpage and not the plaintext document so we must convert it.
48
+ file_name = File.basename(item_hash.fetch("link"))
46
49
 
47
- [
48
- item_hash.fetch("title"),
49
- "https://www.rfc-editor.org/rfc/#{file_name}.txt",
50
- ]
50
+ [
51
+ item_hash.fetch("title"),
52
+ "https://www.rfc-editor.org/rfc/#{file_name}.txt",
53
+ ]
54
+ end
51
55
  end
52
56
  end
53
57
  end
@@ -18,7 +18,9 @@ module RfcReader
18
18
  # @param term [String]
19
19
  # @return [String] the raw HTML of the search results for the given term
20
20
  def self.fetch_by(term:)
21
- Net::HTTP.post_form(RFC_SEARCH_URI, { combo_box: term }).body
21
+ ErrorContext.wrap("Fetching RFC search results") do
22
+ Net::HTTP.post_form(RFC_SEARCH_URI, { combo_box: term }).body
23
+ end
22
24
  end
23
25
 
24
26
  # Example: HTML fragment we're trying to parse title and link info from.
@@ -71,13 +73,23 @@ module RfcReader
71
73
  # @param html [String] the HTML of the search results
72
74
  # @return [Hash<String, String>] from RFC title to text file url
73
75
  def self.parse(html)
74
- # NOTE: The first element in the table is just some general search information. See example HTML above.
75
- Nokogiri::HTML(html).xpath("//div[@class='scrolltable']//table[@class='gridtable']//tr").drop(1).to_h do |tr_node|
76
- td_nodes = tr_node.elements
77
- title = td_nodes[2].text.strip
78
- url = td_nodes[1].elements.map { _1.attribute("href").text.strip }.find { _1.end_with?(".txt") }
76
+ ErrorContext.wrap("Parsing RFC search results") do
77
+ # NOTE: The first element in the table is just some general search information. See example HTML above.
78
+ Nokogiri::HTML(html)
79
+ .xpath("//div[@class='scrolltable']//table[@class='gridtable']//tr")
80
+ .drop(1)
81
+ .to_h do |tr_node|
82
+ td_nodes = tr_node.elements
83
+ title = td_nodes[2]
84
+ .text
85
+ .strip
86
+ url = td_nodes[1]
87
+ .elements
88
+ .map { _1.attribute("href").text.strip }
89
+ .find { _1.end_with?(".txt") }
79
90
 
80
- [title, url]
91
+ [title, url]
92
+ end
81
93
  end
82
94
  end
83
95
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RfcReader
4
- VERSION = "1.0.1"
4
+ VERSION = "1.1.1"
5
5
  end
data/lib/rfc_reader.rb CHANGED
@@ -8,4 +8,12 @@ module RfcReader
8
8
  autoload :Search, "rfc_reader/search"
9
9
  autoload :Terminal, "rfc_reader/terminal"
10
10
  autoload :Library, "rfc_reader/library"
11
+ autoload :ErrorContext, "rfc_reader/error_context"
12
+
13
+ # @return [Integer] exit code
14
+ def self.start
15
+ ErrorContext.handler do
16
+ Command.start
17
+ end
18
+ end
11
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rfc-reader
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Robell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-09-28 00:00:00.000000000 Z
11
+ date: 2024-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -77,6 +77,7 @@ files:
77
77
  - exe/rfc-reader
78
78
  - lib/rfc_reader.rb
79
79
  - lib/rfc_reader/command.rb
80
+ - lib/rfc_reader/error_context.rb
80
81
  - lib/rfc_reader/library.rb
81
82
  - lib/rfc_reader/recent.rb
82
83
  - lib/rfc_reader/search.rb