webmention 3.0.0 → 6.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/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
|
-
[](https://rubygems.org/gems/webmention)
|
6
|
-
[](https://rubygems.org/gems/webmention)
|
7
|
-
[](https://codeclimate.com/github/indieweb/webmention-client-ruby)
|
9
|
-
[](https://codeclimate.com/github/indieweb/webmention-client-ruby/code)
|
5
|
+
[](https://rubygems.org/gems/webmention)
|
6
|
+
[](https://rubygems.org/gems/webmention)
|
7
|
+
[](https://github.com/indieweb/webmention-client-ruby/actions/workflows/ci.yml)
|
8
|
+
[](https://codeclimate.com/github/indieweb/webmention-client-ruby)
|
9
|
+
[](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
|