micromicro 2.0.1 → 3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +3 -96
- data/lib/micro_micro/collections/base_collection.rb +2 -1
- data/lib/micro_micro/collections/items_collection.rb +81 -0
- data/lib/micro_micro/collections/properties_collection.rb +102 -3
- data/lib/micro_micro/collections/relationships_collection.rb +82 -4
- data/lib/micro_micro/document.rb +12 -6
- data/lib/micro_micro/helpers.rb +6 -0
- data/lib/micro_micro/implied_property.rb +13 -0
- data/lib/micro_micro/item.rb +65 -46
- data/lib/micro_micro/parsers/base_property_parser.rb +2 -0
- data/lib/micro_micro/parsers/date_time_parser.rb +3 -5
- data/lib/micro_micro/parsers/date_time_property_parser.rb +3 -0
- data/lib/micro_micro/parsers/embedded_markup_property_parser.rb +1 -0
- data/lib/micro_micro/parsers/image_element_parser.rb +68 -0
- data/lib/micro_micro/parsers/implied_name_property_parser.rb +1 -0
- data/lib/micro_micro/parsers/implied_photo_property_parser.rb +7 -6
- data/lib/micro_micro/parsers/implied_url_property_parser.rb +1 -0
- data/lib/micro_micro/parsers/plain_text_property_parser.rb +1 -0
- data/lib/micro_micro/parsers/url_property_parser.rb +4 -5
- data/lib/micro_micro/parsers/value_class_pattern_parser.rb +4 -2
- data/lib/micro_micro/property.rb +78 -20
- data/lib/micro_micro/relationship.rb +26 -3
- data/lib/micro_micro/version.rb +1 -1
- data/lib/micromicro.rb +8 -6
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75bb9eaa673828648bc3f0a78d9822f434ae99317ae9499998f984c661c0771b
|
4
|
+
data.tar.gz: 2ab7a2896a8498507cb57f7e1c5a10c1e5dff8091328370a10e645420374ec12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a24f38005e975861d4e5ce1c3b728ea4535d7a094cae2515a8b6e7f0e7bb7abe7558eec44651a7e833d0120d397bcb45687022c374a039f5d755d46798264e8c
|
7
|
+
data.tar.gz: 58f27f493e1255ef7b4ea1904cb643c1392f62dacea645708142f153eccfba2ed0a9ed52e33f76f4ec4eae5012b9dce159ec963c216a5506969b4a47be4e2042
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 3.1.0 / 2022-09-24
|
4
|
+
|
5
|
+
- **New feature:** parse `img[srcset]` (microformats/microformats2-parsing#7) (cdda328)
|
6
|
+
- Improve usage of activesupport extensions (5ed120c)
|
7
|
+
|
8
|
+
## 3.0.0 / 2022-08-28
|
9
|
+
|
10
|
+
- Improved YARD documentation
|
11
|
+
- New `Item` instance methods (8105d6f):
|
12
|
+
- `MicroMicro::Item#children?`
|
13
|
+
- `MicroMicro::Item#id?`
|
14
|
+
- **Breaking change:** Remove property-centric methods from `MicroMicro::Item` (926dedb):
|
15
|
+
- `MicroMicro::Item#plain_text_properties`
|
16
|
+
- `MicroMicro::Item#url_properties`
|
17
|
+
- Add predicate methods to `MicroMicro::Collections::PropertiesCollection` (82e91c8):
|
18
|
+
- `MicroMicro::Collections::PropertiesCollection#plain_text_properties?`
|
19
|
+
- `MicroMicro::Collections::PropertiesCollection#url_properties?`
|
20
|
+
- Add collections search methods `#where` and `#find_by` (847cb77)
|
21
|
+
- **Breaking change:** Refactor `.node_set_from` class methods into private classes (b18a714)
|
22
|
+
|
3
23
|
## 2.0.1 / 2022-08-20
|
4
24
|
|
5
25
|
- Use ruby/debug instead of pry-byebug (2965b2e)
|
data/README.md
CHANGED
@@ -18,15 +18,9 @@
|
|
18
18
|
|
19
19
|
<small>¹ …with some exceptions until [this pull request](https://github.com/microformats/tests/pull/112) is merged.</small>
|
20
20
|
|
21
|
-
## Getting Started
|
22
|
-
|
23
|
-
Before installing and using MicroMicro, you'll want to have [Ruby](https://www.ruby-lang.org) 2.7 (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).
|
24
|
-
|
25
|
-
MicroMicro is developed using Ruby 2.7.6 and is additionally tested against Ruby 3.0 and 3.1 using [GitHub Actions](https://github.com/jgarber623/micromicro/actions).
|
26
|
-
|
27
21
|
## Installation
|
28
22
|
|
29
|
-
If you're using [Bundler](https://bundler.io) to manage gem dependencies, add MicroMicro to your project's Gemfile:
|
23
|
+
Before installing and using MicroMicro, you'll want to have [Ruby](https://www.ruby-lang.org) 2.7 (or newer) installed. If you're using [Bundler](https://bundler.io) to manage gem dependencies, add MicroMicro to your project's Gemfile:
|
30
24
|
|
31
25
|
```ruby
|
32
26
|
gem 'micromicro'
|
@@ -42,13 +36,7 @@ gem install micromicro
|
|
42
36
|
|
43
37
|
## Usage
|
44
38
|
|
45
|
-
|
46
|
-
|
47
|
-
MicroMicro's `parse` method accepts two arguments: a `String` of markup and a `String` representing the URL associated with that markup.
|
48
|
-
|
49
|
-
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.
|
50
|
-
|
51
|
-
An example using a simple `String` of HTML as input:
|
39
|
+
MicroMicro's `parse` method accepts two arguments: a `String` of markup and a `String` representing the URL associated with that markup. The resulting `MicroMicro::Document` may be converted to a `Hash` which may be further manipulated using conventional Ruby tooling.
|
52
40
|
|
53
41
|
```ruby
|
54
42
|
require 'micromicro'
|
@@ -60,88 +48,7 @@ doc.to_h
|
|
60
48
|
#=> { :items => [{ :type => ["h-card"], :properties => { :name => ["Jason Garber"] } }], :rels => {}, :"rel-urls" => {} }
|
61
49
|
```
|
62
50
|
|
63
|
-
|
64
|
-
|
65
|
-
Another example pulling the source HTML from [Tantek](https://tantek.com)'s website:
|
66
|
-
|
67
|
-
```ruby
|
68
|
-
require 'net/http'
|
69
|
-
require 'micromicro'
|
70
|
-
|
71
|
-
url = 'https://tantek.com'
|
72
|
-
rsp = Net::HTTP.get(URI.parse(url))
|
73
|
-
|
74
|
-
doc = MicroMicro.parse(rsp, url)
|
75
|
-
#=> #<MicroMicro::Document items: #<MicroMicro::Collections::ItemsCollection count: 1, members: […]>, relationships: #<MicroMicro::Collections::RelationshipsCollection count: 31, members: […]>>
|
76
|
-
|
77
|
-
doc.to_h
|
78
|
-
#=> { :items => [{ :type => ["h-card"], :properties => {…}, :children => […]}], :rels => {…}, :'rel-urls' => {…} }
|
79
|
-
```
|
80
|
-
|
81
|
-
### Advanced Usage
|
82
|
-
|
83
|
-
Building on the example above, a MicroMicro-parsed document is navigable and manipulable using a familiar `Enumerable`-esque interface.
|
84
|
-
|
85
|
-
#### Items
|
86
|
-
|
87
|
-
```ruby
|
88
|
-
doc.items.first
|
89
|
-
#=> #<MicroMicro::Item types: ["h-card"], properties: 42, children: 6>
|
90
|
-
|
91
|
-
# 🆕 in v1.0.0
|
92
|
-
doc.items.types
|
93
|
-
#=> ["h-card"]
|
94
|
-
|
95
|
-
doc.items.first.children
|
96
|
-
#=> #<MicroMicro::Collections::ItemsCollection count: 6, members: […]>
|
97
|
-
```
|
98
|
-
|
99
|
-
#### Properties
|
100
|
-
|
101
|
-
```ruby
|
102
|
-
doc.items.first.properties
|
103
|
-
#=> #<MicroMicro::Collections::PropertiesCollection count: 42, members: […]>
|
104
|
-
|
105
|
-
# 🆕 in v1.0.0
|
106
|
-
doc.items.first.plain_text_properties
|
107
|
-
#=> #<MicroMicro::Collections::PropertiesCollection count: 34, members: […]>
|
108
|
-
|
109
|
-
# 🆕 in v1.0.0
|
110
|
-
doc.items.first.url_properties
|
111
|
-
#=> #<MicroMicro::Collections::PropertiesCollection count: 11, members: […]>
|
112
|
-
|
113
|
-
# 🆕 in v1.0.0
|
114
|
-
doc.items.first.properties.names
|
115
|
-
#=> ["category", "name", "note", "org", "photo", "pronoun", "pronouns", "role", "uid", "url"]
|
116
|
-
|
117
|
-
# 🆕 in v1.0.0
|
118
|
-
doc.items.first.properties.values
|
119
|
-
#=> [{:value=>"https://tantek.com/photo.jpg", :alt=>""}, "https://tantek.com/", "Tantek Çelik", "Inventor, writer, teacher, runner, coder, more.", "Inventor", "writer", "teacher", "runner", "coder", …]
|
120
|
-
|
121
|
-
doc.items.first.properties[7]
|
122
|
-
#=> #<MicroMicro::Property name: "category", prefix: "p", value: "teacher">
|
123
|
-
|
124
|
-
doc.items.first.properties.take(5).map { |property| [property.name, property.value] }
|
125
|
-
#=> [["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."]]
|
126
|
-
```
|
127
|
-
|
128
|
-
#### Relationships
|
129
|
-
|
130
|
-
```ruby
|
131
|
-
doc.relationships.first
|
132
|
-
#=> #<MicroMicro::Relationship href: "https://tantek.com/", rels: ["canonical"]>
|
133
|
-
|
134
|
-
# 🆕 in v1.0.0
|
135
|
-
doc.relationships.rels
|
136
|
-
#=> ["alternate", "apple-touch-icon-precomposed", "author", "authorization_endpoint", "bookmark", "canonical", "hub", "icon", "me", "microsub", …]
|
137
|
-
|
138
|
-
# 🆕 in v1.0.0
|
139
|
-
doc.relationships.urls
|
140
|
-
#=> ["http://dribbble.com/tantek/", "http://last.fm/user/tantekc", "https://aperture.p3k.io/microsub/277", "https://en.wikipedia.org/wiki/User:Tantek", "https://github.com/tantek", "https://indieauth.com/auth", "https://indieauth.com/openid", "https://micro.blog/t", "https://pubsubhubbub.superfeedr.com/", "https://tantek.com/", …]
|
141
|
-
|
142
|
-
doc.relationships.find { |relationship| relationship.rels.include?('webmention') }
|
143
|
-
# => #<MicroMicro::Relationship href: "https://webmention.io/tantek.com/webmention", rels: ["webmention"]>
|
144
|
-
```
|
51
|
+
See [USAGE.md](https://github.com/jgarber623/micromicro/blob/main/USAGE.md) for detailed examples of MicroMicro's features. Additional structured documentation is available on [RubyDoc.info](https://rubydoc.info/gems/micromicro).
|
145
52
|
|
146
53
|
## Contributing
|
147
54
|
|
@@ -3,15 +3,96 @@
|
|
3
3
|
module MicroMicro
|
4
4
|
module Collections
|
5
5
|
class ItemsCollection < BaseCollection
|
6
|
+
class ItemsCollectionSearch
|
7
|
+
attr_reader :results
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@results = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def search(items, **args, &block)
|
14
|
+
items.each do |item|
|
15
|
+
results << item if item_matches_conditions?(item, **args, &block)
|
16
|
+
|
17
|
+
search(item.properties.filter_map { |property| property.item if property.item_node? }, **args, &block)
|
18
|
+
search(item.children, **args, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
results
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def item_matches_conditions?(item, **args)
|
27
|
+
return yield(item) if args.none?
|
28
|
+
|
29
|
+
args.all? { |key, value| (Array(item.public_send(key.to_sym)) & Array(value)).any? }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private_constant :ItemsCollectionSearch
|
34
|
+
|
35
|
+
# Return the first {MicroMicro::Item} from a search.
|
36
|
+
#
|
37
|
+
# @see #where
|
38
|
+
#
|
39
|
+
# @param (see #where)
|
40
|
+
# @yieldparam (see #where))
|
41
|
+
# @return [MicroMicro::Item, nil]
|
42
|
+
def find_by(**args, &block)
|
43
|
+
where(**args, &block).first
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return an Array of this collection's {MicroMicro::Item}s as Hashes.
|
47
|
+
#
|
48
|
+
# @see MicroMicro::Item#to_h
|
49
|
+
#
|
6
50
|
# @return [Array<Hash{Symbol => Array<String, Hash>}>]
|
7
51
|
def to_a
|
8
52
|
map(&:to_h)
|
9
53
|
end
|
10
54
|
|
55
|
+
# Retrieve an Array of this collection's unique {MicroMicro::Item} types.
|
56
|
+
#
|
57
|
+
# @see MicroMicro::Item#types
|
58
|
+
#
|
11
59
|
# @return [Array<String>]
|
12
60
|
def types
|
13
61
|
@types ||= flat_map(&:types).uniq.sort
|
14
62
|
end
|
63
|
+
|
64
|
+
# Recursively search this collection for {MicroMicro::Item}s matching the
|
65
|
+
# given conditions.
|
66
|
+
#
|
67
|
+
# If a Hash is supplied, the returned collection will include
|
68
|
+
# {MicroMicro::Item}s matching _all_ conditions. Keys must be Symbols
|
69
|
+
# matching an instance method on {MicroMicro::Item} and values may be
|
70
|
+
# either a String or an Array of Strings.
|
71
|
+
#
|
72
|
+
# @example Search using a Hash with a String value
|
73
|
+
# MicroMicro.parse(markup, url).items.where(types: 'h-card')
|
74
|
+
#
|
75
|
+
# @example Search using a Hash with an Array value
|
76
|
+
# MicroMicro.parse(markup, url).items.where(types: ['h-card', 'h-entry'])
|
77
|
+
#
|
78
|
+
# When passing a block, each {MicroMicro::Item} in this collection is
|
79
|
+
# yielded to the block and the returned collection will include
|
80
|
+
# {MicroMicro::Item}s that cause the block to return a value other than
|
81
|
+
# +false+ or +nil+.
|
82
|
+
#
|
83
|
+
# @example Search using a block
|
84
|
+
# MicroMicro.parse(markup, url).items.where do |item|
|
85
|
+
# item.properties.names.include?('email')
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
# @param args [Hash{Symbol => String, Array<String>}]
|
89
|
+
# @yieldparam item [MicroMicro::Item]
|
90
|
+
# @return [MicroMicro::Collections::ItemsCollection]
|
91
|
+
def where(**args, &block)
|
92
|
+
return self if args.none? && !block
|
93
|
+
|
94
|
+
self.class.new(ItemsCollectionSearch.new.search(self, **args, &block))
|
95
|
+
end
|
15
96
|
end
|
16
97
|
end
|
17
98
|
end
|
@@ -3,30 +3,129 @@
|
|
3
3
|
module MicroMicro
|
4
4
|
module Collections
|
5
5
|
class PropertiesCollection < BaseCollection
|
6
|
+
class PropertiesCollectionSearch
|
7
|
+
def search(properties, **args, &block)
|
8
|
+
properties.select { |property| property_matches_conditions?(property, **args, &block) }
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def property_matches_conditions?(property, **args)
|
14
|
+
return yield(property) if args.none?
|
15
|
+
|
16
|
+
args.all? { |key, value| (Array(property.public_send(key.to_sym)) & Array(value)).any? }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private_constant :PropertiesCollectionSearch
|
21
|
+
|
22
|
+
# Return the first {MicroMicro::Property} from a search.
|
23
|
+
#
|
24
|
+
# @see #where
|
25
|
+
#
|
26
|
+
# @param (see #where)
|
27
|
+
# @yieldparam (see #where))
|
28
|
+
# @return [MicroMicro::Property, nil]
|
29
|
+
def find_by(**args, &block)
|
30
|
+
where(**args, &block).first
|
31
|
+
end
|
32
|
+
|
33
|
+
# Retrieve an Array of this collection's unique {MicroMicro::Property}
|
34
|
+
# names.
|
35
|
+
#
|
36
|
+
# @see MicroMicro::Property#name
|
37
|
+
#
|
6
38
|
# @return [Array<String>]
|
7
39
|
def names
|
8
40
|
@names ||= map(&:name).uniq.sort
|
9
41
|
end
|
10
42
|
|
43
|
+
# A collection of plain text {MicroMicro::Property}s parsed from the node.
|
44
|
+
#
|
45
|
+
# @see MicroMicro::Property#plain_text_property?
|
46
|
+
#
|
11
47
|
# @return [MicroMicro::Collections::PropertiesCollection]
|
12
48
|
def plain_text_properties
|
13
|
-
self.class.new(select(&:plain_text_property?))
|
49
|
+
@plain_text_properties ||= self.class.new(select(&:plain_text_property?))
|
14
50
|
end
|
15
51
|
|
52
|
+
# Does this {MicroMicro::Collections::PropertiesCollection} include any
|
53
|
+
# plain text {MicroMicro::Property}s?
|
54
|
+
#
|
55
|
+
# @return [Boolean]
|
56
|
+
def plain_text_properties?
|
57
|
+
plain_text_properties.any?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Return a Hash of this collection's {MicroMicro::Property}s as Arrays.
|
61
|
+
#
|
62
|
+
# @see MicroMicro::Property#name
|
63
|
+
# @see MicroMicro::Property#value
|
64
|
+
#
|
16
65
|
# @return [Hash{Symbol => Array<String, Hash>}]
|
17
66
|
def to_h
|
18
|
-
group_by(&:name).
|
67
|
+
group_by(&:name).transform_keys(&:to_sym).deep_transform_values(&:value)
|
19
68
|
end
|
20
69
|
|
70
|
+
# A collection of url {MicroMicro::Property}s parsed from the node.
|
71
|
+
#
|
72
|
+
# @see MicroMicro::Property#url_property?
|
73
|
+
#
|
21
74
|
# @return [MicroMicro::Collections::PropertiesCollection]
|
22
75
|
def url_properties
|
23
|
-
self.class.new(select(&:url_property?))
|
76
|
+
@url_properties ||= self.class.new(select(&:url_property?))
|
77
|
+
end
|
78
|
+
|
79
|
+
# Does this {MicroMicro::Collections::PropertiesCollection} include any
|
80
|
+
# url {MicroMicro::Property}s?
|
81
|
+
#
|
82
|
+
# @return [Boolean]
|
83
|
+
def url_properties?
|
84
|
+
url_properties.any?
|
24
85
|
end
|
25
86
|
|
87
|
+
# Return an Array of this collection's unique {MicroMicro::Property}
|
88
|
+
# values.
|
89
|
+
#
|
90
|
+
# @see MicroMicro::Property#value
|
91
|
+
#
|
26
92
|
# @return [Array<String, Hash>]
|
27
93
|
def values
|
28
94
|
@values ||= map(&:value).uniq
|
29
95
|
end
|
96
|
+
|
97
|
+
# Search this collection for {MicroMicro::Property}s matching the given
|
98
|
+
# conditions.
|
99
|
+
#
|
100
|
+
# If a Hash is supplied, the returned collection will include
|
101
|
+
# {MicroMicro::Property}s matching _all_ conditions. Keys must be Symbols
|
102
|
+
# matching an instance method on {MicroMicro::Property} and values may be
|
103
|
+
# either a String or an Array of Strings.
|
104
|
+
#
|
105
|
+
# @example Search using a Hash with a String value
|
106
|
+
# MicroMicro.parse(markup, url).properties.where(name: 'url')
|
107
|
+
#
|
108
|
+
# @example Search using a Hash with an Array value
|
109
|
+
# MicroMicro.parse(markup, url).properties.where(name: ['name', 'url'])
|
110
|
+
#
|
111
|
+
# When passing a block, each {MicroMicro::Property} in this collection
|
112
|
+
# is yielded to the block and the returned collection will include
|
113
|
+
# {MicroMicro::Property}s that cause the block to return a value other
|
114
|
+
# than +false+ or +nil+.
|
115
|
+
#
|
116
|
+
# @example Search using a block
|
117
|
+
# MicroMicro.parse(markup, url).properties.where do |property|
|
118
|
+
# property.value.is_a?(Hash)
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# @param args [Hash{Symbol => String, Array<String>}]
|
122
|
+
# @yieldparam property [MicroMicro::Property]
|
123
|
+
# @return [MicroMicro::Collections::PropertiesCollection]
|
124
|
+
def where(**args, &block)
|
125
|
+
return self if args.none? && !block
|
126
|
+
|
127
|
+
self.class.new(PropertiesCollectionSearch.new.search(self, **args, &block))
|
128
|
+
end
|
30
129
|
end
|
31
130
|
end
|
32
131
|
end
|
@@ -3,31 +3,109 @@
|
|
3
3
|
module MicroMicro
|
4
4
|
module Collections
|
5
5
|
class RelationshipsCollection < BaseCollection
|
6
|
+
class RelationshipsCollectionSearch
|
7
|
+
def search(relationships, **args, &block)
|
8
|
+
relationships.select { |relationship| relationship_matches_conditions?(relationship, **args, &block) }
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def relationship_matches_conditions?(relationship, **args)
|
14
|
+
return yield(relationship) if args.none?
|
15
|
+
|
16
|
+
args.all? { |key, value| (Array(relationship.public_send(key.to_sym)) & Array(value)).any? }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private_constant :RelationshipsCollectionSearch
|
21
|
+
|
22
|
+
# Return the first {MicroMicro::Relationship} from a search.
|
23
|
+
#
|
24
|
+
# @see #where
|
25
|
+
#
|
26
|
+
# @param (see #where)
|
27
|
+
# @yieldparam (see #where))
|
28
|
+
# @return [MicroMicro::Relationship, nil]
|
29
|
+
def find_by(**args, &block)
|
30
|
+
where(**args, &block).first
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return a Hash of this collection's {MicroMicro::Relationship}s grouped
|
34
|
+
# by their +rel+ attribute value.
|
35
|
+
#
|
6
36
|
# @see https://microformats.org/wiki/microformats2-parsing#parse_a_hyperlink_element_for_rel_microformats
|
37
|
+
# microformats.org: microformats2 parsing specification § Parse a hyperlink element for rel microformats
|
7
38
|
#
|
8
39
|
# @return [Hash{Symbol => Array<String>}]
|
9
40
|
def group_by_rel
|
10
|
-
each_with_object(Hash.new { |hash, key| hash[key] =
|
11
|
-
member.rels.each { |rel| hash[rel] << member.href }
|
12
|
-
end.
|
41
|
+
each_with_object(Hash.new { |hash, key| hash[key] = Set.new }) do |member, hash|
|
42
|
+
member.rels.each { |rel| hash[rel.to_sym] << member.href }
|
43
|
+
end.transform_values(&:to_a)
|
13
44
|
end
|
14
45
|
|
46
|
+
# Return a Hash of this collection's {MicroMicro::Relationship}s grouped
|
47
|
+
# by their +href+ attribute value.
|
48
|
+
#
|
15
49
|
# @see https://microformats.org/wiki/microformats2-parsing#parse_a_hyperlink_element_for_rel_microformats
|
50
|
+
# microformats.org: microformats2 parsing specification § Parse a hyperlink element for rel microformats
|
16
51
|
#
|
17
52
|
# @return [Hash{Symbol => Hash{Symbol => Array, String}}]
|
18
53
|
def group_by_url
|
19
|
-
group_by(&:href).
|
54
|
+
group_by(&:href).to_h { |k, v| [k.to_sym, v.first.to_h.except(:href)] }
|
20
55
|
end
|
21
56
|
|
57
|
+
# Retrieve an Array of this collection's unique {MicroMicro::Relationship}
|
58
|
+
# +rel+ attrivute values.
|
59
|
+
#
|
60
|
+
# @see MicroMicro::Relationship#rels
|
61
|
+
#
|
22
62
|
# @return [Array<String>]
|
23
63
|
def rels
|
24
64
|
@rels ||= flat_map(&:rels).uniq.sort
|
25
65
|
end
|
26
66
|
|
67
|
+
# Retrieve an Array of this collection's unique {MicroMicro::Relationship}
|
68
|
+
# +href+ attribute values.
|
69
|
+
#
|
70
|
+
# @see MicroMicro::Relationship#urls
|
71
|
+
#
|
27
72
|
# @return [Array<String>]
|
28
73
|
def urls
|
29
74
|
@urls ||= map(&:href).uniq.sort
|
30
75
|
end
|
76
|
+
|
77
|
+
# Search this collection for {MicroMicro::Relationship}s matching the
|
78
|
+
# given conditions.
|
79
|
+
#
|
80
|
+
# If a Hash is supplied, the returned collection will include
|
81
|
+
# {MicroMicro::Relationship}s matching _all_ conditions. Keys must be
|
82
|
+
# Symbols matching an instance method on {MicroMicro::Relationship} and
|
83
|
+
# values may be either a String or an Array of Strings.
|
84
|
+
#
|
85
|
+
# @example Search using a Hash with a String value
|
86
|
+
# MicroMicro.parse(markup, url).relationships.where(rels: 'webmention')
|
87
|
+
#
|
88
|
+
# @example Search using a Hash with an Array value
|
89
|
+
# MicroMicro.parse(markup, url).relationships.where(rels: ['me', 'webmention'])
|
90
|
+
#
|
91
|
+
# When passing a block, each {MicroMicro::Relationship} in this collection
|
92
|
+
# is yielded to the block and the returned collection will include
|
93
|
+
# {MicroMicro::Relationship}s that cause the block to return a value other
|
94
|
+
# than +false+ or +nil+.
|
95
|
+
#
|
96
|
+
# @example Search using a block
|
97
|
+
# MicroMicro.parse(markup, url).relationships.where do |relationship|
|
98
|
+
# relationship.href.match?(%r{https://webmention.io/.+})
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# @param args [Hash{Symbol => String, Array<String>}]
|
102
|
+
# @yieldparam relationship [MicroMicro::Relationship]
|
103
|
+
# @return [MicroMicro::Collections::RelationshipsCollection]
|
104
|
+
def where(**args, &block)
|
105
|
+
return self if args.none? && !block
|
106
|
+
|
107
|
+
self.class.new(RelationshipsCollectionSearch.new.search(self, **args, &block))
|
108
|
+
end
|
31
109
|
end
|
32
110
|
end
|
33
111
|
end
|
data/lib/micro_micro/document.rb
CHANGED
@@ -4,10 +4,10 @@ module MicroMicro
|
|
4
4
|
class Document
|
5
5
|
# Parse a string of HTML for microformats2-encoded data.
|
6
6
|
#
|
7
|
+
# @example Parse a String of markup
|
7
8
|
# MicroMicro::Document.new('<a href="/" class="h-card" rel="me">Jason Garber</a>', 'https://sixtwothree.org')
|
8
9
|
#
|
9
|
-
#
|
10
|
-
#
|
10
|
+
# @example Parse a String of markup from a URL
|
11
11
|
# url = 'https://tantek.com'
|
12
12
|
# markup = Net::HTTP.get(URI.parse(url))
|
13
13
|
#
|
@@ -19,8 +19,9 @@ module MicroMicro
|
|
19
19
|
@document = Nokogiri::HTML(markup, base_url).resolve_relative_urls!
|
20
20
|
end
|
21
21
|
|
22
|
-
# :nocov:
|
23
22
|
# @return [String]
|
23
|
+
#
|
24
|
+
# :nocov:
|
24
25
|
def inspect
|
25
26
|
"#<#{self.class}:#{format('%#0x', object_id)} " \
|
26
27
|
"items: #{items.inspect}, " \
|
@@ -28,23 +29,28 @@ module MicroMicro
|
|
28
29
|
end
|
29
30
|
# :nocov:
|
30
31
|
|
31
|
-
# A collection of
|
32
|
+
# A collection of {MicroMicro::Item}s parsed from the provided markup.
|
32
33
|
#
|
33
34
|
# @return [MicroMicro::Collections::ItemsCollection]
|
34
35
|
def items
|
35
36
|
@items ||= Collections::ItemsCollection.new(Item.from_context(document.element_children))
|
36
37
|
end
|
37
38
|
|
38
|
-
# A collection of
|
39
|
+
# A collection of {MicroMicro::Relationship}s parsed from the provided markup.
|
39
40
|
#
|
40
41
|
# @return [MicroMicro::Collections::RelationshipsCollection]
|
41
42
|
def relationships
|
42
|
-
@relationships ||= Collections::RelationshipsCollection.new(Relationship.
|
43
|
+
@relationships ||= Collections::RelationshipsCollection.new(Relationship.from_context(document))
|
43
44
|
end
|
44
45
|
|
45
46
|
# Return the parsed document as a Hash.
|
46
47
|
#
|
47
48
|
# @see https://microformats.org/wiki/microformats2-parsing#parse_a_document_for_microformats
|
49
|
+
# microformats.org: Parse a document for microformats
|
50
|
+
#
|
51
|
+
# @see MicroMicro::Collections::ItemsCollection#to_a
|
52
|
+
# @see MicroMicro::Collections::RelationshipsCollection#group_by_rel
|
53
|
+
# @see MicroMicro::Collections::RelationshipsCollection#group_by_url
|
48
54
|
#
|
49
55
|
# @return [Hash{Symbol => Array, Hash}]
|
50
56
|
def to_h
|
data/lib/micro_micro/helpers.rb
CHANGED
@@ -4,6 +4,8 @@ module MicroMicro
|
|
4
4
|
module Helpers
|
5
5
|
IGNORED_NODE_NAMES = %w[script style template].freeze
|
6
6
|
|
7
|
+
private_constant :IGNORED_NODE_NAMES
|
8
|
+
|
7
9
|
# @param node [Nokogiri::XML::Element]
|
8
10
|
# @param attributes_map [Hash{String => Array}]
|
9
11
|
# @return [String, nil]
|
@@ -56,7 +58,9 @@ module MicroMicro
|
|
56
58
|
end
|
57
59
|
|
58
60
|
# @see https://microformats.org/wiki/microformats2-parsing#parse_an_element_for_properties
|
61
|
+
# microformats.org: microformats2 parsing specification § Parse an element for properties
|
59
62
|
# @see https://microformats.org/wiki/microformats2-parsing#parsing_for_implied_properties
|
63
|
+
# microformats.org: microformats2 parsing specification § Parsing for implied properties
|
60
64
|
#
|
61
65
|
# @param context [Nokogiri::HTML::Document, Nokogiri::XML::NodeSet, Nokogiri::XML::Element]
|
62
66
|
# @yield [context]
|
@@ -70,6 +74,7 @@ module MicroMicro
|
|
70
74
|
end
|
71
75
|
|
72
76
|
# @see https://microformats.org/wiki/value-class-pattern#Basic_Parsing
|
77
|
+
# microformats.org: Value Class Pattern § Basic Parsing
|
73
78
|
#
|
74
79
|
# @param node [Nokogiri::XML::Element]
|
75
80
|
# @return [Boolean]
|
@@ -78,6 +83,7 @@ module MicroMicro
|
|
78
83
|
end
|
79
84
|
|
80
85
|
# @see https://microformats.org/wiki/value-class-pattern#Parsing_value_from_a_title_attribute
|
86
|
+
# microformats.org: Value Class Pattern § Parsing value from a title attribute
|
81
87
|
#
|
82
88
|
# @param node [Nokogiri::XML::Element]
|
83
89
|
# @return [Boolean]
|
@@ -8,11 +8,24 @@ module MicroMicro
|
|
8
8
|
'url' => Parsers::ImpliedUrlPropertyParser
|
9
9
|
}.freeze
|
10
10
|
|
11
|
+
private_constant :IMPLIED_PROPERTY_PARSERS_MAP
|
12
|
+
|
13
|
+
# Always return +true+ when asked if this {MicroMicro::ImpliedProperty} is
|
14
|
+
# an implied property.
|
15
|
+
#
|
16
|
+
# @see https://microformats.org/wiki/microformats2-parsing#parsing_for_implied_properties
|
17
|
+
# microformats.org: microformats2 parsing specification § Parsing for implied properties
|
18
|
+
#
|
19
|
+
# @see MicroMicro::Property#implied?
|
20
|
+
#
|
11
21
|
# @return [Boolean]
|
12
22
|
def implied?
|
13
23
|
true
|
14
24
|
end
|
15
25
|
|
26
|
+
# Always return +false+ when asked if this {MicroMicro::ImpliedProperty} is
|
27
|
+
# a {MicroMicro::Item} node.
|
28
|
+
#
|
16
29
|
# @return [Boolean]
|
17
30
|
def item_node?
|
18
31
|
false
|