pincers 0.1.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/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:
|