webmention 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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