pincers 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 51cc155a36f380781aa91b00a53f5996aae5b9b1
4
- data.tar.gz: b7ef59a62aa573b6be45914692fc603ef91195c0
3
+ metadata.gz: b243194fe1a23f2a16a34c0e7007de2d9f4eecfc
4
+ data.tar.gz: b442f0a02e79c6fc600d1b50cff6187ffc0bc3d4
5
5
  SHA512:
6
- metadata.gz: 78e3f46f2d2a4bd2bcb82d6ca2c5eda70f2683f5bc769eb086204eb82e7168b7b02a6687550b47870187f2172319184d6f47d1b98a33984f7fc50860f40a0868
7
- data.tar.gz: 95d1bbf4b2cc3ccea044ecc0c8bfea63477b56d489487edf2d9f110943e5b231d955f25c47e25a0e8c661a593d696c9003642cacdceaace35ad26f1ae7943c5b
6
+ metadata.gz: 9814b3a3c6139e62e53fea8b0448173196e2fd657656b9cfb49aa7856fd12e0d312842de844284e481821d223af838abf25f547107f7c90db921e10ee5969554
7
+ data.tar.gz: d91861c7c3ab3b717acfc703f758de7ea09dd1a89d6394b5c80932c069b4710a3d17b0b16dbc8004378ed4ce1d6985a77b4d631f9b5f1055515de171738bb95c
data/lib/pincers.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require "nokogiri"
1
2
  require "forwardable"
2
3
  require "pincers/version"
3
4
  require "pincers/errors"
@@ -1,4 +1,3 @@
1
- require 'nokogiri'
2
1
  require 'pincers/backend/base'
3
2
 
4
3
  module Pincers::Backend
@@ -1,4 +1,5 @@
1
1
  module Pincers::Core
2
+
2
3
  class Download < Struct.new(:mime, :data)
3
4
 
4
5
  def self.from_http_response(_response)
@@ -0,0 +1,72 @@
1
+ require 'pincers/css/parser'
2
+ require 'pincers/support/xpath_builder'
3
+
4
+ module Pincers::Core
5
+ class Query
6
+
7
+ def self.build_from_options(_backend, _selector, _options={}, &_block)
8
+ limit = _options.delete(:limit)
9
+ lang = :xpath
10
+ exp = nil
11
+
12
+ if _selector
13
+ parser = Pincers::CSS::Parser.new _selector
14
+ if parser.is_extended?
15
+ exp = parser.to_xpath('.//') # Should we use // for root?
16
+ exp = exp.first if exp.length == 1
17
+ else
18
+ lang = :css
19
+ exp = _selector
20
+ end
21
+ elsif _options[:xpath]
22
+ exp = _options[:xpath]
23
+ else
24
+ builder = Pincers::Support::XPathBuilder.new _options
25
+ _block.call builder unless _block.nil?
26
+ exp = builder.expression
27
+ end
28
+
29
+ self.new _backend, lang, exp, limit
30
+ end
31
+
32
+ attr_reader :lang, :query, :limit
33
+
34
+ def initialize(_backend, _lang, _query, _limit)
35
+ @backend = _backend
36
+ @lang = _lang
37
+ @query = _query
38
+ @limit = _limit
39
+ end
40
+
41
+ def execute(_elements, _force_limit=nil)
42
+ fun = case @lang
43
+ when :xpath then :search_by_xpath
44
+ else :search_by_css end
45
+
46
+ query_elements _elements, fun, _force_limit || @limit
47
+ end
48
+
49
+ private
50
+
51
+ def query_elements(_elements, _fun, _limit)
52
+ elements = []
53
+ explode_elements(_elements) do |element, query|
54
+ limit = _limit.nil? ? nil : _limit - elements.count
55
+ elements += @backend.send(_fun, element, query, limit)
56
+ break if !_limit.nil? && elements.length >= _limit
57
+ end
58
+ elements
59
+ end
60
+
61
+ def explode_elements(_elements)
62
+ _elements.each do |element|
63
+ if @query.is_a? Array
64
+ @query.each { |q| yield element, q }
65
+ else
66
+ yield element, @query
67
+ end
68
+ end
69
+ end
70
+
71
+ end
72
+ end
@@ -116,7 +116,7 @@ module Pincers::Core
116
116
  when :parent
117
117
  backend.switch_to_parent_frame
118
118
  when String
119
- backend.switch_to_frame css(_frame).element!
119
+ backend.switch_to_frame search(_frame).element!
120
120
  when SearchContext
121
121
  backend.switch_to_frame _frame.element!
122
122
  else
@@ -1,7 +1,7 @@
1
1
  require 'pincers/extension/queries'
2
2
  require 'pincers/extension/actions'
3
3
  require 'pincers/extension/labs'
4
- require 'pincers/support/query'
4
+ require 'pincers/core/query'
5
5
 
6
6
  module Pincers::Core
7
7
  class SearchContext
@@ -91,18 +91,15 @@ module Pincers::Core
91
91
  if elements.last.nil? then nil else wrap_siblings [elements.last] end
92
92
  end
93
93
 
94
- def css(_selector, _options={})
95
- wrap_errors do
96
- query = Pincers::Support::Query.new backend, :css, _selector, _options[:limit]
97
- wrap_childs query
94
+ def search(_selector=nil, _options={}, &_block)
95
+ if _selector.is_a? Hash
96
+ _options = _selector
97
+ _selector = nil
98
98
  end
99
- end
100
99
 
101
- def xpath(_selector, _options={})
102
- wrap_errors do
103
- query = Pincers::Support::Query.new backend, :xpath, _selector, _options[:limit]
104
- wrap_childs query
105
- end
100
+ query = Query.build_from_options(backend, _selector, _options, &_block)
101
+
102
+ wrap_errors { wrap_childs query }
106
103
  end
107
104
 
108
105
  def attribute(_name, _value=nil)
@@ -0,0 +1,29 @@
1
+ require 'pincers/css/xpath_visitor'
2
+
3
+ module Pincers::CSS
4
+
5
+ class Parser
6
+
7
+ IS_EXTENDED_RGX = /:(contains|has|first|last|even|odd|eq|gt|lt|button|checkbox|file|image|password|radio|reset|submit|text|selected|checked)([^\w\-]|$)/
8
+
9
+ attr_reader :selector
10
+
11
+ def is_extended?
12
+ IS_EXTENDED_RGX === selector
13
+ end
14
+
15
+ def initialize(_selector)
16
+ @selector = _selector
17
+ end
18
+
19
+ def to_xpath(_prefix='//')
20
+ # use nokogiri parser and our custom visitor
21
+ ::Nokogiri::CSS.xpath_for @selector, {
22
+ prefix: _prefix,
23
+ visitor: XPathVisitor.new
24
+ }
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,81 @@
1
+ module Pincers::CSS
2
+
3
+ class XPathVisitor < ::Nokogiri::CSS::XPathVisitor
4
+
5
+ # jQuery extended functions and classes
6
+
7
+ def visit_function_contains(_node) # override nokofiri impl to search in value attribute too
8
+ "(contains(., #{_node.value[1]}) or contains(@value, #{_node.value[1]}))"
9
+ end
10
+
11
+ def visit_function_has(_node)
12
+ _node.value[1].accept(self)
13
+ end
14
+
15
+ def visit_function_eq(_node) # override nokogiri impl to make it zero-based
16
+ "(position()-1)=#{_node.value[1]}"
17
+ end
18
+
19
+ def visit_function_gt(_node) # override nokogiri impl to make it zero-based
20
+ # "((#{_node.value[1]} >= 0 and position() > #{_node.value[1]}) or (#{_node.value[1]} < 0 and position() < #{_node.value[1]}))"
21
+ "(position()-1)>#{_node.value[1]}"
22
+ end
23
+
24
+ def visit_function_lt(_node)
25
+ "(position()-1)<#{_node.value[1]}"
26
+ end
27
+
28
+ def visit_pseudo_class_input(_node)
29
+ "((name()='input' and not(@type='hidden')) or name()='textarea' or name()='select' or name()='button')"
30
+ end
31
+
32
+ def visit_pseudo_class_button(_node)
33
+ "(name()='button' or (name()='input' and @type='button'))"
34
+ end
35
+
36
+ def visit_pseudo_class_checkbox(_node)
37
+ "@type='checkbox'"
38
+ end
39
+
40
+ def visit_pseudo_class_file(_node)
41
+ "@type='file'"
42
+ end
43
+
44
+ def visit_pseudo_class_image(_node)
45
+ "@type='image'"
46
+ end
47
+
48
+ def visit_pseudo_class_password(_node)
49
+ "@type='password'"
50
+ end
51
+
52
+ def visit_pseudo_class_radio(_node)
53
+ "@type='radio'"
54
+ end
55
+
56
+ def visit_pseudo_class_reset(_node)
57
+ "@type='reset'"
58
+ end
59
+
60
+ def visit_pseudo_class_text(_node)
61
+ "@type='text'"
62
+ end
63
+
64
+ def visit_pseudo_class_selected(_node)
65
+ "@selected"
66
+ end
67
+
68
+ def visit_pseudo_class_checked(_node)
69
+ "@checked"
70
+ end
71
+
72
+ def visit_pseudo_class_odd(_node)
73
+ "position() mod 2 = 1"
74
+ end
75
+
76
+ def visit_pseudo_class_even(_node)
77
+ "position() mod 2 = 0"
78
+ end
79
+
80
+ end
81
+ end
@@ -49,11 +49,11 @@ module Pincers::Extension
49
49
  end
50
50
 
51
51
  def find_option_by_label(_label, _options)
52
- css("option", _options).find { |o| o.text == _label }
52
+ search(_options.merge(xpath: ".//option[contains(.,'#{_label}')]")).first
53
53
  end
54
54
 
55
55
  def find_option_by_value(_value, _options)
56
- css("option", _options).find { |o| o[:value] == _value.to_s }
56
+ search(_options.merge(xpath: ".//option[@value='#{_value}']")).first
57
57
  end
58
58
 
59
59
  end
@@ -5,7 +5,7 @@ module Pincers::Extension
5
5
  nk_root = Pincers.for_nokogiri to_html
6
6
 
7
7
  unless root?
8
- nk_root = nk_root.css('body > *') # nokogiri will inject valid html structure around contents
8
+ nk_root = nk_root.search('body > *') # nokogiri will inject valid html structure around contents
9
9
  end
10
10
 
11
11
  if _block.nil?
@@ -1,21 +1,8 @@
1
- require 'pincers/support/xpath_builder'
2
-
3
1
  module Pincers::Extension
4
2
  module Queries
5
3
 
6
4
  TEXT_INPUTS = ['text', 'email', 'number', 'email', 'color', 'password', 'search', 'tel', 'url']
7
5
 
8
- def search(_options={}, &_block)
9
- query_options = {
10
- limit: _options.delete(:limit)
11
- }
12
-
13
- builder = Pincers::Support::XPathBuilder.new _options
14
- _block.call builder unless _block.nil?
15
-
16
- xpath builder.expression, query_options
17
- end
18
-
19
6
  def id
20
7
  self[:id]
21
8
  end
@@ -40,11 +27,11 @@ module Pincers::Extension
40
27
  end
41
28
 
42
29
  def selected(_options={})
43
- first!.css('option', _options).select { |opt| opt.selected? }
30
+ first!.search('option', _options).select(&:selected?)
44
31
  end
45
32
 
46
33
  def checked(_options={})
47
- first!.css('input', _options).select { |opt| opt.checked? }
34
+ first!.search('input', _options).select(&:checked?)
48
35
  end
49
36
 
50
37
  def input_mode
@@ -1,3 +1,3 @@
1
1
  module Pincers
2
- VERSION = "0.5.2"
2
+ VERSION = "0.6.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pincers
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ignacio Baixas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-04 00:00:00.000000000 Z
11
+ date: 2015-09-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -219,8 +219,11 @@ files:
219
219
  - lib/pincers/backend/webdriver.rb
220
220
  - lib/pincers/core/cookies.rb
221
221
  - lib/pincers/core/download.rb
222
+ - lib/pincers/core/query.rb
222
223
  - lib/pincers/core/root_context.rb
223
224
  - lib/pincers/core/search_context.rb
225
+ - lib/pincers/css/parser.rb
226
+ - lib/pincers/css/xpath_visitor.rb
224
227
  - lib/pincers/errors.rb
225
228
  - lib/pincers/extension/actions.rb
226
229
  - lib/pincers/extension/labs.rb
@@ -233,7 +236,6 @@ files:
233
236
  - lib/pincers/support/cookie.rb
234
237
  - lib/pincers/support/cookie_jar.rb
235
238
  - lib/pincers/support/http_client.rb
236
- - lib/pincers/support/query.rb
237
239
  - lib/pincers/support/xpath_builder.rb
238
240
  - lib/pincers/version.rb
239
241
  homepage: https://github.com/platanus/pincers
@@ -1,31 +0,0 @@
1
- module Pincers::Support
2
- class Query
3
-
4
- attr_reader :lang, :query, :limit
5
-
6
- def initialize(_backend, _lang, _query, _limit)
7
- @backend = _backend
8
- @lang = _lang
9
- @query = _query
10
- @limit = _limit
11
- end
12
-
13
- def execute(_elements, _force_limit=nil)
14
- fun = case @lang
15
- when :xpath then :search_by_xpath
16
- else :search_by_css end
17
-
18
- explode_elements _elements, fun, _force_limit || @limit
19
- end
20
-
21
- def explode_elements(_elements, _fun, _limit)
22
- _elements.inject([]) do |r, element|
23
- limit = _limit.nil? ? nil : _limit - r.count
24
- r = r + @backend.send(_fun, element, @query, limit)
25
- break r if !_limit.nil? && r.length >= _limit
26
- next r
27
- end
28
- end
29
-
30
- end
31
- end