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 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
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.5
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in ryoba.gemspec
6
+ gemspec
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
@@ -0,0 +1,6 @@
1
+ require "nokogiri"
2
+
3
+ require_relative "ryoba/version"
4
+ require_relative "ryoba/error"
5
+ require_relative "ryoba/nokogiri/xml/node"
6
+ require_relative "ryoba/nokogiri/xml/searchable"
@@ -0,0 +1,2 @@
1
+ class Ryoba::Error < RuntimeError
2
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Ryoba
2
+ VERSION = "1.0.0"
3
+ 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: []