webmention 3.0.0 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -0
- data/CONTRIBUTING.md +2 -2
- data/README.md +27 -69
- data/USAGE.md +153 -0
- data/lib/webmention/client.rb +116 -37
- data/lib/webmention/error_response.rb +40 -0
- data/lib/webmention/parser.rb +28 -0
- data/lib/webmention/parsers/html_parser.rb +33 -19
- data/lib/webmention/parsers/json_parser.rb +43 -0
- data/lib/webmention/parsers/plaintext_parser.rb +17 -0
- data/lib/webmention/request.rb +107 -0
- data/lib/webmention/response.rb +64 -0
- data/lib/webmention/url.rb +46 -0
- data/lib/webmention/verification.rb +79 -0
- data/lib/webmention/version.rb +3 -1
- data/lib/webmention.rb +85 -23
- data/webmention.gemspec +16 -13
- metadata +24 -57
- data/.editorconfig +0 -10
- data/.gitignore +0 -34
- data/.reek.yml +0 -8
- data/.rubocop +0 -3
- data/.rubocop.yml +0 -28
- data/.ruby-version +0 -1
- data/.simplecov +0 -12
- data/.travis.yml +0 -16
- data/Gemfile +0 -15
- data/Rakefile +0 -21
- data/lib/webmention/exceptions.rb +0 -15
- data/lib/webmention/parsers/base_parser.rb +0 -27
- data/lib/webmention/parsers.rb +0 -11
- data/lib/webmention/services/http_request_service.rb +0 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df8e59b193b673f72a5fecba8c0e381aa28fbb648c6a1b2fdddcb29f9824573e
|
4
|
+
data.tar.gz: 982c8060b33da7a7ca2e5aa4dd905d6bd49f4a16fd3390b1b6f79ff902b741af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e8dafddec503fba0d36ea31444de7cd6a162dcc1cc76ae88e7587a9f7e2222cfcf8a06b09e071f92aabd4836f9c58438d7bebbf21aed33655b33f2941bbed07
|
7
|
+
data.tar.gz: 18fe26ed4639d359dd705ae574b02b2bc406a6f74582e6a3fe6f042a061c884e5aefed90280eb3e58a4eefcfca73d4edb8f50553fe2235987dd218e323a3bed3
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,46 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v6.0.0 / 2022-05-13
|
4
|
+
|
5
|
+
### New Features
|
6
|
+
|
7
|
+
- Top-level module methods:
|
8
|
+
- `Webmention.send_webmention(source, target)`
|
9
|
+
- `Webmention.send_webmentions(source, *targets)`
|
10
|
+
- `Webmention.mentioned_urls(url)`
|
11
|
+
- New JSON and plaintext parsers
|
12
|
+
- [Vouch](https://indieweb.org/Vouch) URL support (9829269)
|
13
|
+
- Webmention verification support (5fe5f58 and 100644)
|
14
|
+
- Fewer exceptions! HTTP response handling updated to return similar objects (`Webmention::Response` and `Webmention::ErrorResponse`).
|
15
|
+
- Fewer runtime dependencies!
|
16
|
+
|
17
|
+
### Breaking Changes
|
18
|
+
|
19
|
+
- `Webmention.send_mention` renamed to `Webmention.send_webmention`
|
20
|
+
- `Webmention.client` method removed
|
21
|
+
- `Webmention::Client#send_all_mentions` removed in favor of `Webmention.mentioned_urls` and `Webmention.send_webmentions`
|
22
|
+
- Response objects from `Webmention.send_webmention` and `Webmention.send_webmentions` have changed from instances of `HTTP::Response` to instances of `Webmention::Response` or `Webmention::ErrorResponse`
|
23
|
+
- Remove Absolutely and Addressable dependencies
|
24
|
+
- Add support for Ruby 3 (a31aae6)
|
25
|
+
- Update minimum supported Ruby version to 2.6 (e4fed8e)
|
26
|
+
|
27
|
+
### Development Changes
|
28
|
+
|
29
|
+
- Remove Reek development dependency (806bbc7)
|
30
|
+
- Update development Ruby version to 2.6.10 (7e52ec9)
|
31
|
+
- Migrate test suite to RSpec (79ac684)
|
32
|
+
- Migrate to GitHub Actions (f5a3d7a)
|
33
|
+
|
34
|
+
## v5.0.0 / 2020-12-13
|
35
|
+
|
36
|
+
- Update absolutely and indieweb-endpoints gems to v5.0 (89f4ea8)
|
37
|
+
|
38
|
+
## v4.0.0 / 2020-08-23
|
39
|
+
|
40
|
+
- **Breaking change:** Update minimum supported Ruby version to 2.5 (b2bc62f)
|
41
|
+
- Update indieweb-endpoints to 4.0 (e61588f)
|
42
|
+
- Update project Ruby version to 2.5.8 (2a626a6)
|
43
|
+
|
3
44
|
## v3.0.0 / 2020-05-19
|
4
45
|
|
5
46
|
- Reject "internal" URLs when sending webmentions (#24) (ccc82c8)
|
data/CONTRIBUTING.md
CHANGED
@@ -8,9 +8,9 @@ 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 2.
|
11
|
+
webmention-client-ruby is developed using Ruby 2.6.10 and is additionally tested against Ruby 2.7, 3.0, and 3.1 using [GitHub Actions](https://github.com/indieweb/webmention-client-ruby/actions).
|
12
12
|
|
13
|
-
Before making changes to webmention-client-ruby, you'll want to install Ruby 2.
|
13
|
+
Before making changes to webmention-client-ruby, you'll want to install Ruby 2.6.10. It's recommended that you use a Ruby version managment tool like [rbenv](https://github.com/rbenv/rbenv), [chruby](https://github.com/postmodern/chruby), or [rvm](https://github.com/rvm/rvm). Once you've installed Ruby 2.6.10 using your method of choice, install the project's gems by running:
|
14
14
|
|
15
15
|
```sh
|
16
16
|
bundle install
|
data/README.md
CHANGED
@@ -1,110 +1,68 @@
|
|
1
1
|
# webmention-client-ruby
|
2
2
|
|
3
|
-
**A Ruby gem for sending [Webmention](https://indieweb.org/Webmention) notifications.**
|
3
|
+
**A Ruby gem for sending and verifying [Webmention](https://indieweb.org/Webmention) notifications.**
|
4
4
|
|
5
|
-
[![Gem](https://img.shields.io/gem/v/webmention.svg?style=for-the-badge)](https://rubygems.org/gems/webmention)
|
6
|
-
[![Downloads](https://img.shields.io/gem/dt/webmention.svg?style=for-the-badge)](https://rubygems.org/gems/webmention)
|
7
|
-
[![Build](https://img.shields.io/
|
8
|
-
[![Maintainability](https://img.shields.io/codeclimate/maintainability/indieweb/webmention-client-ruby.svg?style=for-the-badge)](https://codeclimate.com/github/indieweb/webmention-client-ruby)
|
9
|
-
[![Coverage](https://img.shields.io/codeclimate/c/indieweb/webmention-client-ruby.svg?style=for-the-badge)](https://codeclimate.com/github/indieweb/webmention-client-ruby/code)
|
5
|
+
[![Gem](https://img.shields.io/gem/v/webmention.svg?logo=rubygems&style=for-the-badge)](https://rubygems.org/gems/webmention)
|
6
|
+
[![Downloads](https://img.shields.io/gem/dt/webmention.svg?logo=rubygems&style=for-the-badge)](https://rubygems.org/gems/webmention)
|
7
|
+
[![Build](https://img.shields.io/github/workflow/status/indieweb/webmention-client-ruby/CI?logo=github&style=for-the-badge)](https://github.com/indieweb/webmention-client-ruby/actions/workflows/ci.yml)
|
8
|
+
[![Maintainability](https://img.shields.io/codeclimate/maintainability/indieweb/webmention-client-ruby.svg?logo=code-climate&style=for-the-badge)](https://codeclimate.com/github/indieweb/webmention-client-ruby)
|
9
|
+
[![Coverage](https://img.shields.io/codeclimate/c/indieweb/webmention-client-ruby.svg?logo=code-climate&style=for-the-badge)](https://codeclimate.com/github/indieweb/webmention-client-ruby/code)
|
10
10
|
|
11
11
|
## Key Features
|
12
12
|
|
13
|
-
-
|
14
|
-
-
|
15
|
-
-
|
13
|
+
- Crawl a URL for mentioned URLs.
|
14
|
+
- Perform [endpoint discovery](https://www.w3.org/TR/webmention/#sender-discovers-receiver-webmention-endpoint) on mentioned URLs.
|
15
|
+
- Send webmentions to one or more mentioned URLs (and optionally include a [vouch](https://indieweb.org/Vouch) URL).
|
16
|
+
- Verify that a received webmention's source URL links to a target URL (and optionally verify that a vouch URL mentions the source URL's domain).
|
16
17
|
|
17
18
|
## Getting Started
|
18
19
|
|
19
|
-
Before installing and using webmention-client-ruby, you'll want to have [Ruby](https://www.ruby-lang.org) 2.
|
20
|
+
Before installing and using webmention-client-ruby, you'll want to have [Ruby](https://www.ruby-lang.org) 2.6 (or newer) installed. It's recommended that you use a Ruby version managment tool like [rbenv](https://github.com/rbenv/rbenv), [chruby](https://github.com/postmodern/chruby), or [rvm](https://github.com/rvm/rvm).
|
20
21
|
|
21
|
-
webmention-client-ruby is developed using Ruby 2.
|
22
|
+
webmention-client-ruby is developed using Ruby 2.6.10 and is additionally tested against Ruby 2.7, 3.0, and 3.1 using [GitHub Actions](https://github.com/indieweb/webmention-client-ruby/actions).
|
22
23
|
|
23
24
|
## Installation
|
24
25
|
|
25
|
-
If you're using [Bundler](https://bundler.io) to manage gem dependencies, add webmention-client-ruby to your project's Gemfile
|
26
|
+
If you're using [Bundler](https://bundler.io) to manage gem dependencies, add webmention-client-ruby to your project's `Gemfile`:
|
26
27
|
|
27
28
|
```ruby
|
28
|
-
source 'https://rubygems.org'
|
29
|
-
|
30
29
|
gem 'webmention'
|
31
30
|
```
|
32
31
|
|
33
|
-
…and
|
32
|
+
…and run `bundle install` in your shell.
|
33
|
+
|
34
|
+
To install the gem manually, run the following in your shell:
|
34
35
|
|
35
36
|
```sh
|
36
|
-
|
37
|
+
gem install webmention
|
37
38
|
```
|
38
39
|
|
39
40
|
## Usage
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
require 'webmention'
|
45
|
-
|
46
|
-
source = 'https://source.example.com/post/100' # A post on your website
|
47
|
-
target = 'https://target.example.com/post/100' # A post on someone else's website
|
48
|
-
|
49
|
-
Webmention.send_mention(source, target) # => #<HTTP::Response/1.1 200 OK {…}>
|
50
|
-
```
|
51
|
-
|
52
|
-
If no Webmention endpoint is found for a given source URL, the `send_mention` method will return `nil`.
|
53
|
-
|
54
|
-
**Note:** `HTTP::Response` objects may return a variety of status codes that will vary depending on the endpoint's capabilities and the success or failure of the request. See [the Webmention spec](https://www.w3.org/TR/webmention/) for more on status codes on their implications.
|
55
|
-
|
56
|
-
### Sending multiple webmentions
|
57
|
-
|
58
|
-
To send webmentions to all URLs mentioned within a source URL's [h-entry](http://microformats.org/wiki/h-entry):
|
59
|
-
|
60
|
-
```ruby
|
61
|
-
require 'webmention'
|
62
|
-
|
63
|
-
client = Webmention.client('https://source.example.com/post/100')
|
64
|
-
|
65
|
-
client.mentioned_urls # => Array
|
66
|
-
client.send_all_mentions # => Hash
|
67
|
-
```
|
68
|
-
|
69
|
-
This example will crawl `https://source.example.com/post/100`, parse its markup for the first h-entry, perform endpoint discovery on mentioned URLs, and attempt to send webmentions to those URLs.
|
70
|
-
|
71
|
-
**Note:** If no h-entry is found at the provided source URL, the `send_all_mentions` method will search the source URL's `<body>` for mentioned URLs.
|
72
|
-
|
73
|
-
The `send_all_mentions` method returns a hash of mentioned URLs and the associated HTTP response (an [`HTTP::Response` object](https://github.com/httprb/http/wiki/Response-Handling)):
|
74
|
-
|
75
|
-
```ruby
|
76
|
-
{
|
77
|
-
'https://target.example.com/post/100' => #<HTTP::Response/1.1 200 OK {…}>,
|
78
|
-
'https://target.example.com/post/101' => #<HTTP::Response/1.1 200 OK {…}>
|
79
|
-
}
|
80
|
-
```
|
81
|
-
|
82
|
-
### Exception Handling
|
42
|
+
See [USAGE.md](https://github.com/indieweb/webmention-client-ruby/blob/main/USAGE.md) for documentation of webmention-client-ruby's features.
|
83
43
|
|
84
|
-
|
44
|
+
## Migrating to version 6
|
85
45
|
|
86
|
-
|
46
|
+
webmention-client-ruby was completely rewritten for version 6 to better support new features and future development. Some notes on migrating to the new version:
|
87
47
|
|
88
|
-
|
48
|
+
♻️ **Renamed:** for clarity and consistency, the `Webmention.send_mention` method has been renamed `Webmention.send_webmention`. Both methods use the same interface.
|
89
49
|
|
90
|
-
|
50
|
+
❌ **Removed:** the `Webmention.client` method has been removed in favor of the additional module methods [noted above](#usage). While the underlying `Webmention::Client` class still exists, its interface has changed and its direct usage is generally unnecessary.
|
91
51
|
|
92
|
-
|
93
|
-
- `Webmention::Client::TimeoutError`
|
94
|
-
- `Webmention::Client::TooManyRedirectsError`
|
52
|
+
❌ **Removed:** `Webmention::Client#send_all_mentions` has been removed in favor of `Webmention.send_webmentions`. Combine `Webmention.mentioned_urls` and `Webmention.send_webmentions` to achieve similar results.
|
95
53
|
|
96
|
-
|
54
|
+
🛠 **Refactored:** Exception handling has been greatly improved [as noted above](#exception-handling).
|
97
55
|
|
98
56
|
## Contributing
|
99
57
|
|
100
|
-
Interested in helping improve webmention-client-ruby? Awesome! Your help is greatly appreciated. See [CONTRIBUTING.md](https://github.com/indieweb/webmention-client-ruby/blob/
|
58
|
+
Interested in helping improve webmention-client-ruby? Awesome! Your help is greatly appreciated. See [CONTRIBUTING.md](https://github.com/indieweb/webmention-client-ruby/blob/main/CONTRIBUTING.md) for details.
|
101
59
|
|
102
60
|
## Acknowledgments
|
103
61
|
|
104
|
-
webmention-client-ruby is written and maintained by [
|
62
|
+
webmention-client-ruby is written and maintained by [Jason Garber](https://sixtwothree.org) ([@jgarber623](https://github.com/jgarber623)) with help from [these additional contributors](https://github.com/indieweb/webmention-client-ruby/graphs/contributors). Prior to 2018, webmention-client-ruby was written and maintained by [Aaron Parecki](https://aaronparecki.com) ([@aaronpk](https://github.com/aaronpk)) and [Nat Welch](https://natwelch.com) ([@icco](https://github.com/icco)).
|
105
63
|
|
106
64
|
To learn more about Webmention, see [indieweb.org/Webmention](https://indieweb.org/Webmention) and [webmention.net](https://webmention.net).
|
107
65
|
|
108
66
|
## License
|
109
67
|
|
110
|
-
webmention-client-ruby is freely available under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html). See [LICENSE](https://github.com/indieweb/webmention-client-ruby/blob/
|
68
|
+
webmention-client-ruby is freely available under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html). See [LICENSE](https://github.com/indieweb/webmention-client-ruby/blob/main/LICENSE) for more details.
|
data/USAGE.md
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
# Using webmention-client-ruby
|
2
|
+
|
3
|
+
Before using webmention-client-ruby, please read the [Getting Started](https://github.com/indieweb/webmention-client-ruby/blob/main/README.md#getting-started) and [Installation](https://github.com/indieweb/webmention-client-ruby/blob/main/README.md#installation) sections of the project's [README.md](https://github.com/indieweb/webmention-client-ruby/blob/main/README.md).
|
4
|
+
|
5
|
+
## Sending a webmention
|
6
|
+
|
7
|
+
With webmention-client-ruby installed, you may send a webmention from a source URL to a target URL:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
require 'webmention'
|
11
|
+
|
12
|
+
source = 'https://jgarber.example/post/100' # A post on your website
|
13
|
+
target = 'https://aaronpk.example/post/100' # A post on someone else's website
|
14
|
+
|
15
|
+
response = Webmention.send_webmention(source, target)
|
16
|
+
```
|
17
|
+
|
18
|
+
`Webmention.send_webmention` will return either a `Webmention::Response` or a `Webmention::ErrorResponse`. Instances of both classes respond to `ok?`. Building on the examples above:
|
19
|
+
|
20
|
+
A `Webmention::ErrorResponse` may be returned when:
|
21
|
+
|
22
|
+
1. The target URL does not advertise a Webmention endpoint.
|
23
|
+
2. The request to the target URL raises an `HTTP::Error` or an `OpenSSL::SSL::SSLError`.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
response.ok?
|
27
|
+
#=> false
|
28
|
+
|
29
|
+
response.class
|
30
|
+
#=> Webmention::ErrorResponse
|
31
|
+
|
32
|
+
response.message
|
33
|
+
#=> "No webmention endpoint found for target URL https://aaronpk.example/post/100"
|
34
|
+
```
|
35
|
+
|
36
|
+
A `Webmention::Response` will be returned in all other cases.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
response.ok?
|
40
|
+
#=> true
|
41
|
+
|
42
|
+
response.class
|
43
|
+
#=> Webmention::Response
|
44
|
+
```
|
45
|
+
|
46
|
+
Instances of `Webmention::Response` include useful methods delegated to the underlying `HTTP::Response` object:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
response.headers #=> HTTP::Headers
|
50
|
+
response.body #=> HTTP::Response::Body
|
51
|
+
response.code #=> Integer
|
52
|
+
response.reason #=> String
|
53
|
+
response.mime_type #=> String
|
54
|
+
response.uri #=> HTTP::URI
|
55
|
+
```
|
56
|
+
|
57
|
+
💡 **Note:** `Webmention::Response` objects may return a variety of status codes that will vary depending on the endpoint's capabilities and the success or failure of the request. See [the Webmention spec](https://www.w3.org/TR/webmention/) for more on status codes on their implications. A `Webmention::Response` responding affirmatively to `ok?` _may_ also have a non-successful HTTP status code (e.g. `404 Not Found`).
|
58
|
+
|
59
|
+
## Sending multiple webmentions
|
60
|
+
|
61
|
+
To send webmentions to multiple target URLs mentioned by a source URL:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
source = 'https://jgarber.example/post/100'
|
65
|
+
targets = ['https://aaronpk.example/notes/1', 'https://adactio.example/notes/1']
|
66
|
+
|
67
|
+
responses = Webmention.send_webmentions(source, targets)
|
68
|
+
```
|
69
|
+
|
70
|
+
`Webmention.send_webmentions` will return an array of `Webmention::Response` and `Webmention::ErrorResponse` objects.
|
71
|
+
|
72
|
+
## Including a vouch URL
|
73
|
+
|
74
|
+
webmention-client-ruby supports submitting a [vouch](https://indieweb.org/Vouch) URL when sending webmentions:
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
# Send a webmention with a vouch URL to a target URL
|
78
|
+
Webmention.send_webmention(source, target, vouch: 'https://tantek.example/notes/1')
|
79
|
+
|
80
|
+
# Send webmentions with a vouch URL to multiple target URLs
|
81
|
+
Webmention.send_webmentions(source, targets, vouch: 'https://tantek.example/notes/1')
|
82
|
+
```
|
83
|
+
|
84
|
+
## Discovering mentioned URLs
|
85
|
+
|
86
|
+
To retrieve unique URLs mentioned by a URL:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
urls = Webmention.mentioned_urls('https://jgarber.example/post/100')
|
90
|
+
```
|
91
|
+
|
92
|
+
`Webmention.mentioned_urls` will crawl the provided URL, parse the response body, and return a sorted list of unique URLs. Response bodies are parsed using MIME type-specific rules as noted in the [Verifying a webmention](#verifying-a-webmention) section below.
|
93
|
+
|
94
|
+
When parsing HTML documents, webmention-client-ruby will find the first [h-entry](https://microformats.org/wiki/h-entry) and search its markup for URLs. If no h-entry is found, the parser will search the document's `<body>`.
|
95
|
+
|
96
|
+
💡 **Note:** Links pointing to the supplied URL (or those with internal fragment identifiers) will be rejected. You may wish to additionally filter the results returned by `Webmention.mentioned_urls` before sending webmentions.
|
97
|
+
|
98
|
+
## Verifying a webmention
|
99
|
+
|
100
|
+
webmention-client-ruby verifies [HTML](https://www.w3.org/TR/html/), [JSON](https://json.org), and plaintext files in accordance with [Section 3.2.2](https://www.w3.org/TR/webmention/#webmention-verification) of [the W3C's Webmention Recommendation](https://www.w3.org/TR/webmention/):
|
101
|
+
|
102
|
+
> The receiver **should** use per-media-type rules to determine whether the source document mentions the target URL.
|
103
|
+
|
104
|
+
In plaintext documents, webmention-client-ruby will search the source URL for exact matches of the target URL. If the source URL is a JSON document, key/value pairs whose value equals the target URL are matched.
|
105
|
+
|
106
|
+
HTML documents are searched for a variety of elements and attributes whose values may be (or include) URLs:
|
107
|
+
|
108
|
+
| Element | Attributes |
|
109
|
+
|:-------------|:----------------|
|
110
|
+
| `a` | `href` |
|
111
|
+
| `area` | `href` |
|
112
|
+
| `audio` | `src` |
|
113
|
+
| `blockquote` | `cite` |
|
114
|
+
| `del` | `cite` |
|
115
|
+
| `embed` | `src` |
|
116
|
+
| `img` | `src`, `srcset` |
|
117
|
+
| `ins` | `cite` |
|
118
|
+
| `object` | `data` |
|
119
|
+
| `q` | `cite` |
|
120
|
+
| `source` | `src`, `srcset` |
|
121
|
+
| `track` | `src` |
|
122
|
+
| `video` | `src` |
|
123
|
+
|
124
|
+
To verify a received webmention:
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
# Verify that a source URL links to a target URL
|
128
|
+
verification = Webmention.verify_webmention(source, target)
|
129
|
+
|
130
|
+
# Verify that a source URL links to a target URL and that the vouch URL mentions
|
131
|
+
# the source URL's domain
|
132
|
+
verification = Webmention.verify_webmention(source, target, vouch: 'https://tantek.example/notes/1')
|
133
|
+
```
|
134
|
+
|
135
|
+
`Webmention.verify_webmention` returns an instance of `Webmention::Verification` which includes the following methods (each returns either `true` or `false`):
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
verification.source_mentions_target?
|
139
|
+
verification.verified?
|
140
|
+
verification.verify_vouch?
|
141
|
+
verification.vouch_mentions_source?
|
142
|
+
```
|
143
|
+
|
144
|
+
💡 **Note:** `Webmention.verify_webmention` parses HTML documents using the same rules outlined in [Discovering mentioned URLs](#discovering-mentioned-urls).
|
145
|
+
|
146
|
+
## Exception Handling
|
147
|
+
|
148
|
+
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.
|
149
|
+
|
150
|
+
When crawling the supplied URL, `Webmention.mentioned_urls` _may_ raise a `NoMethodError` if:
|
151
|
+
|
152
|
+
- a `Webmention::ErrorResponse` is returned, or
|
153
|
+
- the response is of an unsupported MIME type.
|
data/lib/webmention/client.rb
CHANGED
@@ -1,64 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Webmention
|
2
4
|
class Client
|
3
|
-
|
4
|
-
#
|
5
|
-
# client = Webmention::Client.new('https://source.example.com/post/100')
|
6
|
-
#
|
7
|
-
# @param source [String] An absolute URL representing the source document
|
8
|
-
def initialize(source)
|
9
|
-
raise ArgumentError, "source must be a String (given #{source.class.name})" unless source.is_a?(String)
|
5
|
+
@registered_parsers = {}
|
10
6
|
|
11
|
-
|
7
|
+
class << self
|
8
|
+
# @api private
|
9
|
+
attr_reader :registered_parsers
|
10
|
+
end
|
12
11
|
|
13
|
-
|
12
|
+
# @return [Webmention::Url]
|
13
|
+
attr_reader :source_url
|
14
|
+
|
15
|
+
# @return [Webmention::Url]
|
16
|
+
attr_reader :vouch_url
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
def self.register_parser(klass)
|
20
|
+
klass.mime_types.each { |mime_type| @registered_parsers[mime_type] = klass }
|
14
21
|
end
|
15
22
|
|
16
|
-
#
|
23
|
+
# Create a new Webmention::Client.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# Webmention::Client.new('https://jgarber.example/posts/100')
|
17
27
|
#
|
18
|
-
# @
|
19
|
-
|
20
|
-
|
28
|
+
# @example
|
29
|
+
# Webmention::Client.new('https://jgarber.example/posts/100', vouch: 'https://tantek.example/notes/1')
|
30
|
+
#
|
31
|
+
# @param source [String, HTTP::URI, #to_s]
|
32
|
+
# An absolute URL representing a source document.
|
33
|
+
# @param vouch [String, HTTP::URI, #to_s]
|
34
|
+
# An absolute URL representing a document vouching for the source document.
|
35
|
+
# See https://indieweb.org/Vouch for additional details.
|
36
|
+
#
|
37
|
+
# @return [Webmention::Client]
|
38
|
+
def initialize(source, vouch: nil)
|
39
|
+
@source_url = Url.new(source)
|
40
|
+
@vouch_url = Url.new(vouch)
|
41
|
+
end
|
42
|
+
|
43
|
+
# :nocov:
|
44
|
+
# @return [String]
|
45
|
+
def inspect
|
46
|
+
"#<#{self.class}:#{format('%#0x', object_id)} " \
|
47
|
+
"source_url: #{source_url} " \
|
48
|
+
"vouch_url: #{vouch_url}>"
|
21
49
|
end
|
50
|
+
# :nocov:
|
22
51
|
|
23
|
-
#
|
52
|
+
# Retrieve unique URLs mentioned by this client's source URL.
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# client = Webmention::Client.new('https://jgarber.example/posts/100')
|
56
|
+
# client.mentioned_urls
|
57
|
+
#
|
58
|
+
# @raise [NoMethodError]
|
59
|
+
# Raised when response is a Webmention::ErrorResponse or response is of an
|
60
|
+
# unsupported MIME type.
|
24
61
|
#
|
25
62
|
# @return [Array<String>]
|
26
|
-
# @raise [Webmention::UnsupportedMimeTypeError]
|
27
63
|
def mentioned_urls
|
28
|
-
|
64
|
+
response = source_url.response
|
29
65
|
|
30
|
-
|
66
|
+
self.class
|
67
|
+
.registered_parsers[response.mime_type]
|
68
|
+
.new(response.body, response.uri)
|
69
|
+
.results
|
70
|
+
.uniq
|
71
|
+
.reject { |url| url.match(/^#{response.uri}(?:#.*)?$/) }
|
72
|
+
.sort
|
31
73
|
end
|
32
74
|
|
33
|
-
# Send a webmention from this client's source URL to
|
75
|
+
# Send a webmention from this client's source URL to a target URL.
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# client = Webmention::Client.new('https://jgarber.example/posts/100')
|
79
|
+
# client.send_webmention('https://aaronpk.example/notes/1')
|
34
80
|
#
|
35
|
-
# @param target [String
|
36
|
-
#
|
37
|
-
#
|
38
|
-
|
39
|
-
|
81
|
+
# @param target [String, HTTP::URI, #to_s]
|
82
|
+
# An absolute URL representing a target document.
|
83
|
+
#
|
84
|
+
# @return [Webmention::Response, Webmention::ErrorResponse]
|
85
|
+
def send_webmention(target)
|
86
|
+
target_url = Url.new(target)
|
40
87
|
|
41
|
-
return
|
88
|
+
# A Webmention endpoint exists. Send the request and return the response.
|
89
|
+
if target_url.webmention_endpoint?
|
90
|
+
return Request.post(target_url.webmention_endpoint, **request_options_for(target))
|
91
|
+
end
|
42
92
|
|
43
|
-
|
44
|
-
|
45
|
-
raise Webmention.const_get(exception.class.name.split('::').last), exception
|
46
|
-
end
|
93
|
+
# An error was encountered fetching the target URL. Return the response.
|
94
|
+
return target_url.response unless target_url.response.ok?
|
47
95
|
|
48
|
-
|
96
|
+
# No Webmention endpoint exists. Return a new ErrorResponse.
|
97
|
+
ErrorResponse.new("No webmention endpoint found for target URL #{target}", target_url.response.request)
|
98
|
+
end
|
49
99
|
|
50
|
-
|
51
|
-
|
100
|
+
# Send webmentions from this client's source URL to multiple target URLs.
|
101
|
+
#
|
102
|
+
# @example
|
103
|
+
# client = Webmention::Client.new('https://jgarber.example/posts/100')
|
104
|
+
# targets = ['https://aaronpk.example/notes/1', 'https://adactio.example/notes/1']
|
105
|
+
# client.send_webmentions(targets)
|
106
|
+
#
|
107
|
+
# @param *targets [Array<String, HTTP::URI, #to_s>]
|
108
|
+
# An array of absolute URLs representing multiple target documents.
|
109
|
+
#
|
110
|
+
# @return [Array<Webmention::Response, Webmention::ErrorResponse>]
|
111
|
+
def send_webmentions(*targets)
|
112
|
+
targets.map { |target| send_webmention(target) }
|
52
113
|
end
|
53
114
|
|
54
|
-
|
55
|
-
|
115
|
+
# Verify that this client's source URL links to a target URL.
|
116
|
+
#
|
117
|
+
# @param target [String, HTTP::URI, #to_s]
|
118
|
+
# An absolute URL representing a target document.
|
119
|
+
#
|
120
|
+
# @raise (see Webmention::Client#mentioned_urls)
|
121
|
+
#
|
122
|
+
# @return [Webmention::Verification]
|
123
|
+
def verify_webmention(target)
|
124
|
+
Verification.new(source_url, Url.new(target), vouch_url: vouch_url)
|
56
125
|
end
|
57
126
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
127
|
+
private
|
128
|
+
|
129
|
+
# @param target [String, HTTP::URI, #to_s]
|
130
|
+
#
|
131
|
+
# @return [Hash{Symbol => String}]
|
132
|
+
def request_options_for(target)
|
133
|
+
opts = {
|
134
|
+
source: source_url,
|
135
|
+
target: target,
|
136
|
+
vouch: vouch_url
|
137
|
+
}
|
138
|
+
|
139
|
+
opts.transform_values { |value| value.to_s.strip }
|
140
|
+
.delete_if { |_, value| value.empty? }
|
62
141
|
end
|
63
142
|
end
|
64
143
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Webmention
|
4
|
+
class ErrorResponse
|
5
|
+
# @return [String]
|
6
|
+
attr_reader :message
|
7
|
+
|
8
|
+
# @return [Webmention::Request]
|
9
|
+
attr_reader :request
|
10
|
+
|
11
|
+
# Create a new Webmention::ErrorResponse.
|
12
|
+
#
|
13
|
+
# Instances of this class represent HTTP requests that generated errors
|
14
|
+
# (e.g. connection error, SSL error) or that could not locate a Webmention
|
15
|
+
# endpoint. The nature of the error is captured in the <code>#message</code>
|
16
|
+
# instance method.
|
17
|
+
#
|
18
|
+
# @param message [String]
|
19
|
+
# @param request [Webmention::Request]
|
20
|
+
#
|
21
|
+
# @return [Webmention::ErrorResponse]
|
22
|
+
def initialize(message, request)
|
23
|
+
@message = message
|
24
|
+
@request = request
|
25
|
+
end
|
26
|
+
|
27
|
+
# :nocov:
|
28
|
+
# @return [String]
|
29
|
+
def inspect
|
30
|
+
"#<#{self.class}:#{format('%#0x', object_id)} " \
|
31
|
+
"message: #{message}>"
|
32
|
+
end
|
33
|
+
# :nocov:
|
34
|
+
|
35
|
+
# @return [Boolean]
|
36
|
+
def ok?
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Webmention
|
4
|
+
# @api private
|
5
|
+
class Parser
|
6
|
+
URI_REGEXP = URI::DEFAULT_PARSER.make_regexp(%w[http https]).freeze
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# @return [Array<String>]
|
10
|
+
attr_reader :mime_types
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param response_body [HTTP::Response::Body, String, #to_s]
|
14
|
+
# @param response_uri [String, HTTP::URI, #to_s]
|
15
|
+
def initialize(response_body, response_uri)
|
16
|
+
@response_body = response_body.to_s
|
17
|
+
@response_uri = HTTP::URI.parse(response_uri.to_s)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
# @return [String]
|
23
|
+
attr_reader :response_body
|
24
|
+
|
25
|
+
# @return [HTTP::URI]
|
26
|
+
attr_reader :response_uri
|
27
|
+
end
|
28
|
+
end
|