This class encapsulates a form parsed out of an HTML page. Each type of input fields available in a form can be accessed through this object. See GlobalForm for more methods.
Find a form and print out its fields
form = page.forms.first # => WWW::Mechanize::Form
form.fields.each { |f| puts f.name }
Set the input field ‘name’ to "Aaron"
form['name'] = 'Aaron' puts form['name']
| fields | -> | elements |
| action | [RW] | |
| buttons | [R] | |
| checkboxes | [R] | |
| enctype | [RW] | |
| fields | [R] | |
| file_uploads | [R] | |
| form_node | [R] | |
| method | [RW] | |
| name | [RW] | |
| page | [R] | |
| radiobuttons | [R] |
# File lib/www/mechanize/form.rb, line 36
36: def initialize(node, mech=nil, page=nil)
37: @enctype = node['enctype'] || 'application/x-www-form-urlencoded'
38: @form_node = node
39: @action = Mechanize.html_unescape(node['action'])
40: @method = (node['method'] || 'GET').upcase
41: @name = node['name']
42: @clicked_buttons = []
43: @page = page
44: @mech = mech
45:
46: parse
47: end
Set the value of the first input field with the name passed in
Set the value in the input field ‘name’ to "Aaron"
form['name'] = 'Aaron'
# File lib/www/mechanize/form.rb, line 107
107: def []=(field_name, value)
108: f = field(field_name)
109: if f.nil?
110: add_field!(field_name, value)
111: else
112: f.value = value
113: end
114: end
This method adds a button to the query. If the form needs to be submitted with multiple buttons, pass each button to this method.
# File lib/www/mechanize/form.rb, line 172
172: def add_button_to_query(button)
173: @clicked_buttons << button
174: end
This method builds an array of arrays that represent the query parameters to be used with this form. The return value can then be used to create a query string for this form.
# File lib/www/mechanize/form.rb, line 134
134: def build_query(buttons = [])
135: query = []
136:
137: fields().each do |f|
138: query.push(*f.query_value)
139: end
140:
141: checkboxes().each do |f|
142: query.push(*f.query_value) if f.checked
143: end
144:
145: radio_groups = {}
146: radiobuttons().each do |f|
147: radio_groups[f.name] ||= []
148: radio_groups[f.name] << f
149: end
150:
151: # take one radio button from each group
152: radio_groups.each_value do |g|
153: checked = g.select {|f| f.checked}
154:
155: if checked.size == 1
156: f = checked.first
157: query.push(*f.query_value)
158: elsif checked.size > 1
159: raise "multiple radiobuttons are checked in the same group!"
160: end
161: end
162:
163: @clicked_buttons.each { |b|
164: query.push(*b.query_value)
165: }
166:
167: query
168: end
Removes all fields with name field_name.
# File lib/www/mechanize/form.rb, line 196
196: def delete_field!(field_name)
197: @fields.delete_if{ |f| f.name == field_name}
198: end
# File lib/www/mechanize/form.rb, line 56
56: def has_value?(value)
57: ! fields.find { |f| f.value.eql? value }.nil?
58: end
Treat form fields like accessors.
# File lib/www/mechanize/form.rb, line 117
117: def method_missing(id,*args)
118: method = id.to_s.gsub(/=$/, '')
119: if field(method)
120: return field(method).value if args.empty?
121: return field(method).value = args[0]
122: end
123: super
124: end
This method calculates the request data to be sent back to the server for this form, depending on if this is a regular post, get, or a multi-part post,
# File lib/www/mechanize/form.rb, line 179
179: def request_data
180: query_params = build_query()
181: case @enctype.downcase
182: when 'multipart/form-data'
183: boundary = rand_string(20)
184: @enctype << "; boundary=#{boundary}"
185: params = []
186: query_params.each { |k,v| params << param_to_multipart(k, v) }
187: @file_uploads.each { |f| params << file_to_multipart(f) }
188: params.collect { |p| "--#{boundary}\r\n#{p}" }.join('') +
189: "--#{boundary}--\r\n"
190: else
191: WWW::Mechanize.build_query_string(query_params)
192: end
193: end
This method sets multiple fields on the form. It takes a list of field name, value pairs. If there is more than one field found with the same name, this method will set the first one found. If you want to set the value of a duplicate field, use a value which is an Array with the second value of the array as the index in to the form. The index is zero based. For example, to set the second field named ‘foo’, you could do the following:
form.set_fields( :foo => ['bar', 1] )
# File lib/www/mechanize/form.rb, line 82
82: def set_fields(fields = {})
83: fields.each do |k,v|
84: value = nil
85: index = 0
86: v.each do |val|
87: index = val.to_i unless value.nil?
88: value = val if value.nil?
89: end
90: self.fields.name(k.to_s).[](index).value = value
91: end
92: end
Submit this form with the button passed in
# File lib/www/mechanize/form.rb, line 127
127: def submit(button=nil)
128: @mech.submit(self, button)
129: end
# File lib/www/mechanize/form.rb, line 265
265: def file_to_multipart(file)
266: file_name = file.file_name ? ::File.basename(file.file_name) : ''
267: body = "Content-Disposition: form-data; name=\"" +
268: "#{mime_value_quote(file.name)}\"; " +
269: "filename=\"#{mime_value_quote(file_name)}\"\r\n" +
270: "Content-Transfer-Encoding: binary\r\n"
271:
272: if file.file_data.nil? and ! file.file_name.nil?
273: file.file_data = ::File.open(file.file_name, "rb") { |f| f.read }
274: file.mime_type = WEBrick::HTTPUtils.mime_type(file.file_name,
275: WEBrick::HTTPUtils::DefaultMimeTypes)
276: end
277:
278: if file.mime_type != nil
279: body << "Content-Type: #{file.mime_type}\r\n"
280: end
281:
282: body <<
283: if file.file_data.respond_to? :read
284: "\r\n#{file.file_data.read}\r\n"
285: else
286: "\r\n#{file.file_data}\r\n"
287: end
288:
289: body
290: end
# File lib/www/mechanize/form.rb, line 255
255: def mime_value_quote(str)
256: str.gsub(/(["\r\\])/){|s| '\\' + s}
257: end
# File lib/www/mechanize/form.rb, line 259
259: def param_to_multipart(name, value)
260: return "Content-Disposition: form-data; name=\"" +
261: "#{mime_value_quote(name)}\"\r\n" +
262: "\r\n#{value}\r\n"
263: end
# File lib/www/mechanize/form.rb, line 201
201: def parse
202: @fields = WWW::Mechanize::List.new
203: @buttons = WWW::Mechanize::List.new
204: @file_uploads = WWW::Mechanize::List.new
205: @radiobuttons = WWW::Mechanize::List.new
206: @checkboxes = WWW::Mechanize::List.new
207:
208: # Find all input tags
209: (form_node/'input').each do |node|
210: type = (node['type'] || 'text').downcase
211: name = node['name']
212: next if name.nil? && !(type == 'submit' || type =='button')
213: case type
214: when 'radio'
215: @radiobuttons << RadioButton.new(node['name'], node['value'], node.has_attribute?('checked'), self)
216: when 'checkbox'
217: @checkboxes << CheckBox.new(node['name'], node['value'], node.has_attribute?('checked'), self)
218: when 'file'
219: @file_uploads << FileUpload.new(node['name'], nil)
220: when 'submit'
221: @buttons << Button.new(node['name'], node['value'])
222: when 'button'
223: @buttons << Button.new(node['name'], node['value'])
224: when 'image'
225: @buttons << ImageButton.new(node['name'], node['value'])
226: else
227: @fields << Field.new(node['name'], node['value'] || '')
228: end
229: end
230:
231: # Find all textarea tags
232: (form_node/'textarea').each do |node|
233: next if node['name'].nil?
234: @fields << Field.new(node['name'], node.inner_text)
235: end
236:
237: # Find all select tags
238: (form_node/'select').each do |node|
239: next if node['name'].nil?
240: if node.has_attribute? 'multiple'
241: @fields << MultiSelectList.new(node['name'], node)
242: else
243: @fields << SelectList.new(node['name'], node)
244: end
245: end
246: end