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 |
| pretty_inspect | -> | inspect |
| 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 = Util.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
Fetch the value of the first input field with the name passed in
Fetch the value set in the input field ‘name‘
puts form['name']
# File lib/www/mechanize/form.rb, line 100
100: def [](field_name)
101: f = field(field_name)
102: f && f.value
103: 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 109
109: def []=(field_name, value)
110: f = field(field_name)
111: if f.nil?
112: add_field!(field_name, value)
113: else
114: f.value = value
115: end
116: 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 180
180: def add_button_to_query(button)
181: @clicked_buttons << button
182: end
Add a field with field_name and value
# File lib/www/mechanize/form.rb, line 65
65: def add_field!(field_name, value = nil)
66: fields << Field.new(field_name, value)
67: 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 142
142: def build_query(buttons = [])
143: query = []
144:
145: fields().each do |f|
146: query.push(*f.query_value)
147: end
148:
149: checkboxes().each do |f|
150: query.push(*f.query_value) if f.checked
151: end
152:
153: radio_groups = {}
154: radiobuttons().each do |f|
155: radio_groups[f.name] ||= []
156: radio_groups[f.name] << f
157: end
158:
159: # take one radio button from each group
160: radio_groups.each_value do |g|
161: checked = g.select {|f| f.checked}
162:
163: if checked.size == 1
164: f = checked.first
165: query.push(*f.query_value)
166: elsif checked.size > 1
167: raise "multiple radiobuttons are checked in the same group!"
168: end
169: end
170:
171: @clicked_buttons.each { |b|
172: query.push(*b.query_value)
173: }
174:
175: query
176: end
Submit form using button. Defaults to the first button.
# File lib/www/mechanize/form.rb, line 135
135: def click_button(button = buttons.first)
136: submit(button)
137: end
Removes all fields with name field_name.
# File lib/www/mechanize/form.rb, line 204
204: def delete_field!(field_name)
205: @fields.delete_if{ |f| f.name == field_name}
206: end
Returns whether or not the form contains a field with field_name
# File lib/www/mechanize/form.rb, line 50
50: def has_field?(field_name)
51: ! fields.find { |f| f.name.eql? field_name }.nil?
52: 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 119
119: def method_missing(id,*args)
120: method = id.to_s.gsub(/=$/, '')
121: if field(method)
122: return field(method).value if args.empty?
123: return field(method).value = args[0]
124: end
125: super
126: 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 187
187: def request_data
188: query_params = build_query()
189: case @enctype.downcase
190: when /^multipart\/form-data/
191: boundary = rand_string(20)
192: @enctype = "multipart/form-data; boundary=#{boundary}"
193: params = []
194: query_params.each { |k,v| params << param_to_multipart(k, v) }
195: @file_uploads.each { |f| params << file_to_multipart(f) }
196: params.collect { |p| "--#{boundary}\r\n#{p}" }.join('') +
197: "--#{boundary}--\r\n"
198: else
199: WWW::Mechanize::Util.build_query_string(query_params)
200: end
201: 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 a Hash with the key 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 => { 1 => 'bar' } )
# File lib/www/mechanize/form.rb, line 77
77: def set_fields(fields = {})
78: fields.each do |k,v|
79: case v
80: when Hash
81: v.each do |index, value|
82: self.fields_with(:name => k.to_s).[](index).value = value
83: end
84: else
85: value = nil
86: index = 0
87: [v].flatten.each do |val|
88: index = val.to_i unless value.nil?
89: value = val if value.nil?
90: end
91: self.fields_with(:name => k.to_s).[](index).value = value
92: end
93: end
94: end
Submit this form with the button passed in
# File lib/www/mechanize/form.rb, line 129
129: def submit(button=nil)
130: @mech.submit(self, button)
131: end
# File lib/www/mechanize/form.rb, line 307
307: def file_to_multipart(file)
308: file_name = file.file_name ? ::File.basename(file.file_name) : ''
309: body = "Content-Disposition: form-data; name=\"" +
310: "#{mime_value_quote(file.name)}\"; " +
311: "filename=\"#{mime_value_quote(file_name)}\"\r\n" +
312: "Content-Transfer-Encoding: binary\r\n"
313:
314: if file.file_data.nil? and ! file.file_name.nil?
315: file.file_data = ::File.open(file.file_name, "rb") { |f| f.read }
316: file.mime_type = WEBrick::HTTPUtils.mime_type(file.file_name,
317: WEBrick::HTTPUtils::DefaultMimeTypes)
318: end
319:
320: if file.mime_type != nil
321: body << "Content-Type: #{file.mime_type}\r\n"
322: end
323:
324: body <<
325: if file.file_data.respond_to? :read
326: "\r\n#{file.file_data.read}\r\n"
327: else
328: "\r\n#{file.file_data}\r\n"
329: end
330:
331: body
332: end
# File lib/www/mechanize/form.rb, line 297
297: def mime_value_quote(str)
298: str.gsub(/(["\r\\])/){|s| '\\' + s}
299: end
# File lib/www/mechanize/form.rb, line 301
301: def param_to_multipart(name, value)
302: return "Content-Disposition: form-data; name=\"" +
303: "#{mime_value_quote(name)}\"\r\n" +
304: "\r\n#{value}\r\n"
305: end
# File lib/www/mechanize/form.rb, line 235
235: def parse
236: @fields = WWW::Mechanize::List.new
237: @buttons = WWW::Mechanize::List.new
238: @file_uploads = WWW::Mechanize::List.new
239: @radiobuttons = WWW::Mechanize::List.new
240: @checkboxes = WWW::Mechanize::List.new
241:
242: # Find all input tags
243: form_node.search('input').each do |node|
244: type = (node['type'] || 'text').downcase
245: name = node['name']
246: next if name.nil? && !(type == 'submit' || type =='button')
247: case type
248: when 'radio'
249: @radiobuttons << RadioButton.new(node['name'], node['value'], !!node['checked'], self)
250: when 'checkbox'
251: @checkboxes << CheckBox.new(node['name'], node['value'], !!node['checked'], self)
252: when 'file'
253: @file_uploads << FileUpload.new(node['name'], nil)
254: when 'submit'
255: @buttons << Button.new(node['name'], node['value'])
256: when 'button'
257: @buttons << Button.new(node['name'], node['value'])
258: when 'image'
259: @buttons << ImageButton.new(node['name'], node['value'])
260: else
261: @fields << Field.new(node['name'], node['value'] || '')
262: end
263: end
264:
265: # Find all textarea tags
266: form_node.search('textarea').each do |node|
267: next if node['name'].nil?
268: @fields << Field.new(node['name'], node.inner_text)
269: end
270:
271: # Find all select tags
272: form_node.search('select').each do |node|
273: next if node['name'].nil?
274: if node.has_attribute? 'multiple'
275: @fields << MultiSelectList.new(node['name'], node)
276: else
277: @fields << SelectList.new(node['name'], node)
278: end
279: end
280:
281: # Find all submit button tags
282: # FIXME: what can I do with the reset buttons?
283: form_node.search('button').each do |node|
284: type = (node['type'] || 'submit').downcase
285: next if type == 'reset'
286: @buttons << Button.new(node['name'], node['value'])
287: end
288: end