micromicro 0.1.0

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