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