micromicro 2.0.1 → 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 +4 -4
- data/CHANGELOG.md +15 -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 +101 -2
- data/lib/micro_micro/collections/relationships_collection.rb +78 -0
- 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 +1 -0
- 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/implied_name_property_parser.rb +1 -0
- data/lib/micro_micro/parsers/implied_photo_property_parser.rb +2 -0
- 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 +2 -0
- data/lib/micro_micro/parsers/value_class_pattern_parser.rb +3 -1
- data/lib/micro_micro/property.rb +76 -18
- data/lib/micro_micro/relationship.rb +25 -2
- data/lib/micro_micro/version.rb +1 -1
- data/lib/micromicro.rb +5 -4
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6734c4d9cbd071d3288a5f8c72de07095fc6db2915224f97684a0577362bcdb0
|
4
|
+
data.tar.gz: 9225043ef91161f2a92fa338bc874771dc55d9e81ffe969fe7b005c9dbc28403
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 58824c1699a2aff5e22d906cbb449f95f55a665d7374ba62e26ddfa3fd6986ae952e22965446e4bbe973d2e17a1f05cba32e74f7d30e1e327fbc262ebce7b1f1
|
7
|
+
data.tar.gz: aa9db8c15e10ecc08fa65db0cf6964914681ef72b552a300c4b95899b4a837835c1c69baff9b8e48586df34eefb7bb4156e55a5c81582deda4a9948554224f48
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## v3.0.0 / 2022-08-28
|
4
|
+
|
5
|
+
- Improved YARD documentation
|
6
|
+
- New `Item` instance methods (8105d6f):
|
7
|
+
- `MicroMicro::Item#children?`
|
8
|
+
- `MicroMicro::Item#id?`
|
9
|
+
- **Breaking change:** Remove property-centric methods from `MicroMicro::Item` (926dedb):
|
10
|
+
- `MicroMicro::Item#plain_text_properties`
|
11
|
+
- `MicroMicro::Item#url_properties`
|
12
|
+
- Add predicate methods to `MicroMicro::Collections::PropertiesCollection` (82e91c8):
|
13
|
+
- `MicroMicro::Collections::PropertiesCollection#plain_text_properties?`
|
14
|
+
- `MicroMicro::Collections::PropertiesCollection#url_properties?`
|
15
|
+
- Add collections search methods `#where` and `#find_by` (847cb77)
|
16
|
+
- **Breaking change:** Refactor `.node_set_from` class methods into private classes (b18a714)
|
17
|
+
|
3
18
|
## 2.0.1 / 2022-08-20
|
4
19
|
|
5
20
|
- 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.
|
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
67
|
group_by(&:name).symbolize_keys.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,7 +3,38 @@
|
|
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
|
@@ -12,22 +43,69 @@ module MicroMicro
|
|
12
43
|
end.symbolize_keys.transform_values(&:uniq)
|
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
54
|
group_by(&:href).symbolize_keys.transform_values { |relationships| relationships.first.to_h.slice!(: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
|
data/lib/micro_micro/item.rb
CHANGED
@@ -4,38 +4,47 @@ module MicroMicro
|
|
4
4
|
class Item
|
5
5
|
include Collectible
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
# @param context [Nokogiri::HTML::Document, Nokogiri::XML::NodeSet, Nokogiri::XML::Element]
|
10
|
-
# @return [Array<MicroMicro::Item>]
|
11
|
-
def self.from_context(context)
|
12
|
-
node_set_from(context).map { |node| new(node) }
|
13
|
-
end
|
7
|
+
class ItemNodeSearch
|
8
|
+
attr_reader :node_set
|
14
9
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
10
|
+
def initialize(document)
|
11
|
+
@node_set = Nokogiri::XML::NodeSet.new(document, [])
|
12
|
+
end
|
13
|
+
|
14
|
+
# rubocop:disable Metrics
|
15
|
+
def search(context)
|
16
|
+
context.each { |node| search(node) } if context.is_a?(Nokogiri::XML::NodeSet)
|
17
|
+
|
18
|
+
if context.is_a?(Nokogiri::XML::Element) && !Helpers.ignore_node?(context)
|
19
|
+
if Helpers.item_node?(context)
|
20
|
+
node_set << context unless Helpers.item_nodes?(context.ancestors) && Helpers.property_node?(context)
|
21
|
+
else
|
22
|
+
search(context.element_children)
|
23
|
+
end
|
29
24
|
end
|
25
|
+
|
26
|
+
node_set
|
30
27
|
end
|
28
|
+
# rubocop:enable Metrics
|
29
|
+
end
|
30
|
+
|
31
|
+
private_constant :ItemNodeSearch
|
31
32
|
|
32
|
-
|
33
|
+
# Extract {MicroMicro::Item}s from a context.
|
34
|
+
#
|
35
|
+
# @param context [Nokogiri::HTML::Document, Nokogiri::XML::NodeSet, Nokogiri::XML::Element]
|
36
|
+
# @return [Array<MicroMicro::Item>]
|
37
|
+
def self.from_context(context)
|
38
|
+
ItemNodeSearch
|
39
|
+
.new(context.document)
|
40
|
+
.search(context)
|
41
|
+
.map { |node| new(node) }
|
33
42
|
end
|
34
|
-
# rubocop:enable Metrics
|
35
43
|
|
36
44
|
# Parse a node for microformats2-encoded data.
|
37
45
|
#
|
38
46
|
# @param node [Nokogiri::XML::Element]
|
47
|
+
# @return [MicroMicro::Item]
|
39
48
|
def initialize(node)
|
40
49
|
@node = node
|
41
50
|
|
@@ -44,24 +53,40 @@ module MicroMicro
|
|
44
53
|
properties << implied_url if implied_url?
|
45
54
|
end
|
46
55
|
|
47
|
-
# A collection of child
|
56
|
+
# A collection of child {MicroMicro::Item}s parsed from the node.
|
48
57
|
#
|
49
58
|
# @see https://microformats.org/wiki/microformats2-parsing#parse_an_element_for_class_microformats
|
59
|
+
# microformats.org: microformats2 parsing specification § Parse an element for class microformats
|
50
60
|
#
|
51
61
|
# @return [MicroMicro::Collections::ItemsCollection]
|
52
62
|
def children
|
53
63
|
@children ||= Collections::ItemsCollection.new(self.class.from_context(node.element_children))
|
54
64
|
end
|
55
65
|
|
56
|
-
#
|
66
|
+
# Does this {MicroMicro::Item} contain any child {MicroMicro::Item}s?
|
67
|
+
#
|
68
|
+
# @return [Boolean]
|
69
|
+
def children?
|
70
|
+
children.any?
|
71
|
+
end
|
72
|
+
|
73
|
+
# The value of the node's +id+ attribute, if present.
|
57
74
|
#
|
58
75
|
# @return [String, nil]
|
59
76
|
def id
|
60
77
|
@id ||= node['id']&.strip
|
61
78
|
end
|
62
79
|
|
63
|
-
#
|
80
|
+
# Does this {MicroMicro::Item} have an +id+ attribute value?
|
81
|
+
#
|
82
|
+
# @return [Boolean]
|
83
|
+
def id?
|
84
|
+
id.present?
|
85
|
+
end
|
86
|
+
|
64
87
|
# @return [String]
|
88
|
+
#
|
89
|
+
# :nocov:
|
65
90
|
def inspect
|
66
91
|
"#<#{self.class}:#{format('%#0x', object_id)} " \
|
67
92
|
"types: #{types.inspect}, " \
|
@@ -70,23 +95,23 @@ module MicroMicro
|
|
70
95
|
end
|
71
96
|
# :nocov:
|
72
97
|
|
73
|
-
# A collection of
|
74
|
-
#
|
75
|
-
# @return [MicroMicro::Collections::PropertiesCollection]
|
76
|
-
def plain_text_properties
|
77
|
-
@plain_text_properties ||= properties.plain_text_properties
|
78
|
-
end
|
79
|
-
|
80
|
-
# A collection of properties parsed from the node.
|
98
|
+
# A collection of {MicroMicro::Property}s parsed from the node.
|
81
99
|
#
|
82
100
|
# @return [MicroMicro::Collections::PropertiesCollection]
|
83
101
|
def properties
|
84
102
|
@properties ||= Collections::PropertiesCollection.new(Property.from_context(node.element_children))
|
85
103
|
end
|
86
104
|
|
87
|
-
# Return the parsed
|
105
|
+
# Return the parsed {MicroMicro::Item} as a Hash.
|
88
106
|
#
|
89
107
|
# @see https://microformats.org/wiki/microformats2-parsing#parse_an_element_for_class_microformats
|
108
|
+
# microformats.org: microformats2 parsing specification § Parse an element for class microformats
|
109
|
+
#
|
110
|
+
# @see MicroMicro::Item#children
|
111
|
+
# @see MicroMicro::Item#id
|
112
|
+
# @see MicroMicro::Item#properties
|
113
|
+
# @see MicroMicro::Item#types
|
114
|
+
# @see MicroMicro::Collections::PropertiesCollection#to_h
|
90
115
|
#
|
91
116
|
# @return [Hash]
|
92
117
|
def to_h
|
@@ -95,28 +120,22 @@ module MicroMicro
|
|
95
120
|
properties: properties.to_h
|
96
121
|
}
|
97
122
|
|
98
|
-
hash[:id] = id if id
|
99
|
-
hash[:children] = children.to_a if children
|
123
|
+
hash[:id] = id if id?
|
124
|
+
hash[:children] = children.to_a if children?
|
100
125
|
|
101
126
|
hash
|
102
127
|
end
|
103
128
|
|
104
|
-
# An
|
129
|
+
# An Array of root class names parsed from the node's +class+ attribute.
|
105
130
|
#
|
106
131
|
# @return [Array<String>]
|
107
132
|
def types
|
108
133
|
@types ||= Helpers.root_class_names_from(node)
|
109
134
|
end
|
110
135
|
|
111
|
-
# A collection of url properties parsed from the node.
|
112
|
-
#
|
113
|
-
# @return [MicroMicro::Collections::PropertiesCollection]
|
114
|
-
def url_properties
|
115
|
-
@url_properties ||= properties.url_properties
|
116
|
-
end
|
117
|
-
|
118
136
|
private
|
119
137
|
|
138
|
+
# @return [Nokogiri::XML::Element]
|
120
139
|
attr_reader :node
|
121
140
|
|
122
141
|
# @return [MicroMicro::ImpliedProperty]
|
@@ -173,7 +192,7 @@ module MicroMicro
|
|
173
192
|
|
174
193
|
# @return [Boolean]
|
175
194
|
def nested_items?
|
176
|
-
@nested_items ||= properties.find(&:item_node?) || children
|
195
|
+
@nested_items ||= properties.find(&:item_node?) || children?
|
177
196
|
end
|
178
197
|
end
|
179
198
|
end
|
@@ -10,7 +10,9 @@ module MicroMicro
|
|
10
10
|
end
|
11
11
|
|
12
12
|
# @see https://microformats.org/wiki/microformats2-parsing#parsing_a_p-_property
|
13
|
+
# microformats.org: microformats2 parsing specification § Parsing a +p-+ property
|
13
14
|
# @see https://microformats.org/wiki/microformats2-parsing#parsing_an_e-_property
|
15
|
+
# microformats.org: microformats2 parsing specification § Parsing an +e-+ property
|
14
16
|
#
|
15
17
|
# @return [String]
|
16
18
|
def value
|
@@ -30,6 +30,7 @@ module MicroMicro
|
|
30
30
|
# Value Class Pattern date and time parsing specification.
|
31
31
|
#
|
32
32
|
# @see https://microformats.org/wiki/value-class-pattern#Date_and_time_parsing
|
33
|
+
# microformats.org: Value Class Pattern § Date and time parsing
|
33
34
|
#
|
34
35
|
# @param string [String, #to_s]
|
35
36
|
def initialize(string)
|
@@ -10,6 +10,7 @@ module MicroMicro
|
|
10
10
|
}.freeze
|
11
11
|
|
12
12
|
# @see https://microformats.org/wiki/microformats2-parsing#parsing_a_dt-_property
|
13
|
+
# microformats.org: microformats2 parsing specification § Parsing a +dt-+ property
|
13
14
|
#
|
14
15
|
# @return [String]
|
15
16
|
def value
|
@@ -19,6 +20,7 @@ module MicroMicro
|
|
19
20
|
private
|
20
21
|
|
21
22
|
# @see https://microformats.org/wiki/value-class-pattern#microformats2_parsers_implied_date
|
23
|
+
# microformats.org: Value Class Pattern § microformats2 parsers implied date
|
22
24
|
#
|
23
25
|
# @return [MicroMicro::Parsers::DateTimeParser, nil]
|
24
26
|
def adopted_date_time_parser
|
@@ -39,6 +41,7 @@ module MicroMicro
|
|
39
41
|
end
|
40
42
|
|
41
43
|
# @see https://microformats.org/wiki/value-class-pattern#microformats2_parsers_implied_date
|
44
|
+
# microformats.org: Value Class Pattern § microformats2 parsers implied date
|
42
45
|
#
|
43
46
|
# @return [Boolean]
|
44
47
|
def imply_date?
|
@@ -4,6 +4,7 @@ module MicroMicro
|
|
4
4
|
module Parsers
|
5
5
|
class EmbeddedMarkupPropertyParser < BasePropertyParser
|
6
6
|
# @see https://microformats.org/wiki/microformats2-parsing#parsing_an_e-_property
|
7
|
+
# microformats.org: microformats2 parsing specification § Parsing an +e-+ property
|
7
8
|
#
|
8
9
|
# @return [Hash{Symbol => String}]
|
9
10
|
def value
|
@@ -11,7 +11,9 @@ module MicroMicro
|
|
11
11
|
}.freeze
|
12
12
|
|
13
13
|
# @see https://microformats.org/wiki/microformats2-parsing#parsing_for_implied_properties
|
14
|
+
# microformats.org: microformats2 parsing specification § Parsing for implied properties
|
14
15
|
# @see https://microformats.org/wiki/microformats2-parsing#parse_an_img_element_for_src_and_alt
|
16
|
+
# microformats.org: microformats2 parsing specification § Parse an img element for src and alt
|
15
17
|
#
|
16
18
|
# @return [String, Hash{Symbol => String}, nil]
|
17
19
|
def value
|
@@ -11,6 +11,7 @@ module MicroMicro
|
|
11
11
|
}.freeze
|
12
12
|
|
13
13
|
# @see https://microformats.org/wiki/microformats2-parsing#parsing_for_implied_properties
|
14
|
+
# microformats.org: microformats2 parsing specification § Parsing for implied properties
|
14
15
|
#
|
15
16
|
# @return [String, nil]
|
16
17
|
def value
|
@@ -16,7 +16,9 @@ module MicroMicro
|
|
16
16
|
}.freeze
|
17
17
|
|
18
18
|
# @see https://microformats.org/wiki/microformats2-parsing#parsing_a_u-_property
|
19
|
+
# microformats.org: microformats2 parsing specification § Parsing a +u-+ property
|
19
20
|
# @see https://microformats.org/wiki/microformats2-parsing#parse_an_img_element_for_src_and_alt
|
21
|
+
# microformats.org: microformats2 parsing specification § Parse an img element for src and alt
|
20
22
|
#
|
21
23
|
# @return [String, Hash{Symbol => String}]
|
22
24
|
def value
|
@@ -4,7 +4,9 @@ module MicroMicro
|
|
4
4
|
module Parsers
|
5
5
|
class ValueClassPatternParser
|
6
6
|
# @see https://microformats.org/wiki/value-class-pattern#Basic_Parsing
|
7
|
+
# microformats.org: Value Class Pattern § Basic Parsing
|
7
8
|
# @see https://microformats.org/wiki/value-class-pattern#Date_and_time_values
|
9
|
+
# microformats.org: Value Class Pattern § Date and time values
|
8
10
|
HTML_ATTRIBUTES_MAP = {
|
9
11
|
'alt' => %w[area img],
|
10
12
|
'value' => %w[data],
|
@@ -37,7 +39,7 @@ module MicroMicro
|
|
37
39
|
Helpers.attribute_value_from(node, HTML_ATTRIBUTES_MAP) || node.text
|
38
40
|
end
|
39
41
|
|
40
|
-
# @param
|
42
|
+
# @param node [Nokogiri::XML::Element]
|
41
43
|
# @param separator [String]
|
42
44
|
def initialize(node, separator = '')
|
43
45
|
@node = node
|
data/lib/micro_micro/property.rb
CHANGED
@@ -11,55 +11,95 @@ module MicroMicro
|
|
11
11
|
'u' => Parsers::UrlPropertyParser
|
12
12
|
}.freeze
|
13
13
|
|
14
|
-
|
14
|
+
private_constant :PROPERTY_PARSERS_MAP
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
class PropertyNodeSearch
|
17
|
+
attr_reader :node_set
|
18
|
+
|
19
|
+
def initialize(document)
|
20
|
+
@node_set = Nokogiri::XML::NodeSet.new(document, [])
|
21
21
|
end
|
22
|
-
end
|
23
22
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
def search(context)
|
24
|
+
context.each { |node| search(node) } if context.is_a?(Nokogiri::XML::NodeSet)
|
25
|
+
|
26
|
+
if context.is_a?(Nokogiri::XML::Element) && !Helpers.ignore_node?(context)
|
27
|
+
node_set << context if Helpers.property_node?(context)
|
29
28
|
|
30
|
-
|
31
|
-
|
29
|
+
search(context.element_children) unless Helpers.item_node?(context)
|
30
|
+
end
|
32
31
|
|
33
|
-
|
32
|
+
node_set
|
34
33
|
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private_constant :PropertyNodeSearch
|
35
37
|
|
36
|
-
|
38
|
+
# This {MicroMicro::Property}'s +name+ value.
|
39
|
+
#
|
40
|
+
# @return [String]
|
41
|
+
attr_reader :name
|
42
|
+
|
43
|
+
# This {MicroMicro::Property}'s node.
|
44
|
+
#
|
45
|
+
# @return [Nokogiri::XML::Element]
|
46
|
+
attr_reader :node
|
47
|
+
|
48
|
+
# This {MicroMicro::Property}'s +prefix+ value.
|
49
|
+
#
|
50
|
+
# @return [String] One of +dt+, +e+, +p+, or +u+.
|
51
|
+
attr_reader :prefix
|
52
|
+
|
53
|
+
# Extract {MicroMicro::Property}s from a context.
|
54
|
+
#
|
55
|
+
# @param context [Nokogiri::XML::NodeSet, Nokogiri::XML::Element]
|
56
|
+
# @return [Array<MicroMicro::Property>]
|
57
|
+
def self.from_context(context)
|
58
|
+
PropertyNodeSearch
|
59
|
+
.new(context.document)
|
60
|
+
.search(context)
|
61
|
+
.flat_map do |node|
|
62
|
+
Helpers.property_class_names_from(node).map { |token| new(node, token) }
|
63
|
+
end
|
37
64
|
end
|
38
65
|
|
66
|
+
# Parse a node for property data.
|
67
|
+
#
|
39
68
|
# @param node [Nokogiri::XML::Element]
|
40
|
-
# @param token [String]
|
69
|
+
# @param token [String] A hyphen-separated token representing a microformats2
|
70
|
+
# property value (e.g. +p-name+, +u-url+).
|
41
71
|
def initialize(node, token)
|
42
72
|
@node = node
|
43
73
|
@prefix, @name = token.split(/-/, 2)
|
44
74
|
end
|
45
75
|
|
76
|
+
# Is this {MicroMicro::Property} a datetime property?
|
77
|
+
#
|
46
78
|
# @return [Boolean]
|
47
79
|
def date_time_property?
|
48
80
|
prefix == 'dt'
|
49
81
|
end
|
50
82
|
|
83
|
+
# Is this {MicroMicro::Property} an embedded markup property?
|
84
|
+
#
|
51
85
|
# @return [Boolean]
|
52
86
|
def embedded_markup_property?
|
53
87
|
prefix == 'e'
|
54
88
|
end
|
55
89
|
|
90
|
+
# Always return +false+ when asked if this {MicroMicro::Property} is an
|
91
|
+
# implied property.
|
92
|
+
#
|
93
|
+
# @see MicroMicro::ImpliedProperty#implied?
|
94
|
+
#
|
56
95
|
# @return [Boolean]
|
57
96
|
def implied?
|
58
97
|
false
|
59
98
|
end
|
60
99
|
|
61
|
-
# :nocov:
|
62
100
|
# @return [String]
|
101
|
+
#
|
102
|
+
# :nocov:
|
63
103
|
def inspect
|
64
104
|
"#<#{self.class}:#{format('%#0x', object_id)} " \
|
65
105
|
"name: #{name.inspect}, " \
|
@@ -68,27 +108,42 @@ module MicroMicro
|
|
68
108
|
end
|
69
109
|
# :nocov:
|
70
110
|
|
111
|
+
# Parse this {MicroMicro::Property}'s node as a {MicroMicro::Item}, if
|
112
|
+
# applicable.
|
113
|
+
#
|
71
114
|
# @return [MicroMicro::Item, nil]
|
72
115
|
def item
|
73
116
|
@item ||= Item.new(node) if item_node?
|
74
117
|
end
|
75
118
|
|
119
|
+
# Should this {MicroMicro::Property}'s node be parsed as a
|
120
|
+
# {MicroMicro::Item}?
|
121
|
+
#
|
122
|
+
# @see MicroMicro::Helpers.item_node?
|
123
|
+
#
|
76
124
|
# @return [Boolean]
|
77
125
|
def item_node?
|
78
126
|
@item_node ||= Helpers.item_node?(node)
|
79
127
|
end
|
80
128
|
|
129
|
+
# Is this {MicroMicro::Property} a plain text property?
|
130
|
+
#
|
81
131
|
# @return [Boolean]
|
82
132
|
def plain_text_property?
|
83
133
|
prefix == 'p'
|
84
134
|
end
|
85
135
|
|
136
|
+
# Is this {MicroMicro::Property} a url property?
|
137
|
+
#
|
86
138
|
# @return [Boolean]
|
87
139
|
def url_property?
|
88
140
|
prefix == 'u'
|
89
141
|
end
|
90
142
|
|
143
|
+
# Return this {MicroMicro::Property}'s parsed value.
|
144
|
+
#
|
91
145
|
# @return [String, Hash]
|
146
|
+
#
|
92
147
|
# rubocop:disable Metrics
|
93
148
|
def value
|
94
149
|
@value ||=
|
@@ -107,6 +162,9 @@ module MicroMicro
|
|
107
162
|
end
|
108
163
|
# rubocop:enable Metrics
|
109
164
|
|
165
|
+
# Returns +true+ if this {MicroMicro::Property}'s +value+ is anything other
|
166
|
+
# than blank or +nil+.
|
167
|
+
#
|
110
168
|
# @return [Boolean]
|
111
169
|
def value?
|
112
170
|
value.present?
|
@@ -4,31 +4,41 @@ module MicroMicro
|
|
4
4
|
class Relationship
|
5
5
|
include Collectible
|
6
6
|
|
7
|
+
# Extract {MicroMicro::Relationship}s from a context.
|
8
|
+
#
|
7
9
|
# @param context [Nokogiri::HTML::Document, Nokogiri::XML::Element]
|
8
10
|
# @return [Array<MicroMicro::Relationship>]
|
9
|
-
def self.
|
11
|
+
def self.from_context(context)
|
10
12
|
context.css('[href][rel]:not([rel=""])')
|
11
13
|
.reject { |node| Helpers.ignore_nodes?(node.ancestors) }
|
12
14
|
.map { |node| new(node) }
|
13
15
|
end
|
14
16
|
|
17
|
+
# Parse a node for relationship data.
|
18
|
+
#
|
15
19
|
# @param node [Nokogiri::XML::Element]
|
20
|
+
# @return [MicroMicro::Relationship]
|
16
21
|
def initialize(node)
|
17
22
|
@node = node
|
18
23
|
end
|
19
24
|
|
25
|
+
# The value of the node's +href+ attribute.
|
26
|
+
#
|
20
27
|
# @return [String]
|
21
28
|
def href
|
22
29
|
@href ||= node['href']
|
23
30
|
end
|
24
31
|
|
32
|
+
# The value of the node's +hreflang+ attribute, if present.
|
33
|
+
#
|
25
34
|
# @return [String, nil]
|
26
35
|
def hreflang
|
27
36
|
@hreflang ||= node['hreflang']&.strip
|
28
37
|
end
|
29
38
|
|
30
|
-
# :nocov:
|
31
39
|
# @return [String]
|
40
|
+
#
|
41
|
+
# :nocov:
|
32
42
|
def inspect
|
33
43
|
"#<#{self.class}:#{format('%#0x', object_id)} " \
|
34
44
|
"href: #{href.inspect}, " \
|
@@ -36,11 +46,15 @@ module MicroMicro
|
|
36
46
|
end
|
37
47
|
# :nocov:
|
38
48
|
|
49
|
+
# The value of the node's +media+ attribute, if present.
|
50
|
+
#
|
39
51
|
# @return [String, nil]
|
40
52
|
def media
|
41
53
|
@media ||= node['media']&.strip
|
42
54
|
end
|
43
55
|
|
56
|
+
# Return the parsed {MicroMicro::Relationship} as a Hash.
|
57
|
+
#
|
44
58
|
# @return [Hash{Symbol => String}]
|
45
59
|
def to_h
|
46
60
|
{
|
@@ -54,21 +68,29 @@ module MicroMicro
|
|
54
68
|
}.select { |_, value| value.present? }
|
55
69
|
end
|
56
70
|
|
71
|
+
# An Array of unique values from node's +rel+ attribute.
|
72
|
+
#
|
57
73
|
# @return [Array<String>]
|
58
74
|
def rels
|
59
75
|
@rels ||= node['rel'].split.uniq.sort
|
60
76
|
end
|
61
77
|
|
78
|
+
# The node's text content.
|
79
|
+
#
|
62
80
|
# @return [String]
|
63
81
|
def text
|
64
82
|
@text ||= node.text
|
65
83
|
end
|
66
84
|
|
85
|
+
# The value of the node's +title+ attribute, if present.
|
86
|
+
#
|
67
87
|
# @return [String, nil]
|
68
88
|
def title
|
69
89
|
@title ||= node['title']&.strip
|
70
90
|
end
|
71
91
|
|
92
|
+
# The value of the node's +type+ attribute, if present.
|
93
|
+
#
|
72
94
|
# @return [String, nil]
|
73
95
|
def type
|
74
96
|
@type ||= node['type']&.strip
|
@@ -76,6 +98,7 @@ module MicroMicro
|
|
76
98
|
|
77
99
|
private
|
78
100
|
|
101
|
+
# @return [Nokogiri::XML::Element]
|
79
102
|
attr_reader :node
|
80
103
|
end
|
81
104
|
end
|
data/lib/micro_micro/version.rb
CHANGED
data/lib/micromicro.rb
CHANGED
@@ -41,13 +41,14 @@ require_relative 'micro_micro/collections/relationships_collection'
|
|
41
41
|
|
42
42
|
module MicroMicro
|
43
43
|
# Parse a string of HTML for microformats2-encoded data.
|
44
|
-
# Convenience method for MicroMicro::Document.new.
|
45
44
|
#
|
45
|
+
# Convenience method for {MicroMicro::Document#initialize}.
|
46
|
+
#
|
47
|
+
# @example
|
46
48
|
# MicroMicro.parse('<a href="/" class="h-card" rel="me">Jason Garber</a>', 'https://sixtwothree.org')
|
47
49
|
#
|
48
|
-
# @param
|
49
|
-
# @
|
50
|
-
# @return [MicroMicro::Document]
|
50
|
+
# @param (see MicroMicro::Document#initialize)
|
51
|
+
# @return (see MicroMicro::Document#initialize)
|
51
52
|
def self.parse(markup, base_url)
|
52
53
|
Document.new(markup, base_url)
|
53
54
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: micromicro
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jason Garber
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-08-
|
11
|
+
date: 2022-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -93,7 +93,7 @@ licenses:
|
|
93
93
|
- MIT
|
94
94
|
metadata:
|
95
95
|
bug_tracker_uri: https://github.com/jgarber623/micromicro/issues
|
96
|
-
changelog_uri: https://github.com/jgarber623/micromicro/blob/
|
96
|
+
changelog_uri: https://github.com/jgarber623/micromicro/blob/v3.0.0/CHANGELOG.md
|
97
97
|
rubygems_mfa_required: 'true'
|
98
98
|
post_install_message:
|
99
99
|
rdoc_options: []
|