pincers 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/pincers.rb +20 -0
- data/lib/pincers/backend/base.rb +87 -0
- data/lib/pincers/backend/nokogiri.rb +29 -0
- data/lib/pincers/backend/webdriver.rb +108 -0
- data/lib/pincers/core/root_context.rb +72 -0
- data/lib/pincers/core/search_context.rb +184 -0
- data/lib/pincers/errors.rb +60 -0
- data/lib/pincers/factory.rb +33 -0
- data/lib/pincers/support/configuration.rb +35 -0
- data/lib/pincers/support/cookie_jar.rb +16 -0
- data/lib/pincers/version.rb +3 -0
- metadata +224 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9be2262d6757498e82ec195e8e8e5109226df675
|
4
|
+
data.tar.gz: 73e0e4943ec46e3dcbd35a043dda468bbb1e75f2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a7fcc330eb821333f73e1d9f4989084e5d7a0d161703542725ab606a244ac10305f69430bb3522e1c7cc5d8a1746a8bb974f4d9585170aa27b59822980c9afd5
|
7
|
+
data.tar.gz: 917e6139161b82a3a817b9fb6deb3d31295e60658e138d9004efcb8712f27f41e691c963fedc200c8de22d12c4d398ef5a0e4f2f1176c9523a68b0605b21ab53
|
data/lib/pincers.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "pincers/version"
|
3
|
+
require "pincers/errors"
|
4
|
+
require "pincers/factory"
|
5
|
+
require "pincers/support/configuration"
|
6
|
+
|
7
|
+
module Pincers
|
8
|
+
extend Factory
|
9
|
+
|
10
|
+
@@config = Support::Configuration.new
|
11
|
+
|
12
|
+
def self.config
|
13
|
+
@@config
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.configure
|
17
|
+
yield @@config
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Pincers::Backend
|
2
|
+
|
3
|
+
class Base
|
4
|
+
|
5
|
+
attr_reader :document
|
6
|
+
|
7
|
+
def initialize(_document)
|
8
|
+
@document = _document
|
9
|
+
end
|
10
|
+
|
11
|
+
def document_root
|
12
|
+
ensure_implementation :document_root
|
13
|
+
end
|
14
|
+
|
15
|
+
def document_url
|
16
|
+
ensure_implementation :document_url
|
17
|
+
end
|
18
|
+
|
19
|
+
def document_title
|
20
|
+
ensure_implementation :document_title
|
21
|
+
end
|
22
|
+
|
23
|
+
def document_source
|
24
|
+
ensure_implementation :document_source
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch_cookies
|
28
|
+
ensure_implementation :fetch_cookies
|
29
|
+
end
|
30
|
+
|
31
|
+
def navigate_to(_url)
|
32
|
+
ensure_implementation :navigate_to
|
33
|
+
end
|
34
|
+
|
35
|
+
def navigate_forward(_steps)
|
36
|
+
ensure_implementation :navigate_forward
|
37
|
+
end
|
38
|
+
|
39
|
+
def navigate_back(_steps)
|
40
|
+
ensure_implementation :navigate_back
|
41
|
+
end
|
42
|
+
|
43
|
+
def refresh_document
|
44
|
+
ensure_implementation :refresh_document
|
45
|
+
end
|
46
|
+
|
47
|
+
def search_by_css(_element, _selector)
|
48
|
+
ensure_implementation :search_by_css
|
49
|
+
end
|
50
|
+
|
51
|
+
def search_by_xpath(_element, _selector)
|
52
|
+
ensure_implementation :search_by_xpath
|
53
|
+
end
|
54
|
+
|
55
|
+
def extract_element_text(_element)
|
56
|
+
ensure_implementation :extract_element_text
|
57
|
+
end
|
58
|
+
|
59
|
+
def extract_element_html(_element)
|
60
|
+
ensure_implementation :extract_element_html
|
61
|
+
end
|
62
|
+
|
63
|
+
def extract_element_attribute(_element, _name)
|
64
|
+
ensure_implementation :extract_element_attribute
|
65
|
+
end
|
66
|
+
|
67
|
+
def clear_input(_element)
|
68
|
+
ensure_implementation :clear_input
|
69
|
+
end
|
70
|
+
|
71
|
+
def fill_input(_element, _value)
|
72
|
+
ensure_implementation :fill_input
|
73
|
+
end
|
74
|
+
|
75
|
+
def load_frame_element(_element)
|
76
|
+
ensure_implementation :load_frame_element
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def ensure_implementation(_name)
|
82
|
+
raise MissingFeatureError.new _name
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'pincers/backend/base'
|
2
|
+
|
3
|
+
module Pincers::Backend
|
4
|
+
|
5
|
+
class Nokogiri < Base
|
6
|
+
|
7
|
+
def search_by_css(_element, _selector)
|
8
|
+
_element.css _selector
|
9
|
+
end
|
10
|
+
|
11
|
+
def search_by_xpath(_element, _selector)
|
12
|
+
_element.xpath _selector
|
13
|
+
end
|
14
|
+
|
15
|
+
def extract_element_text(_element)
|
16
|
+
# _element.text
|
17
|
+
end
|
18
|
+
|
19
|
+
def extract_element_html(_element)
|
20
|
+
# _element['outerHTML']
|
21
|
+
end
|
22
|
+
|
23
|
+
def extract_element_attribute(_element, _name)
|
24
|
+
# _element[_name]
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'pincers/backend/base'
|
2
|
+
|
3
|
+
module Pincers::Backend
|
4
|
+
|
5
|
+
class Webdriver < Base
|
6
|
+
|
7
|
+
attr_reader :driver
|
8
|
+
|
9
|
+
def initialize(_driver)
|
10
|
+
super _driver
|
11
|
+
@driver = _driver
|
12
|
+
end
|
13
|
+
|
14
|
+
def document_root
|
15
|
+
[@driver]
|
16
|
+
end
|
17
|
+
|
18
|
+
def document_url
|
19
|
+
@driver.current_url
|
20
|
+
end
|
21
|
+
|
22
|
+
def document_title
|
23
|
+
@driver.title
|
24
|
+
end
|
25
|
+
|
26
|
+
def document_source
|
27
|
+
@driver.page_source
|
28
|
+
end
|
29
|
+
|
30
|
+
def fetch_cookies
|
31
|
+
@driver.manage.all_cookies
|
32
|
+
end
|
33
|
+
|
34
|
+
def navigate_to(_url)
|
35
|
+
@driver.get _url
|
36
|
+
end
|
37
|
+
|
38
|
+
def navigate_forward(_steps)
|
39
|
+
_steps.times { @driver.navigate.forward }
|
40
|
+
end
|
41
|
+
|
42
|
+
def navigate_back(_steps)
|
43
|
+
_steps.times { @driver.navigate.back }
|
44
|
+
end
|
45
|
+
|
46
|
+
def refresh_document
|
47
|
+
@driver.navigate.refresh
|
48
|
+
end
|
49
|
+
|
50
|
+
def search_by_css(_element, _selector)
|
51
|
+
_element.find_elements css: _selector
|
52
|
+
end
|
53
|
+
|
54
|
+
def search_by_xpath(_element, _selector)
|
55
|
+
_element.find_elements xpath: _selector
|
56
|
+
end
|
57
|
+
|
58
|
+
def extract_element_text(_element)
|
59
|
+
|
60
|
+
_element.text
|
61
|
+
end
|
62
|
+
|
63
|
+
def extract_element_html(_element)
|
64
|
+
if _element == @driver then @driver.page_source else _element.attribute('outerHTML') end
|
65
|
+
end
|
66
|
+
|
67
|
+
def extract_element_attribute(_element, _name)
|
68
|
+
_element[_name]
|
69
|
+
end
|
70
|
+
|
71
|
+
def clear_input(_element)
|
72
|
+
_element.clear
|
73
|
+
end
|
74
|
+
|
75
|
+
def fill_input(_element, _value)
|
76
|
+
_element.send_keys _value
|
77
|
+
end
|
78
|
+
|
79
|
+
def load_frame_element(_element)
|
80
|
+
driver.switch_to.frame _element
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
# wait contitions
|
85
|
+
|
86
|
+
def check_present(_elements)
|
87
|
+
_elements.length > 0
|
88
|
+
end
|
89
|
+
|
90
|
+
def check_not_present(_elements)
|
91
|
+
_elements.length == 0
|
92
|
+
end
|
93
|
+
|
94
|
+
def check_visible(_elements)
|
95
|
+
check_present(_elements) and _elements.first.displayed?
|
96
|
+
end
|
97
|
+
|
98
|
+
def check_enabled(_elements)
|
99
|
+
check_visible(_elements) and _elements.first.enabled?
|
100
|
+
end
|
101
|
+
|
102
|
+
def check_not_visible(_elements)
|
103
|
+
not _elements.any? { |e| e.displayed? }
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'pincers/support/cookie_jar'
|
2
|
+
require 'pincers/core/search_context'
|
3
|
+
|
4
|
+
module Pincers::Core
|
5
|
+
class RootContext < SearchContext
|
6
|
+
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def initialize(_backend, _config={})
|
10
|
+
super _backend.document_root, nil
|
11
|
+
@backend = _backend
|
12
|
+
@config = Pincers.config.values.merge _config
|
13
|
+
end
|
14
|
+
|
15
|
+
def root
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def backend
|
20
|
+
@backend
|
21
|
+
end
|
22
|
+
|
23
|
+
def url
|
24
|
+
wrap_errors { backend.document_url }
|
25
|
+
end
|
26
|
+
|
27
|
+
def uri
|
28
|
+
URI.parse url
|
29
|
+
end
|
30
|
+
|
31
|
+
def title
|
32
|
+
wrap_errors { backend.document_title }
|
33
|
+
end
|
34
|
+
|
35
|
+
def source
|
36
|
+
wrap_errors { backend.document_source }
|
37
|
+
end
|
38
|
+
|
39
|
+
def cookies
|
40
|
+
@cookies ||= CookieJar.new backend
|
41
|
+
end
|
42
|
+
|
43
|
+
def goto(_url)
|
44
|
+
wrap_errors { backend.navigate_to _url }
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def forward(_steps=1)
|
49
|
+
wrap_errors { backend.navigate_forward _steps }
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def back(_steps=1)
|
54
|
+
wrap_errors { backend.navigate_back _steps }
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def refresh
|
59
|
+
wrap_errors { backend.refresh_document _steps }
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def default_timeout
|
64
|
+
@config[:wait_timeout]
|
65
|
+
end
|
66
|
+
|
67
|
+
def default_interval
|
68
|
+
@config[:wait_interval]
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
module Pincers::Core
|
2
|
+
class SearchContext
|
3
|
+
include Enumerable
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
attr_accessor :parent, :elements
|
7
|
+
|
8
|
+
def_delegators :elements, :length, :count, :empty?
|
9
|
+
|
10
|
+
def initialize(_elements, _parent)
|
11
|
+
@elements = _elements
|
12
|
+
@parent = _parent
|
13
|
+
end
|
14
|
+
|
15
|
+
def root
|
16
|
+
parent.root
|
17
|
+
end
|
18
|
+
|
19
|
+
def backend
|
20
|
+
root.backend
|
21
|
+
end
|
22
|
+
|
23
|
+
def document
|
24
|
+
backend.document
|
25
|
+
end
|
26
|
+
|
27
|
+
def element
|
28
|
+
elements.first
|
29
|
+
end
|
30
|
+
|
31
|
+
def element!
|
32
|
+
raise Pincers::EmptySetError.new self if empty?
|
33
|
+
element
|
34
|
+
end
|
35
|
+
|
36
|
+
def each
|
37
|
+
elements.each { |el| yield wrap_elements [el] }
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](*args)
|
41
|
+
if args[0].is_a? String or args[0].is_a? Symbol
|
42
|
+
wrap_errors do
|
43
|
+
backend.extract_element_attribute element!, args[0]
|
44
|
+
end
|
45
|
+
else
|
46
|
+
wrap_elements Array(elements.send(:[],*args))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def first
|
51
|
+
if elements.first.nil? then nil else wrap_elements [elements.first] end
|
52
|
+
end
|
53
|
+
|
54
|
+
def last
|
55
|
+
if elements.last.nil? then nil else wrap_elements [elements.last] end
|
56
|
+
end
|
57
|
+
|
58
|
+
def css(_selector, _options={})
|
59
|
+
search_with_options _options do
|
60
|
+
explode_elements { |e| backend.search_by_css e, _selector }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def xpath(_selector, _options={})
|
65
|
+
search_with_options _options do
|
66
|
+
explode_elements { |e| backend.search_by_xpath e, _selector }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def text
|
71
|
+
wrap_errors do
|
72
|
+
backend.extract_element_text element!
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def classes
|
77
|
+
wrap_errors do
|
78
|
+
class_attr = backend.extract_element_attribute element!, 'class'
|
79
|
+
(class_attr || '').split(' ')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_html
|
84
|
+
wrap_errors do
|
85
|
+
elements.map { |e| backend.extract_element_html e }.join
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Input related
|
90
|
+
|
91
|
+
def set(_value)
|
92
|
+
wrap_errors do
|
93
|
+
backend.clear_input element!
|
94
|
+
backend.fill_input element!
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def select(_value)
|
99
|
+
# TODO.
|
100
|
+
end
|
101
|
+
|
102
|
+
# context related
|
103
|
+
|
104
|
+
def enter
|
105
|
+
wrap_errors do
|
106
|
+
RootContext.new backend.load_frame_element(element!), root.config
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Any methods missing are forwarded to the main element (first)
|
111
|
+
|
112
|
+
def method_missing(_method, *_args, &_block)
|
113
|
+
wrap_errors do
|
114
|
+
m = /^(.*)_all$/.match _method.to_s
|
115
|
+
if m then
|
116
|
+
return [] if empty?
|
117
|
+
elements.map { |e| e.send(m[1], *_args, &_block) }
|
118
|
+
else
|
119
|
+
element!.send(_method, *_args, &_block)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def respond_to?(_method, _include_all=false)
|
125
|
+
return true if super
|
126
|
+
m = /^.*_all$/.match _method.to_s
|
127
|
+
if m then
|
128
|
+
return true if empty?
|
129
|
+
elements.first.respond_to? m[1], _include_all
|
130
|
+
else
|
131
|
+
return true if empty?
|
132
|
+
elements.first.respond_to? _method, _include_all
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def wrap_errors
|
139
|
+
begin
|
140
|
+
yield
|
141
|
+
rescue Pincers::Error
|
142
|
+
raise
|
143
|
+
rescue Exception => exc
|
144
|
+
raise Pincers::BackendError.new self, exc
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def wrap_elements(_elements)
|
149
|
+
SearchContext.new _elements, self
|
150
|
+
end
|
151
|
+
|
152
|
+
def search_with_options(_options, &_block)
|
153
|
+
wrap_errors do
|
154
|
+
wait_for = _options.delete(:wait)
|
155
|
+
return wrap_elements _block.call unless wait_for
|
156
|
+
wrap_elements poll_until(wait_for, _options, &_block)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def explode_elements
|
161
|
+
elements.inject([]) do |r, element|
|
162
|
+
r + yield(element)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def poll_until(_condition, _options, &_search)
|
167
|
+
check_method = "check_#{_condition}"
|
168
|
+
raise Pincers::MissingFeatureError.new check_method unless backend.respond_to? check_method
|
169
|
+
|
170
|
+
timeout = _options.fetch :timeout, root.default_timeout
|
171
|
+
interval = _options.fetch :interval, root.default_interval
|
172
|
+
end_time = Time.now + timeout
|
173
|
+
|
174
|
+
until Time.now > end_time
|
175
|
+
new_elements = _search.call
|
176
|
+
return new_elements if backend.send check_method, new_elements
|
177
|
+
sleep interval
|
178
|
+
end
|
179
|
+
|
180
|
+
raise Pincers::ConditionTimeoutError.new self, _condition
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Pincers
|
2
|
+
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
class ConfigurationError < Error; end
|
6
|
+
|
7
|
+
class MissingFeatureError < Error
|
8
|
+
|
9
|
+
attr_reader :feature
|
10
|
+
|
11
|
+
def initialize(_feature)
|
12
|
+
@feature = _feature
|
13
|
+
super "This backend does not provide '#{_feature}'"
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
class ContextError < Error
|
19
|
+
|
20
|
+
attr_reader :context
|
21
|
+
|
22
|
+
def initialize(_context, _msg)
|
23
|
+
super _msg
|
24
|
+
@context = _context
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
class EmptySetError < ContextError
|
30
|
+
|
31
|
+
def initialize(_context)
|
32
|
+
super _context, "This set is empty"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class ConditionTimeoutError < ContextError
|
38
|
+
|
39
|
+
def initialize(_context, _condition)
|
40
|
+
super _context, "Timed out waiting element to be #{_condition}"
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class BackendError < ContextError
|
46
|
+
|
47
|
+
attr_reader :document
|
48
|
+
attr_reader :original
|
49
|
+
|
50
|
+
def initialize(_context, _exc)
|
51
|
+
super _context, "Backend error: #{_exc.message}"
|
52
|
+
@document = _context.document
|
53
|
+
@original = _exc
|
54
|
+
end
|
55
|
+
|
56
|
+
# IDEA: join backtraces?
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'pincers/core/root_context'
|
2
|
+
|
3
|
+
module Pincers
|
4
|
+
module Factory
|
5
|
+
|
6
|
+
def for_webdriver(_driver, _options={})
|
7
|
+
require 'pincers/backend/webdriver'
|
8
|
+
|
9
|
+
unless _driver.is_a? Selenium::WebDriver::Driver
|
10
|
+
_driver = Selenium::WebDriver::Driver.for _driver, _options
|
11
|
+
end
|
12
|
+
|
13
|
+
context Backend::Webdriver.new _driver
|
14
|
+
end
|
15
|
+
|
16
|
+
def for_nokogiri(_document, _options={})
|
17
|
+
require 'pincers/backend/nokogiri'
|
18
|
+
|
19
|
+
unless _document.is_a? ::Nokogiri::HTML::Document
|
20
|
+
_document = ::Nokogiri::HTML _document, _options[:url], _options[:encoding], _options[:flags]
|
21
|
+
end
|
22
|
+
|
23
|
+
context Backend::Nokogiri.new _document
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def context(_backend)
|
29
|
+
Core::RootContext.new _backend
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Pincers::Support
|
2
|
+
class Configuration
|
3
|
+
|
4
|
+
class Option < Struct.new(:name, :type, :text); end
|
5
|
+
|
6
|
+
FIELDS = [
|
7
|
+
[:wait_timeout, 10.0],
|
8
|
+
[:wait_interval, 0.2]
|
9
|
+
];
|
10
|
+
|
11
|
+
FIELDS.each do |field|
|
12
|
+
define_method "#{field[0]}=" do |val|
|
13
|
+
@values[field[0]] = val
|
14
|
+
end
|
15
|
+
|
16
|
+
define_method "#{field[0]}" do
|
17
|
+
@values[field[0]]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :values
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
reset
|
25
|
+
end
|
26
|
+
|
27
|
+
def set(_options)
|
28
|
+
@values.merge! _options
|
29
|
+
end
|
30
|
+
|
31
|
+
def reset
|
32
|
+
@values = Hash[FIELDS]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pincers
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ignacio Baixas
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: selenium-webdriver
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.45'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.45'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: nokogiri
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.6.6
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.6.6
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.6'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.6'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-nc
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: guard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: guard-rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: terminal-notifier-guard
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.6.1
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.6.1
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: pry-remote
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: pry-nav
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description:
|
182
|
+
email:
|
183
|
+
- ignacio@platan.us
|
184
|
+
executables: []
|
185
|
+
extensions: []
|
186
|
+
extra_rdoc_files: []
|
187
|
+
files:
|
188
|
+
- lib/pincers/backend/base.rb
|
189
|
+
- lib/pincers/backend/nokogiri.rb
|
190
|
+
- lib/pincers/backend/webdriver.rb
|
191
|
+
- lib/pincers/core/root_context.rb
|
192
|
+
- lib/pincers/core/search_context.rb
|
193
|
+
- lib/pincers/errors.rb
|
194
|
+
- lib/pincers/factory.rb
|
195
|
+
- lib/pincers/support/configuration.rb
|
196
|
+
- lib/pincers/support/cookie_jar.rb
|
197
|
+
- lib/pincers/version.rb
|
198
|
+
- lib/pincers.rb
|
199
|
+
homepage: https://github.com/platanus/pincers
|
200
|
+
licenses:
|
201
|
+
- MIT
|
202
|
+
metadata: {}
|
203
|
+
post_install_message:
|
204
|
+
rdoc_options: []
|
205
|
+
require_paths:
|
206
|
+
- lib
|
207
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
208
|
+
requirements:
|
209
|
+
- - '>='
|
210
|
+
- !ruby/object:Gem::Version
|
211
|
+
version: '0'
|
212
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
213
|
+
requirements:
|
214
|
+
- - '>='
|
215
|
+
- !ruby/object:Gem::Version
|
216
|
+
version: '0'
|
217
|
+
requirements: []
|
218
|
+
rubyforge_project:
|
219
|
+
rubygems_version: 2.0.14
|
220
|
+
signing_key:
|
221
|
+
specification_version: 4
|
222
|
+
summary: Web automation framework with multiple backend support
|
223
|
+
test_files: []
|
224
|
+
has_rdoc:
|