mechanize 1.0.1.beta.20110107104205 → 2.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of mechanize might be problematic. Click here for more details.
- data.tar.gz.sig +2 -0
- data/{lib/mechanize/chain/post_connect_hook.rb → .gemtest} +0 -0
- data/CHANGELOG.rdoc +51 -6
- data/EXAMPLES.rdoc +5 -3
- data/GUIDE.rdoc +72 -32
- data/LICENSE.rdoc +20 -340
- data/Manifest.txt +20 -27
- data/README.rdoc +12 -9
- data/Rakefile +5 -2
- data/examples/spider.rb +13 -2
- data/lib/mechanize.rb +545 -267
- data/lib/mechanize/content_type_error.rb +1 -1
- data/lib/mechanize/cookie.rb +72 -65
- data/lib/mechanize/cookie_jar.rb +197 -148
- data/lib/mechanize/element_matcher.rb +35 -0
- data/lib/mechanize/file.rb +3 -1
- data/lib/mechanize/file_connection.rb +17 -0
- data/lib/mechanize/file_request.rb +26 -0
- data/lib/mechanize/file_response.rb +61 -47
- data/lib/mechanize/form.rb +57 -58
- data/lib/mechanize/form/image_button.rb +2 -3
- data/lib/mechanize/form/multi_select_list.rb +71 -55
- data/lib/mechanize/form/select_list.rb +34 -62
- data/lib/mechanize/monkey_patch.rb +13 -11
- data/lib/mechanize/page.rb +277 -270
- data/lib/mechanize/page/image.rb +6 -2
- data/lib/mechanize/redirect_limit_reached_error.rb +1 -1
- data/lib/mechanize/redirect_not_get_or_head_error.rb +1 -1
- data/lib/mechanize/response_code_error.rb +3 -3
- data/lib/mechanize/unsupported_scheme_error.rb +1 -1
- data/lib/mechanize/uri_resolver.rb +82 -0
- data/lib/mechanize/util.rb +76 -60
- data/test/helper.rb +35 -5
- data/test/htdocs/dir with spaces/foo.html +1 -0
- data/test/htdocs/rails_3_encoding_hack_form_test.html +27 -0
- data/test/htdocs/tc_base_images.html +10 -0
- data/test/htdocs/tc_images.html +8 -0
- data/test/htdocs/test_click.html +11 -0
- data/test/servlets.rb +3 -2
- data/test/test_authenticate.rb +5 -5
- data/test/test_errors.rb +8 -8
- data/test/test_follow_meta.rb +4 -4
- data/test/test_form_as_hash.rb +4 -4
- data/test/test_forms.rb +3 -7
- data/test/test_hash_api.rb +2 -2
- data/test/test_headers.rb +1 -1
- data/test/test_images.rb +19 -0
- data/test/test_mech.rb +6 -6
- data/test/test_mechanize.rb +687 -0
- data/test/{test_cookie_class.rb → test_mechanize_cookie.rb} +52 -45
- data/test/test_mechanize_cookie_jar.rb +400 -0
- data/test/test_mechanize_file.rb +7 -1
- data/test/test_mechanize_file_request.rb +19 -0
- data/test/test_mechanize_file_response.rb +21 -0
- data/test/test_mechanize_form_image_button.rb +12 -0
- data/test/test_mechanize_page.rb +165 -0
- data/test/test_mechanize_uri_resolver.rb +29 -0
- data/test/{test_util.rb → test_mechanize_util.rb} +1 -1
- data/test/test_multi_select.rb +12 -0
- data/test/test_post_form.rb +7 -0
- data/test/test_redirect_verb_handling.rb +6 -6
- data/test/test_scheme.rb +0 -7
- data/test/test_verbs.rb +3 -3
- metadata +106 -72
- metadata.gz.sig +0 -0
- data/lib/mechanize/chain.rb +0 -36
- data/lib/mechanize/chain/auth_headers.rb +0 -78
- data/lib/mechanize/chain/body_decoding_handler.rb +0 -50
- data/lib/mechanize/chain/connection_resolver.rb +0 -28
- data/lib/mechanize/chain/custom_headers.rb +0 -21
- data/lib/mechanize/chain/handler.rb +0 -9
- data/lib/mechanize/chain/header_resolver.rb +0 -48
- data/lib/mechanize/chain/parameter_resolver.rb +0 -22
- data/lib/mechanize/chain/pre_connect_hook.rb +0 -20
- data/lib/mechanize/chain/request_resolver.rb +0 -31
- data/lib/mechanize/chain/response_body_parser.rb +0 -36
- data/lib/mechanize/chain/response_header_handler.rb +0 -34
- data/lib/mechanize/chain/response_reader.rb +0 -39
- data/lib/mechanize/chain/ssl_resolver.rb +0 -40
- data/lib/mechanize/chain/uri_resolver.rb +0 -75
- data/test/chain/test_argument_validator.rb +0 -14
- data/test/chain/test_auth_headers.rb +0 -25
- data/test/chain/test_custom_headers.rb +0 -18
- data/test/chain/test_header_resolver.rb +0 -27
- data/test/chain/test_parameter_resolver.rb +0 -35
- data/test/chain/test_request_resolver.rb +0 -29
- data/test/chain/test_response_reader.rb +0 -24
- data/test/test_cookie_jar.rb +0 -324
- data/test/test_page.rb +0 -124
@@ -1,67 +1,83 @@
|
|
1
|
-
class
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
1
|
+
# This class represents a select list where multiple values can be selected.
|
2
|
+
# MultiSelectList#value= accepts an array, and those values are used as
|
3
|
+
# values for the select list. For example, to select multiple values,
|
4
|
+
# simply do this:
|
5
|
+
#
|
6
|
+
# list.value = ['one', 'two']
|
7
|
+
#
|
8
|
+
# Single values are still supported, so these two are the same:
|
9
|
+
#
|
10
|
+
# list.value = ['one']
|
11
|
+
# list.value = 'one'
|
12
|
+
class Mechanize::Form::MultiSelectList < Mechanize::Form::Field
|
13
|
+
extend Mechanize::ElementMatcher
|
13
14
|
|
14
|
-
|
15
|
-
value = []
|
16
|
-
@options = []
|
15
|
+
attr_accessor :options
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@options << option
|
22
|
-
end
|
23
|
-
super(node, value)
|
24
|
-
end
|
17
|
+
def initialize node
|
18
|
+
value = []
|
19
|
+
@options = []
|
25
20
|
|
26
|
-
|
27
|
-
|
28
|
-
|
21
|
+
# parse
|
22
|
+
node.search('option').each do |n|
|
23
|
+
option = Mechanize::Form::Option.new(n, self)
|
24
|
+
@options << option
|
25
|
+
end
|
26
|
+
super(node, value)
|
27
|
+
end
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
29
|
+
##
|
30
|
+
# Find one option on this select list with +criteria+
|
31
|
+
# Example:
|
32
|
+
# select_list.option_with(:value => '1').value = 'foo'
|
35
33
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
##
|
35
|
+
# Find all options on this select list with +criteria+
|
36
|
+
# Example:
|
37
|
+
# select_list.options_with(:value => /1|2/).each do |field|
|
38
|
+
# field.value = '20'
|
39
|
+
# end
|
41
40
|
|
42
|
-
|
43
|
-
def selected_options
|
44
|
-
@options.find_all { |o| o.selected? }
|
45
|
-
end
|
41
|
+
elements_with :option
|
46
42
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
option = options.find { |o| o.value == value }
|
51
|
-
if option.nil?
|
52
|
-
@value.push(value)
|
53
|
-
else
|
54
|
-
option.select
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
43
|
+
def query_value
|
44
|
+
value ? value.collect { |v| [name, v] } : ''
|
45
|
+
end
|
58
46
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
47
|
+
# Select no options
|
48
|
+
def select_none
|
49
|
+
@value = []
|
50
|
+
options.each { |o| o.untick }
|
51
|
+
end
|
52
|
+
|
53
|
+
# Select all options
|
54
|
+
def select_all
|
55
|
+
@value = []
|
56
|
+
options.each { |o| o.tick }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get a list of all selected options
|
60
|
+
def selected_options
|
61
|
+
@options.find_all { |o| o.selected? }
|
62
|
+
end
|
63
|
+
|
64
|
+
def value=(values)
|
65
|
+
select_none
|
66
|
+
[values].flatten.each do |value|
|
67
|
+
option = options.find { |o| o.value == value }
|
68
|
+
if option.nil?
|
69
|
+
@value.push(value)
|
70
|
+
else
|
71
|
+
option.select
|
64
72
|
end
|
65
73
|
end
|
66
74
|
end
|
75
|
+
|
76
|
+
def value
|
77
|
+
value = []
|
78
|
+
value.push(*@value)
|
79
|
+
value.push(*selected_options.collect { |o| o.value })
|
80
|
+
value
|
81
|
+
end
|
82
|
+
|
67
83
|
end
|
@@ -1,68 +1,40 @@
|
|
1
|
-
class
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
selected_options.reverse[1..selected_options.length].each do |o|
|
14
|
-
o.unselect
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
##
|
20
|
-
# Find all options on this select list with +criteria+
|
21
|
-
# Example:
|
22
|
-
# select_list.options_with(:value => /1|2/).each do |field|
|
23
|
-
# field.value = '20'
|
24
|
-
# end
|
25
|
-
def options_with criteria
|
26
|
-
criteria = {:name => criteria} if String === criteria
|
27
|
-
f = @options.find_all do |thing|
|
28
|
-
criteria.all? { |k,v| v === thing.send(k) }
|
29
|
-
end
|
30
|
-
yield f if block_given?
|
31
|
-
f
|
32
|
-
end
|
33
|
-
|
34
|
-
##
|
35
|
-
# Find one option on this select list with +criteria+
|
36
|
-
# Example:
|
37
|
-
# select_list.option_with(:value => '1').value = 'foo'
|
38
|
-
def option_with criteria
|
39
|
-
f = options_with(criteria).first
|
40
|
-
yield f if block_given?
|
41
|
-
f
|
42
|
-
end
|
43
|
-
|
44
|
-
def value
|
45
|
-
value = super
|
46
|
-
if value.length > 0
|
47
|
-
value.last
|
48
|
-
elsif @options.length > 0
|
49
|
-
@options.first.value
|
50
|
-
else
|
51
|
-
nil
|
52
|
-
end
|
1
|
+
# This class represents a select list or drop down box in a Form. Set the
|
2
|
+
# value for the list by calling SelectList#value=. SelectList contains a
|
3
|
+
# list of Option that were found. After finding the correct option, set
|
4
|
+
# the select lists value to the option value:
|
5
|
+
# selectlist.value = selectlist.options.first.value
|
6
|
+
# Options can also be selected by "clicking" or selecting them. See Option
|
7
|
+
class Mechanize::Form::SelectList < Mechanize::Form::MultiSelectList
|
8
|
+
def initialize node
|
9
|
+
super
|
10
|
+
if selected_options.length > 1
|
11
|
+
selected_options.reverse[1..selected_options.length].each do |o|
|
12
|
+
o.unselect
|
53
13
|
end
|
14
|
+
end
|
15
|
+
end
|
54
16
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
17
|
+
def value
|
18
|
+
value = super
|
19
|
+
if value.length > 0
|
20
|
+
value.last
|
21
|
+
elsif @options.length > 0
|
22
|
+
@options.first.value
|
23
|
+
else
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
62
27
|
|
63
|
-
|
64
|
-
|
65
|
-
|
28
|
+
def value=(new)
|
29
|
+
if new != new.to_s and new.respond_to? :first
|
30
|
+
super([new.first])
|
31
|
+
else
|
32
|
+
super([new.to_s])
|
66
33
|
end
|
67
34
|
end
|
35
|
+
|
36
|
+
def query_value
|
37
|
+
value ? [[name, value]] : nil
|
38
|
+
end
|
68
39
|
end
|
40
|
+
|
@@ -1,14 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
if RUBY_VERSION < '1.9' then
|
2
|
+
module Net
|
3
|
+
class HTTP
|
4
|
+
alias :old_keep_alive? :keep_alive?
|
5
|
+
def keep_alive?(req, res)
|
6
|
+
return false if /close/i =~ req['connection'].to_s
|
7
|
+
return false if @seems_1_0_server
|
8
|
+
return false if /close/i =~ res['connection'].to_s
|
9
|
+
return true if /keep-alive/i =~ res['connection'].to_s
|
10
|
+
return false if /close/i =~ res['proxy-connection'].to_s
|
11
|
+
return true if /keep-alive/i =~ res['proxy-connection'].to_s
|
12
|
+
(@curr_http_version == '1.1')
|
13
|
+
end
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
data/lib/mechanize/page.rb
CHANGED
@@ -1,313 +1,320 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
require '
|
7
|
-
require 'mechanize
|
1
|
+
# = Synopsis
|
2
|
+
# This class encapsulates an HTML page. If Mechanize finds a content
|
3
|
+
# type of 'text/html', this class will be instantiated and returned.
|
4
|
+
#
|
5
|
+
# == Example
|
6
|
+
# require 'rubygems'
|
7
|
+
# require 'mechanize'
|
8
|
+
#
|
9
|
+
# agent = Mechanize.new
|
10
|
+
# agent.get('http://google.com/').class #=> Mechanize::Page
|
11
|
+
#
|
12
|
+
class Mechanize::Page < Mechanize::File
|
13
|
+
extend Forwardable
|
14
|
+
extend Mechanize::ElementMatcher
|
15
|
+
|
16
|
+
attr_accessor :mech
|
17
|
+
|
18
|
+
def initialize(uri=nil, response=nil, body=nil, code=nil, mech=nil)
|
19
|
+
raise Mechanize::ContentTypeError, response['content-type'] unless
|
20
|
+
response['content-type'] =~ /^(text\/html)|(application\/xhtml\+xml)/i
|
21
|
+
|
22
|
+
@bases = nil
|
23
|
+
@encoding = nil
|
24
|
+
@encodings = [nil]
|
25
|
+
@forms = nil
|
26
|
+
@frames = nil
|
27
|
+
@iframes = nil
|
28
|
+
@links = nil
|
29
|
+
@mech = mech
|
30
|
+
@meta = nil
|
31
|
+
@parser = nil
|
32
|
+
|
33
|
+
@encodings << Mechanize::Util.detect_charset(body) if body
|
34
|
+
|
35
|
+
response.each do |header, value|
|
36
|
+
next unless value =~ /charset/i
|
37
|
+
@encodings << charset(value)
|
38
|
+
end
|
8
39
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
# require 'mechanize'
|
17
|
-
#
|
18
|
-
# agent = Mechanize.new
|
19
|
-
# agent.get('http://google.com/').class #=> Mechanize::Page
|
20
|
-
#
|
21
|
-
class Page < Mechanize::File
|
22
|
-
extend Forwardable
|
40
|
+
if body
|
41
|
+
# Force the encoding to be 8BIT so we can perform regular expressions.
|
42
|
+
# We'll set it to the detected encoding later
|
43
|
+
body.force_encoding('ASCII-8BIT') if body.respond_to?(:force_encoding)
|
44
|
+
|
45
|
+
body.scan(/<meta .*?>/i) do |meta|
|
46
|
+
next unless meta =~ /http-equiv\s*=\s*(["'])?content-type\1/i
|
23
47
|
|
24
|
-
|
48
|
+
meta =~ /content=(["'])?(.*?)\1/i
|
25
49
|
|
26
|
-
|
27
|
-
@encoding = nil
|
50
|
+
encoding = charset $2
|
28
51
|
|
29
|
-
|
30
|
-
response.send(method) do |header,v|
|
31
|
-
next unless v =~ /charset/i
|
32
|
-
encoding = v[/charset=([^; ]+)/, 1]
|
33
|
-
@encoding = encoding unless encoding == 'none'
|
52
|
+
@encodings << encoding if encoding
|
34
53
|
end
|
54
|
+
end
|
35
55
|
|
36
|
-
|
37
|
-
|
38
|
-
body.force_encoding('ASCII-8BIT') if body && body.respond_to?(:force_encoding)
|
56
|
+
super(uri, response, body, code)
|
57
|
+
end
|
39
58
|
|
40
|
-
|
59
|
+
def title
|
60
|
+
@title ||=
|
61
|
+
if doc = parser
|
62
|
+
title = doc.search('title').inner_text
|
63
|
+
title.empty? ? nil : title
|
64
|
+
end
|
65
|
+
end
|
41
66
|
|
42
|
-
|
43
|
-
|
67
|
+
def charset content_type
|
68
|
+
charset = content_type[/charset=([^; ]+)/i, 1]
|
69
|
+
return nil if charset == 'none'
|
70
|
+
charset
|
71
|
+
end
|
44
72
|
|
45
|
-
|
73
|
+
def encoding=(encoding)
|
74
|
+
@encoding = encoding
|
46
75
|
|
47
|
-
|
48
|
-
|
49
|
-
|
76
|
+
if @parser
|
77
|
+
parser_encoding = @parser.encoding
|
78
|
+
if (parser_encoding && parser_encoding.downcase) != (encoding && encoding.downcase)
|
79
|
+
# lazy reinitialize the parser with the new encoding
|
80
|
+
@parser = nil
|
81
|
+
end
|
50
82
|
end
|
51
83
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
84
|
+
encoding
|
85
|
+
end
|
86
|
+
|
87
|
+
def encoding
|
88
|
+
parser.respond_to?(:encoding) ? parser.encoding : nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def parser
|
92
|
+
return @parser if @parser
|
93
|
+
return nil unless @body
|
94
|
+
|
95
|
+
if @encoding then
|
96
|
+
@parser = mech.html_parser.parse(html_body, nil, @encoding)
|
97
|
+
else
|
98
|
+
@encodings.reverse_each do |encoding|
|
99
|
+
@parser = mech.html_parser.parse(html_body, nil, encoding)
|
67
100
|
|
68
|
-
|
69
|
-
@encoding = encoding
|
101
|
+
break if @parser.errors.empty?
|
70
102
|
|
71
|
-
|
72
|
-
|
73
|
-
if (parser_encoding && parser_encoding.downcase) != (encoding && encoding.downcase)
|
74
|
-
# lazy reinitialize the parser with the new encoding
|
75
|
-
@parser = nil
|
103
|
+
break unless @parser.errors.any? do |error|
|
104
|
+
error.message =~ /(indicate encoding)|(Invalid char)/
|
76
105
|
end
|
77
106
|
end
|
78
|
-
|
79
|
-
encoding
|
80
107
|
end
|
81
108
|
|
82
|
-
|
83
|
-
|
84
|
-
end
|
109
|
+
@parser
|
110
|
+
end
|
85
111
|
|
86
|
-
|
87
|
-
return @parser if @parser
|
112
|
+
alias :root :parser
|
88
113
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
@parser = mech.html_parser.parse(html_body)
|
94
|
-
end
|
95
|
-
end
|
114
|
+
# Get the content type
|
115
|
+
def content_type
|
116
|
+
response['content-type']
|
117
|
+
end
|
96
118
|
|
97
|
-
|
98
|
-
|
99
|
-
|
119
|
+
# Search through the page like HPricot
|
120
|
+
def_delegator :parser, :search, :search
|
121
|
+
def_delegator :parser, :/, :/
|
122
|
+
def_delegator :parser, :at, :at
|
100
123
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
124
|
+
##
|
125
|
+
# :method: form_with(criteria)
|
126
|
+
#
|
127
|
+
# Find a single form matching +criteria+.
|
128
|
+
# Example:
|
129
|
+
# page.form_with(:action => '/post/login.php') do |f|
|
130
|
+
# ...
|
131
|
+
# end
|
132
|
+
|
133
|
+
##
|
134
|
+
# :method: forms_with(criteria)
|
135
|
+
#
|
136
|
+
# Find all forms form matching +criteria+.
|
137
|
+
# Example:
|
138
|
+
# page.forms_with(:action => '/post/login.php').each do |f|
|
139
|
+
# ...
|
140
|
+
# end
|
105
141
|
|
106
|
-
|
107
|
-
def_delegator :parser, :search, :search
|
108
|
-
def_delegator :parser, :/, :/
|
109
|
-
def_delegator :parser, :at, :at
|
110
|
-
|
111
|
-
##
|
112
|
-
# :method: form_with(criteria)
|
113
|
-
#
|
114
|
-
# Find a single form matching +criteria+.
|
115
|
-
# Example:
|
116
|
-
# page.form_with(:action => '/post/login.php') do |f|
|
117
|
-
# ...
|
118
|
-
# end
|
119
|
-
|
120
|
-
##
|
121
|
-
# :method: forms_with(criteria)
|
122
|
-
#
|
123
|
-
# Find all forms form matching +criteria+.
|
124
|
-
# Example:
|
125
|
-
# page.forms_with(:action => '/post/login.php').each do |f|
|
126
|
-
# ...
|
127
|
-
# end
|
128
|
-
|
129
|
-
##
|
130
|
-
# :method: link_with(criteria)
|
131
|
-
#
|
132
|
-
# Find a single link matching +criteria+.
|
133
|
-
# Example:
|
134
|
-
# page.link_with(:href => /foo/).click
|
135
|
-
|
136
|
-
##
|
137
|
-
# :method: links_with(criteria)
|
138
|
-
#
|
139
|
-
# Find all links matching +criteria+.
|
140
|
-
# Example:
|
141
|
-
# page.links_with(:href => /foo/).each do |link|
|
142
|
-
# puts link.href
|
143
|
-
# end
|
144
|
-
|
145
|
-
##
|
146
|
-
# :method: base_with(criteria)
|
147
|
-
#
|
148
|
-
# Find a single base tag matching +criteria+.
|
149
|
-
# Example:
|
150
|
-
# page.base_with(:href => /foo/).click
|
151
|
-
|
152
|
-
##
|
153
|
-
# :method: bases_with(criteria)
|
154
|
-
#
|
155
|
-
# Find all base tags matching +criteria+.
|
156
|
-
# Example:
|
157
|
-
# page.bases_with(:href => /foo/).each do |base|
|
158
|
-
# puts base.href
|
159
|
-
# end
|
160
|
-
|
161
|
-
##
|
162
|
-
# :method: frame_with(criteria)
|
163
|
-
#
|
164
|
-
# Find a single frame tag matching +criteria+.
|
165
|
-
# Example:
|
166
|
-
# page.frame_with(:src => /foo/).click
|
167
|
-
|
168
|
-
##
|
169
|
-
# :method: frames_with(criteria)
|
170
|
-
#
|
171
|
-
# Find all frame tags matching +criteria+.
|
172
|
-
# Example:
|
173
|
-
# page.frames_with(:src => /foo/).each do |frame|
|
174
|
-
# p frame.src
|
175
|
-
# end
|
176
|
-
|
177
|
-
##
|
178
|
-
# :method: iframe_with(criteria)
|
179
|
-
#
|
180
|
-
# Find a single iframe tag matching +criteria+.
|
181
|
-
# Example:
|
182
|
-
# page.iframe_with(:src => /foo/).click
|
183
|
-
|
184
|
-
##
|
185
|
-
# :method: iframes_with(criteria)
|
186
|
-
#
|
187
|
-
# Find all iframe tags matching +criteria+.
|
188
|
-
# Example:
|
189
|
-
# page.iframes_with(:src => /foo/).each do |iframe|
|
190
|
-
# p iframe.src
|
191
|
-
# end
|
192
|
-
|
193
|
-
# let's meta program!
|
194
|
-
[:form, :link, :base, :frame, :iframe].each do |type|
|
195
|
-
eval(<<-eomethod)
|
196
|
-
def #{type}s_with(criteria)
|
197
|
-
criteria = {:name => criteria} if String === criteria
|
198
|
-
f = #{type}s.find_all do |thing|
|
199
|
-
criteria.all? do |k,v|
|
200
|
-
k = :dom_id if(k.to_s == "id")
|
201
|
-
v === thing.send(k)
|
202
|
-
end
|
203
|
-
end
|
204
|
-
yield f if block_given?
|
205
|
-
f
|
206
|
-
end
|
207
|
-
|
208
|
-
def #{type}_with(criteria)
|
209
|
-
f = #{type}s_with(criteria).first
|
210
|
-
yield f if block_given?
|
211
|
-
f
|
212
|
-
end
|
213
|
-
alias :#{type} :#{type}_with
|
214
|
-
eomethod
|
215
|
-
end
|
142
|
+
elements_with :form
|
216
143
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
144
|
+
##
|
145
|
+
# :method: link_with(criteria)
|
146
|
+
#
|
147
|
+
# Find a single link matching +criteria+.
|
148
|
+
# Example:
|
149
|
+
# page.link_with(:href => /foo/).click
|
150
|
+
|
151
|
+
##
|
152
|
+
# :method: links_with(criteria)
|
153
|
+
#
|
154
|
+
# Find all links matching +criteria+.
|
155
|
+
# Example:
|
156
|
+
# page.links_with(:href => /foo/).each do |link|
|
157
|
+
# puts link.href
|
158
|
+
# end
|
226
159
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
160
|
+
elements_with :link
|
161
|
+
|
162
|
+
##
|
163
|
+
# :method: base_with(criteria)
|
164
|
+
#
|
165
|
+
# Find a single base tag matching +criteria+.
|
166
|
+
# Example:
|
167
|
+
# page.base_with(:href => /foo/).click
|
168
|
+
|
169
|
+
##
|
170
|
+
# :method: bases_with(criteria)
|
171
|
+
#
|
172
|
+
# Find all base tags matching +criteria+.
|
173
|
+
# Example:
|
174
|
+
# page.bases_with(:href => /foo/).each do |base|
|
175
|
+
# puts base.href
|
176
|
+
# end
|
177
|
+
|
178
|
+
elements_with :base
|
179
|
+
|
180
|
+
##
|
181
|
+
# :method: frame_with(criteria)
|
182
|
+
#
|
183
|
+
# Find a single frame tag matching +criteria+.
|
184
|
+
# Example:
|
185
|
+
# page.frame_with(:src => /foo/).click
|
186
|
+
|
187
|
+
##
|
188
|
+
# :method: frames_with(criteria)
|
189
|
+
#
|
190
|
+
# Find all frame tags matching +criteria+.
|
191
|
+
# Example:
|
192
|
+
# page.frames_with(:src => /foo/).each do |frame|
|
193
|
+
# p frame.src
|
194
|
+
# end
|
195
|
+
|
196
|
+
elements_with :frame
|
197
|
+
|
198
|
+
##
|
199
|
+
# :method: iframe_with(criteria)
|
200
|
+
#
|
201
|
+
# Find a single iframe tag matching +criteria+.
|
202
|
+
# Example:
|
203
|
+
# page.iframe_with(:src => /foo/).click
|
204
|
+
|
205
|
+
##
|
206
|
+
# :method: iframes_with(criteria)
|
207
|
+
#
|
208
|
+
# Find all iframe tags matching +criteria+.
|
209
|
+
# Example:
|
210
|
+
# page.iframes_with(:src => /foo/).each do |iframe|
|
211
|
+
# p iframe.src
|
212
|
+
# end
|
213
|
+
|
214
|
+
elements_with :iframe
|
215
|
+
|
216
|
+
##
|
217
|
+
# Return a list of all link and area tags
|
218
|
+
def links
|
219
|
+
@links ||= %w{ a area }.map do |tag|
|
220
|
+
search(tag).map do |node|
|
221
|
+
Link.new(node, @mech, self)
|
234
222
|
end
|
223
|
+
end.flatten
|
224
|
+
end
|
225
|
+
|
226
|
+
##
|
227
|
+
# Return a list of all form tags
|
228
|
+
def forms
|
229
|
+
@forms ||= search('form').map do |html_form|
|
230
|
+
form = Mechanize::Form.new(html_form, @mech, self)
|
231
|
+
form.action ||= @uri.to_s
|
232
|
+
form
|
235
233
|
end
|
234
|
+
end
|
236
235
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
end
|
236
|
+
##
|
237
|
+
# Return a list of all meta tags
|
238
|
+
def meta
|
239
|
+
@meta ||= search('head > meta').map do |node|
|
240
|
+
next unless node['http-equiv'] && node['content']
|
241
|
+
(equiv, content) = node['http-equiv'], node['content']
|
242
|
+
if equiv && equiv.downcase == 'refresh'
|
243
|
+
Meta.parse(content, uri) do |delay, href|
|
244
|
+
node['delay'] = delay
|
245
|
+
node['href'] = href
|
246
|
+
Meta.new(node, @mech, self)
|
249
247
|
end
|
250
|
-
end
|
251
|
-
end
|
248
|
+
end
|
249
|
+
end.compact
|
250
|
+
end
|
252
251
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
252
|
+
##
|
253
|
+
# Return a list of all base tags
|
254
|
+
def bases
|
255
|
+
@bases ||=
|
256
|
+
search('base').map { |node| Base.new(node, @mech, self) }
|
257
|
+
end
|
259
258
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
259
|
+
##
|
260
|
+
# Return a list of all frame tags
|
261
|
+
def frames
|
262
|
+
@frames ||=
|
263
|
+
search('frame').map { |node| Frame.new(node, @mech, self) }
|
264
|
+
end
|
266
265
|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
266
|
+
##
|
267
|
+
# Return a list of all iframe tags
|
268
|
+
def iframes
|
269
|
+
@iframes ||=
|
270
|
+
search('iframe').map { |node| Frame.new(node, @mech, self) }
|
271
|
+
end
|
273
272
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
273
|
+
##
|
274
|
+
# Return a list of all img tags
|
275
|
+
def images
|
276
|
+
@images ||=
|
277
|
+
search('img').map { |node| Image.new(node, self) }
|
278
|
+
end
|
280
279
|
|
281
|
-
|
282
|
-
|
283
|
-
|
280
|
+
def image_urls
|
281
|
+
@image_urls ||= images.map(&:url).uniq
|
282
|
+
end
|
284
283
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
284
|
+
##
|
285
|
+
# Return a list of all label tags
|
286
|
+
def labels
|
287
|
+
@labels ||=
|
288
|
+
search('label').map { |node| Label.new(node, self) }
|
289
|
+
end
|
291
290
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
end
|
298
|
-
@labels_hash = hash
|
291
|
+
def labels_hash
|
292
|
+
unless @labels_hash
|
293
|
+
hash = {}
|
294
|
+
labels.each do |label|
|
295
|
+
hash[label.node['for']] = label if label.for
|
299
296
|
end
|
300
|
-
|
297
|
+
@labels_hash = hash
|
301
298
|
end
|
299
|
+
return @labels_hash
|
300
|
+
end
|
302
301
|
|
303
|
-
|
302
|
+
private
|
304
303
|
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
end
|
304
|
+
def html_body
|
305
|
+
if @body
|
306
|
+
@body.empty? ? '<html></html>' : @body
|
307
|
+
else
|
308
|
+
''
|
311
309
|
end
|
312
310
|
end
|
313
311
|
end
|
312
|
+
|
313
|
+
require 'mechanize/headers'
|
314
|
+
require 'mechanize/page/image'
|
315
|
+
require 'mechanize/page/label'
|
316
|
+
require 'mechanize/page/link'
|
317
|
+
require 'mechanize/page/base'
|
318
|
+
require 'mechanize/page/frame'
|
319
|
+
require 'mechanize/page/meta'
|
320
|
+
|