micromicro 0.1.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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +14 -0
  3. data/.gitignore +34 -0
  4. data/.gitmodules +3 -0
  5. data/.reek.yml +8 -0
  6. data/.rspec +2 -0
  7. data/.rubocop +3 -0
  8. data/.rubocop.yml +25 -0
  9. data/.ruby-version +1 -0
  10. data/.simplecov +11 -0
  11. data/.travis.yml +19 -0
  12. data/CHANGELOG.md +5 -0
  13. data/CONTRIBUTING.md +37 -0
  14. data/Gemfile +14 -0
  15. data/LICENSE +21 -0
  16. data/README.md +122 -0
  17. data/Rakefile +18 -0
  18. data/lib/micro_micro/collections/base_collection.rb +37 -0
  19. data/lib/micro_micro/collections/items_collection.rb +10 -0
  20. data/lib/micro_micro/collections/properties_collection.rb +18 -0
  21. data/lib/micro_micro/collections/relations_collection.rb +23 -0
  22. data/lib/micro_micro/document.rb +71 -0
  23. data/lib/micro_micro/implied_property.rb +25 -0
  24. data/lib/micro_micro/item.rb +151 -0
  25. data/lib/micro_micro/parsers/base_property_parser.rb +33 -0
  26. data/lib/micro_micro/parsers/date_time_parser.rb +85 -0
  27. data/lib/micro_micro/parsers/date_time_property_parser.rb +65 -0
  28. data/lib/micro_micro/parsers/embedded_markup_property_parser.rb +28 -0
  29. data/lib/micro_micro/parsers/implied_name_property_parser.rb +78 -0
  30. data/lib/micro_micro/parsers/implied_photo_property_parser.rb +69 -0
  31. data/lib/micro_micro/parsers/implied_url_property_parser.rb +61 -0
  32. data/lib/micro_micro/parsers/plain_text_property_parser.rb +39 -0
  33. data/lib/micro_micro/parsers/url_property_parser.rb +75 -0
  34. data/lib/micro_micro/parsers/value_class_pattern_parser.rb +92 -0
  35. data/lib/micro_micro/property.rb +116 -0
  36. data/lib/micro_micro/relation.rb +78 -0
  37. data/lib/micro_micro/version.rb +3 -0
  38. data/lib/micromicro.rb +39 -0
  39. data/micromicro.gemspec +28 -0
  40. metadata +128 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ae75d6afb6f7d98bc3a2fc12333ccbd31824e1cecd8336057e551fe6b392a643
4
+ data.tar.gz: 877da43669217907635d71015ec574ebc656e8755c8f6267229cebde36f281b0
5
+ SHA512:
6
+ metadata.gz: a4b5a68c2d343fe0935d2103016aacf0b94d7a7c391d3ea360160a25fcb01bc16b7c70ae715e12b17142cedb928bea782b8057db5002df3e9ece2a99fdf1558c
7
+ data.tar.gz: 16a62f01b013d56547cebcac3001ecf59b77b3c14653f473a833d66589c6be31b5c53c2e6c547e0ce5c65f3dafb18feeb94be30ae0f57c65a96def024d6afccb
@@ -0,0 +1,14 @@
1
+ # EditorConfig is awesome: https://EditorConfig.org
2
+ root = true
3
+
4
+ [*]
5
+ charset = utf-8
6
+ end_of_line = lf
7
+ insert_final_newline = true
8
+ indent_size = 2
9
+ indent_style = space
10
+ trim_trailing_whitespace = true
11
+
12
+ [*.md]
13
+ indent_size = 4
14
+ indent_style = tab
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ # Documentation cache and generated files:
17
+ /.yardoc/
18
+ /_yardoc/
19
+ /doc/
20
+ /rdoc/
21
+
22
+ # Environment normalization:
23
+ /.bundle/
24
+ /vendor/bundle
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
@@ -0,0 +1,3 @@
1
+ [submodule "microformats_test_suite"]
2
+ path = spec/support/fixtures/microformats_test_suite
3
+ url = https://github.com/jgarber623/microformats-test-suite
@@ -0,0 +1,8 @@
1
+ detectors:
2
+ DuplicateMethodCall:
3
+ max_calls: 3
4
+ IrresponsibleModule:
5
+ enabled: false
6
+
7
+ exclude_paths:
8
+ - vendor/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --order random
2
+ --require spec_helper
@@ -0,0 +1,3 @@
1
+ --display-style-guide
2
+ --extra-details
3
+ --parallel
@@ -0,0 +1,25 @@
1
+ require:
2
+ - rubocop-performance
3
+ - rubocop-rspec
4
+
5
+ AllCops:
6
+ NewCops: enable
7
+
8
+ Layout/HashAlignment:
9
+ EnforcedHashRocketStyle: table
10
+
11
+ Layout/LineLength:
12
+ Enabled: false
13
+
14
+ Metrics/BlockLength:
15
+ Exclude:
16
+ - spec/**/*.rb
17
+
18
+ Style/Documentation:
19
+ Enabled: false
20
+
21
+ Style/FrozenStringLiteralComment:
22
+ Enabled: false
23
+
24
+ Style/SymbolArray:
25
+ Enabled: false
@@ -0,0 +1 @@
1
+ 2.5.8
@@ -0,0 +1,11 @@
1
+ require 'simplecov-console'
2
+
3
+ formatters = [SimpleCov::Formatter::HTMLFormatter]
4
+
5
+ if RSpec.configuration.files_to_run.length > 1
6
+ formatters << SimpleCov::Formatter::Console
7
+ end
8
+
9
+ SimpleCov.start do
10
+ formatter SimpleCov::Formatter::MultiFormatter.new(formatters)
11
+ end
@@ -0,0 +1,19 @@
1
+ dist: bionic
2
+ language: ruby
3
+ rvm:
4
+ - 2.5
5
+ - 2.6
6
+ - 2.7
7
+ cache:
8
+ - bundler
9
+ before_script:
10
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
11
+ - chmod +x ./cc-test-reporter
12
+ - ./cc-test-reporter before-build
13
+ after_script:
14
+ - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
15
+ notifications:
16
+ email: false
17
+ slack:
18
+ rooms:
19
+ secure: JmMccxJmz97LtzKLPd+Hp1MeRUKL8kscN2A2CPyEyRJC7u/U+41SHtXkojR4OpQDoCsP3MexhqoJ/Fu1eLDxZ3e1B7PFrwKCfSQc8YZhLYfE0B9Tfy4M+LBQkgoLwsUHP2Vxyho0iZ1knL+070EJQlEpTox+9N8eTfOX6SOpiNLDHTRd4217yI7faog790OYGKTbOCRj5fo+kPAeN8NrEZavjx6QeWBcJ/wRv6O2V6dhrnqOho2ktSj14/yeNsVoYc4Ondk5Hy/zSCPRjvqFO9wI2mC6l7u9W0ffy09trvkXTTmPQeCbzFKgybHSKpmxCE6frFOSkO+aBVGJ/p4hxej5001nzZrCfZF3MBEKQwAFHBLX6hIIbCOZmR915vl+8NGPkCy4o8W/BlRWKtVWIbDCk8sxf+WbIHLkSsfBBGHmABxQXvDS3ejKh+Rm35ejdrak2jWg/cxYHBbZDHBNPjLHbYzfziMSlNgbQ3V9fVfpNC1AI7jXNSgOsuX6Uv5dvTk40Wa4xEiNOkEtLMn55exnrxoJ+B68aZPvA/71/rvHKPyjgBNxu7Zb+5mrVHja+DNTdotomWdzfmILn2CV1xvfhQAFmph4lagiRHqpLXATJ6N0907tNILJUzbvMPONVbcyQlx1UHnbiezagjQ+dwoOyjof184M8jECba7zXzs=
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 / 2020-05-06
4
+
5
+ - Initial release!
@@ -0,0 +1,37 @@
1
+ # Contributing to MicroMicro
2
+
3
+ There are a couple ways you can help improve MicroMicro:
4
+
5
+ 1. Fix an existing [issue][issues] and submit a [pull request][pulls].
6
+ 1. Review open [pull requests][pulls].
7
+ 1. Report a new [issue][issues]. _Only do this after you've made sure the behavior or problem you're observing isn't already documented in an open issue._
8
+
9
+ ## Getting Started
10
+
11
+ MicroMicro is developed using Ruby 2.5.8 and is additionally tested against Ruby 2.6 and 2.7 using [Travis CI](https://travis-ci.com/jgarber623/micromicro).
12
+
13
+ Before making changes to MicroMicro, you'll want to install Ruby 2.5.8. 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.5.8 using your method of choice, install the project's gems by running:
14
+
15
+ ```sh
16
+ bundle install
17
+ ```
18
+
19
+ ## Making Changes
20
+
21
+ 1. Fork and clone the project's repo.
22
+ 1. Install development dependencies as outlined above.
23
+ 1. Create a feature branch for the code changes you're looking to make: `git checkout -b my-new-feature`.
24
+ 1. _Write some code!_
25
+ 1. If your changes would benefit from testing, add the necessary tests and verify everything passes by running `bundle exec rspec`.
26
+ 1. Commit your changes: `git commit -am 'Add some new feature or fix some issue'`. _(See [this excellent article](https://chris.beams.io/posts/git-commit/) for tips on writing useful Git commit messages.)_
27
+ 1. Push the branch to your fork: `git push -u origin my-new-feature`.
28
+ 1. Create a new [pull request][pulls] and we'll review your changes.
29
+
30
+ ## Code Style
31
+
32
+ Code formatting conventions are defined in the `.editorconfig` file which uses the [EditorConfig](http://editorconfig.org) syntax. There are [plugins for a variety of editors](http://editorconfig.org/#download) that utilize the settings in the `.editorconfig` file. We recommended you install the EditorConfig plugin for your editor of choice.
33
+
34
+ Your bug fix or feature addition won't be rejected if it runs afoul of any (or all) of these guidelines, but following the guidelines will definitely make everyone's lives a little easier.
35
+
36
+ [issues]: https://github.com/jgarber623/micromicro/issues
37
+ [pulls]: https://github.com/jgarber623/micromicro/pulls
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in micromicro.gemspec
4
+ gemspec
5
+
6
+ gem 'pry-byebug', '~> 3.9'
7
+ gem 'rake', '~> 13.0'
8
+ gem 'reek', '~> 6.0'
9
+ gem 'rspec', '~> 3.9'
10
+ gem 'rubocop', '~> 0.82.0'
11
+ gem 'rubocop-performance', '~> 1.5'
12
+ gem 'rubocop-rspec', '~> 1.38'
13
+ gem 'simplecov', '~> 0.18.5'
14
+ gem 'simplecov-console', '~> 0.7.2'
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019–present Jason Garber
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,122 @@
1
+ # MicroMicro
2
+
3
+ **A Ruby gem for extracting [microformats2](http://microformats.org/wiki/microformats2)-encoded data from HTML documents.**
4
+
5
+ [![Build](https://img.shields.io/travis/com/jgarber623/micromicro/master.svg?style=for-the-badge)](https://travis-ci.com/jgarber623/micromicro)
6
+ [![Dependencies](https://img.shields.io/depfu/jgarber623/micromicro.svg?style=for-the-badge)](https://depfu.com/github/jgarber623/micromicro)
7
+ [![Maintainability](https://img.shields.io/codeclimate/maintainability/jgarber623/micromicro.svg?style=for-the-badge)](https://codeclimate.com/github/jgarber623/micromicro)
8
+ [![Coverage](https://img.shields.io/codeclimate/c/jgarber623/micromicro.svg?style=for-the-badge)](https://codeclimate.com/github/jgarber623/micromicro/code)
9
+
10
+ ## Key Features
11
+
12
+ - Parses microformats2-encoded HTML documents according to the [microformats2 parsing specification](http://microformats.org/wiki/microformats2-parsing)
13
+ - Passes all microformats2 tests from [the official test suite](https://github.com/microformats/tests)¹
14
+ - Supports Ruby 2.5 and newer
15
+
16
+ **Note:** MicroMicro **does not** parse [Classic Microformats](http://microformats.org/wiki/Main_Page#Classic_Microformats) (referred to in [the parsing specification](http://microformats.org/wiki/microformats2-parsing#note_backward_compatibility_details) as "backcompat root classes" and "backcompat properties"). If parsing documents marked up in this fashion, consider using [the official microformats-ruby parser](https://github.com/microformats/microformats-ruby).
17
+
18
+ <small>¹ …with some exceptions until [this pull request](https://github.com/microformats/tests/pull/112) is merged.</small>
19
+
20
+ ## Getting Started
21
+
22
+ Before installing and using MicroMicro, you'll want to have [Ruby](https://www.ruby-lang.org) 2.5 (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).
23
+
24
+ MicroMicro is developed using Ruby 2.5.8 and is additionally tested against Ruby 2.6 and 2.7 using [Travis CI](https://travis-ci.com/jgarber623/micromicro).
25
+
26
+ ## Installation
27
+
28
+ If you're using [Bundler](https://bundler.io), add MicroMicro to your project's `Gemfile`:
29
+
30
+ ```ruby
31
+ source 'https://rubygems.org'
32
+
33
+ gem 'micromicro'
34
+ ```
35
+
36
+ …and hop over to your command prompt and run…
37
+
38
+ ```sh
39
+ $ bundle install
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ ### Basic Usage
45
+
46
+ MicroMicro's `parse` method accepts two arguments: a `String` of markup and a `String` representing the URL associated with that markup.
47
+
48
+ The markup (typically HTML) can be retrieved from the Web using a library of your choosing or provided inline as a simple `String` (e.g. `<div class="h-card">Jason Garber</div>`) The URL provided is used to resolve relative URLs in accordance with the document's language rules.
49
+
50
+ An example using a simple `String` of HTML as input:
51
+
52
+ ```ruby
53
+ require 'micromicro'
54
+
55
+ doc = MicroMicro.parse('<div class="h-card">Jason Garber</div>', 'https://sixtwothree.org')
56
+ #=> #<MicroMicro::Document items: #<MicroMicro::Collections::ItemsCollection count: 1, members: [#<MicroMicro::Item types: ["h-card"], properties: 1, children: 0>]>, relations: #<MicroMicro::Collections::RelationsCollection count: 0, members: []>>
57
+
58
+ doc.to_h
59
+ #=> { :items => [{ :type => ["h-card"], :properties => { :name => ["Jason Garber"] } }], :rels => {}, :"rel-urls" => {} }
60
+ ```
61
+
62
+ The `Hash` produced by calling `doc.to_h` may be converted to JSON (e.g. `doc.to_h.to_json`) for storage, additional manipulation, or use with other tools.
63
+
64
+ Another example pulling the source HTML from [Tantek](https://tantek.com)'s website:
65
+
66
+ ```ruby
67
+ require "net/http"
68
+ require "micromicro"
69
+
70
+ url = "https://tantek.com"
71
+ rsp = Net::HTTP.get(URI.parse(url))
72
+
73
+ doc = MicroMicro.parse(rsp, url)
74
+ #=> #<MicroMicro::Document items: #<MicroMicro::Collections::ItemsCollection count: 1, members: […]>, relations: #<MicroMicro::Collections::RelationsCollection count: 31, members: […]>>
75
+
76
+ doc.to_h
77
+ #=> { :items => [{ :type => ["h-card"], :properties => {…}, :children => […]}], :rels => {…}, :'rel-urls' => {…} }
78
+ ```
79
+
80
+ ### Advanced Usage
81
+
82
+ Building on the example above, a MicroMicro-parsed document is navigable and manipulable using a familiar `Enumerable`-esque interface.
83
+
84
+ ```ruby
85
+ doc.items.first
86
+ #=> #<MicroMicro::Item types: ["h-card"], properties: 42, children: 6>
87
+
88
+ doc.items.first.properties
89
+ #=> #<MicroMicro::Collections::PropertiesCollection count: 42, members: […]>
90
+
91
+ doc.items.first.properties[7]
92
+ #=> #<MicroMicro::Property name: "category", prefix: "p", value: "teacher">
93
+
94
+ doc.items.first.properties.take(5).map { |property| [property.name, property.value] }
95
+ #=> [["photo", { :value => "https://tantek.com/photo.jpg", :alt => "" }], ["url", "https://tantek.com/"], ["uid", "https://tantek.com/"], ["name", "Tantek Çelik"], ["role", "Inventor, writer, teacher, runner, coder, more."]]
96
+
97
+ doc.items.first.children
98
+ #=> #<MicroMicro::Collections::ItemsCollection count: 6, members: […]>
99
+
100
+ doc.relations.first
101
+ #=> #<MicroMicro::Relation href: "https://tantek.com/", rels: ["canonical"]>
102
+
103
+ doc.relations.map(&:rels).flatten.uniq.sort
104
+ #=> ["alternate", "apple-touch-icon-precomposed", "author", "authorization_endpoint", "bookmark", "canonical", "hub", "icon", "me", "microsub", …]
105
+
106
+ doc.relations.find { |relation| relation.rels.include?('webmention') }
107
+ # => #<MicroMicro::Relation href: "https://webmention.io/tantek.com/webmention", rels: ["webmention"]>
108
+ ```
109
+
110
+ ## Contributing
111
+
112
+ Interested in helping improve MicroMicro? Awesome! Your help is greatly appreciated. See [CONTRIBUTING.md](https://github.com/jgarber623/micromicro/blob/master/CONTRIBUTING.md) for details.
113
+
114
+ ## Acknowledgments
115
+
116
+ MicroMicro wouldn't exist without the hard work of everyone involved in the [microformats](http://microformats.org) community. Additionally, the comprehensive [microformats test suite](https://github.com/microformats/tests) was invaluable in the development of this Ruby gem.
117
+
118
+ MicroMicro is written and maintained by [Jason Garber](https://sixtwothree.org).
119
+
120
+ ## License
121
+
122
+ MicroMicro is freely available under the [MIT License](https://opensource.org/licenses/MIT). Use it, learn from it, fork it, improve it, change it, tailor it to your needs.
@@ -0,0 +1,18 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'reek/rake/task'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubocop/rake_task'
6
+
7
+ Reek::Rake::Task.new do |task|
8
+ task.fail_on_error = false
9
+ task.source_files = FileList['**/*.rb'].exclude('vendor/**/*.rb')
10
+ end
11
+
12
+ RSpec::Core::RakeTask.new
13
+
14
+ RuboCop::RakeTask.new do |task|
15
+ task.fail_on_error = false
16
+ end
17
+
18
+ task default: [:rubocop, :reek, :spec]
@@ -0,0 +1,37 @@
1
+ module MicroMicro
2
+ module Collections
3
+ class BaseCollection
4
+ include Enumerable
5
+
6
+ delegate :[], :each, :last, :length, :split, to: :members
7
+
8
+ # @param members [Array<MicroMicro::Item, MicroMicro::Property, MicroMicro::Relation>]
9
+ def initialize(members = [])
10
+ @members = members
11
+
12
+ decorate_members if respond_to?(:decorate_members, true)
13
+ end
14
+
15
+ # @return [String]
16
+ def inspect
17
+ format(%(#<#{self.class.name}:%#0x count: #{count}, members: #{members.inspect}>), object_id)
18
+ end
19
+
20
+ # @param member [MicroMicro::Item, MicroMicro::Property, MicroMicro::Relation]
21
+ # @return [self]
22
+ def push(member)
23
+ members.push(member)
24
+
25
+ decorate_members if respond_to?(:decorate_members, true)
26
+
27
+ self
28
+ end
29
+
30
+ alias << push
31
+
32
+ private
33
+
34
+ attr_reader :members
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,10 @@
1
+ module MicroMicro
2
+ module Collections
3
+ class ItemsCollection < BaseCollection
4
+ # @return [Array<Hash{Symbol => Array<String, Hash>}>]
5
+ def to_a
6
+ map(&:to_h)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,18 @@
1
+ module MicroMicro
2
+ module Collections
3
+ class PropertiesCollection < BaseCollection
4
+ # @return [Hash{Symbol => Array<String, Hash>}]
5
+ def to_h
6
+ group_by(&:name).symbolize_keys.deep_transform_values do |property|
7
+ property.item_node? ? property.value.to_h : property.value
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def decorate_members
14
+ each { |member| member.collection = self }
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,23 @@
1
+ module MicroMicro
2
+ module Collections
3
+ class RelationsCollection < BaseCollection
4
+ # @see microformats2 Parsing Specification section 1.4
5
+ # @see http://microformats.org/wiki/microformats2-parsing#parse_a_hyperlink_element_for_rel_microformats
6
+ #
7
+ # @return [Hash{Symbole => Hash{Symbol => Array, String}}]
8
+ def group_by_url
9
+ group_by(&:href).symbolize_keys.transform_values { |relations| relations.first.to_h.slice!(:href) }
10
+ end
11
+
12
+ # @see microformats2 Parsing Specification section 1.4
13
+ # @see http://microformats.org/wiki/microformats2-parsing#parse_a_hyperlink_element_for_rel_microformats
14
+ #
15
+ # @return [Hash{Symbol => Array<String>}]
16
+ def group_by_rel
17
+ each_with_object(Hash.new { |hash, key| hash[key] = [] }) do |member, hash|
18
+ member.rels.each { |rel| hash[rel] << member.href }
19
+ end.symbolize_keys.transform_values(&:uniq)
20
+ end
21
+ end
22
+ end
23
+ end