webmention 2.2.0 → 3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7e2e981cfe2dea4eec4e8e38fb9d948e14dd9b50c52f7455ca3583fbf4969aa
4
- data.tar.gz: 1f9f50af537f60c4fa705690d7553563c54a74ff09c4d653b2a1ab6940b71d47
3
+ metadata.gz: 39599574ee4962a000832e7a47c48fd78d58797181a12a554ca26065b2d860c1
4
+ data.tar.gz: aaf047a87adf3b2c171622725aabb9a5f5fc64267a98077c7f2eb8b8e5db0c55
5
5
  SHA512:
6
- metadata.gz: 3a45c8cfe715e165a81927e37af2ad020562a6af4c33ad59d15bed148e8a219623ac459dafb70c6ceae4c2cbb56f7e62406790ff1db7a89e8f34400cb6e3ee18
7
- data.tar.gz: db933998d46aac3dfdfd28202de070438062e8e7c2f3c18b03a160c97f242c6b926bf24fb81a4804f68360736b60c196825ecc2547fc11751527c5fdb0de0959
6
+ metadata.gz: db45f488f6e2f527b075764ad580817294ac8971248365270102ee244820c04a7f044612eebe6b7ebc52dbc1fe754b7b997e0da01f2a7b9994d4d51a77e92f82
7
+ data.tar.gz: 254fe4bda676161657ff674d75a0866f7711dca89d5f7b5f0f961178efbbd9915a3b89ac94497442906b73ee572ababc90fedf3909c85b8e39c2ed53b897689b
@@ -6,6 +6,7 @@ AllCops:
6
6
 
7
7
  Layout/HashAlignment:
8
8
  EnforcedColonStyle: table
9
+ EnforcedHashRocketStyle: table
9
10
 
10
11
  Layout/LineLength:
11
12
  Enabled: false
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## v3.0.0 / 2020-05-19
4
+
5
+ - Reject "internal" URLs when sending webmentions (#24) (ccc82c8)
6
+ - Select only HTTP/HTTPS URLs when sending webmentions (#22) (39e5852)
7
+
3
8
  ## 2.2.0 / 2020-05-18
4
9
 
5
10
  - Update absolutely and indieweb-endpoints gems (350d2ed)
@@ -14,16 +14,25 @@ require 'webmention/parsers/base_parser'
14
14
  require 'webmention/parsers/html_parser'
15
15
 
16
16
  require 'webmention/services/http_request_service'
17
- require 'webmention/services/node_parser_service'
18
17
 
19
18
  module Webmention
20
- class << self
21
- def client(source)
22
- Client.new(source)
23
- end
19
+ # Create a new Webmention::Client
20
+ # Convenience method for Webmention::Client.new
21
+ #
22
+ # client = Webmention.client('https://source.example.com/post/100')
23
+ #
24
+ # @param source [String] An absolute URL representing the source document
25
+ # @return [Webmention::Client]
26
+ def self.client(source)
27
+ Client.new(source)
28
+ end
24
29
 
25
- def send_mention(source, target)
26
- client(source).send_mention(target)
27
- end
30
+ # Send a webmention from the source URL to the target URL
31
+ #
32
+ # @param source [String] An absolute URL representing the source document
33
+ # @param target [String] An absolute URL representing the target document
34
+ # @return [HTTP::Response, nil]
35
+ def self.send_mention(source, target)
36
+ client(source).send_mention(target)
28
37
  end
29
38
  end
@@ -1,5 +1,10 @@
1
1
  module Webmention
2
2
  class Client
3
+ # Create a new Webmention::Client
4
+ #
5
+ # client = Webmention::Client.new('https://source.example.com/post/100')
6
+ #
7
+ # @param source [String] An absolute URL representing the source document
3
8
  def initialize(source)
4
9
  raise ArgumentError, "source must be a String (given #{source.class.name})" unless source.is_a?(String)
5
10
 
@@ -8,16 +13,28 @@ module Webmention
8
13
  raise ArgumentError, 'source must be an absolute URL (e.g. https://example.com)' unless source_uri.absolute?
9
14
  end
10
15
 
16
+ # Send webmentions to all mentioned URLs in this client's source document
17
+ #
18
+ # @return [Hash{String => HTTP::Response, nil}]
11
19
  def send_all_mentions
12
20
  mentioned_urls.each_with_object({}) { |url, hash| hash[url] = send_mention(url) }
13
21
  end
14
22
 
23
+ # Extract mentioned URLs from this client's source document
24
+ #
25
+ # @return [Array<String>]
26
+ # @raise [Webmention::UnsupportedMimeTypeError]
15
27
  def mentioned_urls
16
28
  raise UnsupportedMimeTypeError, "Unsupported MIME Type: #{source_response.mime_type}" unless parser_for_mime_type
17
29
 
18
30
  @mentioned_urls ||= parser_for_mime_type.new(source_response).results
19
31
  end
20
32
 
33
+ # Send a webmention from this client's source URL to the target URL
34
+ #
35
+ # @param target [String] An absolute URL representing the target document
36
+ # @return [HTTP::Response, nil]
37
+ # @raise [Webmention::ArgumentError, Webmention::ConnectionError, Webmention::InvalidURIError, Webmention::TimeoutError, Webmention::TooManyRedirectsError]
21
38
  def send_mention(target)
22
39
  endpoint = IndieWeb::Endpoints.get(target).webmention
23
40
 
@@ -13,10 +13,6 @@ module Webmention
13
13
  raise UnsupportedMimeTypeError, "Unsupported MIME Type: #{response.mime_type}" unless self.class.mime_types.include?(response.mime_type)
14
14
  end
15
15
 
16
- def results
17
- @results ||= parse_response_body
18
- end
19
-
20
16
  private
21
17
 
22
18
  def response_body
@@ -5,17 +5,22 @@ module Webmention
5
5
 
6
6
  Parsers.register(self)
7
7
 
8
- HTML_ATTRIBUTE_MAP = {
9
- cite: %w[blockquote del ins q],
10
- data: %w[object],
11
- href: %w[a area],
12
- poster: %w[video],
13
- src: %w[audio embed img source track video],
14
- srcset: %w[img source]
8
+ HTML_ATTRIBUTES_MAP = {
9
+ 'cite' => %w[blockquote del ins q],
10
+ 'data' => %w[object],
11
+ 'href' => %w[a area],
12
+ 'poster' => %w[video],
13
+ 'src' => %w[audio embed img source track video],
14
+ 'srcset' => %w[img source]
15
15
  }.freeze
16
16
 
17
- CSS_SELECTORS_MAP = HTML_ATTRIBUTE_MAP.each_with_object({}) do |(attribute, elements), hash|
18
- hash[attribute] = elements.map { |element| "#{element}[#{attribute}]" }
17
+ CSS_SELECTORS_ARRAY = HTML_ATTRIBUTES_MAP.flat_map { |attribute, names| names.map { |name| "#{name}[#{attribute}]" } }.freeze
18
+
19
+ # Parse an HTML string for URLs
20
+ #
21
+ # @return [Array<String>] Unique external URLs whose scheme matches http/https
22
+ def results
23
+ @results ||= resolved_urls.uniq.select { |url| url.match?(%r{https?://}) }.reject { |url| url.match(/^#{response_url}(?:#.*)?$/) }
19
24
  end
20
25
 
21
26
  private
@@ -24,23 +29,36 @@ module Webmention
24
29
  @doc ||= Nokogiri::HTML(response_body)
25
30
  end
26
31
 
27
- # Parse an HTML string for URLs
28
- #
29
- # @return [Array] the URLs
30
- def parse_response_body
31
- CSS_SELECTORS_MAP
32
- .each_with_object([]) { |(*args), array| array << search_node(*args) }
33
- .flatten
34
- .map { |url| Absolutely.to_abs(base: response_url, relative: url) }
35
- .uniq
32
+ def resolved_urls
33
+ UrlAttributesParser.parse(*url_attributes).map { |url| Absolutely.to_abs(base: response_url, relative: url) }
36
34
  end
37
35
 
38
36
  def root_node
39
- @root_node ||= doc.at_css('.h-entry .e-content') || doc.at_css('.h-entry') || doc.css('body')
37
+ doc.at_css('.h-entry .e-content', '.h-entry') || doc.css('body')
40
38
  end
41
39
 
42
- def search_node(attribute, selectors)
43
- Services::NodeParserService.nodes_from(root_node, selectors).map { |node| Services::NodeParserService.values_from(node, attribute) }.reject(&:empty?)
40
+ def url_attributes
41
+ url_nodes.flat_map(&:attribute_nodes).select { |attribute| HTML_ATTRIBUTES_MAP.key?(attribute.name) }
42
+ end
43
+
44
+ def url_nodes
45
+ root_node.css(*CSS_SELECTORS_ARRAY)
46
+ end
47
+
48
+ module UrlAttributesParser
49
+ # @param attributes [Array<Nokogiri::XML::Attr>]
50
+ # @return [Array<String>]
51
+ def self.parse(*attributes)
52
+ attributes.flat_map { |attribute| value_from(attribute) }
53
+ end
54
+
55
+ # @param attribute [Nokogiri::XML::Attr]
56
+ # @return [String, Array<String>]
57
+ def self.value_from(attribute)
58
+ return attribute.value unless attribute.name == 'srcset'
59
+
60
+ attribute.value.split(',').map { |value| value.strip.match(/^\S+/).to_s }
61
+ end
44
62
  end
45
63
  end
46
64
  end
@@ -1,3 +1,3 @@
1
1
  module Webmention
2
- VERSION = '2.2.0'.freeze
2
+ VERSION = '3.0.0'.freeze
3
3
  end
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: 2.2.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Parecki
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-05-18 00:00:00.000000000 Z
12
+ date: 2020-05-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: absolutely
@@ -109,7 +109,6 @@ files:
109
109
  - lib/webmention/parsers/base_parser.rb
110
110
  - lib/webmention/parsers/html_parser.rb
111
111
  - lib/webmention/services/http_request_service.rb
112
- - lib/webmention/services/node_parser_service.rb
113
112
  - lib/webmention/version.rb
114
113
  - webmention.gemspec
115
114
  homepage: https://github.com/indieweb/webmention-client-ruby
@@ -117,7 +116,7 @@ licenses:
117
116
  - Apache-2.0
118
117
  metadata:
119
118
  bug_tracker_uri: https://github.com/indieweb/webmention-client-ruby/issues
120
- changelog_uri: https://github.com/indieweb/webmention-client-ruby/blob/v2.2.0/CHANGELOG.md
119
+ changelog_uri: https://github.com/indieweb/webmention-client-ruby/blob/v3.0.0/CHANGELOG.md
121
120
  post_install_message:
122
121
  rdoc_options: []
123
122
  require_paths:
@@ -1,25 +0,0 @@
1
- module Webmention
2
- module Services
3
- module NodeParserService
4
- # Search a node for matching elements
5
- #
6
- # @param node [Nokogiri::XML::Element]
7
- # @param selectors [Array]
8
- # @return [Nokogiri::XML::NodeSet]
9
- def self.nodes_from(node, selectors)
10
- node.css(*selectors)
11
- end
12
-
13
- # Derive attribute values from a single node
14
- #
15
- # @param node [Nokogiri::XML::Element]
16
- # @param attribute [Symbol]
17
- # @return [Array] the HTML attribute values
18
- def self.values_from(node, attribute)
19
- return Array(node[attribute]) unless attribute == :srcset
20
-
21
- node[attribute].split(',').map { |value| value.strip.match(/^\S+/).to_s }
22
- end
23
- end
24
- end
25
- end