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.

Files changed (243) hide show
  1. data/EXAMPLES +55 -0
  2. data/README +3 -12
  3. data/lib/mechanize/cookie.rb +1 -2
  4. data/lib/mechanize/form.rb +182 -0
  5. data/lib/mechanize/form_elements.rb +105 -0
  6. data/lib/mechanize/page.rb +130 -0
  7. data/lib/mechanize/page_elements.rb +28 -0
  8. data/lib/mechanize.rb +27 -369
  9. data/test/htdocs/file_upload.html +13 -0
  10. data/test/htdocs/frame_test.html +23 -0
  11. data/test/server.rb +1 -0
  12. data/test/servlets.rb +6 -0
  13. data/test/tc_cookies.rb +2 -3
  14. data/test/tc_forms.rb +2 -3
  15. data/test/tc_frames.rb +22 -0
  16. data/test/tc_links.rb +2 -3
  17. data/test/tc_mech.rb +2 -3
  18. data/test/tc_response_code.rb +2 -3
  19. data/test/tc_upload.rb +34 -0
  20. data/test/test_includes.rb +5 -0
  21. data/test/ts_mech.rb +2 -0
  22. metadata +59 -307
  23. data/doc/classes/Net/HTTP/Copy.html +0 -134
  24. data/doc/classes/Net/HTTP/Delete.html +0 -134
  25. data/doc/classes/Net/HTTP/Get.html +0 -134
  26. data/doc/classes/Net/HTTP/Head.html +0 -134
  27. data/doc/classes/Net/HTTP/Lock.html +0 -134
  28. data/doc/classes/Net/HTTP/Mkcol.html +0 -134
  29. data/doc/classes/Net/HTTP/Move.html +0 -134
  30. data/doc/classes/Net/HTTP/Options.html +0 -134
  31. data/doc/classes/Net/HTTP/Post.html +0 -134
  32. data/doc/classes/Net/HTTP/Propfind.html +0 -134
  33. data/doc/classes/Net/HTTP/Proppatch.html +0 -134
  34. data/doc/classes/Net/HTTP/Put.html +0 -134
  35. data/doc/classes/Net/HTTP/Trace.html +0 -134
  36. data/doc/classes/Net/HTTP/Unlock.html +0 -134
  37. data/doc/classes/Net/HTTP.html +0 -1716
  38. data/doc/classes/Net/HTTP.src/M000103.html +0 -18
  39. data/doc/classes/Net/HTTP.src/M000104.html +0 -18
  40. data/doc/classes/Net/HTTP.src/M000105.html +0 -18
  41. data/doc/classes/Net/HTTP.src/M000106.html +0 -18
  42. data/doc/classes/Net/HTTP.src/M000107.html +0 -29
  43. data/doc/classes/Net/HTTP.src/M000108.html +0 -18
  44. data/doc/classes/Net/HTTP.src/M000109.html +0 -22
  45. data/doc/classes/Net/HTTP.src/M000110.html +0 -18
  46. data/doc/classes/Net/HTTP.src/M000111.html +0 -18
  47. data/doc/classes/Net/HTTP.src/M000112.html +0 -18
  48. data/doc/classes/Net/HTTP.src/M000113.html +0 -18
  49. data/doc/classes/Net/HTTP.src/M000114.html +0 -22
  50. data/doc/classes/Net/HTTP.src/M000115.html +0 -29
  51. data/doc/classes/Net/HTTP.src/M000116.html +0 -18
  52. data/doc/classes/Net/HTTP.src/M000117.html +0 -19
  53. data/doc/classes/Net/HTTP.src/M000118.html +0 -19
  54. data/doc/classes/Net/HTTP.src/M000119.html +0 -18
  55. data/doc/classes/Net/HTTP.src/M000121.html +0 -18
  56. data/doc/classes/Net/HTTP.src/M000122.html +0 -28
  57. data/doc/classes/Net/HTTP.src/M000123.html +0 -19
  58. data/doc/classes/Net/HTTP.src/M000124.html +0 -30
  59. data/doc/classes/Net/HTTP.src/M000125.html +0 -18
  60. data/doc/classes/Net/HTTP.src/M000126.html +0 -18
  61. data/doc/classes/Net/HTTP.src/M000127.html +0 -18
  62. data/doc/classes/Net/HTTP.src/M000128.html +0 -18
  63. data/doc/classes/Net/HTTP.src/M000129.html +0 -18
  64. data/doc/classes/Net/HTTP.src/M000130.html +0 -18
  65. data/doc/classes/Net/HTTP.src/M000133.html +0 -28
  66. data/doc/classes/Net/HTTP.src/M000134.html +0 -20
  67. data/doc/classes/Net/HTTP.src/M000135.html +0 -28
  68. data/doc/classes/Net/HTTP.src/M000136.html +0 -18
  69. data/doc/classes/Net/HTTP.src/M000137.html +0 -18
  70. data/doc/classes/Net/HTTP.src/M000138.html +0 -18
  71. data/doc/classes/Net/HTTP.src/M000139.html +0 -18
  72. data/doc/classes/Net/HTTP.src/M000140.html +0 -18
  73. data/doc/classes/Net/HTTP.src/M000141.html +0 -18
  74. data/doc/classes/Net/HTTP.src/M000142.html +0 -18
  75. data/doc/classes/Net/HTTP.src/M000143.html +0 -18
  76. data/doc/classes/Net/HTTP.src/M000144.html +0 -18
  77. data/doc/classes/Net/HTTP.src/M000145.html +0 -18
  78. data/doc/classes/Net/HTTP.src/M000146.html +0 -18
  79. data/doc/classes/Net/HTTP.src/M000147.html +0 -18
  80. data/doc/classes/Net/HTTP.src/M000148.html +0 -18
  81. data/doc/classes/Net/HTTP.src/M000152.html +0 -19
  82. data/doc/classes/Net/HTTP.src/M000153.html +0 -39
  83. data/doc/classes/Net/HTTP.src/M000154.html +0 -18
  84. data/doc/classes/Net/HTTP.src/M000156.html +0 -24
  85. data/doc/classes/Net/HTTP.src/M000157.html +0 -18
  86. data/doc/classes/Net/HTTP.src/M000158.html +0 -19
  87. data/doc/classes/Net/HTTP.src/M000159.html +0 -21
  88. data/doc/classes/Net/HTTP.src/M000161.html +0 -19
  89. data/doc/classes/Net/HTTPError.html +0 -120
  90. data/doc/classes/Net/HTTPExceptions.html +0 -137
  91. data/doc/classes/Net/HTTPFatalError.html +0 -120
  92. data/doc/classes/Net/HTTPGenericRequest.html +0 -274
  93. data/doc/classes/Net/HTTPGenericRequest.src/M000088.html +0 -26
  94. data/doc/classes/Net/HTTPGenericRequest.src/M000089.html +0 -18
  95. data/doc/classes/Net/HTTPGenericRequest.src/M000090.html +0 -18
  96. data/doc/classes/Net/HTTPGenericRequest.src/M000091.html +0 -18
  97. data/doc/classes/Net/HTTPGenericRequest.src/M000092.html +0 -19
  98. data/doc/classes/Net/HTTPGenericRequest.src/M000093.html +0 -20
  99. data/doc/classes/Net/HTTPGenericRequest.src/M000094.html +0 -20
  100. data/doc/classes/Net/HTTPHeader.html +0 -734
  101. data/doc/classes/Net/HTTPHeader.src/M000055.html +0 -23
  102. data/doc/classes/Net/HTTPHeader.src/M000056.html +0 -19
  103. data/doc/classes/Net/HTTPHeader.src/M000057.html +0 -22
  104. data/doc/classes/Net/HTTPHeader.src/M000058.html +0 -22
  105. data/doc/classes/Net/HTTPHeader.src/M000059.html +0 -19
  106. data/doc/classes/Net/HTTPHeader.src/M000060.html +0 -19
  107. data/doc/classes/Net/HTTPHeader.src/M000061.html +0 -20
  108. data/doc/classes/Net/HTTPHeader.src/M000063.html +0 -18
  109. data/doc/classes/Net/HTTPHeader.src/M000065.html +0 -20
  110. data/doc/classes/Net/HTTPHeader.src/M000066.html +0 -20
  111. data/doc/classes/Net/HTTPHeader.src/M000067.html +0 -18
  112. data/doc/classes/Net/HTTPHeader.src/M000068.html +0 -18
  113. data/doc/classes/Net/HTTPHeader.src/M000069.html +0 -18
  114. data/doc/classes/Net/HTTPHeader.src/M000070.html +0 -20
  115. data/doc/classes/Net/HTTPHeader.src/M000072.html +0 -30
  116. data/doc/classes/Net/HTTPHeader.src/M000073.html +0 -43
  117. data/doc/classes/Net/HTTPHeader.src/M000075.html +0 -21
  118. data/doc/classes/Net/HTTPHeader.src/M000076.html +0 -22
  119. data/doc/classes/Net/HTTPHeader.src/M000077.html +0 -20
  120. data/doc/classes/Net/HTTPHeader.src/M000078.html +0 -21
  121. data/doc/classes/Net/HTTPHeader.src/M000079.html +0 -19
  122. data/doc/classes/Net/HTTPHeader.src/M000080.html +0 -18
  123. data/doc/classes/Net/HTTPHeader.src/M000081.html +0 -19
  124. data/doc/classes/Net/HTTPHeader.src/M000082.html +0 -19
  125. data/doc/classes/Net/HTTPHeader.src/M000083.html +0 -23
  126. data/doc/classes/Net/HTTPHeader.src/M000084.html +0 -18
  127. data/doc/classes/Net/HTTPHeader.src/M000086.html +0 -18
  128. data/doc/classes/Net/HTTPHeader.src/M000087.html +0 -18
  129. data/doc/classes/Net/HTTPRequest.html +0 -150
  130. data/doc/classes/Net/HTTPRequest.src/M000102.html +0 -21
  131. data/doc/classes/Net/HTTPResponse.html +0 -425
  132. data/doc/classes/Net/HTTPResponse.src/M000162.html +0 -18
  133. data/doc/classes/Net/HTTPResponse.src/M000163.html +0 -18
  134. data/doc/classes/Net/HTTPResponse.src/M000164.html +0 -23
  135. data/doc/classes/Net/HTTPResponse.src/M000165.html +0 -18
  136. data/doc/classes/Net/HTTPResponse.src/M000166.html +0 -32
  137. data/doc/classes/Net/HTTPResponse.src/M000167.html +0 -18
  138. data/doc/classes/Net/HTTPRetriableError.html +0 -120
  139. data/doc/classes/Net/HTTPServerException.html +0 -120
  140. data/doc/classes/Net/ProtoAuthError.html +0 -113
  141. data/doc/classes/Net/ProtoCommandError.html +0 -113
  142. data/doc/classes/Net/ProtoFatalError.html +0 -113
  143. data/doc/classes/Net/ProtoRetriableError.html +0 -113
  144. data/doc/classes/Net/ProtoServerError.html +0 -113
  145. data/doc/classes/Net/ProtoSyntaxError.html +0 -113
  146. data/doc/classes/Net/ProtoUnknownError.html +0 -113
  147. data/doc/classes/Net/ProtocolError.html +0 -111
  148. data/doc/classes/Net/WriteAdapter.html +0 -235
  149. data/doc/classes/Net/WriteAdapter.src/M000095.html +0 -19
  150. data/doc/classes/Net/WriteAdapter.src/M000096.html +0 -18
  151. data/doc/classes/Net/WriteAdapter.src/M000097.html +0 -18
  152. data/doc/classes/Net/WriteAdapter.src/M000099.html +0 -19
  153. data/doc/classes/Net/WriteAdapter.src/M000100.html +0 -18
  154. data/doc/classes/Net/WriteAdapter.src/M000101.html +0 -18
  155. data/doc/classes/REXML/Comment.html +0 -137
  156. data/doc/classes/REXML/Comment.src/M000053.html +0 -18
  157. data/doc/classes/REXML/Node.html +0 -240
  158. data/doc/classes/REXML/Node.src/M000047.html +0 -21
  159. data/doc/classes/REXML/Node.src/M000048.html +0 -21
  160. data/doc/classes/REXML/Node.src/M000049.html +0 -22
  161. data/doc/classes/REXML/Node.src/M000050.html +0 -18
  162. data/doc/classes/REXML/Node.src/M000051.html +0 -18
  163. data/doc/classes/REXML/Node.src/M000052.html +0 -18
  164. data/doc/classes/REXML/Text.html +0 -137
  165. data/doc/classes/REXML/Text.src/M000054.html +0 -18
  166. data/doc/classes/REXML.html +0 -109
  167. data/doc/classes/WWW/Button.html +0 -190
  168. data/doc/classes/WWW/Button.src/M000028.html +0 -18
  169. data/doc/classes/WWW/Button.src/M000029.html +0 -18
  170. data/doc/classes/WWW/Button.src/M000030.html +0 -25
  171. data/doc/classes/WWW/CheckBox.html +0 -160
  172. data/doc/classes/WWW/CheckBox.src/M000024.html +0 -18
  173. data/doc/classes/WWW/Cookie.html +0 -207
  174. data/doc/classes/WWW/Cookie.src/M000032.html +0 -59
  175. data/doc/classes/WWW/Cookie.src/M000033.html +0 -21
  176. data/doc/classes/WWW/Cookie.src/M000034.html +0 -18
  177. data/doc/classes/WWW/CookieJar.html +0 -197
  178. data/doc/classes/WWW/CookieJar.src/M000008.html +0 -18
  179. data/doc/classes/WWW/CookieJar.src/M000009.html +0 -22
  180. data/doc/classes/WWW/CookieJar.src/M000010.html +0 -33
  181. data/doc/classes/WWW/CookieJar.src/M000011.html +0 -18
  182. data/doc/classes/WWW/Field.html +0 -174
  183. data/doc/classes/WWW/Field.src/M000045.html +0 -18
  184. data/doc/classes/WWW/Field.src/M000046.html +0 -26
  185. data/doc/classes/WWW/FileUpload.html +0 -163
  186. data/doc/classes/WWW/FileUpload.src/M000031.html +0 -19
  187. data/doc/classes/WWW/Form.html +0 -152
  188. data/doc/classes/WWW/Form.src/M000044.html +0 -19
  189. data/doc/classes/WWW/GlobalForm.html +0 -271
  190. data/doc/classes/WWW/GlobalForm.src/M000004.html +0 -24
  191. data/doc/classes/WWW/GlobalForm.src/M000005.html +0 -26
  192. data/doc/classes/WWW/GlobalForm.src/M000006.html +0 -46
  193. data/doc/classes/WWW/GlobalForm.src/M000007.html +0 -46
  194. data/doc/classes/WWW/ImageButton.html +0 -157
  195. data/doc/classes/WWW/ImageButton.src/M000027.html +0 -22
  196. data/doc/classes/WWW/Link.html +0 -160
  197. data/doc/classes/WWW/Link.src/M000025.html +0 -20
  198. data/doc/classes/WWW/Mechanize.html +0 -379
  199. data/doc/classes/WWW/Mechanize.src/M000013.html +0 -21
  200. data/doc/classes/WWW/Mechanize.src/M000014.html +0 -18
  201. data/doc/classes/WWW/Mechanize.src/M000015.html +0 -24
  202. data/doc/classes/WWW/Mechanize.src/M000016.html +0 -19
  203. data/doc/classes/WWW/Mechanize.src/M000017.html +0 -23
  204. data/doc/classes/WWW/Mechanize.src/M000018.html +0 -32
  205. data/doc/classes/WWW/Mechanize.src/M000019.html +0 -19
  206. data/doc/classes/WWW/Mechanize.src/M000020.html +0 -18
  207. data/doc/classes/WWW/Mechanize.src/M000021.html +0 -33
  208. data/doc/classes/WWW/Mechanize.src/M000022.html +0 -18
  209. data/doc/classes/WWW/Meta.html +0 -113
  210. data/doc/classes/WWW/Page.html +0 -313
  211. data/doc/classes/WWW/Page.src/M000036.html +0 -18
  212. data/doc/classes/WWW/Page.src/M000037.html +0 -18
  213. data/doc/classes/WWW/Page.src/M000038.html +0 -18
  214. data/doc/classes/WWW/Page.src/M000039.html +0 -19
  215. data/doc/classes/WWW/Page.src/M000040.html +0 -19
  216. data/doc/classes/WWW/Page.src/M000041.html +0 -19
  217. data/doc/classes/WWW/Page.src/M000042.html +0 -19
  218. data/doc/classes/WWW/Page.src/M000043.html +0 -19
  219. data/doc/classes/WWW/RadioButton.html +0 -160
  220. data/doc/classes/WWW/RadioButton.src/M000012.html +0 -18
  221. data/doc/classes/WWW/ResponseCodeError.html +0 -150
  222. data/doc/classes/WWW/ResponseCodeError.src/M000035.html +0 -18
  223. data/doc/classes/WWW/SelectList.html +0 -160
  224. data/doc/classes/WWW/SelectList.src/M000026.html +0 -28
  225. data/doc/classes/WWW.html +0 -130
  226. data/doc/created.rid +0 -1
  227. data/doc/files/CHANGELOG.html +0 -136
  228. data/doc/files/LICENSE.html +0 -531
  229. data/doc/files/README.html +0 -161
  230. data/doc/files/lib/mechanize/cookie_rb.html +0 -101
  231. data/doc/files/lib/mechanize/net-overrides/net/http_rb.html +0 -139
  232. data/doc/files/lib/mechanize/net-overrides/net/https_rb.html +0 -109
  233. data/doc/files/lib/mechanize/net-overrides/net/protocol_rb.html +0 -117
  234. data/doc/files/lib/mechanize/parsing_rb.html +0 -267
  235. data/doc/files/lib/mechanize/parsing_rb.src/M000001.html +0 -22
  236. data/doc/files/lib/mechanize/parsing_rb.src/M000002.html +0 -44
  237. data/doc/files/lib/mechanize/parsing_rb.src/M000003.html +0 -34
  238. data/doc/files/lib/mechanize_rb.html +0 -152
  239. data/doc/fr_class_index.html +0 -80
  240. data/doc/fr_file_index.html +0 -35
  241. data/doc/fr_method_index.html +0 -194
  242. data/doc/index.html +0 -24
  243. 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.0"
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
- post(uri, query)
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
@@ -12,6 +12,7 @@ s.mount("/send_cookies", SendCookiesTest)
12
12
  s.mount("/form_post", FormTest)
13
13
  s.mount("/form post", FormTest)
14
14
  s.mount("/response_code", ResponseCodeTest)
15
+ s.mount("/file_upload", FileUploadTest)
15
16
 
16
17
  trap("INT") { s.stop }
17
18
 
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
- def setup
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
- def setup
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
- def setup
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
- def setup
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) }
@@ -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
- def setup
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
@@ -0,0 +1,5 @@
1
+ module TestMethods
2
+ def setup
3
+ @port = 2000
4
+ end
5
+ end
data/test/ts_mech.rb CHANGED
@@ -6,4 +6,6 @@ require 'tc_forms'
6
6
  require 'tc_mech'
7
7
  require 'tc_links'
8
8
  require 'tc_response_code'
9
+ require 'tc_upload'
10
+ require 'tc_forms'
9
11