diamond-mechanize 2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. data/CHANGELOG.rdoc +718 -0
  2. data/EXAMPLES.rdoc +187 -0
  3. data/FAQ.rdoc +11 -0
  4. data/GUIDE.rdoc +163 -0
  5. data/LICENSE.rdoc +20 -0
  6. data/Manifest.txt +159 -0
  7. data/README.rdoc +64 -0
  8. data/Rakefile +49 -0
  9. data/lib/mechanize.rb +1079 -0
  10. data/lib/mechanize/content_type_error.rb +13 -0
  11. data/lib/mechanize/cookie.rb +232 -0
  12. data/lib/mechanize/cookie_jar.rb +194 -0
  13. data/lib/mechanize/download.rb +59 -0
  14. data/lib/mechanize/element_matcher.rb +36 -0
  15. data/lib/mechanize/file.rb +65 -0
  16. data/lib/mechanize/file_connection.rb +17 -0
  17. data/lib/mechanize/file_request.rb +26 -0
  18. data/lib/mechanize/file_response.rb +74 -0
  19. data/lib/mechanize/file_saver.rb +39 -0
  20. data/lib/mechanize/form.rb +543 -0
  21. data/lib/mechanize/form/button.rb +6 -0
  22. data/lib/mechanize/form/check_box.rb +12 -0
  23. data/lib/mechanize/form/field.rb +54 -0
  24. data/lib/mechanize/form/file_upload.rb +21 -0
  25. data/lib/mechanize/form/hidden.rb +3 -0
  26. data/lib/mechanize/form/image_button.rb +19 -0
  27. data/lib/mechanize/form/keygen.rb +34 -0
  28. data/lib/mechanize/form/multi_select_list.rb +94 -0
  29. data/lib/mechanize/form/option.rb +50 -0
  30. data/lib/mechanize/form/radio_button.rb +55 -0
  31. data/lib/mechanize/form/reset.rb +3 -0
  32. data/lib/mechanize/form/select_list.rb +44 -0
  33. data/lib/mechanize/form/submit.rb +3 -0
  34. data/lib/mechanize/form/text.rb +3 -0
  35. data/lib/mechanize/form/textarea.rb +3 -0
  36. data/lib/mechanize/headers.rb +23 -0
  37. data/lib/mechanize/history.rb +82 -0
  38. data/lib/mechanize/http.rb +8 -0
  39. data/lib/mechanize/http/agent.rb +1004 -0
  40. data/lib/mechanize/http/auth_challenge.rb +59 -0
  41. data/lib/mechanize/http/auth_realm.rb +31 -0
  42. data/lib/mechanize/http/content_disposition_parser.rb +188 -0
  43. data/lib/mechanize/http/www_authenticate_parser.rb +155 -0
  44. data/lib/mechanize/monkey_patch.rb +16 -0
  45. data/lib/mechanize/page.rb +440 -0
  46. data/lib/mechanize/page/base.rb +7 -0
  47. data/lib/mechanize/page/frame.rb +27 -0
  48. data/lib/mechanize/page/image.rb +30 -0
  49. data/lib/mechanize/page/label.rb +20 -0
  50. data/lib/mechanize/page/link.rb +98 -0
  51. data/lib/mechanize/page/meta_refresh.rb +68 -0
  52. data/lib/mechanize/parser.rb +173 -0
  53. data/lib/mechanize/pluggable_parsers.rb +144 -0
  54. data/lib/mechanize/redirect_limit_reached_error.rb +19 -0
  55. data/lib/mechanize/redirect_not_get_or_head_error.rb +21 -0
  56. data/lib/mechanize/response_code_error.rb +21 -0
  57. data/lib/mechanize/response_read_error.rb +27 -0
  58. data/lib/mechanize/robots_disallowed_error.rb +28 -0
  59. data/lib/mechanize/test_case.rb +663 -0
  60. data/lib/mechanize/unauthorized_error.rb +3 -0
  61. data/lib/mechanize/unsupported_scheme_error.rb +6 -0
  62. data/lib/mechanize/util.rb +101 -0
  63. data/test/data/htpasswd +1 -0
  64. data/test/data/server.crt +16 -0
  65. data/test/data/server.csr +12 -0
  66. data/test/data/server.key +15 -0
  67. data/test/data/server.pem +15 -0
  68. data/test/htdocs/alt_text.html +10 -0
  69. data/test/htdocs/bad_form_test.html +9 -0
  70. data/test/htdocs/button.jpg +0 -0
  71. data/test/htdocs/canonical_uri.html +9 -0
  72. data/test/htdocs/dir with spaces/foo.html +1 -0
  73. data/test/htdocs/empty_form.html +6 -0
  74. data/test/htdocs/file_upload.html +26 -0
  75. data/test/htdocs/find_link.html +41 -0
  76. data/test/htdocs/form_multi_select.html +16 -0
  77. data/test/htdocs/form_multival.html +37 -0
  78. data/test/htdocs/form_no_action.html +18 -0
  79. data/test/htdocs/form_no_input_name.html +16 -0
  80. data/test/htdocs/form_order_test.html +11 -0
  81. data/test/htdocs/form_select.html +16 -0
  82. data/test/htdocs/form_set_fields.html +14 -0
  83. data/test/htdocs/form_test.html +188 -0
  84. data/test/htdocs/frame_referer_test.html +10 -0
  85. data/test/htdocs/frame_test.html +30 -0
  86. data/test/htdocs/google.html +13 -0
  87. data/test/htdocs/index.html +6 -0
  88. data/test/htdocs/link with space.html +5 -0
  89. data/test/htdocs/meta_cookie.html +11 -0
  90. data/test/htdocs/no_title_test.html +6 -0
  91. data/test/htdocs/noindex.html +9 -0
  92. data/test/htdocs/rails_3_encoding_hack_form_test.html +27 -0
  93. data/test/htdocs/relative/tc_relative_links.html +21 -0
  94. data/test/htdocs/robots.html +8 -0
  95. data/test/htdocs/robots.txt +2 -0
  96. data/test/htdocs/tc_bad_charset.html +9 -0
  97. data/test/htdocs/tc_bad_links.html +5 -0
  98. data/test/htdocs/tc_base_link.html +8 -0
  99. data/test/htdocs/tc_blank_form.html +11 -0
  100. data/test/htdocs/tc_charset.html +6 -0
  101. data/test/htdocs/tc_checkboxes.html +19 -0
  102. data/test/htdocs/tc_encoded_links.html +5 -0
  103. data/test/htdocs/tc_field_precedence.html +11 -0
  104. data/test/htdocs/tc_follow_meta.html +8 -0
  105. data/test/htdocs/tc_form_action.html +48 -0
  106. data/test/htdocs/tc_links.html +19 -0
  107. data/test/htdocs/tc_meta_in_body.html +9 -0
  108. data/test/htdocs/tc_pretty_print.html +17 -0
  109. data/test/htdocs/tc_referer.html +16 -0
  110. data/test/htdocs/tc_relative_links.html +19 -0
  111. data/test/htdocs/tc_textarea.html +23 -0
  112. data/test/htdocs/test_click.html +11 -0
  113. data/test/htdocs/unusual______.html +5 -0
  114. data/test/test_mechanize.rb +1164 -0
  115. data/test/test_mechanize_cookie.rb +451 -0
  116. data/test/test_mechanize_cookie_jar.rb +483 -0
  117. data/test/test_mechanize_download.rb +43 -0
  118. data/test/test_mechanize_file.rb +61 -0
  119. data/test/test_mechanize_file_connection.rb +21 -0
  120. data/test/test_mechanize_file_request.rb +19 -0
  121. data/test/test_mechanize_file_saver.rb +21 -0
  122. data/test/test_mechanize_form.rb +875 -0
  123. data/test/test_mechanize_form_check_box.rb +38 -0
  124. data/test/test_mechanize_form_encoding.rb +114 -0
  125. data/test/test_mechanize_form_field.rb +63 -0
  126. data/test/test_mechanize_form_file_upload.rb +20 -0
  127. data/test/test_mechanize_form_image_button.rb +12 -0
  128. data/test/test_mechanize_form_keygen.rb +32 -0
  129. data/test/test_mechanize_form_multi_select_list.rb +84 -0
  130. data/test/test_mechanize_form_option.rb +55 -0
  131. data/test/test_mechanize_form_radio_button.rb +78 -0
  132. data/test/test_mechanize_form_select_list.rb +76 -0
  133. data/test/test_mechanize_form_textarea.rb +52 -0
  134. data/test/test_mechanize_headers.rb +35 -0
  135. data/test/test_mechanize_history.rb +103 -0
  136. data/test/test_mechanize_http_agent.rb +1225 -0
  137. data/test/test_mechanize_http_auth_challenge.rb +39 -0
  138. data/test/test_mechanize_http_auth_realm.rb +49 -0
  139. data/test/test_mechanize_http_content_disposition_parser.rb +118 -0
  140. data/test/test_mechanize_http_www_authenticate_parser.rb +146 -0
  141. data/test/test_mechanize_link.rb +80 -0
  142. data/test/test_mechanize_page.rb +118 -0
  143. data/test/test_mechanize_page_encoding.rb +182 -0
  144. data/test/test_mechanize_page_frame.rb +16 -0
  145. data/test/test_mechanize_page_link.rb +390 -0
  146. data/test/test_mechanize_page_meta_refresh.rb +127 -0
  147. data/test/test_mechanize_parser.rb +289 -0
  148. data/test/test_mechanize_pluggable_parser.rb +52 -0
  149. data/test/test_mechanize_redirect_limit_reached_error.rb +24 -0
  150. data/test/test_mechanize_redirect_not_get_or_head_error.rb +14 -0
  151. data/test/test_mechanize_subclass.rb +22 -0
  152. data/test/test_mechanize_util.rb +103 -0
  153. data/test/test_multi_select.rb +119 -0
  154. metadata +216 -0
@@ -0,0 +1,6 @@
1
+ ##
2
+ # A Submit button in a Form
3
+
4
+ class Mechanize::Form::Button < Mechanize::Form::Field
5
+ end
6
+
@@ -0,0 +1,12 @@
1
+ ##
2
+ # This class represents a check box found in a Form. To activate the CheckBox
3
+ # in the Form, set the checked method to true.
4
+
5
+ class Mechanize::Form::CheckBox < Mechanize::Form::RadioButton
6
+
7
+ def query_value
8
+ [[@name, @value || "on"]]
9
+ end
10
+
11
+ end
12
+
@@ -0,0 +1,54 @@
1
+ ##
2
+ # This class represents a field in a form. It handles the following input
3
+ # tags found in a form:
4
+ #
5
+ # * text
6
+ # * password
7
+ # * hidden
8
+ # * int
9
+ # * textarea
10
+ # * keygen
11
+ #
12
+ # To set the value of a field, just use the value method:
13
+ #
14
+ # field.value = "foo"
15
+
16
+ class Mechanize::Form::Field
17
+ attr_accessor :name, :value, :node, :type
18
+
19
+ def initialize node, value = node['value']
20
+ @node = node
21
+ @name = Mechanize::Util.html_unescape(node['name'])
22
+ @value = if value.is_a? String
23
+ Mechanize::Util.html_unescape(value)
24
+ else
25
+ value
26
+ end
27
+
28
+ @type = node['type']
29
+ end
30
+
31
+ def query_value
32
+ [[@name, @value || '']]
33
+ end
34
+
35
+ def <=> other
36
+ return 0 if self == other
37
+ return 1 if Hash === node
38
+ return -1 if Hash === other.node
39
+ node <=> other.node
40
+ end
41
+
42
+ # This method is a shortcut to get field's DOM id.
43
+ # Common usage: form.field_with(:dom_id => "foo")
44
+ def dom_id
45
+ node['id']
46
+ end
47
+
48
+ # This method is a shortcut to get field's DOM id.
49
+ # Common usage: form.field_with(:dom_class => "foo")
50
+ def dom_class
51
+ node['class']
52
+ end
53
+ end
54
+
@@ -0,0 +1,21 @@
1
+ # This class represents a file upload field found in a form. To use this
2
+ # class, set FileUpload#file_data= to the data of the file you want to upload
3
+ # and FileUpload#mime_type= to the appropriate mime type of the file.
4
+ #
5
+ # See the example in EXAMPLES
6
+
7
+ class Mechanize::Form::FileUpload < Mechanize::Form::Field
8
+ attr_accessor :file_name # File name
9
+ attr_accessor :mime_type # Mime Type (Optional)
10
+
11
+ alias :file_data :value
12
+ alias :file_data= :value=
13
+
14
+ def initialize node, file_name
15
+ @file_name = Mechanize::Util.html_unescape(file_name)
16
+ @file_data = nil
17
+ @node = node
18
+ super(node, @file_data)
19
+ end
20
+ end
21
+
@@ -0,0 +1,3 @@
1
+ class Mechanize::Form::Hidden < Mechanize::Form::Field
2
+ end
3
+
@@ -0,0 +1,19 @@
1
+ ##
2
+ # This class represents an image button in a form. Use the x and y methods to
3
+ # set the x and y positions for where the mouse "clicked".
4
+
5
+ class Mechanize::Form::ImageButton < Mechanize::Form::Button
6
+ attr_accessor :x, :y
7
+
8
+ def initialize *args
9
+ @x = nil
10
+ @y = nil
11
+ super
12
+ end
13
+
14
+ def query_value
15
+ [["#{@name}.x", (@x || 0).to_s],
16
+ ["#{@name}.y", (@y || 0).to_s]]
17
+ end
18
+ end
19
+
@@ -0,0 +1,34 @@
1
+ ##
2
+ # This class represents a keygen (public / private key generator) found in a
3
+ # Form. The field will automatically generate a key pair and compute its own
4
+ # value to match the challenge. Call key to access the public/private key
5
+ # pair.
6
+
7
+ class Mechanize::Form::Keygen < Mechanize::Form::Field
8
+ # The challenge for this <keygen>.
9
+ attr_reader :challenge
10
+
11
+ # The key associated with this <keygen> tag.
12
+ attr_reader :key
13
+
14
+ def initialize(node, value = nil)
15
+ super
16
+ @challenge = node['challenge']
17
+
18
+ @spki = OpenSSL::Netscape::SPKI.new
19
+ @spki.challenge = @challenge
20
+
21
+ @key = nil
22
+ generate_key if value.nil? || value.empty?
23
+ end
24
+
25
+ # Generates a key pair and sets the field's value.
26
+ def generate_key(key_size = 2048)
27
+ # Spec at http://dev.w3.org/html5/spec/Overview.html#the-keygen-element
28
+ @key = OpenSSL::PKey::RSA.new key_size
29
+ @spki.public_key = @key.public_key
30
+ @spki.sign @key, OpenSSL::Digest::MD5.new
31
+ self.value = @spki.to_pem
32
+ end
33
+ end
34
+
@@ -0,0 +1,94 @@
1
+ ##
2
+ # This class represents a select list where multiple values can be selected.
3
+ # MultiSelectList#value= accepts an array, and those values are used as
4
+ # values for the select list. For example, to select multiple values,
5
+ # simply do this:
6
+ #
7
+ # list.value = ['one', 'two']
8
+ #
9
+ # Single values are still supported, so these two are the same:
10
+ #
11
+ # list.value = ['one']
12
+ # list.value = 'one'
13
+
14
+ class Mechanize::Form::MultiSelectList < Mechanize::Form::Field
15
+
16
+ extend Mechanize::ElementMatcher
17
+
18
+ attr_accessor :options
19
+
20
+ def initialize node
21
+ value = []
22
+ @options = []
23
+
24
+ # parse
25
+ node.search('option').each do |n|
26
+ @options << Mechanize::Form::Option.new(n, self)
27
+ end
28
+
29
+ super node, value
30
+ end
31
+
32
+ ##
33
+ # :method: option_with
34
+ #
35
+ # Find one option on this select list with +criteria+
36
+ #
37
+ # Example:
38
+ #
39
+ # select_list.option_with(:value => '1').value = 'foo'
40
+
41
+ ##
42
+ # :method: options_with
43
+ #
44
+ # Find all options on this select list with +criteria+
45
+ #
46
+ # Example:
47
+ #
48
+ # select_list.options_with(:value => /1|2/).each do |field|
49
+ # field.value = '20'
50
+ # end
51
+
52
+ elements_with :option
53
+
54
+ def query_value
55
+ value ? value.map { |v| [name, v] } : ''
56
+ end
57
+
58
+ # Select no options
59
+ def select_none
60
+ @value = []
61
+ options.each { |o| o.untick }
62
+ end
63
+
64
+ # Select all options
65
+ def select_all
66
+ @value = []
67
+ options.each { |o| o.tick }
68
+ end
69
+
70
+ # Get a list of all selected options
71
+ def selected_options
72
+ @options.find_all { |o| o.selected? }
73
+ end
74
+
75
+ def value=(values)
76
+ select_none
77
+ [values].flatten.each do |value|
78
+ option = options.find { |o| o.value == value }
79
+ if option.nil?
80
+ @value.push(value)
81
+ else
82
+ option.select
83
+ end
84
+ end
85
+ end
86
+
87
+ def value
88
+ value = []
89
+ value.concat @value
90
+ value.concat selected_options.map { |o| o.value }
91
+ value
92
+ end
93
+
94
+ end
@@ -0,0 +1,50 @@
1
+ ##
2
+ # This class contains an option found within SelectList. A SelectList can
3
+ # have many Option classes associated with it. An option can be selected by
4
+ # calling Option#tick, or Option#click.
5
+ #
6
+ # To select the first option in a list:
7
+ #
8
+ # select_list.first.tick
9
+
10
+ class Mechanize::Form::Option
11
+ attr_reader :value, :selected, :text, :select_list
12
+
13
+ alias :to_s :value
14
+ alias :selected? :selected
15
+
16
+ def initialize(node, select_list)
17
+ @text = node.inner_text
18
+ @value = Mechanize::Util.html_unescape(node['value'] || node.inner_text)
19
+ @selected = node.has_attribute? 'selected'
20
+ @select_list = select_list # The select list this option belongs to
21
+ end
22
+
23
+ # Select this option
24
+ def select
25
+ unselect_peers
26
+ @selected = true
27
+ end
28
+
29
+ # Unselect this option
30
+ def unselect
31
+ @selected = false
32
+ end
33
+
34
+ alias :tick :select
35
+ alias :untick :unselect
36
+
37
+ # Toggle the selection value of this option
38
+ def click
39
+ unselect_peers
40
+ @selected = !@selected
41
+ end
42
+
43
+ private
44
+ def unselect_peers
45
+ return unless Mechanize::Form::SelectList === @select_list
46
+
47
+ @select_list.select_none
48
+ end
49
+ end
50
+
@@ -0,0 +1,55 @@
1
+ ##
2
+ # This class represents a radio button found in a Form. To activate the
3
+ # RadioButton in the Form, set the checked method to true.
4
+
5
+ class Mechanize::Form::RadioButton < Mechanize::Form::Field
6
+ attr_accessor :checked
7
+
8
+ def initialize node, form
9
+ @checked = !!node['checked']
10
+ @form = form
11
+ super(node)
12
+ end
13
+
14
+ def check
15
+ uncheck_peers
16
+ @checked = true
17
+ end
18
+
19
+ alias checked? checked
20
+
21
+ def uncheck
22
+ @checked = false
23
+ end
24
+
25
+ def click
26
+ checked ? uncheck : check
27
+ end
28
+
29
+ def label
30
+ (id = self['id']) && @form.page.labels_hash[id] || nil
31
+ end
32
+
33
+ def text
34
+ label.text rescue nil
35
+ end
36
+
37
+ def [](key)
38
+ @node[key]
39
+ end
40
+
41
+ def pretty_print_instance_variables # :nodoc:
42
+ [:@checked, :@name, :@value]
43
+ end
44
+
45
+ private
46
+
47
+ def uncheck_peers
48
+ @form.radiobuttons_with(:name => name).each do |b|
49
+ next if b.value == value
50
+ b.uncheck
51
+ end
52
+ end
53
+
54
+ end
55
+
@@ -0,0 +1,3 @@
1
+ class Mechanize::Form::Reset < Mechanize::Form::Button
2
+ end
3
+
@@ -0,0 +1,44 @@
1
+ # This class represents a select list or drop down box in a Form. Set the
2
+ # value for the list by calling SelectList#value=. SelectList contains a list
3
+ # of Option that were found. After finding the correct option, set the select
4
+ # lists value to the option value:
5
+ #
6
+ # selectlist.value = selectlist.options.first.value
7
+ #
8
+ # Options can also be selected by "clicking" or selecting them. See Option
9
+ class Mechanize::Form::SelectList < Mechanize::Form::MultiSelectList
10
+
11
+ def initialize node
12
+ super
13
+ if selected_options.length > 1
14
+ selected_options.reverse[1..selected_options.length].each do |o|
15
+ o.unselect
16
+ end
17
+ end
18
+ end
19
+
20
+ def value
21
+ value = super
22
+ if value.length > 0
23
+ value.last
24
+ elsif @options.length > 0
25
+ @options.first.value
26
+ else
27
+ nil
28
+ end
29
+ end
30
+
31
+ def value=(new)
32
+ if new != new.to_s and new.respond_to? :first
33
+ super([new.first])
34
+ else
35
+ super([new.to_s])
36
+ end
37
+ end
38
+
39
+ def query_value
40
+ value ? [[name, value]] : nil
41
+ end
42
+
43
+ end
44
+
@@ -0,0 +1,3 @@
1
+ class Mechanize::Form::Submit < Mechanize::Form::Button
2
+ end
3
+
@@ -0,0 +1,3 @@
1
+ class Mechanize::Form::Text < Mechanize::Form::Field
2
+ end
3
+
@@ -0,0 +1,3 @@
1
+ class Mechanize::Form::Textarea < Mechanize::Form::Field
2
+ end
3
+
@@ -0,0 +1,23 @@
1
+ class Mechanize::Headers < Hash
2
+ def [](key)
3
+ super(key.downcase)
4
+ end
5
+
6
+ def []=(key, value)
7
+ super(key.downcase, value)
8
+ end
9
+
10
+ def key?(key)
11
+ super(key.downcase)
12
+ end
13
+
14
+ def canonical_each
15
+ block_given? or return enum_for(__method__)
16
+ each { |key, value|
17
+ key = key.capitalize
18
+ key.gsub!(/-([a-z])/) { "-#{$1.upcase}" }
19
+ yield [key, value]
20
+ }
21
+ end
22
+ end
23
+