ferrumwizard 0.1.3 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/ferrumwizard.rb +175 -97
- data.tar.gz.sig +0 -0
- metadata +36 -36
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b8068fb08b6eb2a2c96e42a22e4e2290484fa98ca27271b3731d858e39d7bf5
|
4
|
+
data.tar.gz: a939996048a46f321bf66c4dc86991f9a37c91393599782e9d3a147a473e2216
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 17c6c8faf26098abcbd716cedb7ba4a4c9d935595af6c4e1626887b3a465ab6632d68502f4f01f5f61f5af59a78c0a629da36e4ea6c0b11a35d1ce1f994872d1
|
7
|
+
data.tar.gz: 950098796922e1b514df83e92438f533ad92fa6bfb40208001d3b1de12b7810b87fa3f888a8f6733061e5f0672fb8e2919358ca52a11bef12b5bb24b40bcf451
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/ferrumwizard.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
# file: ferrumwizard.rb
|
4
4
|
|
5
|
+
require 'yaml'
|
5
6
|
require 'rexle'
|
6
7
|
require 'ferrum'
|
7
8
|
|
@@ -10,125 +11,204 @@ class FerrumWizard
|
|
10
11
|
|
11
12
|
attr_reader :browser, :links, :radio, :buttons, :js_methods
|
12
13
|
|
13
|
-
def initialize(url, headless: true, timeout: 10,
|
14
|
+
def initialize(url=nil, headless: true, timeout: 10, cookies: nil,
|
15
|
+
debug: false)
|
14
16
|
|
15
17
|
@url, @debug = url, debug
|
16
18
|
@browser = Ferrum::Browser.new headless: headless, timeout: timeout
|
17
|
-
sleep
|
18
|
-
|
19
|
-
|
19
|
+
sleep 3
|
20
|
+
|
21
|
+
if url then
|
22
|
+
|
23
|
+
loadx(cookies) if cookies
|
24
|
+
|
25
|
+
@browser.goto(@url)
|
26
|
+
@browser.network.wait_for_idle
|
27
|
+
sleep 4
|
28
|
+
|
29
|
+
end
|
20
30
|
end
|
21
|
-
|
31
|
+
|
22
32
|
def inspect()
|
23
33
|
"#<FerrumWizard>"
|
24
34
|
end
|
25
|
-
|
35
|
+
|
36
|
+
# Intended to load all the cookies for a user to login automatically
|
37
|
+
#
|
38
|
+
# Follow these steps to load the cookies file:
|
39
|
+
#
|
40
|
+
# 1. launch the Ferrum browser
|
41
|
+
# fw = FerrumWizard.new( headless: false, debug: false)
|
42
|
+
#
|
43
|
+
# 2. load the cookies before you visit the website
|
44
|
+
# fw.load_cookies('/tmp/indeed2.txt')
|
45
|
+
#
|
46
|
+
# 3. visit the website
|
47
|
+
# url='https://somewebsite.com'
|
48
|
+
# fw.browser.goto(url)
|
49
|
+
#
|
50
|
+
def load_cookies(filepath)
|
51
|
+
|
52
|
+
rawcookies = YAML.load(File.read(filepath))
|
53
|
+
|
54
|
+
rawcookies.each do |h|
|
55
|
+
|
56
|
+
if @debug then
|
57
|
+
puts 'name: ' + h['name']
|
58
|
+
puts 'h: ' + h.inspect
|
59
|
+
sleep 0.7
|
60
|
+
end
|
61
|
+
|
62
|
+
browser.cookies.set(name: h['name'], value: h['value'],
|
63
|
+
domain: h['domain'], expires: h['expires'],
|
64
|
+
httponly: h['httpOnly'])
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
alias loadx load_cookies
|
70
|
+
|
26
71
|
def login(usernamex=nil, passwordx=nil, username: usernamex, password: passwordx)
|
27
|
-
|
28
|
-
puts 'username: ' + username.inspect if @debug
|
29
72
|
|
30
|
-
|
31
|
-
b.goto(@url)
|
32
|
-
@browser.network.wait_for_idle
|
33
|
-
sleep 3
|
73
|
+
puts 'username: ' + username.inspect if @debug
|
34
74
|
|
35
75
|
# search for the username input box
|
36
|
-
e_username =
|
76
|
+
e_username = @browser.at_xpath('//input[@type="email"]')
|
37
77
|
puts 'e_username: ' + e_username.inspect if @debug
|
38
78
|
sleep 1
|
39
79
|
# search for the password input box
|
40
|
-
|
80
|
+
found = @browser.at_xpath('//input[@type="password"]')
|
81
|
+
|
82
|
+
e_password = if found then
|
83
|
+
found
|
84
|
+
else
|
85
|
+
@browser.xpath('//input').find {|x| x.property(:id) =~ /password/i}
|
86
|
+
end
|
87
|
+
|
41
88
|
sleep 1
|
42
|
-
|
89
|
+
|
43
90
|
if username and e_username then
|
44
91
|
puts 'entering the username' if @debug
|
45
|
-
e_username.focus.type(username)
|
92
|
+
e_username.focus.type(username)
|
46
93
|
sleep 1
|
47
94
|
end
|
48
|
-
|
95
|
+
|
49
96
|
e_password.focus.type(password, :Enter) if e_password
|
50
|
-
|
97
|
+
|
51
98
|
after_login()
|
52
|
-
|
53
|
-
end
|
54
|
-
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
# login2 is used for websites where the user is presented with the username
|
103
|
+
# input box on the first page and the password input box on the next page.
|
104
|
+
#
|
55
105
|
def login2(usernamex=nil, passwordx=nil, username: usernamex, password: passwordx)
|
56
|
-
|
57
|
-
puts 'username: ' + username.inspect if @debug
|
58
106
|
|
59
|
-
|
60
|
-
b.goto(@url)
|
61
|
-
@browser.network.wait_for_idle
|
62
|
-
sleep 3
|
107
|
+
puts 'username: ' + username.inspect if @debug
|
63
108
|
|
64
109
|
# search for the username input box
|
65
|
-
e_username =
|
110
|
+
e_username = @browser.at_xpath('//input[@type="email"]')
|
66
111
|
puts 'e_username: ' + e_username.inspect if @debug
|
67
112
|
sleep 1
|
68
113
|
# search for the password input box
|
69
|
-
|
114
|
+
|
70
115
|
if username and e_username then
|
71
116
|
puts 'entering the username' if @debug
|
72
|
-
e_username.focus.type(username, :Enter)
|
117
|
+
e_username.focus.type(username, :Enter)
|
73
118
|
sleep 2
|
74
119
|
end
|
75
120
|
|
76
|
-
e_password =
|
121
|
+
e_password = @browser.at_xpath('//input[@type="password"]')
|
77
122
|
sleep 1
|
78
|
-
|
123
|
+
|
79
124
|
e_password.focus.type(password, :Enter) if e_password
|
80
|
-
|
125
|
+
|
81
126
|
after_login()
|
82
127
|
|
83
|
-
|
84
|
-
end
|
85
|
-
|
128
|
+
|
129
|
+
end
|
130
|
+
|
86
131
|
def quit
|
87
132
|
@browser.quit
|
88
133
|
end
|
89
|
-
|
134
|
+
|
90
135
|
def scan_page()
|
91
|
-
|
92
|
-
@doc = Rexle.new @browser.body
|
136
|
+
|
137
|
+
@doc = Rexle.new @browser.body
|
93
138
|
fetch_links()
|
94
|
-
scan_form_elements()
|
139
|
+
scan_form_elements()
|
95
140
|
scan_js_links()
|
96
|
-
|
141
|
+
@browser.mouse.scroll_to(0, 800)
|
142
|
+
self
|
143
|
+
end
|
144
|
+
|
145
|
+
# Saves all cookies for a given website into a YAML file
|
146
|
+
# see also load_cookies()
|
147
|
+
#
|
148
|
+
# To use this method follow these steps:
|
149
|
+
#
|
150
|
+
# 1. launch the web browser through Ferrum
|
151
|
+
# fw = FerrumWizard.new(url, headless: false, debug: false)
|
152
|
+
#
|
153
|
+
# 2. go to the browser and login using your credentials
|
154
|
+
# fw.save_cookies(filepath)
|
155
|
+
#
|
156
|
+
# 3. exit the IRB session
|
157
|
+
#
|
158
|
+
def save_cookies(filepath=Tempfile.new('ferrum').path)
|
159
|
+
|
160
|
+
rawcookies = @browser.cookies.all.keys.map do |key|
|
161
|
+
|
162
|
+
if @debug then
|
163
|
+
puts 'key: ' + key.inspect
|
164
|
+
sleep 0.5
|
165
|
+
end
|
166
|
+
|
167
|
+
s = @browser.cookies[key].inspect
|
168
|
+
a = s.scan(/"([^"]+)"=\>/)
|
169
|
+
s2 = s[/(?<=@attributes=).*(?=>)/]
|
170
|
+
eval(s2)
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
File.write filepath, rawcookies.to_yaml
|
175
|
+
|
97
176
|
end
|
98
|
-
|
177
|
+
|
99
178
|
def submit(h)
|
100
179
|
|
101
180
|
e = nil
|
102
|
-
|
181
|
+
|
103
182
|
h.each do |key, value|
|
104
|
-
e = @browser.xpath('//input').find {|x| x.attribute('name') == key.to_s}
|
105
|
-
e.focus.type(value)
|
183
|
+
e = @browser.xpath('//input').find {|x| x.attribute('name') == key.to_s}
|
184
|
+
e.focus.type(value)
|
106
185
|
end
|
107
|
-
|
108
|
-
e.focus.type('', :Enter)
|
109
|
-
|
110
|
-
sleep 4
|
186
|
+
|
187
|
+
e.focus.type('', :Enter)
|
188
|
+
|
189
|
+
sleep 4
|
111
190
|
scan_page()
|
112
|
-
|
191
|
+
|
113
192
|
end
|
114
193
|
|
115
194
|
def to_rb()
|
116
195
|
end
|
117
|
-
|
196
|
+
|
118
197
|
private
|
119
|
-
|
198
|
+
|
120
199
|
def after_login()
|
121
|
-
|
122
|
-
@browser.network.wait_for_idle
|
200
|
+
|
201
|
+
@browser.network.wait_for_idle
|
123
202
|
sleep 4
|
124
203
|
scan_page()
|
125
|
-
|
204
|
+
|
126
205
|
@browser.base_url = File.dirname(@browser.url)
|
127
206
|
@browser.mouse.scroll_to(0, 800)
|
128
207
|
self
|
129
|
-
|
208
|
+
|
130
209
|
end
|
131
|
-
|
210
|
+
|
211
|
+
|
132
212
|
def fetch_buttons()
|
133
213
|
|
134
214
|
a2 = @browser.xpath('//input[@type="button"]')
|
@@ -144,24 +224,24 @@ class FerrumWizard
|
|
144
224
|
buttons = @buttons
|
145
225
|
|
146
226
|
names.each do |name|
|
147
|
-
|
227
|
+
|
148
228
|
define_singleton_method name.to_sym do
|
149
229
|
buttons[name].click
|
150
230
|
@browser.network.wait_for_idle
|
151
231
|
sleep = 1
|
152
232
|
self
|
153
233
|
end
|
154
|
-
|
234
|
+
|
155
235
|
end
|
156
236
|
|
157
237
|
end
|
158
|
-
|
238
|
+
|
159
239
|
def fetch_links()
|
160
|
-
|
240
|
+
|
161
241
|
all_links = @doc.root.xpath('//a[@href]')
|
162
|
-
|
242
|
+
|
163
243
|
all_links.each do |x|
|
164
|
-
|
244
|
+
|
165
245
|
if x.plaintext.empty? then
|
166
246
|
x.text = x.attributes[:href].sub(/\.\w+$/,'')[/([^\/]+)$/].split(/[_]|(?=[A-Z])/).join(' ')
|
167
247
|
else
|
@@ -169,76 +249,74 @@ class FerrumWizard
|
|
169
249
|
end
|
170
250
|
|
171
251
|
end
|
172
|
-
|
252
|
+
|
173
253
|
valid_links = all_links.reject do |x|
|
174
|
-
|
254
|
+
|
175
255
|
puts 'x: ' + x.inspect if @debug
|
176
256
|
r = (x.attributes[:target] == '_blank')
|
177
257
|
|
178
258
|
puts 'r: ' + r.inspect if @debug
|
179
259
|
r
|
180
|
-
|
260
|
+
|
181
261
|
end
|
182
|
-
|
262
|
+
|
183
263
|
indices = valid_links.map {|x| all_links.index x}
|
184
264
|
|
185
265
|
active_links = @browser.xpath('//a[@href]')
|
186
266
|
valid_active_links = indices.map {|n| active_links[n]}
|
187
|
-
|
188
267
|
|
189
|
-
|
268
|
+
|
269
|
+
@links = valid_active_links.flat_map.with_index do |x, i|
|
190
270
|
|
191
271
|
a = valid_links[i].text.split(/\W+/).map {|label| [label, x]}
|
192
272
|
a << [valid_links[i].text, x]
|
193
|
-
|
273
|
+
|
194
274
|
puts 'a: ' + a.inspect if @debug
|
195
275
|
a + a.map {|x2, obj| [x2.downcase, obj]}
|
196
|
-
|
276
|
+
|
197
277
|
end.to_h
|
198
|
-
|
278
|
+
|
199
279
|
names = @links.keys.map(&:downcase).uniq.select {|x| x =~ /^[\w ]+$/}
|
200
280
|
links = @links
|
201
|
-
|
281
|
+
|
202
282
|
names.each do |name|
|
203
|
-
|
283
|
+
|
204
284
|
define_singleton_method name.gsub(/ +/,'_').to_sym do
|
205
|
-
|
285
|
+
|
206
286
|
links[name].click
|
207
287
|
@browser.network.wait_for_idle
|
208
|
-
|
288
|
+
|
209
289
|
sleep 1
|
210
290
|
scan_page()
|
211
291
|
self
|
212
|
-
|
292
|
+
|
213
293
|
end
|
214
|
-
|
294
|
+
|
215
295
|
end
|
216
|
-
|
296
|
+
|
217
297
|
end
|
218
298
|
|
219
299
|
def scan_form_elements()
|
220
|
-
|
300
|
+
|
221
301
|
# find radio buttons
|
222
|
-
|
223
|
-
#a = doc.root.xpath('//input[@type="radio"]')
|
302
|
+
|
224
303
|
a = @browser.xpath('//input[@type="radio"]')
|
225
|
-
#h = a.group_by {|x| x.attributes[:name]}
|
226
304
|
h = a.group_by {|x| x.attribute('name')}
|
227
305
|
@radio = h.values
|
228
306
|
define_singleton_method(:on) { @radio[0][0].click; self }
|
229
307
|
define_singleton_method(:off) { @radio[0][1].click; self }
|
230
|
-
|
308
|
+
|
231
309
|
fetch_buttons()
|
232
|
-
|
310
|
+
|
233
311
|
end
|
234
|
-
|
312
|
+
|
235
313
|
def scan_js_links()
|
236
|
-
|
314
|
+
|
237
315
|
@js_methods = {}
|
238
316
|
b = @browser
|
239
|
-
|
317
|
+
|
240
318
|
b.xpath('//a').select {|x| x.attribute('href') =~ /^javascript/}.each do |e|
|
241
|
-
|
319
|
+
|
242
320
|
|
243
321
|
s = e.attribute('href')[/(?<=^javascript:)[^\(]+/]
|
244
322
|
puts 's: ' + s.inspect if @debug
|
@@ -248,17 +326,17 @@ class FerrumWizard
|
|
248
326
|
a << [s.split(/\W+|(?=[A-Z])/).join('_').downcase, s]
|
249
327
|
#@js_methods[s] = a
|
250
328
|
|
251
|
-
a.concat a.map {|x, name| [x.downcase, name] }
|
329
|
+
a.concat a.map {|x, name| [x.downcase, name] }
|
252
330
|
|
253
331
|
puts 'a: ' + a.inspect if @debug
|
254
332
|
|
255
333
|
a.uniq.select {|x, _| x =~ /^[a-z0-9_]+$/}.each do |x, name|
|
256
|
-
|
334
|
+
|
257
335
|
if @debug then
|
258
336
|
puts 'x: ' + x.inspect
|
259
337
|
puts 'name: ' + name.inspect
|
260
338
|
end
|
261
|
-
|
339
|
+
|
262
340
|
define_singleton_method(x.to_sym) do |*args|
|
263
341
|
#args = raw_args.map {|x| x[/^[0-9]+$/] ? x.to_i : x}
|
264
342
|
js_method = "%s(%s)" % [name, args.map(&:inspect).join(', ')]
|
@@ -267,9 +345,9 @@ class FerrumWizard
|
|
267
345
|
sleep 4
|
268
346
|
self.scan_page()
|
269
347
|
end
|
270
|
-
|
348
|
+
|
271
349
|
end
|
272
|
-
|
350
|
+
|
273
351
|
end
|
274
352
|
end
|
275
353
|
|
@@ -278,7 +356,7 @@ class FerrumWizard
|
|
278
356
|
puts 'method_missing: ' + method_name.inspect if @debug
|
279
357
|
node = @browser.at_css '.' + method_name.to_s
|
280
358
|
node.text if node
|
281
|
-
|
282
|
-
end
|
283
|
-
|
359
|
+
|
360
|
+
end
|
361
|
+
|
284
362
|
end
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ferrumwizard
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Robertson
|
@@ -11,31 +11,31 @@ cert_chain:
|
|
11
11
|
- |
|
12
12
|
-----BEGIN CERTIFICATE-----
|
13
13
|
MIIEXjCCAsagAwIBAgIBATANBgkqhkiG9w0BAQsFADAsMSowKAYDVQQDDCFnZW1t
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
/
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
/
|
24
|
-
|
25
|
-
|
26
|
-
|
14
|
+
YXN0ZXIvREM9amFtZXNyb2JlcnRzb24vREM9ZXUwHhcNMjIwMzMwMTI1ODU1WhcN
|
15
|
+
MjMwMzMwMTI1ODU1WjAsMSowKAYDVQQDDCFnZW1tYXN0ZXIvREM9amFtZXNyb2Jl
|
16
|
+
cnRzb24vREM9ZXUwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDROL32
|
17
|
+
3LQKGcDR6x6XFa1US/Vq98DVnMeZHCSKdf471I4gJIOA7sQnrQTB6IZKTxb94Wjr
|
18
|
+
OSeGzlJpVq6pa7ltxvb9T7YQVVRrXYMC+u0gD9ukolnkpV/4Rh2/IIMxSNKncoZB
|
19
|
+
LKPseizGKlli4gs134gAu3wuWdCC7/UWPG/XyocdJC8tLtf/zi4JuRJTojKqYLOp
|
20
|
+
KsP9jHPmGVr81cW8HePmhQ/+LiYlKDE4Fwj4yl16XqhF7/5YOz9e5LOHsMUEord4
|
21
|
+
JscQ3GnhMfEXGJpwqCwNEpM3xAwcHp2DDdrwtT36ujSfnTJ3UpUIQUKVehA2i9rm
|
22
|
+
uDcDTr1PATGcOMPpExvLZu3a9uC81mj9z+axH5mWQ7jZ92sze79oAQTsMiMyBavJ
|
23
|
+
djSpnVBo71PFk8QekgIVVBIzG0iN5zoNUrSthvL/xUWXM6ea015HEDCCIEL417ID
|
24
|
+
humVWZyzKf7ITCdZWcxTgTgFfuPMctcICT5u7va+FrycYpdtt8kXvtD3VnkCAwEA
|
25
|
+
AaOBijCBhzAJBgNVHRMEAjAAMAsGA1UdDwQEAwIEsDAdBgNVHQ4EFgQUDipvmNU2
|
26
|
+
WydgAK8QPGb0vhhoGl0wJgYDVR0RBB8wHYEbZ2VtbWFzdGVyQGphbWVzcm9iZXJ0
|
27
27
|
c29uLmV1MCYGA1UdEgQfMB2BG2dlbW1hc3RlckBqYW1lc3JvYmVydHNvbi5ldTAN
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
28
|
+
BgkqhkiG9w0BAQsFAAOCAYEAHqRvm6iqjJ+bpEzSSgVmOMOMcgIoN6px1LMVAOmY
|
29
|
+
BJpF5F0fJr99thc1EYZJoRTwEcXJYhCTqKg+3xhNKpCzk2qHsaLKYEygPeBpyJOg
|
30
|
+
LyfHLrj98QLPYyFzqhWsqZAAAGC9WSF/kBJazpuotU2ec/Xw/e3NPopedV/Zvuhs
|
31
|
+
+/OKZWwRez/hg97ckaCYAp/7OdrVhJvR87MaQnN52Uk8OQbuPSyUNQUJ044HWHtu
|
32
|
+
lEJjsDetEFhNB69j3wAIMjMEZao29/dZhALbUDp+9ewK7uYbrX9Bo68NX+H+XcCZ
|
33
|
+
VFrdrjkyJUOHwSmvjYXN1V0Yz8kVVFU7E+Q4RHL8yAwBv+ynd927HtZVjs+455Pc
|
34
|
+
z+9gNpBQVr6LLXLJgJF2pTaIoYhgG6pcoMQHGVoxWdKzvOcl0h1epeJSp/aynX/r
|
35
|
+
FK+cyrQNA9DLJYJuz6uO7Z+gXZWjwAO38LUwF01w49asSv/5ZH2HH/EauX5xWpe+
|
36
|
+
ry6lYQlb8j50Iys5elAy1p0i
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date:
|
38
|
+
date: 2022-03-30 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: rexle
|
@@ -46,7 +46,7 @@ dependencies:
|
|
46
46
|
version: '1.5'
|
47
47
|
- - ">="
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: 1.5.
|
49
|
+
version: 1.5.14
|
50
50
|
type: :runtime
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -56,29 +56,29 @@ dependencies:
|
|
56
56
|
version: '1.5'
|
57
57
|
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: 1.5.
|
59
|
+
version: 1.5.14
|
60
60
|
- !ruby/object:Gem::Dependency
|
61
61
|
name: ferrum
|
62
62
|
requirement: !ruby/object:Gem::Requirement
|
63
63
|
requirements:
|
64
|
-
- - ">="
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
version: 0.9.0
|
67
64
|
- - "~>"
|
68
65
|
- !ruby/object:Gem::Version
|
69
|
-
version: '0.
|
66
|
+
version: '0.11'
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0.11'
|
70
70
|
type: :runtime
|
71
71
|
prerelease: false
|
72
72
|
version_requirements: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: 0.9.0
|
77
74
|
- - "~>"
|
78
75
|
- !ruby/object:Gem::Version
|
79
|
-
version: '0.
|
76
|
+
version: '0.11'
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0.11'
|
80
80
|
description:
|
81
|
-
email:
|
81
|
+
email: digital.robertson@gmail.com
|
82
82
|
executables: []
|
83
83
|
extensions: []
|
84
84
|
extra_rdoc_files: []
|
@@ -103,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
103
|
- !ruby/object:Gem::Version
|
104
104
|
version: '0'
|
105
105
|
requirements: []
|
106
|
-
rubygems_version: 3.
|
106
|
+
rubygems_version: 3.2.22
|
107
107
|
signing_key:
|
108
108
|
specification_version: 4
|
109
109
|
summary: Makes web scraping easier using the Ferrum gem.
|
metadata.gz.sig
CHANGED
Binary file
|