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 206 206: def add_button_to_query(button) 207: @clicked_buttons << button 208: 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 162 162: def build_query(buttons = []) 163: query = [] 164: 165: fields().each do |f| 166: qval = proc_query(f) 167: query.push(*qval) 168: end 169: 170: checkboxes().each do |f| 171: if f.checked 172: qval = proc_query(f) 173: query.push(*qval) 174: end 175: end 176: 177: radio_groups = {} 178: radiobuttons().each do |f| 179: fname = from_native_charset(f.name) 180: radio_groups[fname] ||= [] 181: radio_groups[fname] << f 182: end 183: 184: # take one radio button from each group 185: radio_groups.each_value do |g| 186: checked = g.select {|f| f.checked} 187: 188: if checked.size == 1 189: f = checked.first 190: qval = proc_query(f) 191: query.push(*qval) 192: elsif checked.size > 1 193: raise "multiple radiobuttons are checked in the same group!" 194: end 195: end 196: 197: @clicked_buttons.each { |b| 198: qval = proc_query(b) 199: query.push(*qval) 200: } 201: query 202: 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 230 230: def delete_field!(field_name) 231: @fields.delete_if{ |f| f.name == field_name} 232: 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 213 213: def request_data 214: query_params = build_query() 215: case @enctype.downcase 216: when /^multipart\/form-data/ 217: boundary = rand_string(20) 218: @enctype = "multipart/form-data; boundary=#{boundary}" 219: params = [] 220: query_params.each { |k,v| params << param_to_multipart(k, v) unless k.nil? } 221: @file_uploads.each { |f| params << file_to_multipart(f) } 222: params.collect { |p| "--#{boundary}\r\n#{p}" }.join('') + 223: "--#{boundary}--\r\n" 224: else 225: WWW::Mechanize::Util.build_query_string(query_params) 226: end 227: 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, headers = {} 130: @mech.submit(self, button, headers) 131: end
# File lib/www/mechanize/form.rb, line 333 333: def file_to_multipart(file) 334: file_name = file.file_name ? ::File.basename(file.file_name) : '' 335: body = "Content-Disposition: form-data; name=\"" + 336: "#{mime_value_quote(file.name)}\"; " + 337: "filename=\"#{mime_value_quote(file_name)}\"\r\n" + 338: "Content-Transfer-Encoding: binary\r\n" 339: 340: if file.file_data.nil? and ! file.file_name.nil? 341: file.file_data = ::File.open(file.file_name, "rb") { |f| f.read } 342: file.mime_type = WEBrick::HTTPUtils.mime_type(file.file_name, 343: WEBrick::HTTPUtils::DefaultMimeTypes) 344: end 345: 346: if file.mime_type != nil 347: body << "Content-Type: #{file.mime_type}\r\n" 348: end 349: 350: body << 351: if file.file_data.respond_to? :read 352: "\r\n#{file.file_data.read}\r\n" 353: else 354: "\r\n#{file.file_data}\r\n" 355: end 356: 357: body 358: end
# File lib/www/mechanize/form.rb, line 149 149: def from_native_charset(str, enc=nil) 150: if page 151: enc ||= page.encoding 152: Util.from_native_charset(str,enc) 153: else 154: str 155: end 156: end
# File lib/www/mechanize/form.rb, line 323 323: def mime_value_quote(str) 324: str.gsub(/(["\r\\])/){|s| '\\' + s} 325: end
# File lib/www/mechanize/form.rb, line 327 327: def param_to_multipart(name, value) 328: return "Content-Disposition: form-data; name=\"" + 329: "#{mime_value_quote(name)}\"\r\n" + 330: "\r\n#{value}\r\n" 331: end
# File lib/www/mechanize/form.rb, line 261 261: def parse 262: @fields = [] 263: @buttons = [] 264: @file_uploads = [] 265: @radiobuttons = [] 266: @checkboxes = [] 267: 268: # Find all input tags 269: form_node.search('input').each do |node| 270: type = (node['type'] || 'text').downcase 271: name = node['name'] 272: next if name.nil? && !(type == 'submit' || type =='button') 273: case type 274: when 'radio' 275: @radiobuttons << RadioButton.new(node['name'], node['value'], !!node['checked'], self) 276: when 'checkbox' 277: @checkboxes << CheckBox.new(node['name'], node['value'], !!node['checked'], self) 278: when 'file' 279: @file_uploads << FileUpload.new(node['name'], nil) 280: when 'submit' 281: @buttons << Button.new(node['name'], node['value']) 282: when 'button' 283: @buttons << Button.new(node['name'], node['value']) 284: when 'image' 285: @buttons << ImageButton.new(node['name'], node['value']) 286: else 287: @fields << Field.new(node['name'], node['value'] || '') 288: end 289: end 290: 291: # Find all textarea tags 292: form_node.search('textarea').each do |node| 293: next if node['name'].nil? 294: @fields << Field.new(node['name'], node.inner_text) 295: end 296: 297: # Find all select tags 298: form_node.search('select').each do |node| 299: next if node['name'].nil? 300: if node.has_attribute? 'multiple' 301: @fields << MultiSelectList.new(node['name'], node) 302: else 303: @fields << SelectList.new(node['name'], node) 304: end 305: end 306: 307: # Find all submit button tags 308: # FIXME: what can I do with the reset buttons? 309: form_node.search('button').each do |node| 310: type = (node['type'] || 'submit').downcase 311: next if type == 'reset' 312: @buttons << Button.new(node['name'], node['value']) 313: end 314: end
This method is sub-method of build_query. It converts charset of query value of fields into excepted one.
# File lib/www/mechanize/form.rb, line 141 141: def proc_query(field) 142: return unless field.query_value 143: field.query_value.map{|(name, val)| 144: [from_native_charset(name), from_native_charset(val.to_s)] 145: } 146: end