micromicro 2.0.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|