mechanize 0.4.0 → 0.4.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/EXAMPLES +55 -0
- data/README +3 -12
- data/lib/mechanize/cookie.rb +1 -2
- data/lib/mechanize/form.rb +182 -0
- data/lib/mechanize/form_elements.rb +105 -0
- data/lib/mechanize/page.rb +130 -0
- data/lib/mechanize/page_elements.rb +28 -0
- data/lib/mechanize.rb +27 -369
- data/test/htdocs/file_upload.html +13 -0
- data/test/htdocs/frame_test.html +23 -0
- data/test/server.rb +1 -0
- data/test/servlets.rb +6 -0
- data/test/tc_cookies.rb +2 -3
- data/test/tc_forms.rb +2 -3
- data/test/tc_frames.rb +22 -0
- data/test/tc_links.rb +2 -3
- data/test/tc_mech.rb +2 -3
- data/test/tc_response_code.rb +2 -3
- data/test/tc_upload.rb +34 -0
- data/test/test_includes.rb +5 -0
- data/test/ts_mech.rb +2 -0
- metadata +59 -307
- data/doc/classes/Net/HTTP/Copy.html +0 -134
- data/doc/classes/Net/HTTP/Delete.html +0 -134
- data/doc/classes/Net/HTTP/Get.html +0 -134
- data/doc/classes/Net/HTTP/Head.html +0 -134
- data/doc/classes/Net/HTTP/Lock.html +0 -134
- data/doc/classes/Net/HTTP/Mkcol.html +0 -134
- data/doc/classes/Net/HTTP/Move.html +0 -134
- data/doc/classes/Net/HTTP/Options.html +0 -134
- data/doc/classes/Net/HTTP/Post.html +0 -134
- data/doc/classes/Net/HTTP/Propfind.html +0 -134
- data/doc/classes/Net/HTTP/Proppatch.html +0 -134
- data/doc/classes/Net/HTTP/Put.html +0 -134
- data/doc/classes/Net/HTTP/Trace.html +0 -134
- data/doc/classes/Net/HTTP/Unlock.html +0 -134
- data/doc/classes/Net/HTTP.html +0 -1716
- data/doc/classes/Net/HTTP.src/M000103.html +0 -18
- data/doc/classes/Net/HTTP.src/M000104.html +0 -18
- data/doc/classes/Net/HTTP.src/M000105.html +0 -18
- data/doc/classes/Net/HTTP.src/M000106.html +0 -18
- data/doc/classes/Net/HTTP.src/M000107.html +0 -29
- data/doc/classes/Net/HTTP.src/M000108.html +0 -18
- data/doc/classes/Net/HTTP.src/M000109.html +0 -22
- data/doc/classes/Net/HTTP.src/M000110.html +0 -18
- data/doc/classes/Net/HTTP.src/M000111.html +0 -18
- data/doc/classes/Net/HTTP.src/M000112.html +0 -18
- data/doc/classes/Net/HTTP.src/M000113.html +0 -18
- data/doc/classes/Net/HTTP.src/M000114.html +0 -22
- data/doc/classes/Net/HTTP.src/M000115.html +0 -29
- data/doc/classes/Net/HTTP.src/M000116.html +0 -18
- data/doc/classes/Net/HTTP.src/M000117.html +0 -19
- data/doc/classes/Net/HTTP.src/M000118.html +0 -19
- data/doc/classes/Net/HTTP.src/M000119.html +0 -18
- data/doc/classes/Net/HTTP.src/M000121.html +0 -18
- data/doc/classes/Net/HTTP.src/M000122.html +0 -28
- data/doc/classes/Net/HTTP.src/M000123.html +0 -19
- data/doc/classes/Net/HTTP.src/M000124.html +0 -30
- data/doc/classes/Net/HTTP.src/M000125.html +0 -18
- data/doc/classes/Net/HTTP.src/M000126.html +0 -18
- data/doc/classes/Net/HTTP.src/M000127.html +0 -18
- data/doc/classes/Net/HTTP.src/M000128.html +0 -18
- data/doc/classes/Net/HTTP.src/M000129.html +0 -18
- data/doc/classes/Net/HTTP.src/M000130.html +0 -18
- data/doc/classes/Net/HTTP.src/M000133.html +0 -28
- data/doc/classes/Net/HTTP.src/M000134.html +0 -20
- data/doc/classes/Net/HTTP.src/M000135.html +0 -28
- data/doc/classes/Net/HTTP.src/M000136.html +0 -18
- data/doc/classes/Net/HTTP.src/M000137.html +0 -18
- data/doc/classes/Net/HTTP.src/M000138.html +0 -18
- data/doc/classes/Net/HTTP.src/M000139.html +0 -18
- data/doc/classes/Net/HTTP.src/M000140.html +0 -18
- data/doc/classes/Net/HTTP.src/M000141.html +0 -18
- data/doc/classes/Net/HTTP.src/M000142.html +0 -18
- data/doc/classes/Net/HTTP.src/M000143.html +0 -18
- data/doc/classes/Net/HTTP.src/M000144.html +0 -18
- data/doc/classes/Net/HTTP.src/M000145.html +0 -18
- data/doc/classes/Net/HTTP.src/M000146.html +0 -18
- data/doc/classes/Net/HTTP.src/M000147.html +0 -18
- data/doc/classes/Net/HTTP.src/M000148.html +0 -18
- data/doc/classes/Net/HTTP.src/M000152.html +0 -19
- data/doc/classes/Net/HTTP.src/M000153.html +0 -39
- data/doc/classes/Net/HTTP.src/M000154.html +0 -18
- data/doc/classes/Net/HTTP.src/M000156.html +0 -24
- data/doc/classes/Net/HTTP.src/M000157.html +0 -18
- data/doc/classes/Net/HTTP.src/M000158.html +0 -19
- data/doc/classes/Net/HTTP.src/M000159.html +0 -21
- data/doc/classes/Net/HTTP.src/M000161.html +0 -19
- data/doc/classes/Net/HTTPError.html +0 -120
- data/doc/classes/Net/HTTPExceptions.html +0 -137
- data/doc/classes/Net/HTTPFatalError.html +0 -120
- data/doc/classes/Net/HTTPGenericRequest.html +0 -274
- data/doc/classes/Net/HTTPGenericRequest.src/M000088.html +0 -26
- data/doc/classes/Net/HTTPGenericRequest.src/M000089.html +0 -18
- data/doc/classes/Net/HTTPGenericRequest.src/M000090.html +0 -18
- data/doc/classes/Net/HTTPGenericRequest.src/M000091.html +0 -18
- data/doc/classes/Net/HTTPGenericRequest.src/M000092.html +0 -19
- data/doc/classes/Net/HTTPGenericRequest.src/M000093.html +0 -20
- data/doc/classes/Net/HTTPGenericRequest.src/M000094.html +0 -20
- data/doc/classes/Net/HTTPHeader.html +0 -734
- data/doc/classes/Net/HTTPHeader.src/M000055.html +0 -23
- data/doc/classes/Net/HTTPHeader.src/M000056.html +0 -19
- data/doc/classes/Net/HTTPHeader.src/M000057.html +0 -22
- data/doc/classes/Net/HTTPHeader.src/M000058.html +0 -22
- data/doc/classes/Net/HTTPHeader.src/M000059.html +0 -19
- data/doc/classes/Net/HTTPHeader.src/M000060.html +0 -19
- data/doc/classes/Net/HTTPHeader.src/M000061.html +0 -20
- data/doc/classes/Net/HTTPHeader.src/M000063.html +0 -18
- data/doc/classes/Net/HTTPHeader.src/M000065.html +0 -20
- data/doc/classes/Net/HTTPHeader.src/M000066.html +0 -20
- data/doc/classes/Net/HTTPHeader.src/M000067.html +0 -18
- data/doc/classes/Net/HTTPHeader.src/M000068.html +0 -18
- data/doc/classes/Net/HTTPHeader.src/M000069.html +0 -18
- data/doc/classes/Net/HTTPHeader.src/M000070.html +0 -20
- data/doc/classes/Net/HTTPHeader.src/M000072.html +0 -30
- data/doc/classes/Net/HTTPHeader.src/M000073.html +0 -43
- data/doc/classes/Net/HTTPHeader.src/M000075.html +0 -21
- data/doc/classes/Net/HTTPHeader.src/M000076.html +0 -22
- data/doc/classes/Net/HTTPHeader.src/M000077.html +0 -20
- data/doc/classes/Net/HTTPHeader.src/M000078.html +0 -21
- data/doc/classes/Net/HTTPHeader.src/M000079.html +0 -19
- data/doc/classes/Net/HTTPHeader.src/M000080.html +0 -18
- data/doc/classes/Net/HTTPHeader.src/M000081.html +0 -19
- data/doc/classes/Net/HTTPHeader.src/M000082.html +0 -19
- data/doc/classes/Net/HTTPHeader.src/M000083.html +0 -23
- data/doc/classes/Net/HTTPHeader.src/M000084.html +0 -18
- data/doc/classes/Net/HTTPHeader.src/M000086.html +0 -18
- data/doc/classes/Net/HTTPHeader.src/M000087.html +0 -18
- data/doc/classes/Net/HTTPRequest.html +0 -150
- data/doc/classes/Net/HTTPRequest.src/M000102.html +0 -21
- data/doc/classes/Net/HTTPResponse.html +0 -425
- data/doc/classes/Net/HTTPResponse.src/M000162.html +0 -18
- data/doc/classes/Net/HTTPResponse.src/M000163.html +0 -18
- data/doc/classes/Net/HTTPResponse.src/M000164.html +0 -23
- data/doc/classes/Net/HTTPResponse.src/M000165.html +0 -18
- data/doc/classes/Net/HTTPResponse.src/M000166.html +0 -32
- data/doc/classes/Net/HTTPResponse.src/M000167.html +0 -18
- data/doc/classes/Net/HTTPRetriableError.html +0 -120
- data/doc/classes/Net/HTTPServerException.html +0 -120
- data/doc/classes/Net/ProtoAuthError.html +0 -113
- data/doc/classes/Net/ProtoCommandError.html +0 -113
- data/doc/classes/Net/ProtoFatalError.html +0 -113
- data/doc/classes/Net/ProtoRetriableError.html +0 -113
- data/doc/classes/Net/ProtoServerError.html +0 -113
- data/doc/classes/Net/ProtoSyntaxError.html +0 -113
- data/doc/classes/Net/ProtoUnknownError.html +0 -113
- data/doc/classes/Net/ProtocolError.html +0 -111
- data/doc/classes/Net/WriteAdapter.html +0 -235
- data/doc/classes/Net/WriteAdapter.src/M000095.html +0 -19
- data/doc/classes/Net/WriteAdapter.src/M000096.html +0 -18
- data/doc/classes/Net/WriteAdapter.src/M000097.html +0 -18
- data/doc/classes/Net/WriteAdapter.src/M000099.html +0 -19
- data/doc/classes/Net/WriteAdapter.src/M000100.html +0 -18
- data/doc/classes/Net/WriteAdapter.src/M000101.html +0 -18
- data/doc/classes/REXML/Comment.html +0 -137
- data/doc/classes/REXML/Comment.src/M000053.html +0 -18
- data/doc/classes/REXML/Node.html +0 -240
- data/doc/classes/REXML/Node.src/M000047.html +0 -21
- data/doc/classes/REXML/Node.src/M000048.html +0 -21
- data/doc/classes/REXML/Node.src/M000049.html +0 -22
- data/doc/classes/REXML/Node.src/M000050.html +0 -18
- data/doc/classes/REXML/Node.src/M000051.html +0 -18
- data/doc/classes/REXML/Node.src/M000052.html +0 -18
- data/doc/classes/REXML/Text.html +0 -137
- data/doc/classes/REXML/Text.src/M000054.html +0 -18
- data/doc/classes/REXML.html +0 -109
- data/doc/classes/WWW/Button.html +0 -190
- data/doc/classes/WWW/Button.src/M000028.html +0 -18
- data/doc/classes/WWW/Button.src/M000029.html +0 -18
- data/doc/classes/WWW/Button.src/M000030.html +0 -25
- data/doc/classes/WWW/CheckBox.html +0 -160
- data/doc/classes/WWW/CheckBox.src/M000024.html +0 -18
- data/doc/classes/WWW/Cookie.html +0 -207
- data/doc/classes/WWW/Cookie.src/M000032.html +0 -59
- data/doc/classes/WWW/Cookie.src/M000033.html +0 -21
- data/doc/classes/WWW/Cookie.src/M000034.html +0 -18
- data/doc/classes/WWW/CookieJar.html +0 -197
- data/doc/classes/WWW/CookieJar.src/M000008.html +0 -18
- data/doc/classes/WWW/CookieJar.src/M000009.html +0 -22
- data/doc/classes/WWW/CookieJar.src/M000010.html +0 -33
- data/doc/classes/WWW/CookieJar.src/M000011.html +0 -18
- data/doc/classes/WWW/Field.html +0 -174
- data/doc/classes/WWW/Field.src/M000045.html +0 -18
- data/doc/classes/WWW/Field.src/M000046.html +0 -26
- data/doc/classes/WWW/FileUpload.html +0 -163
- data/doc/classes/WWW/FileUpload.src/M000031.html +0 -19
- data/doc/classes/WWW/Form.html +0 -152
- data/doc/classes/WWW/Form.src/M000044.html +0 -19
- data/doc/classes/WWW/GlobalForm.html +0 -271
- data/doc/classes/WWW/GlobalForm.src/M000004.html +0 -24
- data/doc/classes/WWW/GlobalForm.src/M000005.html +0 -26
- data/doc/classes/WWW/GlobalForm.src/M000006.html +0 -46
- data/doc/classes/WWW/GlobalForm.src/M000007.html +0 -46
- data/doc/classes/WWW/ImageButton.html +0 -157
- data/doc/classes/WWW/ImageButton.src/M000027.html +0 -22
- data/doc/classes/WWW/Link.html +0 -160
- data/doc/classes/WWW/Link.src/M000025.html +0 -20
- data/doc/classes/WWW/Mechanize.html +0 -379
- data/doc/classes/WWW/Mechanize.src/M000013.html +0 -21
- data/doc/classes/WWW/Mechanize.src/M000014.html +0 -18
- data/doc/classes/WWW/Mechanize.src/M000015.html +0 -24
- data/doc/classes/WWW/Mechanize.src/M000016.html +0 -19
- data/doc/classes/WWW/Mechanize.src/M000017.html +0 -23
- data/doc/classes/WWW/Mechanize.src/M000018.html +0 -32
- data/doc/classes/WWW/Mechanize.src/M000019.html +0 -19
- data/doc/classes/WWW/Mechanize.src/M000020.html +0 -18
- data/doc/classes/WWW/Mechanize.src/M000021.html +0 -33
- data/doc/classes/WWW/Mechanize.src/M000022.html +0 -18
- data/doc/classes/WWW/Meta.html +0 -113
- data/doc/classes/WWW/Page.html +0 -313
- data/doc/classes/WWW/Page.src/M000036.html +0 -18
- data/doc/classes/WWW/Page.src/M000037.html +0 -18
- data/doc/classes/WWW/Page.src/M000038.html +0 -18
- data/doc/classes/WWW/Page.src/M000039.html +0 -19
- data/doc/classes/WWW/Page.src/M000040.html +0 -19
- data/doc/classes/WWW/Page.src/M000041.html +0 -19
- data/doc/classes/WWW/Page.src/M000042.html +0 -19
- data/doc/classes/WWW/Page.src/M000043.html +0 -19
- data/doc/classes/WWW/RadioButton.html +0 -160
- data/doc/classes/WWW/RadioButton.src/M000012.html +0 -18
- data/doc/classes/WWW/ResponseCodeError.html +0 -150
- data/doc/classes/WWW/ResponseCodeError.src/M000035.html +0 -18
- data/doc/classes/WWW/SelectList.html +0 -160
- data/doc/classes/WWW/SelectList.src/M000026.html +0 -28
- data/doc/classes/WWW.html +0 -130
- data/doc/created.rid +0 -1
- data/doc/files/CHANGELOG.html +0 -136
- data/doc/files/LICENSE.html +0 -531
- data/doc/files/README.html +0 -161
- data/doc/files/lib/mechanize/cookie_rb.html +0 -101
- data/doc/files/lib/mechanize/net-overrides/net/http_rb.html +0 -139
- data/doc/files/lib/mechanize/net-overrides/net/https_rb.html +0 -109
- data/doc/files/lib/mechanize/net-overrides/net/protocol_rb.html +0 -117
- data/doc/files/lib/mechanize/parsing_rb.html +0 -267
- data/doc/files/lib/mechanize/parsing_rb.src/M000001.html +0 -22
- data/doc/files/lib/mechanize/parsing_rb.src/M000002.html +0 -44
- data/doc/files/lib/mechanize/parsing_rb.src/M000003.html +0 -34
- data/doc/files/lib/mechanize_rb.html +0 -152
- data/doc/fr_class_index.html +0 -80
- data/doc/fr_file_index.html +0 -35
- data/doc/fr_method_index.html +0 -194
- data/doc/index.html +0 -24
- data/doc/rdoc-style.css +0 -208
data/lib/mechanize.rb
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
# Please see the LICENSE file for licensing.
|
8
8
|
#
|
9
9
|
|
10
|
-
Version = "0.4.
|
10
|
+
Version = "0.4.1"
|
11
11
|
|
12
12
|
# required due to the missing get_fields method in Ruby 1.8.2
|
13
13
|
unless RUBY_VERSION > "1.8.2"
|
@@ -16,379 +16,19 @@ end
|
|
16
16
|
require 'net/http'
|
17
17
|
require 'net/https'
|
18
18
|
|
19
|
-
require 'web/htmltools/xmltree' # narf
|
20
|
-
require 'mechanize/parsing'
|
21
|
-
require 'mechanize/cookie'
|
22
19
|
require 'uri'
|
23
20
|
require 'logger'
|
24
21
|
require 'webrick'
|
25
22
|
require 'date'
|
23
|
+
require 'web/htmltools/xmltree' # narf
|
24
|
+
require 'mechanize/parsing'
|
25
|
+
require 'mechanize/cookie'
|
26
|
+
require 'mechanize/form'
|
27
|
+
require 'mechanize/form_elements'
|
28
|
+
require 'mechanize/page'
|
29
|
+
require 'mechanize/page_elements'
|
26
30
|
|
27
31
|
module WWW
|
28
|
-
|
29
|
-
class Field
|
30
|
-
attr_accessor :name, :value
|
31
|
-
|
32
|
-
def initialize(name, value)
|
33
|
-
@name, @value = name, value
|
34
|
-
end
|
35
|
-
|
36
|
-
# Returns an array of Field objects
|
37
|
-
# TODO: is this correct?
|
38
|
-
def self.extract_all_from(root_node)
|
39
|
-
fields = []
|
40
|
-
root_node.each_recursive {|node|
|
41
|
-
if (node.name.downcase == 'input' and
|
42
|
-
%w(text password hidden checkbox radio int).include?(node.attributes['type'].downcase)) or
|
43
|
-
%w(textarea option).include?(node.name.downcase)
|
44
|
-
fields << Field.new(node.attributes['name'], node.attributes['value'])
|
45
|
-
end
|
46
|
-
}
|
47
|
-
return fields
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
class FileUpload
|
52
|
-
# value is the file-name, not the file-content
|
53
|
-
attr_accessor :name
|
54
|
-
|
55
|
-
attr_accessor :file_name, :file_data
|
56
|
-
|
57
|
-
def initialize(name, file_name)
|
58
|
-
@name, @file_name = name, file_name
|
59
|
-
@file_data = nil
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
class Button
|
64
|
-
attr_accessor :name, :value
|
65
|
-
|
66
|
-
def initialize(name, value)
|
67
|
-
@name, @value = name, value
|
68
|
-
end
|
69
|
-
|
70
|
-
def add_to_query(query)
|
71
|
-
query[@name] = @value || "" if @name
|
72
|
-
end
|
73
|
-
|
74
|
-
# Returns an array of Button objects
|
75
|
-
def self.extract_all_from(root_node)
|
76
|
-
buttons = []
|
77
|
-
root_node.each_recursive {|node|
|
78
|
-
if node.name.downcase == 'input' and
|
79
|
-
['submit'].include?(node.attributes['type'].downcase)
|
80
|
-
buttons << Button.new(node.attributes['name'], node.attributes['value'])
|
81
|
-
end
|
82
|
-
}
|
83
|
-
return buttons
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
class ImageButton < Button
|
88
|
-
attr_accessor :x, :y
|
89
|
-
|
90
|
-
def add_to_query(query)
|
91
|
-
if @name
|
92
|
-
query[@name] = @value || ""
|
93
|
-
query[@name+".x"] = (@x || "0").to_s
|
94
|
-
query[@name+".y"] = (@y || "0").to_s
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
class RadioButton
|
100
|
-
attr_accessor :name, :value, :checked
|
101
|
-
|
102
|
-
def initialize(name, value, checked)
|
103
|
-
@name, @value, @checked = name, value, checked
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
class CheckBox
|
108
|
-
attr_accessor :name, :value, :checked
|
109
|
-
|
110
|
-
def initialize(name, value, checked)
|
111
|
-
@name, @value, @checked = name, value, checked
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
class SelectList
|
116
|
-
attr_accessor :name, :value, :options
|
117
|
-
|
118
|
-
def initialize(name, node)
|
119
|
-
@name = name
|
120
|
-
@options = []
|
121
|
-
|
122
|
-
# parse
|
123
|
-
node.each_recursive {|n|
|
124
|
-
if n.name.downcase == 'option'
|
125
|
-
value = n.attributes['value']
|
126
|
-
@options << value
|
127
|
-
@value = value if n.attributes['selected']
|
128
|
-
end
|
129
|
-
}
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
# Class Form does not work in the case there is some invalid (unbalanced) html
|
134
|
-
# involved, such as:
|
135
|
-
#
|
136
|
-
# <td>
|
137
|
-
# <form>
|
138
|
-
# </td>
|
139
|
-
# <td>
|
140
|
-
# <input .../>
|
141
|
-
# </form>
|
142
|
-
# </td>
|
143
|
-
#
|
144
|
-
# GlobalForm takes two nodes, the node where the form tag is located
|
145
|
-
# (form_node), and another node, from which to start looking for form elements
|
146
|
-
# (elements_node) like buttons and the like. For class Form both fall together
|
147
|
-
# into one and the same node.
|
148
|
-
|
149
|
-
class GlobalForm
|
150
|
-
attr_reader :form_node, :elements_node
|
151
|
-
attr_accessor :method, :action, :name
|
152
|
-
|
153
|
-
attr_reader :fields, :buttons, :file_uploads, :radiobuttons, :checkboxes
|
154
|
-
|
155
|
-
def initialize(form_node, elements_node)
|
156
|
-
@form_node, @elements_node = form_node, elements_node
|
157
|
-
|
158
|
-
@method = (@form_node.attributes['method'] || 'GET').upcase
|
159
|
-
@action = @form_node.attributes['action']
|
160
|
-
@name = @form_node.attributes['name']
|
161
|
-
|
162
|
-
parse
|
163
|
-
end
|
164
|
-
|
165
|
-
# In the case of malformed HTML, fields of multiple forms might occure in this forms'
|
166
|
-
# field array. If the fields have the same name, posterior fields overwrite former fields.
|
167
|
-
# To avoid this, this method rejects all posterior duplicate fields.
|
168
|
-
|
169
|
-
def uniq_fields!
|
170
|
-
names_in = {}
|
171
|
-
fields.reject! {|f|
|
172
|
-
if names_in.include?(f.name)
|
173
|
-
true
|
174
|
-
else
|
175
|
-
names_in[f.name] = true
|
176
|
-
false
|
177
|
-
end
|
178
|
-
}
|
179
|
-
end
|
180
|
-
|
181
|
-
def build_query
|
182
|
-
query = {}
|
183
|
-
|
184
|
-
fields().each do |f|
|
185
|
-
query[f.name] = f.value || ""
|
186
|
-
end
|
187
|
-
|
188
|
-
checkboxes().each do |f|
|
189
|
-
query[f.name] = f.value || "on" if f.checked
|
190
|
-
end
|
191
|
-
|
192
|
-
radio_groups = {}
|
193
|
-
radiobuttons().each do |f|
|
194
|
-
radio_groups[f.name] ||= []
|
195
|
-
radio_groups[f.name] << f
|
196
|
-
end
|
197
|
-
|
198
|
-
# take one radio button from each group
|
199
|
-
radio_groups.each_value do |g|
|
200
|
-
checked = g.select {|f| f.checked}
|
201
|
-
|
202
|
-
if checked.size == 1
|
203
|
-
f = checked.first
|
204
|
-
query[f.name] = f.value || ""
|
205
|
-
elsif checked.size > 1
|
206
|
-
raise "multiple radiobuttons are checked in the same group!"
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
query
|
211
|
-
end
|
212
|
-
|
213
|
-
def parse
|
214
|
-
@fields = []
|
215
|
-
@buttons = []
|
216
|
-
@file_uploads = []
|
217
|
-
@radiobuttons = []
|
218
|
-
@checkboxes = []
|
219
|
-
|
220
|
-
@elements_node.each_recursive {|node|
|
221
|
-
case node.name.downcase
|
222
|
-
when 'input'
|
223
|
-
case (node.attributes['type'] || 'text').downcase
|
224
|
-
when 'text', 'password', 'hidden', 'int'
|
225
|
-
@fields << Field.new(node.attributes['name'], node.attributes['value'])
|
226
|
-
when 'radio'
|
227
|
-
@radiobuttons << RadioButton.new(node.attributes['name'], node.attributes['value'], node.attributes.has_key?('checked'))
|
228
|
-
when 'checkbox'
|
229
|
-
@checkboxes << CheckBox.new(node.attributes['name'], node.attributes['value'], node.attributes.has_key?('checked'))
|
230
|
-
when 'file'
|
231
|
-
@file_uploads << FileUpload.new(node.attributes['name'], node.attributes['value'])
|
232
|
-
when 'submit'
|
233
|
-
@buttons << Button.new(node.attributes['name'], node.attributes['value'])
|
234
|
-
when 'image'
|
235
|
-
@buttons << ImageButton.new(node.attributes['name'], node.attributes['value'])
|
236
|
-
end
|
237
|
-
when 'textarea'
|
238
|
-
@fields << Field.new(node.attributes['name'], node.all_text)
|
239
|
-
when 'select'
|
240
|
-
@fields << SelectList.new(node.attributes['name'], node)
|
241
|
-
end
|
242
|
-
}
|
243
|
-
end
|
244
|
-
|
245
|
-
end
|
246
|
-
|
247
|
-
class Form < GlobalForm
|
248
|
-
attr_reader :node
|
249
|
-
|
250
|
-
def initialize(node)
|
251
|
-
@node = node
|
252
|
-
super(@node, @node)
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
class Link
|
257
|
-
attr_reader :node
|
258
|
-
attr_reader :href
|
259
|
-
attr_reader :text
|
260
|
-
|
261
|
-
def initialize(node)
|
262
|
-
@node = node
|
263
|
-
@href = node.attributes['href']
|
264
|
-
@text = node.all_text
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
class Meta < Link
|
269
|
-
end
|
270
|
-
|
271
|
-
# = Synopsis
|
272
|
-
# This class encapsulates a page.
|
273
|
-
#
|
274
|
-
# == Example
|
275
|
-
# require 'rubygems'
|
276
|
-
# require 'mechanize'
|
277
|
-
# require 'logger'
|
278
|
-
#
|
279
|
-
# class Body
|
280
|
-
# def initialize(node)
|
281
|
-
# puts node.attributes['bgcolor']
|
282
|
-
# end
|
283
|
-
# end
|
284
|
-
#
|
285
|
-
# agent = WWW::Mechanize.new { |a| a.log = Logger.new("mech.log") }
|
286
|
-
# agent.user_agent_alias = 'Mac Safari'
|
287
|
-
# page = agent.get("http://www.google.com/")
|
288
|
-
# page.watch_for_set = { 'body' => Body }
|
289
|
-
#
|
290
|
-
# body = page.watches
|
291
|
-
class Page
|
292
|
-
attr_accessor :uri, :cookies, :response, :body, :code, :watch_for_set
|
293
|
-
|
294
|
-
def initialize(uri=nil, cookies=[], response=nil, body=nil, code=nil)
|
295
|
-
@uri, @cookies, @response, @body, @code = uri, cookies, response, body, code
|
296
|
-
end
|
297
|
-
|
298
|
-
def header
|
299
|
-
@response.header
|
300
|
-
end
|
301
|
-
|
302
|
-
def content_type
|
303
|
-
header['Content-Type']
|
304
|
-
end
|
305
|
-
|
306
|
-
def forms
|
307
|
-
parse_html() unless @forms
|
308
|
-
@forms
|
309
|
-
end
|
310
|
-
|
311
|
-
def links
|
312
|
-
parse_html() unless @links
|
313
|
-
@links
|
314
|
-
end
|
315
|
-
|
316
|
-
def root
|
317
|
-
parse_html() unless @root
|
318
|
-
@root
|
319
|
-
end
|
320
|
-
|
321
|
-
# This method watches out for a particular tag, and will call back to the
|
322
|
-
# class specified for the tag in the watch_for_set method. See the example
|
323
|
-
# in this class.
|
324
|
-
def watches
|
325
|
-
parse_html() unless @watches
|
326
|
-
@watches
|
327
|
-
end
|
328
|
-
|
329
|
-
def meta
|
330
|
-
parse_html() unless @meta
|
331
|
-
@meta
|
332
|
-
end
|
333
|
-
|
334
|
-
private
|
335
|
-
|
336
|
-
def parse_html
|
337
|
-
raise "no html" unless content_type() =~ /^text\/html/
|
338
|
-
|
339
|
-
# construct parser and feed with HTML
|
340
|
-
parser = HTMLTree::XMLParser.new
|
341
|
-
begin
|
342
|
-
parser.feed(@body)
|
343
|
-
rescue => ex
|
344
|
-
if ex.message =~ /attempted adding second root element to document/ and
|
345
|
-
# Put the whole document inside a single root element, which I simply name
|
346
|
-
# <root>, just to make the parser happy. It's no longer valid HTML, but
|
347
|
-
# without a single root element, it's not valid HTML as well.
|
348
|
-
|
349
|
-
# TODO: leave a possible doctype definition outside this element.
|
350
|
-
parser = HTMLTree::XMLParser.new
|
351
|
-
parser.feed("<root>" + @body + "</root>")
|
352
|
-
else
|
353
|
-
raise
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
@root = parser.document
|
358
|
-
|
359
|
-
@forms = []
|
360
|
-
@links = []
|
361
|
-
@meta = []
|
362
|
-
@watches = {}
|
363
|
-
|
364
|
-
@root.each_recursive {|node|
|
365
|
-
name = node.name.downcase
|
366
|
-
|
367
|
-
case name
|
368
|
-
when 'form'
|
369
|
-
@forms << Form.new(node)
|
370
|
-
when 'a'
|
371
|
-
@links << Link.new(node)
|
372
|
-
when 'meta'
|
373
|
-
equiv = node.attributes['http-equiv']
|
374
|
-
content = node.attributes['content']
|
375
|
-
if equiv != nil && equiv.downcase == 'refresh'
|
376
|
-
if content != nil && content =~ /^\d+\s*;\s*url\s*=\s*(\S+)/i
|
377
|
-
node.attributes['href'] = $1
|
378
|
-
@meta << Meta.new(node)
|
379
|
-
end
|
380
|
-
end
|
381
|
-
else
|
382
|
-
if @watch_for_set and @watch_for_set.keys.include?( name )
|
383
|
-
@watches[name] = [] unless @watches[name]
|
384
|
-
klass = @watch_for_set[name]
|
385
|
-
@watches[name] << (klass ? klass.new(node) : node)
|
386
|
-
end
|
387
|
-
end
|
388
|
-
}
|
389
|
-
end
|
390
|
-
end
|
391
|
-
|
392
32
|
class ResponseCodeError < RuntimeError
|
393
33
|
attr_reader :response_code
|
394
34
|
|
@@ -503,7 +143,7 @@ class Mechanize
|
|
503
143
|
uri = to_absolute_uri(URI::escape(form.action))
|
504
144
|
case form.method.upcase
|
505
145
|
when 'POST'
|
506
|
-
|
146
|
+
post_form(uri, form)
|
507
147
|
when 'GET'
|
508
148
|
if uri.query.nil?
|
509
149
|
get(uri.to_s + "?" + build_query_string(query))
|
@@ -542,6 +182,24 @@ class Mechanize
|
|
542
182
|
return uri
|
543
183
|
end
|
544
184
|
|
185
|
+
def post_form(url, form)
|
186
|
+
cur_page = current_page() || Page.new
|
187
|
+
|
188
|
+
request_data = [form.request_data]
|
189
|
+
|
190
|
+
# this is called before the request is sent
|
191
|
+
pre_request_hook = proc {|request|
|
192
|
+
log.debug("query: #{ request_data.inspect }")
|
193
|
+
request.add_field('Content-Type', form.enctype)
|
194
|
+
request.add_field('Content-Length', request_data[0].size.to_s)
|
195
|
+
}
|
196
|
+
|
197
|
+
# fetch the page
|
198
|
+
page = fetch_page(to_absolute_uri(url, cur_page), :post, cur_page, pre_request_hook, request_data)
|
199
|
+
add_to_history(page)
|
200
|
+
page
|
201
|
+
end
|
202
|
+
|
545
203
|
# uri is an absolute URI
|
546
204
|
def fetch_page(uri, method=:get, cur_page=current_page(), pre_request_hook=nil, request_data=[])
|
547
205
|
raise "unsupported scheme" unless ['http', 'https'].include?(uri.scheme)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>File Upload Form</title>
|
4
|
+
</head>
|
5
|
+
<body>
|
6
|
+
<h1>File Upload Test</h1>
|
7
|
+
<form enctype="multipart/form-data" action="/file_upload" method="post">
|
8
|
+
Your name: <input type="text" name="name" /><br />
|
9
|
+
File to process: <input name="userfile1" type="file" /><br />
|
10
|
+
<input type="submit" value="Send File" />
|
11
|
+
</form>
|
12
|
+
</body>
|
13
|
+
</html>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
|
2
|
+
"http://www.w3.org/TR/html4/frameset.dtd">
|
3
|
+
<HTML>
|
4
|
+
<HEAD>
|
5
|
+
<TITLE>A simple frameset document</TITLE>
|
6
|
+
</HEAD>
|
7
|
+
<FRAMESET cols="20%, 80%">
|
8
|
+
<FRAMESET rows="100, 200">
|
9
|
+
<FRAME name="frame1" src="/google.html">
|
10
|
+
<FRAME name="frame2" src="/form_test.html">
|
11
|
+
</FRAMESET>
|
12
|
+
<FRAME name="frame3" src="/file_upload.html">
|
13
|
+
<NOFRAMES>
|
14
|
+
<P>This frameset document contains:
|
15
|
+
<UL>
|
16
|
+
<LI><A href="/google.html">Some neat contents</A>
|
17
|
+
<LI><A href="/form_test.html">Form Test</A>
|
18
|
+
<LI><A href="/file_upload.html">Some other neat contents</A>
|
19
|
+
</UL>
|
20
|
+
</NOFRAMES>
|
21
|
+
</FRAMESET>
|
22
|
+
</HTML>
|
23
|
+
|
data/test/server.rb
CHANGED
data/test/servlets.rb
CHANGED
@@ -2,6 +2,12 @@ require 'webrick'
|
|
2
2
|
require 'logger'
|
3
3
|
require 'date'
|
4
4
|
|
5
|
+
class FileUploadTest < WEBrick::HTTPServlet::AbstractServlet
|
6
|
+
def do_POST(req, res)
|
7
|
+
res.body = req.body
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
5
11
|
class ResponseCodeTest < WEBrick::HTTPServlet::AbstractServlet
|
6
12
|
def do_GET(req, res)
|
7
13
|
res['Content-Type'] = "text/html"
|
data/test/tc_cookies.rb
CHANGED
@@ -7,11 +7,10 @@ require 'mechanize'
|
|
7
7
|
require 'servlets'
|
8
8
|
require 'net/http'
|
9
9
|
require 'uri'
|
10
|
+
require 'test_includes'
|
10
11
|
|
11
12
|
class FormsMechTest < Test::Unit::TestCase
|
12
|
-
|
13
|
-
@port = 2000
|
14
|
-
end
|
13
|
+
include TestMethods
|
15
14
|
|
16
15
|
def test_send_cookies
|
17
16
|
agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
|
data/test/tc_forms.rb
CHANGED
@@ -5,11 +5,10 @@ require 'test/unit'
|
|
5
5
|
require 'rubygems'
|
6
6
|
require 'mechanize'
|
7
7
|
require 'servlets'
|
8
|
+
require 'test_includes'
|
8
9
|
|
9
10
|
class FormsMechTest < Test::Unit::TestCase
|
10
|
-
|
11
|
-
@port = 2000
|
12
|
-
end
|
11
|
+
include TestMethods
|
13
12
|
|
14
13
|
def test_post
|
15
14
|
agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
|
data/test/tc_frames.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'mechanize'
|
6
|
+
require 'test_includes'
|
7
|
+
|
8
|
+
class FramesMechTest < Test::Unit::TestCase
|
9
|
+
include TestMethods
|
10
|
+
|
11
|
+
def test_frames
|
12
|
+
agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
|
13
|
+
page = agent.get("http://localhost:#{@port}/frame_test.html")
|
14
|
+
assert_equal(3, page.frames.size)
|
15
|
+
assert_equal("frame1", page.frames[0].name)
|
16
|
+
assert_equal("frame2", page.frames[1].name)
|
17
|
+
assert_equal("frame3", page.frames[2].name)
|
18
|
+
assert_equal("/google.html", page.frames[0].src)
|
19
|
+
assert_equal("/form_test.html", page.frames[1].src)
|
20
|
+
assert_equal("/file_upload.html", page.frames[2].src)
|
21
|
+
end
|
22
|
+
end
|
data/test/tc_links.rb
CHANGED
@@ -3,11 +3,10 @@ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
|
3
3
|
require 'test/unit'
|
4
4
|
require 'rubygems'
|
5
5
|
require 'mechanize'
|
6
|
+
require 'test_includes'
|
6
7
|
|
7
8
|
class LinksMechTest < Test::Unit::TestCase
|
8
|
-
|
9
|
-
@port = 2000
|
10
|
-
end
|
9
|
+
include TestMethods
|
11
10
|
|
12
11
|
def test_find_meta
|
13
12
|
agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
|
data/test/tc_mech.rb
CHANGED
@@ -4,11 +4,10 @@ require 'webrick'
|
|
4
4
|
require 'test/unit'
|
5
5
|
require 'rubygems'
|
6
6
|
require 'mechanize'
|
7
|
+
require 'test_includes'
|
7
8
|
|
8
9
|
class MechMethodsTest < Test::Unit::TestCase
|
9
|
-
|
10
|
-
@port = 2000
|
11
|
-
end
|
10
|
+
include TestMethods
|
12
11
|
|
13
12
|
def test_history
|
14
13
|
agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
|
data/test/tc_response_code.rb
CHANGED
@@ -3,11 +3,10 @@ $:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
|
3
3
|
require 'test/unit'
|
4
4
|
require 'rubygems'
|
5
5
|
require 'mechanize'
|
6
|
+
require 'test_includes'
|
6
7
|
|
7
8
|
class FormsMechTest < Test::Unit::TestCase
|
8
|
-
|
9
|
-
@port = 2000
|
10
|
-
end
|
9
|
+
include TestMethods
|
11
10
|
|
12
11
|
def test_redirect
|
13
12
|
agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
|
data/test/tc_upload.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'mechanize'
|
6
|
+
require 'test_includes'
|
7
|
+
|
8
|
+
class FormsMechTest < Test::Unit::TestCase
|
9
|
+
include TestMethods
|
10
|
+
|
11
|
+
def test_form_enctype
|
12
|
+
agent = WWW::Mechanize.new { |a| a.log = Logger.new(nil) }
|
13
|
+
page = agent.get("http://localhost:#{@port}/file_upload.html")
|
14
|
+
assert_equal('multipart/form-data', page.forms[0].enctype)
|
15
|
+
|
16
|
+
form = page.forms.first
|
17
|
+
form.file_uploads.first.file_name = "README"
|
18
|
+
form.file_uploads.first.mime_type = "text/plain"
|
19
|
+
form.file_uploads.first.file_data = "Hello World\n\n"
|
20
|
+
|
21
|
+
page = agent.submit(form)
|
22
|
+
|
23
|
+
assert_match(
|
24
|
+
"Content-Disposition: form-data; name=\"userfile1\"; filename=\"README\"",
|
25
|
+
page.body
|
26
|
+
)
|
27
|
+
assert_match(
|
28
|
+
"Content-Disposition: form-data; name=\"name\"",
|
29
|
+
page.body
|
30
|
+
)
|
31
|
+
assert_match('Content-Type: text/plain', page.body)
|
32
|
+
assert_match('Hello World', page.body)
|
33
|
+
end
|
34
|
+
end
|