Class: Mechanize::Form

Inherits:
Object
  • Object
show all
Extended by:
Forwardable, ElementMatcher
Defined in:
lib/mechanize/form.rb

Overview

This class encapsulates a form parsed out of an HTML page. Each type of input field available in a form can be accessed through this object.

Examples

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']

Defined Under Namespace

Classes: Button, CheckBox, Field, FileUpload, Hidden, ImageButton, Keygen, MultiSelectList, Option, RadioButton, Reset, SelectList, Submit, Text, Textarea

Constant Summary collapse

CRLF =
"\r\n".freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ElementMatcher

elements_with

Constructor Details

#initialize(node, mech = nil, page = nil) ⇒ Form

Returns a new instance of Form.



43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/mechanize/form.rb', line 43

def initialize(node, mech = nil, page = nil)
  @enctype = node['enctype'] || 'application/x-www-form-urlencoded'
  @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

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args) ⇒ Object

Treat form fields like accessors.



258
259
260
261
262
263
264
265
266
267
# File 'lib/mechanize/form.rb', line 258

def method_missing(meth, *args)
  (method = meth.to_s).chomp!('=')

  if field(method)
    return field(method).value if args.empty?
    return field(method).value = args[0]
  end

  super
end

Instance Attribute Details

#actionObject

Returns the value of attribute action.



23
24
25
# File 'lib/mechanize/form.rb', line 23

def action
  @action
end

#buttonsObject (readonly)

Returns the value of attribute buttons.



25
26
27
# File 'lib/mechanize/form.rb', line 25

def buttons
  @buttons
end

#checkboxesObject (readonly)

Returns the value of attribute checkboxes.



25
26
27
# File 'lib/mechanize/form.rb', line 25

def checkboxes
  @checkboxes
end

#encodingObject

Character encoding of form data (i.e. UTF-8)



31
32
33
# File 'lib/mechanize/form.rb', line 31

def encoding
  @encoding
end

#enctypeObject

Content-Type for form data (i.e. application/x-www-form-urlencoded)



28
29
30
# File 'lib/mechanize/form.rb', line 28

def enctype
  @enctype
end

#fieldsObject (readonly) Also known as: elements

Returns the value of attribute fields.



25
26
27
# File 'lib/mechanize/form.rb', line 25

def fields
  @fields
end

#file_uploadsObject (readonly)

Returns the value of attribute file_uploads.



25
26
27
# File 'lib/mechanize/form.rb', line 25

def file_uploads
  @file_uploads
end

#ignore_encoding_errorObject

When true, character encoding errors will never be never raised on form submission. Default is false



35
36
37
# File 'lib/mechanize/form.rb', line 35

def ignore_encoding_error
  @ignore_encoding_error
end

#methodObject

Returns the value of attribute method.



23
24
25
# File 'lib/mechanize/form.rb', line 23

def method
  @method
end

#nameObject

Returns the value of attribute name.



23
24
25
# File 'lib/mechanize/form.rb', line 23

def name
  @name
end

#nodeObject (readonly) Also known as: form_node

:method: at_xpath

Shorthand for node.at_xpath.

See also Nokogiri::XML::Node#at_xpath for details.



198
199
200
# File 'lib/mechanize/form.rb', line 198

def node
  @node
end

#pageObject (readonly)

Returns the value of attribute page.



41
42
43
# File 'lib/mechanize/form.rb', line 41

def page
  @page
end

#radiobuttonsObject (readonly)

Returns the value of attribute radiobuttons.



25
26
27
# File 'lib/mechanize/form.rb', line 25

def radiobuttons
  @radiobuttons
end

Instance Method Details

#[](field_name) ⇒ Object

Fetch the value of the first input field with the name passed in. Example:

puts form['name']


241
242
243
244
# File 'lib/mechanize/form.rb', line 241

def [](field_name)
  f = field(field_name)
  f && f.value
end

#[]=(field_name, value) ⇒ Object

Set the value of the first input field with the name passed in. Example:

form['name'] = 'Aaron'


248
249
250
251
252
253
254
255
# File 'lib/mechanize/form.rb', line 248

def []=(field_name, value)
  f = field(field_name)
  if f
    f.value = value
  else
    add_field!(field_name, value)
  end
end

#add_button_to_query(button) ⇒ Object

This method adds a button to the query. If the form needs to be submitted with multiple buttons, pass each button to this method.



370
371
372
373
374
375
376
377
378
379
380
# File 'lib/mechanize/form.rb', line 370

def add_button_to_query(button)
  unless button.node.document == @node.document then
    message =
      "#{button.inspect} does not belong to the same page as " \
      "the form #{@name.inspect} in #{@page.uri}"

    raise ArgumentError, message
  end

  @clicked_buttons << button
end

#add_field!(field_name, value = nil) ⇒ Object

Add a field with field_name and value



201
202
203
# File 'lib/mechanize/form.rb', line 201

def add_field!(field_name, value = nil)
  fields << Field.new({'name' => field_name}, value)
end

#build_query(buttons = []) ⇒ Object

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.



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/mechanize/form.rb', line 300

def build_query(buttons = [])
  query = []
  @mech.log.info("form encoding: #{encoding}") if @mech && @mech.log

  save_hash_field_order

  successful_controls = []

  (fields + checkboxes).reject do |f|
    f.node["disabled"] || f.node["name"] == ""
  end.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(&:checked)

    if checked.uniq.size > 1 then
      values = checked.map(&: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

#buttonObject

:method: buttons_with(criteria)

Find all buttons that match criteria Example:

form.buttons_with(:value => /submit/).each do |button|
  button.value = 'hello!'
end


475
# File 'lib/mechanize/form.rb', line 475

elements_with :button

#checkboxObject

:method: checkboxes_with(criteria)

Find all checkboxes that match criteria Example:

form.checkboxes_with(:name => /woo/).each do |field|
  field.check
end


547
# File 'lib/mechanize/form.rb', line 547

elements_with :checkbox,   :checkboxes

#click_button(button = buttons.first) ⇒ Object

Submit form using button. Defaults to the first button.



277
278
279
# File 'lib/mechanize/form.rb', line 277

def click_button(button = buttons.first)
  submit(button)
end

#delete_field!(field_name) ⇒ Object

Removes all fields with name field_name.



425
426
427
# File 'lib/mechanize/form.rb', line 425

def delete_field!(field_name)
  @fields.delete_if{ |f| f.name == field_name}
end

#dom_classObject

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")

However, attribute values are compared literally as string, so form_with(class: “a”) does not match a form with class=“a b”. Use form_with(css: “form.a”) instead.



152
153
154
# File 'lib/mechanize/form.rb', line 152

def dom_class
  @node['class']
end

#dom_idObject

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")


140
141
142
# File 'lib/mechanize/form.rb', line 140

def dom_id
  @node['id']
end

#fieldObject

:method: fields_with(criteria)

Find all fields that match criteria Example:

form.fields_with(:value => /foo/).each do |field|
  field.value = 'hello!'
end


451
# File 'lib/mechanize/form.rb', line 451

elements_with :field

#file_uploadObject

:method: file_uploads_with(criteria)

Find all file upload fields that match criteria Example:

form.file_uploads_with(:file_name => /picutre/).each do |field|
  field.value = 'foo!'
end


499
# File 'lib/mechanize/form.rb', line 499

elements_with :file_upload

#has_field?(field_name) ⇒ Boolean Also known as: has_key?

Returns whether or not the form contains a field with field_name

Returns:

  • (Boolean)


59
60
61
# File 'lib/mechanize/form.rb', line 59

def has_field?(field_name)
  fields.any? { |f| f.name == field_name }
end

#has_value?(value) ⇒ Boolean

Returns whether or not the form contains a field with value

Returns:

  • (Boolean)


66
67
68
# File 'lib/mechanize/form.rb', line 66

def has_value?(value)
  fields.any? { |f| f.value == value }
end

#hidden_field?(field_name) ⇒ Boolean

Returns whether or not the form contains a Hidden field named field_name

Returns:

  • (Boolean)


126
127
128
# File 'lib/mechanize/form.rb', line 126

def hidden_field?(field_name)
  hiddens.find { |f| f.name == field_name }
end

#hiddensObject

Returns all fields of type Hidden



96
97
98
# File 'lib/mechanize/form.rb', line 96

def hiddens
  @hiddens ||= fields.select { |f| f.class == Hidden }
end

#keygensObject

Returns all fields of type Keygen



106
107
108
# File 'lib/mechanize/form.rb', line 106

def keygens
  @keygens ||= fields.select { |f| f.class == Keygen }
end

#keysObject

Returns all field names (keys) for this form



71
72
73
# File 'lib/mechanize/form.rb', line 71

def keys
  fields.map(&:name)
end

#pretty_print(q) ⇒ Object

:nodoc:



549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
# File 'lib/mechanize/form.rb', line 549

def pretty_print(q) # :nodoc:
  q.object_group(self) {
    q.breakable; q.group(1, '{name', '}') { q.breakable; q.pp name }
    q.breakable; q.group(1, '{method', '}') { q.breakable; q.pp method }
    q.breakable; q.group(1, '{action', '}') { q.breakable; q.pp action }
    q.breakable; q.group(1, '{fields', '}') {
      fields.each do |field|
        q.breakable
        q.pp field
      end
    }
    q.breakable; q.group(1, '{radiobuttons', '}') {
      radiobuttons.each { |b| q.breakable; q.pp b }
    }
    q.breakable; q.group(1, '{checkboxes', '}') {
      checkboxes.each { |b| q.breakable; q.pp b }
    }
    q.breakable; q.group(1, '{file_uploads', '}') {
      file_uploads.each { |b| q.breakable; q.pp b }
    }
    q.breakable; q.group(1, '{buttons', '}') {
      buttons.each { |b| q.breakable; q.pp b }
    }
  }
end

#radiobuttonObject

:method: radiobuttons_with(criteria)

Find all radio buttons that match criteria Example:

form.radiobuttons_with(:name => /woo/).each do |field|
  field.check
end


523
# File 'lib/mechanize/form.rb', line 523

elements_with :radiobutton

#request_dataObject

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,



394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
# File 'lib/mechanize/form.rb', line 394

def request_data
  query_params = build_query()

  case @enctype.downcase
  when /^multipart\/form-data/
    boundary = rand_string(20)
    @enctype = "multipart/form-data; boundary=#{boundary}"

    delimiter = "--#{boundary}\r\n"

    data = ::String.new

    query_params.each do |k,v|
      if k
        data << delimiter
        param_to_multipart(k, v, data)
      end
    end

    @file_uploads.each do |f|
      data << delimiter
      file_to_multipart(f, data)
    end

    data << "--#{boundary}--\r\n"
  else
    Mechanize::Util.build_query_string(query_params)
  end
end

#resetObject

This method allows the same form to be submitted second time with the different submit button being clicked.



384
385
386
387
# File 'lib/mechanize/form.rb', line 384

def reset
  # In the future, should add more functionality here to reset the form values to their defaults.
  @clicked_buttons = []
end

#reset_button?(button_name) ⇒ Boolean

Returns whether or not the form contains a Reset button named button_name

Returns:

  • (Boolean)


116
117
118
# File 'lib/mechanize/form.rb', line 116

def reset_button?(button_name)
  resets.find { |f| f.name == button_name }
end

#resetsObject

Returns all buttons of type Reset



86
87
88
# File 'lib/mechanize/form.rb', line 86

def resets
  @resets ||= buttons.select { |f| f.class == Reset }
end

#save_hash_field_orderObject

This method adds an index to all fields that have Hash nodes. This enables field sorting to maintain order.



357
358
359
360
361
362
363
364
365
366
# File 'lib/mechanize/form.rb', line 357

def save_hash_field_order
  index = 0

  fields.each do |field|
    if Hash === field.node
      field.index = index
      index += 1
    end
  end
end

#set_fields(fields = {}) ⇒ Object

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' }


218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/mechanize/form.rb', line 218

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(button = nil, headers = {}) ⇒ Object

Submit the form. Does not include the button as a form parameter. Use click_button or provide button as a parameter.



271
272
273
# File 'lib/mechanize/form.rb', line 271

def submit button = nil, headers = {}
  @mech.submit(self, button, headers)
end

#submit_button?(button_name) ⇒ Boolean

Returns whether or not the form contains a Submit button named button_name

Returns:

  • (Boolean)


111
112
113
# File 'lib/mechanize/form.rb', line 111

def submit_button?(button_name)
  submits.find { |f| f.name == button_name }
end

#submitsObject

Returns all buttons of type Submit



81
82
83
# File 'lib/mechanize/form.rb', line 81

def submits
  @submits ||= buttons.select { |f| f.class == Submit }
end

#text_field?(field_name) ⇒ Boolean

Returns whether or not the form contains a Text field named field_name

Returns:

  • (Boolean)


121
122
123
# File 'lib/mechanize/form.rb', line 121

def text_field?(field_name)
  texts.find { |f| f.name == field_name }
end

#textarea_field?(field_name) ⇒ Boolean

Returns whether or not the form contains a Textarea named field_name

Returns:

  • (Boolean)


131
132
133
# File 'lib/mechanize/form.rb', line 131

def textarea_field?(field_name)
  textareas.find { |f| f.name == field_name }
end

#textareasObject

Returns all fields of type Textarea



101
102
103
# File 'lib/mechanize/form.rb', line 101

def textareas
  @textareas ||= fields.select { |f| f.class == Textarea }
end

#textsObject

Returns all fields of type Text



91
92
93
# File 'lib/mechanize/form.rb', line 91

def texts
  @texts ||= fields.select { |f| f.class == Text }
end

#valuesObject

Returns all field values for this form



76
77
78
# File 'lib/mechanize/form.rb', line 76

def values
  fields.map(&:value)
end