ryoba 1.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 +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +23 -0
- data/lib/ryoba.rb +6 -0
- data/lib/ryoba/error.rb +2 -0
- data/lib/ryoba/nokogiri/xml/node.rb +72 -0
- data/lib/ryoba/nokogiri/xml/searchable.rb +68 -0
- data/lib/ryoba/version.rb +3 -0
- data/ryoba.gemspec +29 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c8f9b74acd04ed36e1171fb82634e96d9da6bea4
|
4
|
+
data.tar.gz: ce38a2666fa0e0cc710386bf97a06e52312a9c6a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c341734a0b8656a61f4889b01a74b3ca2c3593532635ea533cf75c31d6e1859612641c24113e57df12ef1d21ba180b306cd32ab804a087e01a9f49453d0158e7
|
7
|
+
data.tar.gz: be4be1c45496f21e0c4ed2d7da8627bf845306fd2970d77223984120c0b416517d8ec6c2a2a63e9f72271e997838cfe3e88c6aa0027f669d684b6e84ef9ac730
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 Jonathan Hefner
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# ryoba
|
2
|
+
|
3
|
+
[Nokogiri](https://rubygems.org/gems/nokogiri) utility methods.
|
4
|
+
|
5
|
+
|
6
|
+
## API
|
7
|
+
|
8
|
+
- Nokogiri::XML::Node
|
9
|
+
- [#matches!](http://www.rubydoc.info/gems/ryoba/Nokogiri/XML/Node:matches%21)
|
10
|
+
- [#text!](http://www.rubydoc.info/gems/ryoba/Nokogiri/XML/Node:text%21)
|
11
|
+
- [#uri](http://www.rubydoc.info/gems/ryoba/Nokogiri/XML/Node:uri)
|
12
|
+
- Nokogiri::XML::Searchable
|
13
|
+
- [#ancestor](http://www.rubydoc.info/gems/ryoba/Nokogiri/XML/Searchable:ancestor)
|
14
|
+
- [#ancestor!](http://www.rubydoc.info/gems/ryoba/Nokogiri/XML/Searchable:ancestor%21)
|
15
|
+
- [#ancestors!](http://www.rubydoc.info/gems/ryoba/Nokogiri/XML/Searchable:ancestors%21)
|
16
|
+
- [#at!](http://www.rubydoc.info/gems/ryoba/Nokogiri/XML/Searchable:at%21)
|
17
|
+
- [#search!](http://www.rubydoc.info/gems/ryoba/Nokogiri/XML/Searchable:search%21)
|
18
|
+
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Install from [Ruby Gems](https://rubygems.org/gems/ryoba):
|
23
|
+
|
24
|
+
```bash
|
25
|
+
$ gem install ryoba
|
26
|
+
```
|
27
|
+
|
28
|
+
Then require in your Ruby script:
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
require "ryoba"
|
32
|
+
```
|
33
|
+
|
34
|
+
|
35
|
+
## Contributing
|
36
|
+
|
37
|
+
Run `rake test` to run the tests. You can also run `rake irb` for an
|
38
|
+
interactive prompt that pre-loads the project code.
|
39
|
+
|
40
|
+
|
41
|
+
## License
|
42
|
+
|
43
|
+
[MIT License](https://opensource.org/licenses/MIT)
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
require "yard"
|
4
|
+
|
5
|
+
|
6
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
7
|
+
end
|
8
|
+
|
9
|
+
desc "Launch IRB with this gem pre-loaded"
|
10
|
+
task :irb do
|
11
|
+
require "ryoba"
|
12
|
+
require "irb"
|
13
|
+
ARGV.clear
|
14
|
+
IRB.start
|
15
|
+
end
|
16
|
+
|
17
|
+
Rake::TestTask.new(:test) do |t|
|
18
|
+
t.libs << "test"
|
19
|
+
t.libs << "lib"
|
20
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
21
|
+
end
|
22
|
+
|
23
|
+
task :default => :test
|
data/lib/ryoba.rb
ADDED
data/lib/ryoba/error.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require "uri"
|
2
|
+
|
3
|
+
class Nokogiri::XML::Node
|
4
|
+
|
5
|
+
# Equivalent to +.text.strip+, but raises an error if the result is an
|
6
|
+
# empty string.
|
7
|
+
#
|
8
|
+
# @return [String]
|
9
|
+
# @raise [Ryoba::Error]
|
10
|
+
# if result is an empty string
|
11
|
+
def text!
|
12
|
+
result = self.text.strip
|
13
|
+
if result.empty?
|
14
|
+
raise Ryoba::Error.new("No text in:\n#{self.to_html}")
|
15
|
+
end
|
16
|
+
result
|
17
|
+
end
|
18
|
+
|
19
|
+
# Like +Node#matches?+, but, instead of returning a boolean, returns
|
20
|
+
# the Node if it matches +selector+ and raises an error otherwise.
|
21
|
+
#
|
22
|
+
# @param selector [String]
|
23
|
+
# selector to match
|
24
|
+
# @return [self]
|
25
|
+
# @raise [Ryoba::Error]
|
26
|
+
# if Node does not match +selector+
|
27
|
+
def matches!(selector)
|
28
|
+
if !self.matches?(selector)
|
29
|
+
abbreviated = self.to_html[/[^>]+>/]
|
30
|
+
raise Ryoba::Error.new("Node #{abbreviated} does not match #{selector.inspect}")
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
HTML_ELEMENT_URI_ATTRIBUTES = {
|
36
|
+
"a" => "href",
|
37
|
+
"img" => "src",
|
38
|
+
"form" => "action",
|
39
|
+
}
|
40
|
+
|
41
|
+
# Returns a URI from a specified attribute's value. If no attribute
|
42
|
+
# is specified, an appropriate attribute will be chosen from
|
43
|
+
# {HTML_ELEMENT_URI_ATTRIBUTES} using the Node's +name+, if possible.
|
44
|
+
# A relative URI will be converted to an absolute URI, based on the
|
45
|
+
# Node document's +url+, if possible.
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
# xml = Nokogiri::XML(<<-XML, "http://localhost/qux")
|
49
|
+
# <body>
|
50
|
+
# <a href="https://www.example.com/foo">FOO</a>
|
51
|
+
# <div data-src="/bar" />
|
52
|
+
# <p>blah</p>
|
53
|
+
# </body>
|
54
|
+
# XML
|
55
|
+
#
|
56
|
+
# xml.at("a").uri # == URI("https://www.example.com/foo")
|
57
|
+
# xml.at("div").uri("data-src") # == URI("http://localhost/qux/bar")
|
58
|
+
# xml.at("p").uri # == nil
|
59
|
+
#
|
60
|
+
# @param attribute_name [String]
|
61
|
+
# name of the attribute to return as a URI
|
62
|
+
# @return [URI, nil]
|
63
|
+
def uri(attribute_name = nil)
|
64
|
+
attribute_name ||= HTML_ELEMENT_URI_ATTRIBUTES[self.name]
|
65
|
+
url = self[attribute_name]
|
66
|
+
|
67
|
+
if url
|
68
|
+
self.document.url ? URI.join(self.document.url, url) : URI(url)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Nokogiri::XML::Searchable
|
2
|
+
|
3
|
+
# Like +Searchable#search+, but raises an exception if there are no
|
4
|
+
# results.
|
5
|
+
#
|
6
|
+
# @param queries [Array<String>]
|
7
|
+
# @return [Array<Nokogiri::XML::Element>]
|
8
|
+
# @raise [Ryoba::Error]
|
9
|
+
# if all queries yield no results
|
10
|
+
def search!(*queries)
|
11
|
+
results = self.search(*queries)
|
12
|
+
if results.empty?
|
13
|
+
raise Ryoba::Error.new("No elements matching #{queries.map(&:inspect).join(" OR ")}")
|
14
|
+
end
|
15
|
+
results
|
16
|
+
end
|
17
|
+
|
18
|
+
# Like +Searchable#at+, but raises an exception if there are no
|
19
|
+
# results.
|
20
|
+
#
|
21
|
+
# @param queries [Array<String>]
|
22
|
+
# @return [Nokogiri::XML::Element]
|
23
|
+
# @raise [Ryoba::Error]
|
24
|
+
# if all queries yield no results
|
25
|
+
def at!(*queries)
|
26
|
+
result = self.at(*queries)
|
27
|
+
if result.nil?
|
28
|
+
raise Ryoba::Error.new("No elements matching #{queries.map(&:inspect).join(" OR ")}")
|
29
|
+
end
|
30
|
+
result
|
31
|
+
end
|
32
|
+
|
33
|
+
# Like +Searchable#ancestors+, but raises an exception if there are no
|
34
|
+
# matching ancestors.
|
35
|
+
#
|
36
|
+
# @param selector [String]
|
37
|
+
# @return [Array<Nokogiri::XML::Element>]
|
38
|
+
# @raise [Ryoba::Error]
|
39
|
+
# if no ancestors match +selector+
|
40
|
+
def ancestors!(selector = nil)
|
41
|
+
results = self.ancestors(selector)
|
42
|
+
if results.empty?
|
43
|
+
raise Ryoba::Error.new("No ancestors matching #{selector.inspect}")
|
44
|
+
end
|
45
|
+
results
|
46
|
+
end
|
47
|
+
|
48
|
+
# Like +Searchable#ancestors+, but returns only the first matching
|
49
|
+
# ancestor.
|
50
|
+
#
|
51
|
+
# @param selector [String]
|
52
|
+
# @return [Nokogiri::XML::Element, nil]
|
53
|
+
def ancestor(selector = nil)
|
54
|
+
self.ancestors(selector).first
|
55
|
+
end
|
56
|
+
|
57
|
+
# Like +Searchable#ancestors!+, but returns only the first matching
|
58
|
+
# ancestor.
|
59
|
+
#
|
60
|
+
# @param selector [String]
|
61
|
+
# @return [Nokogiri::XML::Element]
|
62
|
+
# @raise [Ryoba::Error]
|
63
|
+
# if no ancestors match +selector+
|
64
|
+
def ancestor!(selector = nil)
|
65
|
+
self.ancestors!(selector).first
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
data/ryoba.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "ryoba/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ryoba"
|
8
|
+
spec.version = Ryoba::VERSION
|
9
|
+
spec.authors = ["Jonathan Hefner"]
|
10
|
+
spec.email = ["jonathan.hefner@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Nokogiri utility methods}
|
13
|
+
spec.homepage = "https://github.com/jonathanhefner/ryoba"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
18
|
+
end
|
19
|
+
spec.bindir = "exe"
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_runtime_dependency "nokogiri", ">= 1.6.6", "< 2.0"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
28
|
+
spec.add_development_dependency "yard", "~> 0.9"
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ryoba
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jonathan Hefner
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-04-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.6.6
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '2.0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.6.6
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.0'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: bundler
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '1.16'
|
40
|
+
type: :development
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.16'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rake
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '10.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '10.0'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: minitest
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '5.0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '5.0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: yard
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0.9'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0.9'
|
89
|
+
description:
|
90
|
+
email:
|
91
|
+
- jonathan.hefner@gmail.com
|
92
|
+
executables: []
|
93
|
+
extensions: []
|
94
|
+
extra_rdoc_files: []
|
95
|
+
files:
|
96
|
+
- ".gitignore"
|
97
|
+
- ".travis.yml"
|
98
|
+
- Gemfile
|
99
|
+
- LICENSE.txt
|
100
|
+
- README.md
|
101
|
+
- Rakefile
|
102
|
+
- lib/ryoba.rb
|
103
|
+
- lib/ryoba/error.rb
|
104
|
+
- lib/ryoba/nokogiri/xml/node.rb
|
105
|
+
- lib/ryoba/nokogiri/xml/searchable.rb
|
106
|
+
- lib/ryoba/version.rb
|
107
|
+
- ryoba.gemspec
|
108
|
+
homepage: https://github.com/jonathanhefner/ryoba
|
109
|
+
licenses:
|
110
|
+
- MIT
|
111
|
+
metadata: {}
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
version: '0'
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubyforge_project:
|
128
|
+
rubygems_version: 2.6.13
|
129
|
+
signing_key:
|
130
|
+
specification_version: 4
|
131
|
+
summary: Nokogiri utility methods
|
132
|
+
test_files: []
|