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 # => 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/mechanize/form.rb, line 35 35: def initialize(node, mech=nil, page=nil) 36: @enctype = node['enctype'] || 'application/x-www-form-urlencoded' 37: @form_node = node 38: @action = Util.html_unescape(node['action']) 39: @method = (node['method'] || 'GET').upcase 40: @name = node['name'] 41: @clicked_buttons = [] 42: @page = page 43: @mech = mech 44: 45: parse 46: 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/mechanize/form.rb, line 111 111: def [](field_name) 112: f = field(field_name) 113: f && f.value 114: 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/mechanize/form.rb, line 120 120: def []=(field_name, value) 121: f = field(field_name) 122: if f.nil? 123: add_field!(field_name, value) 124: else 125: f.value = value 126: end 127: 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/mechanize/form.rb, line 213 213: def add_button_to_query(button) 214: @clicked_buttons << button 215: end
Add a field with field_name and value
# File lib/mechanize/form.rb, line 76 76: def add_field!(field_name, value = nil) 77: fields << Field.new({'name' => field_name}, value) 78: 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/mechanize/form.rb, line 168 168: def build_query(buttons = []) 169: query = [] 170: 171: (fields + checkboxes).sort.each do |f| 172: case f 173: when Form::CheckBox 174: if f.checked 175: qval = proc_query(f) 176: query.push(*qval) 177: end 178: when Form::Field 179: qval = proc_query(f) 180: query.push(*qval) 181: end 182: end 183: 184: radio_groups = {} 185: radiobuttons.each do |f| 186: fname = from_native_charset(f.name) 187: radio_groups[fname] ||= [] 188: radio_groups[fname] << f 189: end 190: 191: # take one radio button from each group 192: radio_groups.each_value do |g| 193: checked = g.select {|f| f.checked} 194: 195: if checked.size == 1 196: f = checked.first 197: qval = proc_query(f) 198: query.push(*qval) 199: elsif checked.size > 1 200: raise "multiple radiobuttons are checked in the same group!" 201: end 202: end 203: 204: @clicked_buttons.each { |b| 205: qval = proc_query(b) 206: query.push(*qval) 207: } 208: query 209: end
Removes all fields with name field_name.
# File lib/mechanize/form.rb, line 237 237: def delete_field!(field_name) 238: @fields.delete_if{ |f| f.name == field_name} 239: end
Returns whether or not the form contains a field with field_name
# File lib/mechanize/form.rb, line 49 49: def has_field?(field_name) 50: ! fields.find { |f| f.name.eql? field_name }.nil? 51: end
# File lib/mechanize/form.rb, line 55 55: def has_value?(value) 56: ! fields.find { |f| f.value.eql? value }.nil? 57: end
# File lib/mechanize/form.rb, line 72 72: def hidden_field?(field_name) !! hiddens.find{|f| f.name == field_name}; end
# File lib/mechanize/form.rb, line 66 66: def hiddens ; @hiddens ||= fields.select { |f| f.class == Hidden }; end
Treat form fields like accessors.
# File lib/mechanize/form.rb, line 130 130: def method_missing(id,*args) 131: method = id.to_s.gsub(/=$/, '') 132: if field(method) 133: return field(method).value if args.empty? 134: return field(method).value = args[0] 135: end 136: super 137: 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/mechanize/form.rb, line 220 220: def request_data 221: query_params = build_query() 222: case @enctype.downcase 223: when /^multipart\/form-data/ 224: boundary = rand_string(20) 225: @enctype = "multipart/form-data; boundary=#{boundary}" 226: params = [] 227: query_params.each { |k,v| params << param_to_multipart(k, v) unless k.nil? } 228: @file_uploads.each { |f| params << file_to_multipart(f) } 229: params.collect { |p| "--#{boundary}\r\n#{p}" }.join('') + 230: "--#{boundary}--\r\n" 231: else 232: Mechanize::Util.build_query_string(query_params) 233: end 234: end
# File lib/mechanize/form.rb, line 70 70: def reset_button?(button_name) !! resets.find{|f| f.name == button_name}; end
# File lib/mechanize/form.rb, line 64 64: def resets ; @resets ||= buttons.select { |f| f.class == Reset }; 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/mechanize/form.rb, line 88 88: def set_fields(fields = {}) 89: fields.each do |k,v| 90: case v 91: when Hash 92: v.each do |index, value| 93: self.fields_with(:name => k.to_s).[](index).value = value 94: end 95: else 96: value = nil 97: index = 0 98: [v].flatten.each do |val| 99: index = val.to_i unless value.nil? 100: value = val if value.nil? 101: end 102: self.fields_with(:name => k.to_s).[](index).value = value 103: end 104: end 105: end
# File lib/mechanize/form.rb, line 69 69: def submit_button?(button_name) !! submits.find{|f| f.name == button_name}; end
# File lib/mechanize/form.rb, line 63 63: def submits ; @submits ||= buttons.select { |f| f.class == Submit }; end
# File lib/mechanize/form.rb, line 71 71: def text_field?(field_name) !! texts.find{|f| f.name == field_name}; end
# File lib/mechanize/form.rb, line 73 73: def textarea_field?(field_name) !!textareas.find{|f| f.name == field_name}; end
# File lib/mechanize/form.rb, line 67 67: def textareas; @textareas ||= fields.select { |f| f.class == Textarea }; end
# File lib/mechanize/form.rb, line 65 65: def texts ; @texts ||= fields.select { |f| f.class == Text }; end
# File lib/mechanize/form.rb, line 429 429: def file_to_multipart(file) 430: file_name = file.file_name ? ::File.basename(file.file_name) : '' 431: body = "Content-Disposition: form-data; name=\"" + 432: "#{mime_value_quote(file.name)}\"; " + 433: "filename=\"#{mime_value_quote(file_name)}\"\r\n" + 434: "Content-Transfer-Encoding: binary\r\n" 435: 436: if file.file_data.nil? and ! file.file_name.nil? 437: file.file_data = ::File.open(file.file_name, "rb") { |f| f.read } 438: file.mime_type = WEBrick::HTTPUtils.mime_type(file.file_name, 439: WEBrick::HTTPUtils::DefaultMimeTypes) 440: end 441: 442: if file.mime_type != nil 443: body << "Content-Type: #{file.mime_type}\r\n" 444: end 445: 446: body << 447: if file.file_data.respond_to? :read 448: "\r\n#{file.file_data.read}\r\n" 449: else 450: "\r\n#{file.file_data}\r\n" 451: end 452: 453: body 454: end
# File lib/mechanize/form.rb, line 160 160: def from_native_charset str 161: Util.from_native_charset(str,page && page.encoding) 162: end
# File lib/mechanize/form.rb, line 419 419: def mime_value_quote(str) 420: str.gsub(/(["\r\\])/){|s| '\\' + s} 421: end
# File lib/mechanize/form.rb, line 423 423: def param_to_multipart(name, value) 424: return "Content-Disposition: form-data; name=\"" + 425: "#{mime_value_quote(name)}\"\r\n" + 426: "\r\n#{value}\r\n" 427: end
# File lib/mechanize/form.rb, line 349 349: def parse 350: @fields = [] 351: @buttons = [] 352: @file_uploads = [] 353: @radiobuttons = [] 354: @checkboxes = [] 355: 356: # Find all input tags 357: form_node.search('input').each do |node| 358: type = (node['type'] || 'text').downcase 359: name = node['name'] 360: next if name.nil? && !(type == 'submit' || type =='button') 361: case type 362: when 'radio' 363: @radiobuttons << RadioButton.new(node, self) 364: when 'checkbox' 365: @checkboxes << CheckBox.new(node, self) 366: when 'file' 367: @file_uploads << FileUpload.new(node, nil) 368: when 'submit' 369: @buttons << Submit.new(node) 370: when 'button' 371: @buttons << Button.new(node) 372: when 'reset' 373: @buttons << Reset.new(node) 374: when 'image' 375: @buttons << ImageButton.new(node) 376: when 'hidden' 377: @fields << Hidden.new(node, node['value'] || '') 378: when 'text' 379: @fields << Text.new(node, node['value'] || '') 380: when 'textarea' 381: @fields << Textarea.new(node, node['value'] || '') 382: else 383: @fields << Field.new(node, node['value'] || '') 384: end 385: end 386: 387: # Find all textarea tags 388: form_node.search('textarea').each do |node| 389: next if node['name'].nil? 390: @fields << Field.new(node, node.inner_text) 391: end 392: 393: # Find all select tags 394: form_node.search('select').each do |node| 395: next if node['name'].nil? 396: if node.has_attribute? 'multiple' 397: @fields << MultiSelectList.new(node) 398: else 399: @fields << SelectList.new(node) 400: end 401: end 402: 403: # Find all submit button tags 404: # FIXME: what can I do with the reset buttons? 405: form_node.search('button').each do |node| 406: type = (node['type'] || 'submit').downcase 407: next if type == 'reset' 408: @buttons << Button.new(node) 409: end 410: end
This method is sub-method of build_query. It converts charset of query value of fields into expected one.
# File lib/mechanize/form.rb, line 152 152: def proc_query(field) 153: return unless field.query_value 154: field.query_value.map{|(name, val)| 155: [from_native_charset(name), from_native_charset(val.to_s)] 156: } 157: end