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.
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']
Character encoding of form data (i.e. UTF-8)
Content-Type for form data (i.e. application/x-www-form-urlencoded)
When true, character encoding errors will never be never raised on form submission. Default is false
# File lib/mechanize/form.rb, line 41 def initialize(node, mech=nil, page=nil) @enctype = node['enctype'] || 'application/x-www-form-urlencoded' @form_node = node @action = Mechanize::Util.html_unescape(node['action']) @method = (node['method'] || 'GET').upcase @name = node['name'] @clicked_buttons = [] @page = page @mech = mech @encoding = node['accept-charset'] || (page && page.encoding) || nil @ignore_encoding_error = false parse end
Add a field with field_name and value
# File lib/mechanize/form.rb, line 103 def add_field!(field_name, value = nil) fields << Field.new({'name' => field_name}, value) 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 206 def build_query(buttons = []) query = [] @mech.log.info("form encoding: #{encoding}") if @mech && @mech.log successful_controls = [] (fields + checkboxes).sort.each do |f| case f when Mechanize::Form::CheckBox if f.checked successful_controls << f end when Mechanize::Form::Field successful_controls << f end end radio_groups = {} radiobuttons.each do |f| fname = from_native_charset(f.name) radio_groups[fname] ||= [] radio_groups[fname] << f end # take one radio button from each group radio_groups.each_value do |g| checked = g.select {|f| f.checked} if checked.uniq.size > 1 then values = checked.map { |button| button.value }.join(', ').inspect name = checked.first.name.inspect raise Mechanize::Error, "radiobuttons #{values} are checked in the #{name} group, " "only one is allowed" else successful_controls << checked.first unless checked.empty? end end @clicked_buttons.each { |b| successful_controls << b } successful_controls.sort.each do |ctrl| # DOM order qval = proc_query(ctrl) query.push(*qval) end query end
Find one checkbox that matches criteria Example:
form.checkbox_with(:name => /woo/).check
# File lib/mechanize/form.rb, line 382
Find all checkboxes that match criteria Example:
form.checkboxes_with(:name => /woo/).each do |field| field.check end
# File lib/mechanize/form.rb, line 391 elements_with :checkbox, :checkboxes
Removes all fields with name field_name.
# File lib/mechanize/form.rb, line 299 def delete_field!(field_name) @fields.delete_if{ |f| f.name == field_name} end
This method is a shortcut to get form's DOM class. Common usage:
page.form_with(:dom_class => "foorm")
Note that you can also use :class to get to this method:
page.form_with(:class => "foorm")
# File lib/mechanize/form.rb, line 98 def dom_class form_node['class'] end
This method is a shortcut to get form's DOM id. Common usage:
page.form_with(:dom_id => "foorm")
Note that you can also use :id to get to this method:
page.form_with(:id => "foorm")
# File lib/mechanize/form.rb, line 89 def dom_id form_node['id'] end
Find one field that matches criteria Example:
form.field_with(:id => "exact_field_id").value = 'hello'
# File lib/mechanize/form.rb, line 310
Find all fields that match criteria Example:
form.fields_with(:value => /foo/).each do |field| field.value = 'hello!' end
# File lib/mechanize/form.rb, line 319 elements_with :field
Find one file upload field that matches criteria Example:
form.file_upload_with(:file_name => /picture/).value = 'foo'
# File lib/mechanize/form.rb, line 346
Find all file upload fields that match criteria Example:
form.file_uploads_with(:file_name => /picutre/).each do |field| field.value = 'foo!' end
# File lib/mechanize/form.rb, line 355 elements_with :file_upload
Returns whether or not the form contains a field with
field_name
# File lib/mechanize/form.rb, line 57 def has_field?(field_name) fields.find { |f| f.name == field_name } end
# File lib/mechanize/form.rb, line 63 def has_value?(value) fields.find { |f| f.value == value } end
# File lib/mechanize/form.rb, line 76 def keygens ; @keygens ||= fields.select { |f| f.class == Keygen }; end
# File lib/mechanize/form.rb, line 67 def keys; fields.map { |f| f.name }; end
Treat form fields like accessors.
# File lib/mechanize/form.rb, line 165 def method_missing(meth, *args) method = meth.to_s.gsub(/=$/, '') if field(method) return field(method).value if args.empty? return field(method).value = args[0] end super 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 274 def request_data query_params = build_query() case @enctype.downcase when /^multipart\/form-data/ boundary = rand_string(20) @enctype = "multipart/form-data; boundary=#{boundary}" params = query_params.map do |k,v| param_to_multipart(k, v) if k end.compact params.concat @file_uploads.map { |f| file_to_multipart(f) } params.map do |part| part.force_encoding('ASCII-8BIT') if part.respond_to? :force_encoding "--#{boundary}\r\n#{part}" end.join('') + "--#{boundary}--\r\n" else Mechanize::Util.build_query_string(query_params) end end
# File lib/mechanize/form.rb, line 72 def resets ; @resets ||= buttons.select { |f| f.class == Reset }; end
This method sets multiple fields on the form. It takes a list of
fields which are 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 121 def set_fields fields = {} fields.each do |name, v| case v when Hash v.each do |index, value| self.fields_with(:name => name.to_s)[index].value = value end else value = nil index = 0 [v].flatten.each do |val| index = val.to_i if value value = val unless value end self.fields_with(:name => name.to_s)[index].value = value end end end
Submit this form with the button passed in
# File lib/mechanize/form.rb, line 177 def submit button=nil, headers = {} @mech.submit(self, button, headers) end
# File lib/mechanize/form.rb, line 71 def submits ; @submits ||= buttons.select { |f| f.class == Submit }; end
# File lib/mechanize/form.rb, line 80 def text_field?(field_name) texts.find{|f| f.name == field_name}; end
# File lib/mechanize/form.rb, line 82 def textarea_field?(field_name) textareas.find{|f| f.name == field_name}; end
# File lib/mechanize/form.rb, line 75 def textareas; @textareas ||= fields.select { |f| f.class == Textarea }; end
# File lib/mechanize/form.rb, line 73 def texts ; @texts ||= fields.select { |f| f.class == Text }; end
# File lib/mechanize/form.rb, line 69 def values; fields.map { |f| f.value }; end
# File lib/mechanize/form.rb, line 508 def file_to_multipart(file) file_name = file.file_name ? ::File.basename(file.file_name) : '' body = "Content-Disposition: form-data; name=\"" + "#{mime_value_quote(file.name)}\"; " + "filename=\"#{mime_value_quote(file_name)}\"\r\n" + "Content-Transfer-Encoding: binary\r\n" if file.file_data.nil? and file.file_name file.file_data = open(file.file_name, "rb") { |f| f.read } file.mime_type = WEBrick::HTTPUtils.mime_type(file.file_name, WEBrick::HTTPUtils::DefaultMimeTypes) end if file.mime_type body << "Content-Type: #{file.mime_type}\r\n" end body << if file.file_data.respond_to? :read "\r\n#{file.file_data.read}\r\n" else "\r\n#{file.file_data}\r\n" end body end
# File lib/mechanize/form.rb, line 197 def from_native_charset str Mechanize::Util.from_native_charset(str, encoding, @ignore_encoding_error, @mech && @mech.log) end
# File lib/mechanize/form.rb, line 498 def mime_value_quote(str) str.gsub(/(["\r\])/){|s| '\' + s} end
# File lib/mechanize/form.rb, line 502 def param_to_multipart(name, value) return "Content-Disposition: form-data; name=\"" + "#{mime_value_quote(name)}\"\r\n" + "\r\n#{value}\r\n" end
# File lib/mechanize/form.rb, line 423 def parse @fields = [] @buttons = [] @file_uploads = [] @radiobuttons = [] @checkboxes = [] # Find all input tags form_node.search('input').each do |node| type = (node['type'] || 'text').downcase name = node['name'] next if name.nil? && !%w[submit button image].include?(type) case type when 'radio' @radiobuttons << RadioButton.new(node, self) when 'checkbox' @checkboxes << CheckBox.new(node, self) when 'file' @file_uploads << FileUpload.new(node, nil) when 'submit' @buttons << Submit.new(node) when 'button' @buttons << Button.new(node) when 'reset' @buttons << Reset.new(node) when 'image' @buttons << ImageButton.new(node) when 'hidden' @fields << Hidden.new(node, node['value'] || '') when 'text' @fields << Text.new(node, node['value'] || '') when 'textarea' @fields << Textarea.new(node, node['value'] || '') else @fields << Field.new(node, node['value'] || '') end end # Find all textarea tags form_node.search('textarea').each do |node| next unless node['name'] @fields << Textarea.new(node, node.inner_text) end # Find all select tags form_node.search('select').each do |node| next unless node['name'] if node.has_attribute? 'multiple' @fields << MultiSelectList.new(node) else @fields << SelectList.new(node) end end # Find all submit button tags # FIXME: what can I do with the reset buttons? form_node.search('button').each do |node| type = (node['type'] || 'submit').downcase next if type == 'reset' @buttons << Button.new(node) end # Find all keygen tags form_node.search('keygen').each do |node| @fields << Keygen.new(node, node['value'] || '') end 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 189 def proc_query(field) return unless field.query_value field.query_value.map{|(name, val)| [from_native_charset(name), from_native_charset(val.to_s)] } end
# File lib/mechanize/form.rb, line 491 def rand_string(len = 10) chars = ("a".."z").to_a + ("A".."Z").to_a string = "" 1.upto(len) { |i| string << chars[rand(chars.size-1)] } string end