nikkou 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +131 -22
- data/Rakefile +1 -1
- data/lib/nikkou/nokogiri/xml/node.rb +71 -7
- data/lib/nikkou/nokogiri/xml/node_set.rb +18 -0
- data/lib/nikkou/version.rb +1 -1
- data/spec/drillable_spec.rb +1 -1
- data/spec/files/test.html +5 -0
- data/spec/findable_spec.rb +1 -1
- data/spec/node_set_spec.rb +22 -8
- data/spec/node_spec.rb +73 -6
- data/spec/spec_helper.rb +0 -1
- metadata +4 -4
data/README.md
CHANGED
@@ -1,75 +1,184 @@
|
|
1
1
|
Nikkou
|
2
2
|
======
|
3
|
-
|
3
|
+
Extract useful data from HTML and XML with ease!
|
4
4
|
|
5
5
|
Description
|
6
6
|
-----------
|
7
7
|
|
8
|
-
Nikkou
|
8
|
+
Nikkou adds additional methods to Nokogiri to make extracting commonly-used data from HTML and XML easier. It lets you transform HTML into structured data very quickly, and it integrates nicely with [Mechanize](https://github.com/sparklemotion/mechanize).
|
9
9
|
|
10
|
-
|
10
|
+
Method Overview
|
11
|
+
---------------
|
12
|
+
|
13
|
+
Here's a summary of the methods Nikkou provides (see "Methods" for details):
|
14
|
+
|
15
|
+
### Formatting
|
16
|
+
|
17
|
+
**parse_text** - Parses the node's text as XML and returns it as a Nokogiri::XML::NodeSet
|
18
|
+
|
19
|
+
**time(options={})** - Intelligently parses the time (relative or absolute) of either the text or a specified attribute; accepts a `time_zone` option
|
20
|
+
|
21
|
+
**url(attribute='href')** - Converts the href (or other specified attribute) into an absolute URL using the document's URI; `<a href="/p/1">Link</a>` yields `http://mysite.com/p/1`
|
22
|
+
|
23
|
+
### Searching
|
24
|
+
|
25
|
+
**attr_equals(attribute, string)** - Finds nodes where the attribute equals the string
|
26
|
+
|
27
|
+
**attr_includes(attribute, string)** - Finds nodes where the attribute includes the string
|
28
|
+
|
29
|
+
**attr_matches(attribute, pattern)** - Finds nodes where the attribute matches the pattern
|
30
|
+
|
31
|
+
**drill(*methods)** - Nil-safe method chaining
|
32
|
+
|
33
|
+
**find(path)** - Same as `search` but returns the first matched node
|
34
|
+
|
35
|
+
**text_equals(string)** - Finds nodes where the text equals the string
|
36
|
+
|
37
|
+
**text_includes(string)** - Finds nodes where the text includes the string
|
38
|
+
|
39
|
+
**text_matches(pattern)** - Finds nodes where the text matches the pattern
|
40
|
+
|
41
|
+
## Methods
|
42
|
+
|
43
|
+
### Formatting
|
44
|
+
|
45
|
+
#### time(options={})
|
11
46
|
|
12
47
|
Returns a Time object (in UTC) by automatically parsing the text or specified attribute of the node.
|
13
48
|
|
14
49
|
```ruby
|
15
50
|
# <a href="/p/1">3 hours ago</a>
|
16
|
-
doc.search('a').first.time
|
51
|
+
doc.search('a').first.time
|
17
52
|
```
|
18
53
|
|
19
54
|
###### Options
|
20
55
|
|
21
|
-
`attribute`
|
56
|
+
`attribute`
|
57
|
+
|
58
|
+
The attribute to parse:
|
22
59
|
|
23
60
|
```ruby
|
24
|
-
# <a href="/p/1" data-published-at="2013-
|
25
|
-
doc.search('a').first.time(attribute: 'data-published-at')
|
61
|
+
# <a href="/p/1" data-published-at="2013-05-22 02:42:34">My link</a>
|
62
|
+
doc.search('a').first.time(attribute: 'data-published-at')
|
26
63
|
```
|
27
64
|
|
28
|
-
`time_zone`
|
65
|
+
`time_zone`
|
66
|
+
|
67
|
+
The document's time zone (the time will be converted from that to UTC):
|
29
68
|
|
30
69
|
```ruby
|
31
70
|
# <a href="/p/1">3 hours ago</a>
|
32
|
-
doc.search('a').first.time(time_zone: 'America/New_York')
|
71
|
+
doc.search('a').first.time(time_zone: 'America/New_York')
|
33
72
|
```
|
34
73
|
|
35
|
-
#### url
|
74
|
+
#### url(attribute='href')
|
36
75
|
|
37
|
-
Returns an absolute URL; useful for parsing relative hrefs. The document's `uri` needs to be set for Nikkou to know what domain to add to relative
|
76
|
+
Returns an absolute URL; useful for parsing relative hrefs. The document's `uri` needs to be set for Nikkou to know what domain to add to relative paths.
|
38
77
|
|
39
78
|
```ruby
|
40
79
|
# <a href="/p/1">My link</a>
|
41
80
|
doc.uri = 'http://mysite.com/mypage'
|
42
|
-
doc.search('a').first.url # http://mysite.com/p/1
|
81
|
+
doc.search('a').first.url # "http://mysite.com/p/1"
|
43
82
|
```
|
44
83
|
|
84
|
+
If Mechanize is being used, the `uri` doesn't need to be manually set.
|
85
|
+
|
45
86
|
###### Options
|
46
87
|
|
47
|
-
`attribute`
|
88
|
+
`attribute`
|
89
|
+
|
90
|
+
The attribute to parse:
|
48
91
|
|
49
92
|
```ruby
|
50
93
|
# <a href="/p/1" data-comments-url="/p/1#comments">My Link</a>
|
51
94
|
doc.uri = 'http://mysite.com/mypage'
|
52
|
-
doc.search('a').first.url('data-comments-url') # http://mysite.com/p/1#comments
|
95
|
+
doc.search('a').first.url('data-comments-url') # "http://mysite.com/p/1#comments"
|
96
|
+
```
|
97
|
+
|
98
|
+
### Searching
|
99
|
+
|
100
|
+
#### attr_equals(attribute, string)
|
101
|
+
|
102
|
+
Selects nodes where the specified attribute equals the string.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
# <div data-type="news">My Text</div>
|
106
|
+
doc.attr_equals('data-type', 'news').first.text # "My Text"
|
107
|
+
```
|
108
|
+
|
109
|
+
#### attr_includes(attribute, string)
|
110
|
+
|
111
|
+
Selects nodes where the specified attribute includes the string.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
# <div data-type="major-news">My Text</div>
|
115
|
+
doc.attr_equals('data-type', 'news').first.text # "My Text"
|
53
116
|
```
|
54
117
|
|
55
|
-
|
118
|
+
#### attr_matches(attribute, pattern)
|
56
119
|
|
57
|
-
Selects nodes with an attribute matching a pattern. The pattern's matches are
|
120
|
+
Selects nodes with an attribute matching a pattern. The pattern's matches are available in `Node#matches`.
|
58
121
|
|
59
122
|
```ruby
|
60
123
|
# <span data-tooltip="3 Comments">My Text</span>
|
61
|
-
doc.
|
62
|
-
doc.
|
124
|
+
doc.attr_matches('data-tooltip', /(\d+) comments/i).first.text # "My Text"
|
125
|
+
doc.attr_matches('data-tooltip', /(\d+) comments/i).first.matches # ["3 Comments", "3"]
|
126
|
+
```
|
127
|
+
|
128
|
+
#### drill(*methods)
|
129
|
+
|
130
|
+
Nil-safe method chaining. Replaces this:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
node = doc.find('.count')
|
134
|
+
if node
|
135
|
+
attribute = node.attr('data-count')
|
136
|
+
if attribute
|
137
|
+
return attribute.to_i
|
138
|
+
end
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
With this:
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
return doc.drill([:find, '.count'], [:attr, 'data-count'], :to_i)
|
146
|
+
```
|
147
|
+
|
148
|
+
#### find(path)
|
149
|
+
|
150
|
+
Same as `search`, but returns the first matched node. Replaces this:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
nodes = node.search('h4')
|
154
|
+
if nodes
|
155
|
+
return nodes.first
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
With this:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
return node.find('h4')
|
163
|
+
```
|
164
|
+
|
165
|
+
#### text_includes(string)
|
166
|
+
|
167
|
+
Selects nodes where the text includes the string.
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
# <div data-type="news">My Text</div>
|
171
|
+
doc.text_includes('Text').first.text # "My Text"
|
63
172
|
```
|
64
173
|
|
65
|
-
|
174
|
+
#### text_matches(pattern)
|
66
175
|
|
67
|
-
Selects nodes with text matching a pattern. The pattern's matches are
|
176
|
+
Selects nodes with text matching a pattern. The pattern's matches are available in `Node#matches`.
|
68
177
|
|
69
178
|
```ruby
|
70
179
|
# <a href="/p/1">3 Comments</a>
|
71
|
-
doc.
|
72
|
-
doc.
|
180
|
+
doc.text_matches(/^(\d+) comments$/i).first.attr('href') # "/p/1"
|
181
|
+
doc.text_matches(/^(\d+) comments$/i).first.matches # ["3 Comments", "3"]
|
73
182
|
```
|
74
183
|
|
75
184
|
License
|
data/Rakefile
CHANGED
@@ -6,14 +6,68 @@ module Nikkou
|
|
6
6
|
include Nikkou::Findable
|
7
7
|
|
8
8
|
attr_accessor :matches
|
9
|
+
|
10
|
+
def attr_equals(attribute, string)
|
11
|
+
list = []
|
12
|
+
traverse do |node|
|
13
|
+
list << node if node.attr(attribute) == string
|
14
|
+
end
|
15
|
+
::Nokogiri::XML::NodeSet.new(document, list)
|
16
|
+
end
|
17
|
+
|
18
|
+
def attr_includes(attribute, string)
|
19
|
+
list = []
|
20
|
+
traverse do |node|
|
21
|
+
next if node.attr(attribute).nil?
|
22
|
+
list << node if node.attr(attribute).include?(string)
|
23
|
+
end
|
24
|
+
::Nokogiri::XML::NodeSet.new(document, list)
|
25
|
+
end
|
26
|
+
|
27
|
+
def attr_matches(attribute, pattern)
|
28
|
+
list = []
|
29
|
+
traverse do |node|
|
30
|
+
next if node.attr(attribute).nil?
|
31
|
+
if node.attr(attribute).match(pattern)
|
32
|
+
node.matches = $~.to_a
|
33
|
+
list << node
|
34
|
+
end
|
35
|
+
end
|
36
|
+
::Nokogiri::XML::NodeSet.new(document, list)
|
37
|
+
end
|
9
38
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
39
|
+
def parse_text
|
40
|
+
parse(text)
|
41
|
+
end
|
42
|
+
|
43
|
+
def text_equals(string)
|
44
|
+
list = []
|
45
|
+
traverse do |node|
|
46
|
+
next if node.is_a?(::Nokogiri::XML::Text)
|
47
|
+
list << node if node.text == string
|
48
|
+
end
|
49
|
+
::Nokogiri::XML::NodeSet.new(document, list)
|
50
|
+
end
|
51
|
+
|
52
|
+
def text_includes(string)
|
53
|
+
list = []
|
54
|
+
traverse do |node|
|
55
|
+
next if node.is_a?(::Nokogiri::XML::Text)
|
56
|
+
list << node if node.text.include?(string)
|
57
|
+
end
|
58
|
+
::Nokogiri::XML::NodeSet.new(document, list)
|
59
|
+
end
|
60
|
+
|
61
|
+
def text_matches(pattern)
|
62
|
+
list = []
|
63
|
+
traverse do |node|
|
64
|
+
next if node.is_a?(::Nokogiri::XML::Text)
|
65
|
+
if node.text.match(pattern)
|
66
|
+
node.matches = $~.to_a
|
67
|
+
list << node
|
68
|
+
end
|
69
|
+
end
|
70
|
+
::Nokogiri::XML::NodeSet.new(document, list)
|
17
71
|
end
|
18
72
|
|
19
73
|
def time(options={})
|
@@ -33,6 +87,16 @@ module Nikkou
|
|
33
87
|
end
|
34
88
|
time_zone.local_to_utc(time)
|
35
89
|
end
|
90
|
+
|
91
|
+
def url(attribute='href')
|
92
|
+
return nil if attr(attribute).nil?
|
93
|
+
href = attr(attribute)
|
94
|
+
return href if href =~ /^https?:\/\//
|
95
|
+
return "http:#{href}" if href.start_with?('//')
|
96
|
+
return nil if document.nil? || document.uri.nil?
|
97
|
+
root_url = "#{document.uri.scheme}://#{document.uri.host}"
|
98
|
+
URI.join(root_url, href).to_s
|
99
|
+
end
|
36
100
|
end
|
37
101
|
end
|
38
102
|
end
|
@@ -5,6 +5,14 @@ module Nikkou
|
|
5
5
|
include Nikkou::Drillable
|
6
6
|
include Nikkou::Findable
|
7
7
|
|
8
|
+
def attr_equals(attribute, string)
|
9
|
+
list = select do |node|
|
10
|
+
return false if node.attr(attribute).nil?
|
11
|
+
node.attr(attribute) == string
|
12
|
+
end
|
13
|
+
self.class.new(document, list)
|
14
|
+
end
|
15
|
+
|
8
16
|
def attr_includes(attribute, string)
|
9
17
|
list = select do |node|
|
10
18
|
return false if node.attr(attribute).nil?
|
@@ -25,8 +33,17 @@ module Nikkou
|
|
25
33
|
self.class.new(document, list)
|
26
34
|
end
|
27
35
|
|
36
|
+
def text_equals(string)
|
37
|
+
list = select do |node|
|
38
|
+
next if node.is_a?(::Nokogiri::XML::Text)
|
39
|
+
node.text == string
|
40
|
+
end
|
41
|
+
self.class.new(document, list)
|
42
|
+
end
|
43
|
+
|
28
44
|
def text_includes(string)
|
29
45
|
list = select do |node|
|
46
|
+
next if node.is_a?(::Nokogiri::XML::Text)
|
30
47
|
node.text.include?(string)
|
31
48
|
end
|
32
49
|
self.class.new(document, list)
|
@@ -35,6 +52,7 @@ module Nikkou
|
|
35
52
|
def text_matches(pattern)
|
36
53
|
list = []
|
37
54
|
each do |node|
|
55
|
+
next if node.is_a?(::Nokogiri::XML::Text)
|
38
56
|
if node.text.match(pattern)
|
39
57
|
node.matches = $~.to_a
|
40
58
|
list << node
|
data/lib/nikkou/version.rb
CHANGED
data/spec/drillable_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Nikkou::Drillable do
|
4
|
-
before do
|
4
|
+
before(:all) do
|
5
5
|
assets_directory = File.expand_path(File.join(File.dirname(__FILE__), 'files'))
|
6
6
|
html_file = File.join(assets_directory, 'test.html')
|
7
7
|
@html = Nokogiri::HTML.parse(File.read(html_file))
|
data/spec/files/test.html
CHANGED
data/spec/findable_spec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Nikkou::Findable do
|
4
|
-
before do
|
4
|
+
before(:all) do
|
5
5
|
assets_directory = File.expand_path(File.join(File.dirname(__FILE__), 'files'))
|
6
6
|
html_file = File.join(assets_directory, 'test.html')
|
7
7
|
@html = Nokogiri::HTML.parse(File.read(html_file))
|
data/spec/node_set_spec.rb
CHANGED
@@ -1,12 +1,19 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Nokogiri::XML::NodeSet do
|
4
|
-
before do
|
4
|
+
before(:all) do
|
5
5
|
assets_directory = File.expand_path(File.join(File.dirname(__FILE__), 'files'))
|
6
6
|
html_file = File.join(assets_directory, 'test.html')
|
7
7
|
@html = Nokogiri::HTML.parse(File.read(html_file))
|
8
8
|
end
|
9
9
|
|
10
|
+
describe '.attr_equals' do
|
11
|
+
it 'finds nodes' do
|
12
|
+
nodes = @html.search('a').attr_equals('href', 'http://www.ipsum.com/')
|
13
|
+
nodes.first.text.should == 'ipsum'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
10
17
|
describe '.attr_includes' do
|
11
18
|
it 'finds nodes' do
|
12
19
|
nodes = @html.search('a').attr_includes('href', 'ipsum.com')
|
@@ -26,6 +33,20 @@ describe Nokogiri::XML::NodeSet do
|
|
26
33
|
end
|
27
34
|
end
|
28
35
|
|
36
|
+
describe '.text_equals' do
|
37
|
+
it 'finds nodes' do
|
38
|
+
nodes = @html.search('a').text_equals('ipsum')
|
39
|
+
nodes.first.text.should == 'ipsum'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '.text_includes' do
|
44
|
+
it 'finds nodes' do
|
45
|
+
nodes = @html.search('a').text_includes('ipsum')
|
46
|
+
nodes.first.text.should == 'ipsum'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
29
50
|
describe '.text_matches' do
|
30
51
|
it 'finds nodes' do
|
31
52
|
nodes = @html.search('a').text_matches(/(\d+) comments/)
|
@@ -37,11 +58,4 @@ describe Nokogiri::XML::NodeSet do
|
|
37
58
|
nodes.first.matches.should == ['12 comments', '12']
|
38
59
|
end
|
39
60
|
end
|
40
|
-
|
41
|
-
describe '.text_includes' do
|
42
|
-
it 'finds nodes' do
|
43
|
-
nodes = @html.search('a').text_includes('ipsum')
|
44
|
-
nodes.first.text.should == 'ipsum'
|
45
|
-
end
|
46
|
-
end
|
47
61
|
end
|
data/spec/node_spec.rb
CHANGED
@@ -1,20 +1,77 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Nokogiri::XML::Node do
|
4
|
-
before do
|
4
|
+
before(:all) do
|
5
5
|
assets_directory = File.expand_path(File.join(File.dirname(__FILE__), 'files'))
|
6
6
|
html_file = File.join(assets_directory, 'test.html')
|
7
7
|
@html = Nokogiri::HTML.parse(File.read(html_file))
|
8
8
|
@html.uri = 'http://www.loremipsum.com/page/2'
|
9
|
+
|
10
|
+
# Set the time zone for .time
|
11
|
+
Time.zone = 'Pacific Time (US & Canada)'
|
9
12
|
end
|
10
13
|
|
11
|
-
describe '.
|
12
|
-
it '
|
13
|
-
@html.search('
|
14
|
+
describe '.attr_equals' do
|
15
|
+
it 'finds nodes' do
|
16
|
+
nodes = @html.search('body').first.attr_equals('href', 'http://www.ipsum.com/')
|
17
|
+
nodes.first.text.should == 'ipsum'
|
14
18
|
end
|
19
|
+
end
|
15
20
|
|
16
|
-
|
17
|
-
|
21
|
+
describe '.attr_includes' do
|
22
|
+
it 'finds nodes' do
|
23
|
+
nodes = @html.search('body').first.attr_includes('href', 'ipsum.com')
|
24
|
+
nodes.first.text.should == 'ipsum'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '.attr_matches' do
|
29
|
+
it 'finds nodes' do
|
30
|
+
nodes = @html.search('body').first.attr_matches('href', /(lorem|ipsum)\.com/)
|
31
|
+
nodes.first.text.should == 'ipsum'
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'sets matches' do
|
35
|
+
nodes = @html.search('body').first.attr_matches('href', /(lorem|ipsum)\.com/)
|
36
|
+
nodes.first.matches.should == ['ipsum.com', 'ipsum']
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '.parse_text' do
|
41
|
+
it 'converts the node\'s text to a node set' do
|
42
|
+
nodes = @html.search('.xml-node').first.parse_text
|
43
|
+
nodes.should be_an_instance_of(Nokogiri::XML::NodeSet)
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns a node set that contains the correct content' do
|
47
|
+
nodes = @html.search('.xml-node').first.parse_text
|
48
|
+
nodes.search('.xml-encoded-node').length.should == 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '.text_equals' do
|
53
|
+
it 'finds nodes' do
|
54
|
+
nodes = @html.search('body').first.text_equals('ipsum')
|
55
|
+
nodes.first.text.should == 'ipsum'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '.text_includes' do
|
60
|
+
it 'finds nodes' do
|
61
|
+
nodes = @html.search('body').first.text_includes('ipsum')
|
62
|
+
nodes.first.text.should == 'ipsum'
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe '.text_matches' do
|
67
|
+
it 'finds nodes' do
|
68
|
+
nodes = @html.search('body').first.text_matches(/(\d+) comments/)
|
69
|
+
nodes.first.text.should == '12 comments'
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'sets matches' do
|
73
|
+
nodes = @html.search('body').first.text_matches(/(\d+) comments/)
|
74
|
+
nodes.first.matches.should == ['12 comments', '12']
|
18
75
|
end
|
19
76
|
end
|
20
77
|
|
@@ -31,4 +88,14 @@ describe Nokogiri::XML::Node do
|
|
31
88
|
@html.search('.post-published-at').first.time(attribute: 'data-published-at', time_zone: 'America/New_York').to_s.should == '2013-04-01 04:00:00 UTC'
|
32
89
|
end
|
33
90
|
end
|
91
|
+
|
92
|
+
describe '.url' do
|
93
|
+
it 'reads absolute URLs' do
|
94
|
+
@html.search('a.absolute-url').first.url.should == 'http://www.absoluteurl.com/'
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'reads relative URLs' do
|
98
|
+
@html.search('a.relative-url').first.url.should == 'http://www.loremipsum.com/p/1'
|
99
|
+
end
|
100
|
+
end
|
34
101
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nikkou
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-06-02 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: nokogiri
|
@@ -91,7 +91,7 @@ dependencies:
|
|
91
91
|
- - ! '>='
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: '0'
|
94
|
-
description:
|
94
|
+
description: Extract useful data from HTML and XML with ease!
|
95
95
|
email:
|
96
96
|
- tombenner@gmail.com
|
97
97
|
executables: []
|
@@ -141,7 +141,7 @@ rubyforge_project:
|
|
141
141
|
rubygems_version: 1.8.24
|
142
142
|
signing_key:
|
143
143
|
specification_version: 3
|
144
|
-
summary:
|
144
|
+
summary: Extract useful data from HTML and XML with ease!
|
145
145
|
test_files:
|
146
146
|
- spec/drillable_spec.rb
|
147
147
|
- spec/files/test.html
|