rfc-reader 1.0.1 → 1.1.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: 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