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 +4 -4
- data/exe/rfc-reader +1 -1
- data/lib/rfc_reader/error_context.rb +102 -0
- data/lib/rfc_reader/library.rb +4 -1
- data/lib/rfc_reader/recent.rb +15 -11
- data/lib/rfc_reader/search.rb +19 -7
- data/lib/rfc_reader/version.rb +1 -1
- data/lib/rfc_reader.rb +8 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc7f0a4142e7e5cb5b1ed4b8a5ecda4728177df15e6f9c8d7e84dcc557b660e4
|
4
|
+
data.tar.gz: 89e7ac7472c84e84d87feed86e66f35f14dbc65a7af39e6a55fe65e158b78e89
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b0588ecb6c225fa8d2ef6c1c27506dda70d59ae2af050797f6ed71536a379423848570022223b04f0911ca3d987431d2b28ab4c019c8c74c31ece1fed3e037a
|
7
|
+
data.tar.gz: 51e9100f05269801e8fcbbe879405181ae0d3910e42593d5a9ad5933d6d8f3f45214cc8a8827d809b75e920029ccfd7584139c707e6a9e3fe0ec12ca750c4f77
|
data/exe/rfc-reader
CHANGED
@@ -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
|
data/lib/rfc_reader/library.rb
CHANGED
@@ -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 =
|
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)
|
data/lib/rfc_reader/recent.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
45
|
-
|
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
|
-
|
49
|
-
|
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
|
data/lib/rfc_reader/search.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
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
|
-
|
91
|
+
[title, url]
|
92
|
+
end
|
81
93
|
end
|
82
94
|
end
|
83
95
|
end
|
data/lib/rfc_reader/version.rb
CHANGED
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.
|
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-
|
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
|