tenderlove-mechanize 0.9.3.20090623142847 → 0.9.3.20090911221705

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. data/Manifest.txt +55 -48
  2. data/Rakefile +12 -22
  3. data/lib/mechanize.rb +618 -4
  4. data/lib/mechanize/chain.rb +33 -0
  5. data/lib/mechanize/chain/auth_headers.rb +78 -0
  6. data/lib/mechanize/chain/body_decoding_handler.rb +46 -0
  7. data/lib/mechanize/chain/connection_resolver.rb +76 -0
  8. data/lib/mechanize/chain/custom_headers.rb +21 -0
  9. data/lib/{www/mechanize → mechanize}/chain/handler.rb +1 -1
  10. data/lib/mechanize/chain/header_resolver.rb +51 -0
  11. data/lib/mechanize/chain/parameter_resolver.rb +22 -0
  12. data/lib/{www/mechanize → mechanize}/chain/post_connect_hook.rb +0 -0
  13. data/lib/mechanize/chain/pre_connect_hook.rb +20 -0
  14. data/lib/mechanize/chain/request_resolver.rb +30 -0
  15. data/lib/mechanize/chain/response_body_parser.rb +38 -0
  16. data/lib/mechanize/chain/response_header_handler.rb +48 -0
  17. data/lib/mechanize/chain/response_reader.rb +39 -0
  18. data/lib/mechanize/chain/ssl_resolver.rb +40 -0
  19. data/lib/mechanize/chain/uri_resolver.rb +75 -0
  20. data/lib/mechanize/content_type_error.rb +14 -0
  21. data/lib/mechanize/cookie.rb +70 -0
  22. data/lib/mechanize/cookie_jar.rb +188 -0
  23. data/lib/mechanize/file.rb +71 -0
  24. data/lib/mechanize/file_response.rb +60 -0
  25. data/lib/mechanize/file_saver.rb +37 -0
  26. data/lib/mechanize/form.rb +378 -0
  27. data/lib/mechanize/form/button.rb +9 -0
  28. data/lib/mechanize/form/check_box.rb +11 -0
  29. data/lib/mechanize/form/field.rb +30 -0
  30. data/lib/mechanize/form/file_upload.rb +22 -0
  31. data/lib/mechanize/form/image_button.rb +21 -0
  32. data/lib/mechanize/form/multi_select_list.rb +67 -0
  33. data/lib/mechanize/form/option.rb +49 -0
  34. data/lib/mechanize/form/radio_button.rb +49 -0
  35. data/lib/mechanize/form/select_list.rb +43 -0
  36. data/lib/mechanize/headers.rb +11 -0
  37. data/lib/mechanize/history.rb +65 -0
  38. data/lib/mechanize/inspect.rb +88 -0
  39. data/lib/{www/mechanize → mechanize}/monkey_patch.rb +4 -6
  40. data/lib/mechanize/page.rb +206 -0
  41. data/lib/mechanize/page/base.rb +8 -0
  42. data/lib/mechanize/page/frame.rb +20 -0
  43. data/lib/mechanize/page/image.rb +26 -0
  44. data/lib/mechanize/page/label.rb +20 -0
  45. data/lib/mechanize/page/link.rb +48 -0
  46. data/lib/mechanize/page/meta.rb +50 -0
  47. data/lib/mechanize/pluggable_parsers.rb +101 -0
  48. data/lib/mechanize/redirect_limit_reached_error.rb +16 -0
  49. data/lib/mechanize/redirect_not_get_or_head_error.rb +18 -0
  50. data/lib/mechanize/response_code_error.rb +22 -0
  51. data/lib/mechanize/unsupported_scheme_error.rb +8 -0
  52. data/lib/mechanize/util.rb +67 -0
  53. data/mechanize.gemspec +8 -8
  54. data/test/chain/test_argument_validator.rb +2 -2
  55. data/test/chain/test_auth_headers.rb +2 -2
  56. data/test/chain/test_custom_headers.rb +2 -2
  57. data/test/chain/test_header_resolver.rb +3 -3
  58. data/test/chain/test_parameter_resolver.rb +4 -4
  59. data/test/chain/test_request_resolver.rb +4 -4
  60. data/test/chain/test_response_reader.rb +3 -3
  61. data/test/helper.rb +1 -1
  62. data/test/htdocs/tc_bad_charset.html +9 -0
  63. data/test/htdocs/tc_charset.html +6 -0
  64. data/test/htdocs/test_bad_encoding.html +52 -0
  65. data/test/test_authenticate.rb +3 -3
  66. data/test/test_bad_links.rb +1 -1
  67. data/test/test_blank_form.rb +1 -1
  68. data/test/test_checkboxes.rb +1 -1
  69. data/test/test_content_type.rb +2 -2
  70. data/test/test_cookie_class.rb +12 -12
  71. data/test/test_cookie_jar.rb +13 -13
  72. data/test/test_cookies.rb +1 -1
  73. data/test/test_encoded_links.rb +1 -1
  74. data/test/test_errors.rb +2 -2
  75. data/test/test_follow_meta.rb +3 -3
  76. data/test/test_form_action.rb +1 -1
  77. data/test/test_form_as_hash.rb +1 -1
  78. data/test/test_form_button.rb +2 -2
  79. data/test/test_form_no_inputname.rb +1 -1
  80. data/test/test_forms.rb +1 -1
  81. data/test/test_frames.rb +1 -1
  82. data/test/test_get_headers.rb +1 -1
  83. data/test/test_gzipping.rb +2 -2
  84. data/test/test_hash_api.rb +1 -1
  85. data/test/test_history.rb +7 -7
  86. data/test/test_history_added.rb +1 -1
  87. data/test/test_html_unscape_forms.rb +7 -7
  88. data/test/test_if_modified_since.rb +1 -1
  89. data/test/test_keep_alive.rb +1 -1
  90. data/test/test_links.rb +2 -2
  91. data/test/test_mech.rb +2 -2
  92. data/test/test_mechanize_file.rb +7 -7
  93. data/test/test_meta.rb +2 -2
  94. data/test/test_multi_select.rb +1 -1
  95. data/test/test_no_attributes.rb +1 -1
  96. data/test/test_option.rb +1 -1
  97. data/test/test_page.rb +3 -3
  98. data/test/test_pluggable_parser.rb +14 -14
  99. data/test/test_post_form.rb +1 -1
  100. data/test/test_pretty_print.rb +2 -2
  101. data/test/test_radiobutton.rb +1 -1
  102. data/test/test_redirect_limit_reached.rb +1 -3
  103. data/test/test_redirect_verb_handling.rb +1 -3
  104. data/test/test_referer.rb +1 -1
  105. data/test/test_relative_links.rb +1 -1
  106. data/test/test_request.rb +1 -1
  107. data/test/test_response_code.rb +3 -3
  108. data/test/test_save_file.rb +3 -3
  109. data/test/test_scheme.rb +3 -3
  110. data/test/test_select.rb +2 -2
  111. data/test/test_select_all.rb +1 -1
  112. data/test/test_select_none.rb +1 -1
  113. data/test/test_select_noopts.rb +1 -1
  114. data/test/test_set_fields.rb +1 -1
  115. data/test/test_ssl_server.rb +1 -1
  116. data/test/test_subclass.rb +1 -1
  117. data/test/test_textarea.rb +1 -1
  118. data/test/test_upload.rb +1 -1
  119. data/test/test_verbs.rb +1 -1
  120. metadata +61 -56
  121. data/lib/www/mechanize.rb +0 -619
  122. data/lib/www/mechanize/chain.rb +0 -34
  123. data/lib/www/mechanize/chain/auth_headers.rb +0 -80
  124. data/lib/www/mechanize/chain/body_decoding_handler.rb +0 -48
  125. data/lib/www/mechanize/chain/connection_resolver.rb +0 -78
  126. data/lib/www/mechanize/chain/custom_headers.rb +0 -23
  127. data/lib/www/mechanize/chain/header_resolver.rb +0 -53
  128. data/lib/www/mechanize/chain/parameter_resolver.rb +0 -24
  129. data/lib/www/mechanize/chain/pre_connect_hook.rb +0 -22
  130. data/lib/www/mechanize/chain/request_resolver.rb +0 -32
  131. data/lib/www/mechanize/chain/response_body_parser.rb +0 -40
  132. data/lib/www/mechanize/chain/response_header_handler.rb +0 -50
  133. data/lib/www/mechanize/chain/response_reader.rb +0 -41
  134. data/lib/www/mechanize/chain/ssl_resolver.rb +0 -42
  135. data/lib/www/mechanize/chain/uri_resolver.rb +0 -77
  136. data/lib/www/mechanize/content_type_error.rb +0 -16
  137. data/lib/www/mechanize/cookie.rb +0 -72
  138. data/lib/www/mechanize/cookie_jar.rb +0 -191
  139. data/lib/www/mechanize/file.rb +0 -73
  140. data/lib/www/mechanize/file_response.rb +0 -62
  141. data/lib/www/mechanize/file_saver.rb +0 -39
  142. data/lib/www/mechanize/form.rb +0 -360
  143. data/lib/www/mechanize/form/button.rb +0 -8
  144. data/lib/www/mechanize/form/check_box.rb +0 -13
  145. data/lib/www/mechanize/form/field.rb +0 -28
  146. data/lib/www/mechanize/form/file_upload.rb +0 -24
  147. data/lib/www/mechanize/form/image_button.rb +0 -23
  148. data/lib/www/mechanize/form/multi_select_list.rb +0 -69
  149. data/lib/www/mechanize/form/option.rb +0 -51
  150. data/lib/www/mechanize/form/radio_button.rb +0 -38
  151. data/lib/www/mechanize/form/select_list.rb +0 -45
  152. data/lib/www/mechanize/headers.rb +0 -12
  153. data/lib/www/mechanize/history.rb +0 -67
  154. data/lib/www/mechanize/inspect.rb +0 -90
  155. data/lib/www/mechanize/page.rb +0 -181
  156. data/lib/www/mechanize/page/base.rb +0 -10
  157. data/lib/www/mechanize/page/frame.rb +0 -22
  158. data/lib/www/mechanize/page/link.rb +0 -50
  159. data/lib/www/mechanize/page/meta.rb +0 -51
  160. data/lib/www/mechanize/pluggable_parsers.rb +0 -103
  161. data/lib/www/mechanize/redirect_limit_reached_error.rb +0 -18
  162. data/lib/www/mechanize/redirect_not_get_or_head_error.rb +0 -20
  163. data/lib/www/mechanize/response_code_error.rb +0 -25
  164. data/lib/www/mechanize/unsupported_scheme_error.rb +0 -10
  165. data/lib/www/mechanize/util.rb +0 -76
@@ -1,181 +0,0 @@
1
- require 'www/mechanize/page/link'
2
- require 'www/mechanize/page/meta'
3
- require 'www/mechanize/page/base'
4
- require 'www/mechanize/page/frame'
5
- require 'www/mechanize/headers'
6
-
7
- module WWW
8
- class Mechanize
9
- # = Synopsis
10
- # This class encapsulates an HTML page. If Mechanize finds a content
11
- # type of 'text/html', this class will be instantiated and returned.
12
- #
13
- # == Example
14
- # require 'rubygems'
15
- # require 'mechanize'
16
- #
17
- # agent = WWW::Mechanize.new
18
- # agent.get('http://google.com/').class #=> WWW::Mechanize::Page
19
- #
20
- class Page < WWW::Mechanize::File
21
- extend Forwardable
22
-
23
- attr_accessor :mech
24
-
25
- def initialize(uri=nil, response=nil, body=nil, code=nil, mech=nil)
26
- @encoding = nil
27
-
28
- method = response.respond_to?(:each_header) ? :each_header : :each
29
- response.send(method) do |header,v|
30
- next unless v =~ /charset/i
31
- encoding = v.split('=').last.strip
32
- @encoding = encoding unless encoding == 'none'
33
- end
34
-
35
- # Force the encoding to be 8BIT so we can perform regular expressions.
36
- # We'll set it to the detected encoding later
37
- body.force_encoding('ASCII-8BIT') if defined?(Encoding) && body
38
-
39
- @encoding ||= Util.detect_charset(body)
40
-
41
- super(uri, response, body, code)
42
- @mech ||= mech
43
-
44
- @encoding = nil if html_body =~ /<meta[^>]*charset[^>]*>/i
45
-
46
- raise Mechanize::ContentTypeError.new(response['content-type']) unless
47
- response['content-type'] =~ /^(text\/html)|(application\/xhtml\+xml)/i
48
- @parser = @links = @forms = @meta = @bases = @frames = @iframes = nil
49
- end
50
-
51
- def title
52
- @title ||= if parser && search('title').inner_text.length > 0
53
- search('title').inner_text
54
- end
55
- end
56
-
57
- def encoding=(encoding)
58
- @encoding = encoding
59
-
60
- if @parser
61
- parser_encoding = @parser.encoding
62
- if (parser_encoding && parser_encoding.downcase) != (encoding && encoding.downcase)
63
- # lazy reinitialize the parser with the new encoding
64
- @parser = nil
65
- end
66
- end
67
-
68
- encoding
69
- end
70
-
71
- def encoding
72
- parser.respond_to?(:encoding) ? parser.encoding : nil
73
- end
74
-
75
- def parser
76
- return @parser if @parser
77
-
78
- if body && response
79
- if mech.html_parser == Nokogiri::HTML
80
- @parser = mech.html_parser.parse(html_body, nil, @encoding)
81
- else
82
- @parser = mech.html_parser.parse(html_body)
83
- end
84
- end
85
-
86
- @parser
87
- end
88
- alias :root :parser
89
-
90
- # Get the content type
91
- def content_type
92
- response['content-type']
93
- end
94
-
95
- # Search through the page like HPricot
96
- def_delegator :parser, :search, :search
97
- def_delegator :parser, :/, :/
98
- def_delegator :parser, :at, :at
99
-
100
- # Find a form matching +criteria+.
101
- # Example:
102
- # page.form_with(:action => '/post/login.php') do |f|
103
- # ...
104
- # end
105
- [:form, :link, :base, :frame, :iframe].each do |type|
106
- eval(<<-eomethod)
107
- def #{type}s_with(criteria)
108
- criteria = {:name => criteria} if String === criteria
109
- f = #{type}s.find_all do |thing|
110
- criteria.all? { |k,v| v === thing.send(k) }
111
- end
112
- yield f if block_given?
113
- f
114
- end
115
-
116
- def #{type}_with(criteria)
117
- f = #{type}s_with(criteria).first
118
- yield f if block_given?
119
- f
120
- end
121
- alias :#{type} :#{type}_with
122
- eomethod
123
- end
124
-
125
- def links
126
- @links ||= %w{ a area }.map do |tag|
127
- search(tag).map do |node|
128
- Link.new(node, @mech, self)
129
- end
130
- end.flatten
131
- end
132
-
133
- def forms
134
- @forms ||= search('form').map do |html_form|
135
- form = Form.new(html_form, @mech, self)
136
- form.action ||= @uri.to_s
137
- form
138
- end
139
- end
140
-
141
- def meta
142
- @meta ||= search('meta').map do |node|
143
- next unless node['http-equiv'] && node['content']
144
- (equiv, content) = node['http-equiv'], node['content']
145
- if equiv && equiv.downcase == 'refresh'
146
- Meta.parse(content, uri) do |delay, href|
147
- node['delay'] = delay
148
- node['href'] = href
149
- Meta.new(node, @mech, self)
150
- end
151
- end
152
- end.compact
153
- end
154
-
155
- def bases
156
- @bases ||=
157
- search('base').map { |node| Base.new(node, @mech, self) }
158
- end
159
-
160
- def frames
161
- @frames ||=
162
- search('frame').map { |node| Frame.new(node, @mech, self) }
163
- end
164
-
165
- def iframes
166
- @iframes ||=
167
- search('iframe').map { |node| Frame.new(node, @mech, self) }
168
- end
169
-
170
- private
171
-
172
- def html_body
173
- if body
174
- body.length > 0 ? body : '<html></html>'
175
- else
176
- ''
177
- end
178
- end
179
- end
180
- end
181
- end
@@ -1,10 +0,0 @@
1
- module WWW
2
- class Mechanize
3
- class Page < WWW::Mechanize::File
4
- # This class encapsulates a Base tag. Mechanize treats base tags just
5
- # like 'a' tags. Base objects will contain links, but most likely will
6
- # have no text.
7
- class Base < Link; end
8
- end
9
- end
10
- end
@@ -1,22 +0,0 @@
1
- module WWW
2
- class Mechanize
3
- class Page < WWW::Mechanize::File
4
- # This class encapsulates a 'frame' tag. Frame objects can be treated
5
- # just like Link objects. They contain src, the link they refer to,
6
- # name, the name of the frame. 'src' and 'name' are aliased to 'href'
7
- # and 'text' respectively so that a Frame object can be treated just
8
- # like a Link.
9
- class Frame < Link
10
- alias :src :href
11
- alias :name :text
12
-
13
- def initialize(node, mech, referer)
14
- super(node, mech, referer)
15
- @node = node
16
- @text = node['name']
17
- @href = node['src']
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,50 +0,0 @@
1
- module WWW
2
- class Mechanize
3
- class Page < WWW::Mechanize::File
4
- # This class encapsulates links. It contains the text and the URI for
5
- # 'a' tags parsed out of an HTML page. If the link contains an image,
6
- # the alt text will be used for that image.
7
- #
8
- # For example, the text for the following links with both be 'Hello World':
9
- #
10
- # <a href="http://rubyforge.org">Hello World</a>
11
- # <a href="http://rubyforge.org"><img src="test.jpg" alt="Hello World"></a>
12
- class Link
13
- attr_reader :node
14
- attr_reader :href
15
- attr_reader :text
16
- attr_reader :attributes
17
- attr_reader :page
18
- alias :to_s :text
19
- alias :referer :page
20
-
21
- def initialize(node, mech, page)
22
- @node = node
23
- @href = node['href']
24
- @text = node.inner_text
25
- @page = page
26
- @mech = mech
27
- @attributes = node
28
-
29
- # If there is no text, try to find an image and use it's alt text
30
- if (@text.nil? || @text.length == 0) && node.search('img').length > 0
31
- @text = ''
32
- node.search('img').each do |e|
33
- @text << ( e['alt'] || '')
34
- end
35
- end
36
-
37
- end
38
-
39
- def uri
40
- @href && URI.parse(@href)
41
- end
42
-
43
- # Click on this link
44
- def click
45
- @mech.click self
46
- end
47
- end
48
- end
49
- end
50
- end
@@ -1,51 +0,0 @@
1
- module WWW
2
- class Mechanize
3
- class Page < WWW::Mechanize::File
4
- # This class encapsulates a Meta tag. Mechanize treats meta tags just
5
- # like 'a' tags. Meta objects will contain links, but most likely will
6
- # have no text.
7
- class Meta < Link
8
-
9
- # Matches the content attribute of a meta tag. After the match:
10
- #
11
- # $1:: delay
12
- # $3:: url
13
- #
14
- CONTENT_REGEXP = /^\s*(\d+\.?\d*)(;|;\s*url=\s*['"]?(\S*?)['"]?)?\s*$/i
15
-
16
- class << self
17
- # Parses the delay and url from the content attribute of a meta tag.
18
- # Parse requires the uri of the current page to infer a url when no
19
- # url is specified. If a block is given, the parsed delay and url
20
- # will be passed to it for further processing.
21
- #
22
- # Returns nil if the delay and url cannot be parsed.
23
- #
24
- # # <meta http-equiv="refresh" content="5;url=http://example.com/" />
25
- # uri = URI.parse('http://current.com/')
26
- #
27
- # Meta.parse("5;url=http://example.com/", uri) # => ['5', 'http://example.com/']
28
- # Meta.parse("5;url=", uri) # => ['5', 'http://current.com/']
29
- # Meta.parse("5", uri) # => ['5', 'http://current.com/']
30
- # Meta.parse("invalid content", uri) # => nil
31
- #
32
- def parse(content, uri)
33
- if content && content =~ CONTENT_REGEXP
34
- delay, url = $1, $3
35
-
36
- url = case url
37
- when nil, "" then uri.to_s
38
- when /^http/i then url
39
- else "http://#{uri.host}#{url}"
40
- end
41
-
42
- block_given? ? yield(delay, url) : [delay, url]
43
- else
44
- nil
45
- end
46
- end
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,103 +0,0 @@
1
- require 'www/mechanize/file'
2
- require 'www/mechanize/file_saver'
3
- require 'www/mechanize/page'
4
-
5
- module WWW
6
- class Mechanize
7
- # = Synopsis
8
- # This class is used to register and maintain pluggable parsers for
9
- # Mechanize to use.
10
- #
11
- # A Pluggable Parser is a parser that Mechanize uses for any particular
12
- # content type. Mechanize will ask PluggableParser for the class it
13
- # should initialize given any content type. This class allows users to
14
- # register their own pluggable parsers, or modify existing pluggable
15
- # parsers.
16
- #
17
- # PluggableParser returns a WWW::Mechanize::File object for content types
18
- # that it does not know how to handle. WWW::Mechanize::File provides
19
- # basic functionality for any content type, so it is a good class to
20
- # extend when building your own parsers.
21
- # == Example
22
- # To create your own parser, just create a class that takes four
23
- # parameters in the constructor. Here is an example of registering
24
- # a pluggable parser that handles CSV files:
25
- # class CSVParser < WWW::Mechanize::File
26
- # attr_reader :csv
27
- # def initialize(uri=nil, response=nil, body=nil, code=nil)
28
- # super(uri, response, body, code)
29
- # @csv = CSV.parse(body)
30
- # end
31
- # end
32
- # agent = WWW::Mechanize.new
33
- # agent.pluggable_parser.csv = CSVParser
34
- # agent.get('http://example.com/test.csv') # => CSVParser
35
- # Now any page that returns the content type of 'text/csv' will initialize
36
- # a CSVParser and return that object to the caller.
37
- #
38
- # To register a pluggable parser for a content type that pluggable parser
39
- # does not know about, just use the hash syntax:
40
- # agent.pluggable_parser['text/something'] = SomeClass
41
- #
42
- # To set the default parser, just use the 'defaut' method:
43
- # agent.pluggable_parser.default = SomeClass
44
- # Now all unknown content types will be instances of SomeClass.
45
- class PluggableParser
46
- CONTENT_TYPES = {
47
- :html => 'text/html',
48
- :wap => 'application/vnd.wap.xhtml+xml',
49
- :xhtml => 'application/xhtml+xml',
50
- :pdf => 'application/pdf',
51
- :csv => 'text/csv',
52
- :xml => 'text/xml',
53
- }
54
-
55
- attr_accessor :default
56
-
57
- def initialize
58
- @parsers = { CONTENT_TYPES[:html] => Page,
59
- CONTENT_TYPES[:xhtml] => Page,
60
- CONTENT_TYPES[:wap] => Page,
61
- }
62
- @default = File
63
- end
64
-
65
- def parser(content_type)
66
- content_type.nil? ? default : @parsers[content_type] || default
67
- end
68
-
69
- def register_parser(content_type, klass)
70
- @parsers[content_type] = klass
71
- end
72
-
73
- def html=(klass)
74
- register_parser(CONTENT_TYPES[:html], klass)
75
- register_parser(CONTENT_TYPES[:xhtml], klass)
76
- end
77
-
78
- def xhtml=(klass)
79
- register_parser(CONTENT_TYPES[:xhtml], klass)
80
- end
81
-
82
- def pdf=(klass)
83
- register_parser(CONTENT_TYPES[:pdf], klass)
84
- end
85
-
86
- def csv=(klass)
87
- register_parser(CONTENT_TYPES[:csv], klass)
88
- end
89
-
90
- def xml=(klass)
91
- register_parser(CONTENT_TYPES[:xml], klass)
92
- end
93
-
94
- def [](content_type)
95
- @parsers[content_type]
96
- end
97
-
98
- def []=(content_type, klass)
99
- @parsers[content_type] = klass
100
- end
101
- end
102
- end
103
- end
@@ -1,18 +0,0 @@
1
- module WWW
2
- class Mechanize
3
- # Thrown when too many redirects are sent
4
- class RedirectLimitReachedError < RuntimeError
5
- attr_reader :page, :response_code, :redirects
6
- def initialize(page, redirects)
7
- @page = page
8
- @redirects = redirects
9
- @response_code = page.code
10
- end
11
-
12
- def to_s
13
- "Maximum redirect limit (#{redirects}) reached"
14
- end
15
- alias :inspect :to_s
16
- end
17
- end
18
- end
@@ -1,20 +0,0 @@
1
- module WWW
2
- class Mechanize
3
- # Thrown when a POST, PUT, or DELETE request results in a redirect
4
- # see RFC 2616 10.3.2, 10.3.3 http://www.ietf.org/rfc/rfc2616.txt
5
- class RedirectNotGetOrHeadError < RuntimeError
6
- attr_reader :page, :response_code, :verb, :uri
7
- def initialize(page, verb)
8
- @page = page
9
- @verb = verb
10
- @uri = page.uri
11
- @response_code = page.code
12
- end
13
-
14
- def to_s
15
- "#{@response_code} redirect received after a #{@verb} request"
16
- end
17
- alias :inspect :to_s
18
- end
19
- end
20
- end