webmention 9.1.0 → 10.0.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 +4 -4
- data/CONTRIBUTING.md +2 -2
- data/README.md +3 -3
- data/USAGE.md +2 -4
- data/lib/webmention/client.rb +16 -33
- data/lib/webmention/error_response.rb +10 -2
- data/lib/webmention/request.rb +14 -28
- data/lib/webmention/response.rb +31 -2
- data/lib/webmention/url.rb +5 -8
- data/lib/webmention/verification.rb +13 -44
- data/lib/webmention.rb +3 -8
- data/webmention.gemspec +2 -1
- metadata +19 -9
- data/lib/webmention/parser.rb +0 -32
- data/lib/webmention/parsers/html_parser.rb +0 -73
- data/lib/webmention/parsers/json_parser.rb +0 -31
- data/lib/webmention/parsers/plaintext_parser.rb +0 -17
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 18cd676b542de4d7d4507fb30a6475cba645feca8dccebb4230d48904165d1dd
|
|
4
|
+
data.tar.gz: 309bb9ad852cc944a747a1b18a25e04009e9c0095f3249212654b4ee920e5132
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f73890fc04da3b8ec912f3b469cdb7f29ef2761b56e0ca4854133981c3b18ecb09c7be7d1e24253935dafd0c858be13fd45c60c0365b2e005f7659ec74ee9467
|
|
7
|
+
data.tar.gz: d00e9b5a4b32fefcdc48e59e3a33d73662e79c563f69517be55984c57ba2fc790d544fac3f58c06b91bee6b92258fa2fe6b7f59aac73c92030d2ca14bbcd4d76
|
data/CONTRIBUTING.md
CHANGED
|
@@ -8,12 +8,12 @@ There are a couple ways you can help improve webmention-client-ruby:
|
|
|
8
8
|
|
|
9
9
|
## Getting Started
|
|
10
10
|
|
|
11
|
-
webmention-client-ruby is developed using Ruby
|
|
11
|
+
webmention-client-ruby is developed using Ruby 4.0 and is tested against additional Ruby versions using [GitHub Actions](https://github.com/indieweb/webmention-client-ruby/actions).
|
|
12
12
|
|
|
13
13
|
> [!TIP]
|
|
14
14
|
> This project is configured with a [Dev Container](https://containers.dev) which includes everything you'd need to contribute to webmention-client-ruby. If you use a supported code editor or IDE, you're encouraged to use the existing Dev Container setup.
|
|
15
15
|
|
|
16
|
-
Before making changes to webmention-client-ruby, you'll want to install Ruby
|
|
16
|
+
Before making changes to webmention-client-ruby, you'll want to install Ruby 4.0. Using a Ruby version managment tool like [rv](https://github.com/spinel-coop/rv) or [rbenv](https://github.com/rbenv/rbenv) is recommended. Once you've installed Ruby 4.0 using your method of choice, install the project's gems by running:
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
19
|
bundle install
|
data/README.md
CHANGED
|
@@ -16,9 +16,9 @@ A Ruby gem for sending and verifying [Webmention](https://indieweb.org/Webmentio
|
|
|
16
16
|
|
|
17
17
|
## Getting Started
|
|
18
18
|
|
|
19
|
-
Before installing and using webmention-client-ruby, you'll want to have [Ruby](https://www.ruby-lang.org) 2.7 (or newer) installed. Using a Ruby version managment tool like [
|
|
19
|
+
Before installing and using webmention-client-ruby, you'll want to have [Ruby](https://www.ruby-lang.org) 2.7 (or newer) installed. Using a Ruby version managment tool like [rv](https://github.com/spinel-coop/rv) or [rbenv](https://github.com/rbenv/rbenv) is recommended.
|
|
20
20
|
|
|
21
|
-
webmention-client-ruby is developed using Ruby
|
|
21
|
+
webmention-client-ruby is developed using Ruby 4.0 and is tested against additional Ruby versions using [GitHub Actions](https://github.com/indieweb/webmention-client-ruby/actions).
|
|
22
22
|
|
|
23
23
|
## Installation
|
|
24
24
|
|
|
@@ -36,7 +36,7 @@ See [USAGE.md](USAGE.md) for documentation of webmention-client-ruby's features.
|
|
|
36
36
|
|
|
37
37
|
## Contributing
|
|
38
38
|
|
|
39
|
-
See [CONTRIBUTING.md](
|
|
39
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for more on how to contribute to webmention-client-ruby. Your help is greatly appreciated!
|
|
40
40
|
|
|
41
41
|
By contributing to and participating in the development of webmention-client-ruby, you acknowledge that you have read and agree to the [IndieWeb Code of Conduct](https://indieweb.org/code-of-conduct).
|
|
42
42
|
|
data/USAGE.md
CHANGED
|
@@ -29,7 +29,7 @@ response.class
|
|
|
29
29
|
#=> Webmention::ErrorResponse
|
|
30
30
|
|
|
31
31
|
response.message
|
|
32
|
-
#=> "No
|
|
32
|
+
#=> "No Webmention endpoint found for target URL https://aaronpk.example/post/100"
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
A `Webmention::Response` will be returned in all other cases.
|
|
@@ -147,6 +147,4 @@ verification.vouch_mentions_source?
|
|
|
147
147
|
|
|
148
148
|
## Exception Handling
|
|
149
149
|
|
|
150
|
-
webmention-client-ruby avoids raising exceptions when making HTTP requests. As noted above, a `Webmention::ErrorResponse` should be returned in cases where an HTTP request triggers an exception.
|
|
151
|
-
|
|
152
|
-
When crawling the supplied URL, `Webmention.mentioned_urls` _may_ raise a `NoMethodError` if a `Webmention::ErrorResponse` is returned, or the response is of an unsupported MIME type.
|
|
150
|
+
webmention-client-ruby avoids raising exceptions when making HTTP requests. As noted above, a `Webmention::ErrorResponse` should be returned in cases where an HTTP request triggers an exception. `Webmention.mentioned_urls` will raise a `Burly::UnsupportedMimeType` exception when the response is of an unsupported MIME type, though.
|
data/lib/webmention/client.rb
CHANGED
|
@@ -2,24 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Webmention
|
|
4
4
|
class Client
|
|
5
|
-
@registered_parsers = {}
|
|
6
|
-
|
|
7
|
-
class << self
|
|
8
|
-
# @api private
|
|
9
|
-
attr_reader :registered_parsers
|
|
10
|
-
end
|
|
11
|
-
|
|
12
5
|
# @return [Url]
|
|
13
6
|
attr_reader :source_url
|
|
14
7
|
|
|
15
8
|
# @return [Url]
|
|
16
9
|
attr_reader :vouch_url
|
|
17
10
|
|
|
18
|
-
# @api private
|
|
19
|
-
def self.register_parser(klass)
|
|
20
|
-
klass.mime_types.each { |mime_type| @registered_parsers[mime_type] = klass }
|
|
21
|
-
end
|
|
22
|
-
|
|
23
11
|
# Create a new {Client}.
|
|
24
12
|
#
|
|
25
13
|
# @example
|
|
@@ -38,14 +26,15 @@ module Webmention
|
|
|
38
26
|
@vouch_url = Url.new(vouch)
|
|
39
27
|
end
|
|
40
28
|
|
|
41
|
-
# :nocov:
|
|
42
29
|
# @return [String]
|
|
30
|
+
#
|
|
31
|
+
# simplecov:disable
|
|
43
32
|
def inspect
|
|
44
33
|
"#<#{self.class}:#{format("%#0x", object_id)} " \
|
|
45
34
|
"source_url: #{source_url} " \
|
|
46
35
|
"vouch_url: #{vouch_url}>"
|
|
47
36
|
end
|
|
48
|
-
# :
|
|
37
|
+
# simplecov:enable
|
|
49
38
|
|
|
50
39
|
# Retrieve unique URLs mentioned by this client's source URL.
|
|
51
40
|
#
|
|
@@ -53,24 +42,18 @@ module Webmention
|
|
|
53
42
|
# client = Webmention::Client.new("https://jgarber.example/posts/100")
|
|
54
43
|
# client.mentioned_urls
|
|
55
44
|
#
|
|
56
|
-
# @raise [
|
|
57
|
-
# Raised when response is an
|
|
58
|
-
# unsupported MIME type.
|
|
45
|
+
# @raise [Burly::UnsupportedMimeType]
|
|
46
|
+
# Raised when response is of an unsupported MIME type.
|
|
59
47
|
#
|
|
60
48
|
# @return [Array<String>]
|
|
61
49
|
def mentioned_urls
|
|
62
50
|
response = source_url.response
|
|
63
51
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
)
|
|
70
|
-
|
|
71
|
-
urls.reject! { |url| url.match(/^#{response.uri}(?:#.*)?$/) }
|
|
72
|
-
|
|
73
|
-
urls.to_a.sort
|
|
52
|
+
response
|
|
53
|
+
.mentioned_urls
|
|
54
|
+
.lazy
|
|
55
|
+
.reject { |url| url.match(/^#{response.uri}(?:#.*)?$/) }
|
|
56
|
+
.sort
|
|
74
57
|
end
|
|
75
58
|
|
|
76
59
|
# Send a webmention from this client's source URL to a target URL.
|
|
@@ -85,17 +68,17 @@ module Webmention
|
|
|
85
68
|
# @return [Response, ErrorResponse]
|
|
86
69
|
def send_webmention(target)
|
|
87
70
|
target_url = Url.new(target)
|
|
71
|
+
endpoint = target_url.webmention_endpoint
|
|
72
|
+
response = target_url.response
|
|
88
73
|
|
|
89
74
|
# A Webmention endpoint exists. Send the request and return the response.
|
|
90
|
-
|
|
91
|
-
return Request.post(target_url.webmention_endpoint, **request_options_for(target))
|
|
92
|
-
end
|
|
75
|
+
return Request.new(:post, endpoint, **request_options_for(target)).perform unless endpoint.nil?
|
|
93
76
|
|
|
94
77
|
# An error was encountered fetching the target URL. Return the response.
|
|
95
|
-
return
|
|
78
|
+
return response unless response.ok?
|
|
96
79
|
|
|
97
80
|
# No Webmention endpoint exists. Return a new ErrorResponse.
|
|
98
|
-
ErrorResponse.new("No
|
|
81
|
+
ErrorResponse.new("No Webmention endpoint found for target URL #{target}", response.request)
|
|
99
82
|
end
|
|
100
83
|
|
|
101
84
|
# Send webmentions from this client's source URL to multiple target URLs.
|
|
@@ -140,7 +123,7 @@ module Webmention
|
|
|
140
123
|
opts.transform_values! { |value| value.to_s.strip }
|
|
141
124
|
opts.reject! { |_, value| value.empty? }
|
|
142
125
|
|
|
143
|
-
opts
|
|
126
|
+
{ form: opts }
|
|
144
127
|
end
|
|
145
128
|
end
|
|
146
129
|
end
|
|
@@ -15,6 +15,8 @@ module Webmention
|
|
|
15
15
|
# endpoint. The nature of the error is captured in the {#message} instance
|
|
16
16
|
# method.
|
|
17
17
|
#
|
|
18
|
+
# @api private
|
|
19
|
+
#
|
|
18
20
|
# @param message [String]
|
|
19
21
|
# @param request [Request]
|
|
20
22
|
def initialize(message, request)
|
|
@@ -22,13 +24,19 @@ module Webmention
|
|
|
22
24
|
@request = request
|
|
23
25
|
end
|
|
24
26
|
|
|
25
|
-
# :nocov:
|
|
26
27
|
# @return [String]
|
|
28
|
+
#
|
|
29
|
+
# simplecov:disable
|
|
27
30
|
def inspect
|
|
28
31
|
"#<#{self.class}:#{format("%#0x", object_id)} " \
|
|
29
32
|
"message: #{message}>"
|
|
30
33
|
end
|
|
31
|
-
# :
|
|
34
|
+
# simplecov:enable
|
|
35
|
+
|
|
36
|
+
# @return [Set]
|
|
37
|
+
def mentioned_urls
|
|
38
|
+
Set.new
|
|
39
|
+
end
|
|
32
40
|
|
|
33
41
|
# @return [Boolean]
|
|
34
42
|
def ok?
|
data/lib/webmention/request.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Webmention
|
|
4
|
+
# @api private
|
|
4
5
|
class Request
|
|
5
6
|
# Set defaults derived from Webmention specification examples.
|
|
6
7
|
#
|
|
@@ -31,28 +32,23 @@ module Webmention
|
|
|
31
32
|
# @return [Hash]
|
|
32
33
|
attr_reader :options
|
|
33
34
|
|
|
34
|
-
#
|
|
35
|
+
# Create a new {Request}.
|
|
35
36
|
#
|
|
36
37
|
# @example
|
|
37
|
-
# Webmention::Request.get
|
|
38
|
-
#
|
|
39
|
-
# @param url [String]
|
|
40
|
-
#
|
|
41
|
-
# @return [Response, ErrorResponse]
|
|
42
|
-
def self.get(url)
|
|
43
|
-
new(:get, url).perform
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Send an HTTP POST request with form-encoded data to the supplied URL.
|
|
38
|
+
# Webmention::Request.new(:get, "https://jgarber.example/posts/100")
|
|
47
39
|
#
|
|
48
40
|
# @example
|
|
49
|
-
# Webmention::Request.
|
|
41
|
+
# Webmention::Request.new(
|
|
42
|
+
# :post,
|
|
50
43
|
# "https://aaronpk.example/webmention",
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
44
|
+
# form: {
|
|
45
|
+
# source: "https://jgarber.examples/posts/100",
|
|
46
|
+
# target: "https://aaronpk.example/notes/1",
|
|
47
|
+
# vouch: "https://tantek.example/notes/1",
|
|
48
|
+
# },
|
|
54
49
|
# )
|
|
55
50
|
#
|
|
51
|
+
# @param method [Symbol]
|
|
56
52
|
# @param url [String]
|
|
57
53
|
# @param options [Hash{Symbol => String}]
|
|
58
54
|
# @option options [String] :source
|
|
@@ -62,31 +58,21 @@ module Webmention
|
|
|
62
58
|
# @option options [String] :vouch
|
|
63
59
|
# An absolute URL representing a document vouching for the source document.
|
|
64
60
|
# See https://indieweb.org/Vouch for additional details.
|
|
65
|
-
#
|
|
66
|
-
# @return [Response, ErrorResponse]
|
|
67
|
-
def self.post(url, **options)
|
|
68
|
-
new(:post, url, form: options.slice(:source, :target, :vouch)).perform
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Create a new {Request}.
|
|
72
|
-
#
|
|
73
|
-
# @param method [Symbol]
|
|
74
|
-
# @param url [String]
|
|
75
|
-
# @param options [Hash{Symbol => String}]
|
|
76
61
|
def initialize(method, url, **options)
|
|
77
62
|
@method = method.to_sym
|
|
78
63
|
@uri = HTTP::URI.parse(url.to_s)
|
|
79
64
|
@options = options
|
|
80
65
|
end
|
|
81
66
|
|
|
82
|
-
# :nocov:
|
|
83
67
|
# @return [String]
|
|
68
|
+
#
|
|
69
|
+
# simplecov:disable
|
|
84
70
|
def inspect
|
|
85
71
|
"#<#{self.class}:#{format("%#0x", object_id)} " \
|
|
86
72
|
"method: #{method.upcase}, " \
|
|
87
73
|
"url: #{uri}>"
|
|
88
74
|
end
|
|
89
|
-
# :
|
|
75
|
+
# simplecov:enable
|
|
90
76
|
|
|
91
77
|
# Submit the {Request}.
|
|
92
78
|
#
|
data/lib/webmention/response.rb
CHANGED
|
@@ -4,6 +4,12 @@ module Webmention
|
|
|
4
4
|
class Response
|
|
5
5
|
extend Forwardable
|
|
6
6
|
|
|
7
|
+
PARSE_CONTEXT = [".h-entry .e-content", ".h-entry", "body"].freeze
|
|
8
|
+
URI_REGEXP = URI::RFC2396_Parser.new.make_regexp(["http", "https"]).freeze
|
|
9
|
+
|
|
10
|
+
private_constant :PARSE_CONTEXT
|
|
11
|
+
private_constant :URI_REGEXP
|
|
12
|
+
|
|
7
13
|
# @return [Request]
|
|
8
14
|
attr_reader :request
|
|
9
15
|
|
|
@@ -37,6 +43,8 @@ module Webmention
|
|
|
37
43
|
# of which may be accessed using the delegated {#code} and {#reason}
|
|
38
44
|
# instance methods.
|
|
39
45
|
#
|
|
46
|
+
# @api private
|
|
47
|
+
#
|
|
40
48
|
# @param response [HTTP::Response]
|
|
41
49
|
# @param request [Request]
|
|
42
50
|
def initialize(response, request)
|
|
@@ -44,15 +52,36 @@ module Webmention
|
|
|
44
52
|
@request = request
|
|
45
53
|
end
|
|
46
54
|
|
|
47
|
-
# :nocov:
|
|
48
55
|
# @return [String]
|
|
56
|
+
#
|
|
57
|
+
# simplecov:disable
|
|
49
58
|
def inspect
|
|
50
59
|
"#<#{self.class}:#{format("%#0x", object_id)} " \
|
|
51
60
|
"code: #{code.inspect}, " \
|
|
52
61
|
"reason: #{reason}, " \
|
|
53
62
|
"url: #{request.uri}>"
|
|
54
63
|
end
|
|
55
|
-
# :
|
|
64
|
+
# simplecov:enable
|
|
65
|
+
|
|
66
|
+
# Parse the response body for unique host names.
|
|
67
|
+
#
|
|
68
|
+
# @return [Set<String>]
|
|
69
|
+
def mentioned_hosts
|
|
70
|
+
@mentioned_hosts ||= Set.new(mentioned_urls.map { |url| HTTP::URI.parse(url).host })
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Parse the response body for mentioned URLs.
|
|
74
|
+
#
|
|
75
|
+
# @return [Set<String>]
|
|
76
|
+
def mentioned_urls
|
|
77
|
+
@mentioned_urls ||=
|
|
78
|
+
Set.new(
|
|
79
|
+
Burly
|
|
80
|
+
.parse(@response.body, context: PARSE_CONTEXT, mime_type: @response.mime_type)
|
|
81
|
+
.map { |url| @response.uri.join(url).to_s }
|
|
82
|
+
.grep(URI_REGEXP)
|
|
83
|
+
)
|
|
84
|
+
end
|
|
56
85
|
|
|
57
86
|
# @return [Boolean]
|
|
58
87
|
def ok?
|
data/lib/webmention/url.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Webmention
|
|
4
|
+
# @api private
|
|
4
5
|
class Url
|
|
5
6
|
extend Forwardable
|
|
6
7
|
|
|
@@ -18,27 +19,23 @@ module Webmention
|
|
|
18
19
|
@uri = HTTP::URI.parse(url.to_s)
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
# :nocov:
|
|
22
22
|
# @return [String]
|
|
23
|
+
#
|
|
24
|
+
# simplecov:disable
|
|
23
25
|
def inspect
|
|
24
26
|
"#<#{self.class}:#{format("%#0x", object_id)} " \
|
|
25
27
|
"uri: #{uri}>"
|
|
26
28
|
end
|
|
27
|
-
# :
|
|
29
|
+
# simplecov:enable
|
|
28
30
|
|
|
29
31
|
# @return [Response, ErrorResponse]
|
|
30
32
|
def response
|
|
31
|
-
@response ||= Request.get
|
|
33
|
+
@response ||= Request.new(:get, uri).perform
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
# @return [String, nil]
|
|
35
37
|
def webmention_endpoint
|
|
36
38
|
@webmention_endpoint ||= IndieWeb::Endpoints::Parser.new(response).to_h[:webmention] if response.ok?
|
|
37
39
|
end
|
|
38
|
-
|
|
39
|
-
# @return [Boolean]
|
|
40
|
-
def webmention_endpoint?
|
|
41
|
-
!webmention_endpoint.nil?
|
|
42
|
-
end
|
|
43
40
|
end
|
|
44
41
|
end
|
|
@@ -4,6 +4,8 @@ module Webmention
|
|
|
4
4
|
class Verification
|
|
5
5
|
# Create a new {Verification}.
|
|
6
6
|
#
|
|
7
|
+
# @api private
|
|
8
|
+
#
|
|
7
9
|
# @param source_url [Url]
|
|
8
10
|
# An {URL} representing a source document.
|
|
9
11
|
# @param target_url [Url]
|
|
@@ -17,70 +19,37 @@ module Webmention
|
|
|
17
19
|
@vouch_url = vouch_url
|
|
18
20
|
end
|
|
19
21
|
|
|
20
|
-
# :nocov:
|
|
21
22
|
# @return [String]
|
|
23
|
+
#
|
|
24
|
+
# simplecov:disable
|
|
22
25
|
def inspect
|
|
23
26
|
"#<#{self.class}:#{format("%#0x", object_id)} " \
|
|
24
|
-
"source_url: #{source_url} " \
|
|
25
|
-
"target_url: #{target_url} " \
|
|
26
|
-
"vouch_url: #{vouch_url}>"
|
|
27
|
+
"source_url: #{@source_url} " \
|
|
28
|
+
"target_url: #{@target_url} " \
|
|
29
|
+
"vouch_url: #{@vouch_url}>"
|
|
27
30
|
end
|
|
28
|
-
# :
|
|
31
|
+
# simplecov:enable
|
|
29
32
|
|
|
30
33
|
# @return [Boolean]
|
|
31
34
|
def source_mentions_target?
|
|
32
|
-
@source_mentions_target ||=
|
|
35
|
+
@source_mentions_target ||= @source_url.response.mentioned_urls.any?(@target_url.to_s)
|
|
33
36
|
end
|
|
34
37
|
|
|
35
38
|
# @return [Boolean]
|
|
36
39
|
def verified?
|
|
37
|
-
return source_mentions_target?
|
|
40
|
+
return source_mentions_target? && vouch_mentions_source? if verify_vouch?
|
|
38
41
|
|
|
39
|
-
source_mentions_target?
|
|
42
|
+
source_mentions_target?
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
# @return [Boolean]
|
|
43
46
|
def verify_vouch?
|
|
44
|
-
|
|
47
|
+
!@vouch_url.to_s.strip.empty?
|
|
45
48
|
end
|
|
46
49
|
|
|
47
50
|
# @return [Boolean]
|
|
48
51
|
def vouch_mentions_source?
|
|
49
|
-
@vouch_mentions_source ||=
|
|
50
|
-
verify_vouch? && mentioned_domains(vouch_url.response).any?(source_url.uri.host)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
private
|
|
54
|
-
|
|
55
|
-
# @return [Url]
|
|
56
|
-
attr_reader :source_url
|
|
57
|
-
|
|
58
|
-
# @return [Url]
|
|
59
|
-
attr_reader :target_url
|
|
60
|
-
|
|
61
|
-
# @return [Url]
|
|
62
|
-
attr_reader :vouch_url
|
|
63
|
-
|
|
64
|
-
# @param response [Response]
|
|
65
|
-
#
|
|
66
|
-
# @raise (see Client#mentioned_urls)
|
|
67
|
-
#
|
|
68
|
-
# @return [Array<String>]
|
|
69
|
-
def mentioned_domains(response)
|
|
70
|
-
Set.new(mentioned_urls(response).map { |url| HTTP::URI.parse(url).host }).to_a
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# @param response [Response]
|
|
74
|
-
#
|
|
75
|
-
# @raise (see Client#mentioned_urls)
|
|
76
|
-
#
|
|
77
|
-
# @return [Array<String>]
|
|
78
|
-
def mentioned_urls(response)
|
|
79
|
-
Set.new(
|
|
80
|
-
Client.registered_parsers[response.mime_type]
|
|
81
|
-
.new(response.body, response.uri)
|
|
82
|
-
.results
|
|
83
|
-
).to_a
|
|
52
|
+
@vouch_mentions_source ||= verify_vouch? && @vouch_url.response.mentioned_hosts.any?(@source_url.uri.host)
|
|
84
53
|
end
|
|
85
54
|
end
|
|
86
55
|
end
|
data/lib/webmention.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
4
|
|
|
5
|
+
require "burly"
|
|
5
6
|
require "http"
|
|
6
7
|
require "indieweb/endpoints"
|
|
7
8
|
require "nokogiri"
|
|
@@ -13,11 +14,6 @@ require_relative "webmention/response"
|
|
|
13
14
|
require_relative "webmention/error_response"
|
|
14
15
|
require_relative "webmention/verification"
|
|
15
16
|
|
|
16
|
-
require_relative "webmention/parser"
|
|
17
|
-
require_relative "webmention/parsers/html_parser"
|
|
18
|
-
require_relative "webmention/parsers/json_parser"
|
|
19
|
-
require_relative "webmention/parsers/plaintext_parser"
|
|
20
|
-
|
|
21
17
|
module Webmention
|
|
22
18
|
# Retrieve unique URLs mentioned by the provided URL.
|
|
23
19
|
#
|
|
@@ -26,9 +22,8 @@ module Webmention
|
|
|
26
22
|
#
|
|
27
23
|
# @param url [String, HTTP::URI, #to_s] An absolute URL.
|
|
28
24
|
#
|
|
29
|
-
# @raise [
|
|
30
|
-
# Raised when response is an
|
|
31
|
-
# unsupported MIME type.
|
|
25
|
+
# @raise [Burly::UnsupportedMimeType]
|
|
26
|
+
# Raised when response is of an unsupported MIME type.
|
|
32
27
|
#
|
|
33
28
|
# @return [Array<String>]
|
|
34
29
|
def self.mentioned_urls(url)
|
data/webmention.gemspec
CHANGED
|
@@ -4,7 +4,7 @@ Gem::Specification.new do |spec|
|
|
|
4
4
|
spec.required_ruby_version = ">= 2.7"
|
|
5
5
|
|
|
6
6
|
spec.name = "webmention"
|
|
7
|
-
spec.version = "
|
|
7
|
+
spec.version = "10.0.0"
|
|
8
8
|
spec.authors = ["Jason Garber"]
|
|
9
9
|
spec.email = ["jason@sixtwothree.org"]
|
|
10
10
|
|
|
@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
|
|
|
28
28
|
"source_code_uri" => "#{spec.homepage}/tree/v#{spec.version}",
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
spec.add_dependency "burly", "~> 0.4"
|
|
31
32
|
spec.add_dependency "http", ">= 5.3"
|
|
32
33
|
spec.add_dependency "indieweb-endpoints", "~> 10.1"
|
|
33
34
|
spec.add_dependency "nokogiri", ">= 1.14"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: webmention
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 10.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jason Garber
|
|
@@ -9,6 +9,20 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: burly
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.4'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.4'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: http
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -66,10 +80,6 @@ files:
|
|
|
66
80
|
- lib/webmention.rb
|
|
67
81
|
- lib/webmention/client.rb
|
|
68
82
|
- lib/webmention/error_response.rb
|
|
69
|
-
- lib/webmention/parser.rb
|
|
70
|
-
- lib/webmention/parsers/html_parser.rb
|
|
71
|
-
- lib/webmention/parsers/json_parser.rb
|
|
72
|
-
- lib/webmention/parsers/plaintext_parser.rb
|
|
73
83
|
- lib/webmention/request.rb
|
|
74
84
|
- lib/webmention/response.rb
|
|
75
85
|
- lib/webmention/url.rb
|
|
@@ -80,11 +90,11 @@ licenses:
|
|
|
80
90
|
- Apache-2.0
|
|
81
91
|
metadata:
|
|
82
92
|
bug_tracker_uri: https://github.com/indieweb/webmention-client-ruby/issues
|
|
83
|
-
changelog_uri: https://github.com/indieweb/webmention-client-ruby/releases/tag/
|
|
84
|
-
documentation_uri: https://rubydoc.info/gems/webmention/
|
|
93
|
+
changelog_uri: https://github.com/indieweb/webmention-client-ruby/releases/tag/v10.0.0
|
|
94
|
+
documentation_uri: https://rubydoc.info/gems/webmention/10.0.0
|
|
85
95
|
homepage_uri: https://github.com/indieweb/webmention-client-ruby
|
|
86
96
|
rubygems_mfa_required: 'true'
|
|
87
|
-
source_code_uri: https://github.com/indieweb/webmention-client-ruby/tree/
|
|
97
|
+
source_code_uri: https://github.com/indieweb/webmention-client-ruby/tree/v10.0.0
|
|
88
98
|
rdoc_options: []
|
|
89
99
|
require_paths:
|
|
90
100
|
- lib
|
|
@@ -99,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
99
109
|
- !ruby/object:Gem::Version
|
|
100
110
|
version: '0'
|
|
101
111
|
requirements: []
|
|
102
|
-
rubygems_version: 4.0.
|
|
112
|
+
rubygems_version: 4.0.20
|
|
103
113
|
specification_version: 4
|
|
104
114
|
summary: A Ruby gem for sending and verifying Webmention notifications.
|
|
105
115
|
test_files: []
|
data/lib/webmention/parser.rb
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Webmention
|
|
4
|
-
# @api private
|
|
5
|
-
class Parser
|
|
6
|
-
URI_PARSER = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::Generic::DEFAULT_PARSER
|
|
7
|
-
|
|
8
|
-
URI_REGEXP = URI_PARSER.make_regexp(["http", "https"]).freeze
|
|
9
|
-
|
|
10
|
-
public_constant :URI_REGEXP
|
|
11
|
-
|
|
12
|
-
class << self
|
|
13
|
-
# @return [Array<String>]
|
|
14
|
-
attr_reader :mime_types
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# @param response_body [HTTP::Response::Body, String, #to_s]
|
|
18
|
-
# @param response_uri [String, HTTP::URI, #to_s]
|
|
19
|
-
def initialize(response_body, response_uri)
|
|
20
|
-
@response_body = response_body.to_s
|
|
21
|
-
@response_uri = HTTP::URI.parse(response_uri.to_s)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
private
|
|
25
|
-
|
|
26
|
-
# @return [String]
|
|
27
|
-
attr_reader :response_body
|
|
28
|
-
|
|
29
|
-
# @return [HTTP::URI]
|
|
30
|
-
attr_reader :response_uri
|
|
31
|
-
end
|
|
32
|
-
end
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Webmention
|
|
4
|
-
module Parsers
|
|
5
|
-
# @api private
|
|
6
|
-
class HtmlParser < Parser
|
|
7
|
-
@mime_types = ["text/html"]
|
|
8
|
-
|
|
9
|
-
Client.register_parser(self)
|
|
10
|
-
|
|
11
|
-
HTML_ATTRIBUTES_MAP = {
|
|
12
|
-
"cite" => ["blockquote", "del", "ins", "q"],
|
|
13
|
-
"data" => ["object"],
|
|
14
|
-
"href" => ["a", "area"],
|
|
15
|
-
"poster" => ["video"],
|
|
16
|
-
"src" => ["audio", "embed", "img", "source", "track", "video"],
|
|
17
|
-
"srcset" => ["img", "source"],
|
|
18
|
-
}.freeze
|
|
19
|
-
|
|
20
|
-
CSS_SELECTORS_ARRAY = HTML_ATTRIBUTES_MAP
|
|
21
|
-
.flat_map { |attribute, names| names.map { |name| "#{name}[#{attribute}]" } }.freeze
|
|
22
|
-
|
|
23
|
-
ROOT_NODE_SELECTORS_ARRAY = [".h-entry .e-content", ".h-entry", "body"].freeze
|
|
24
|
-
|
|
25
|
-
private_constant :HTML_ATTRIBUTES_MAP
|
|
26
|
-
private_constant :CSS_SELECTORS_ARRAY
|
|
27
|
-
private_constant :ROOT_NODE_SELECTORS_ARRAY
|
|
28
|
-
|
|
29
|
-
# @return [Array<String>] An array of absolute URLs.
|
|
30
|
-
def results
|
|
31
|
-
@results ||=
|
|
32
|
-
extract_urls_from(*url_attributes)
|
|
33
|
-
.map { |url| response_uri.join(url).to_s }
|
|
34
|
-
.grep(Parser::URI_REGEXP)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
# @return [Nokogiri::HTML5::Document]
|
|
40
|
-
def doc
|
|
41
|
-
Nokogiri.HTML5(response_body)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# @param attributes [Array<Nokogiri::XML::Attr>]
|
|
45
|
-
#
|
|
46
|
-
# @return [Array<String>]
|
|
47
|
-
def extract_urls_from(*attributes)
|
|
48
|
-
attributes.flat_map do |attribute|
|
|
49
|
-
if attribute.name == "srcset"
|
|
50
|
-
attribute.value.split(",").map { |value| value.strip.match(/^\S+/).to_s }
|
|
51
|
-
else
|
|
52
|
-
attribute.value
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# @return [Nokogiri::XML::Element]
|
|
58
|
-
def root_node
|
|
59
|
-
doc.at_css(*ROOT_NODE_SELECTORS_ARRAY)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# @return [Array<Nokogiri::XML::Attr>]
|
|
63
|
-
def url_attributes
|
|
64
|
-
url_nodes.flat_map(&:attribute_nodes).select { |attribute| HTML_ATTRIBUTES_MAP.key?(attribute.name) }
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# @return [Nokogiri::XML::NodeSet]
|
|
68
|
-
def url_nodes
|
|
69
|
-
root_node.css(*CSS_SELECTORS_ARRAY)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Webmention
|
|
4
|
-
module Parsers
|
|
5
|
-
# @api private
|
|
6
|
-
class JsonParser < Parser
|
|
7
|
-
@mime_types = ["application/json"]
|
|
8
|
-
|
|
9
|
-
Client.register_parser(self)
|
|
10
|
-
|
|
11
|
-
# @return [Array<String>] An array of absolute URLs.
|
|
12
|
-
def results
|
|
13
|
-
@results ||= extract_urls_from(JSON.parse(response_body))
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
private
|
|
17
|
-
|
|
18
|
-
# @param objs [Array<Hash, Array, String, Integer, Boolean, nil>]
|
|
19
|
-
#
|
|
20
|
-
# @return [Array<String>]
|
|
21
|
-
def extract_urls_from(*objs)
|
|
22
|
-
objs.flat_map do |obj|
|
|
23
|
-
return obj.flat_map { |value| extract_urls_from(value) }.compact if obj.is_a?(Array)
|
|
24
|
-
return extract_urls_from(obj.values) if obj.is_a?(Hash)
|
|
25
|
-
|
|
26
|
-
obj if obj.is_a?(String) && obj.match?(Parser::URI_REGEXP)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Webmention
|
|
4
|
-
module Parsers
|
|
5
|
-
# @api private
|
|
6
|
-
class PlaintextParser < Parser
|
|
7
|
-
@mime_types = ["text/plain"]
|
|
8
|
-
|
|
9
|
-
Client.register_parser(self)
|
|
10
|
-
|
|
11
|
-
# @return [Array<String>] An array of absolute URLs.
|
|
12
|
-
def results
|
|
13
|
-
@results ||= URI_PARSER.extract(response_body, ["http", "https"])
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|