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