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,30 @@
1
+ ##
2
+ # An image element on an HTML page
3
+
4
+ class Mechanize::Page::Image
5
+ attr_reader :node
6
+ attr_reader :page
7
+
8
+ def initialize(node, page)
9
+ @node = node
10
+ @page = page
11
+ end
12
+
13
+ def src
14
+ @node['src']
15
+ end
16
+
17
+ def url
18
+ case src
19
+ when %r{^https?://}
20
+ src
21
+ else
22
+ if page.bases[0]
23
+ (page.bases[0].href + src).to_s
24
+ else
25
+ (page.uri + src).to_s
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,20 @@
1
+ ##
2
+ # A form label on an HTML page
3
+
4
+ class Mechanize::Page::Label
5
+ attr_reader :node
6
+ attr_reader :text
7
+ attr_reader :page
8
+ alias :to_s :text
9
+
10
+ def initialize(node, page)
11
+ @node = node
12
+ @text = node.inner_text
13
+ @page = page
14
+ end
15
+
16
+ def for
17
+ (id = @node['for']) && page.search("##{id}") || nil
18
+ end
19
+ end
20
+
@@ -0,0 +1,98 @@
1
+ ##
2
+ # This class encapsulates links. It contains the text and the URI for
3
+ # 'a' tags parsed out of an HTML page. If the link contains an image,
4
+ # the alt text will be used for that image.
5
+ #
6
+ # For example, the text for the following links with both be 'Hello World':
7
+ #
8
+ # <a href="http://example">Hello World</a>
9
+ # <a href="http://example"><img src="test.jpg" alt="Hello World"></a>
10
+
11
+ class Mechanize::Page::Link
12
+ attr_reader :node
13
+ attr_reader :href
14
+ attr_reader :attributes
15
+ attr_reader :page
16
+ alias :referer :page
17
+
18
+ def initialize(node, mech, page)
19
+ @node = node
20
+ @attributes = node
21
+ @href = node['href']
22
+ @mech = mech
23
+ @page = page
24
+ @text = nil
25
+ @uri = nil
26
+ end
27
+
28
+ # Click on this link
29
+ def click
30
+ @mech.click self
31
+ end
32
+
33
+ # This method is a shorthand to get link's DOM id.
34
+ # Common usage:
35
+ # page.link_with(:dom_id => "links_exact_id")
36
+ def dom_id
37
+ node['id']
38
+ end
39
+
40
+ # This method is a shorthand to get a link's DOM class
41
+ # Common usage:
42
+ # page.link_with(:dom_class => "links_exact_class")
43
+ def dom_class
44
+ node['class']
45
+ end
46
+
47
+ def pretty_print(q) # :nodoc:
48
+ q.object_group(self) {
49
+ q.breakable; q.pp text
50
+ q.breakable; q.pp href
51
+ }
52
+ end
53
+
54
+ alias inspect pretty_inspect # :nodoc:
55
+
56
+ # A list of words in the rel attribute, all lower-cased.
57
+ def rel
58
+ @rel ||= (val = attributes['rel']) ? val.downcase.split(' ') : []
59
+ end
60
+
61
+ # Test if the rel attribute includes +kind+.
62
+ def rel? kind
63
+ rel.include? kind
64
+ end
65
+
66
+ # The text content of this link
67
+ def text
68
+ return @text if @text
69
+
70
+ @text = @node.inner_text
71
+
72
+ # If there is no text, try to find an image and use it's alt text
73
+ if (@text.nil? or @text.empty?) and imgs = @node.search('img') then
74
+ @text = imgs.map do |e|
75
+ e['alt']
76
+ end.join
77
+ end
78
+
79
+ @text
80
+ end
81
+
82
+ alias :to_s :text
83
+
84
+ # A URI for the #href for this link. The link is first parsed as a raw
85
+ # link. If that fails parsing an escaped link is attepmted.
86
+
87
+ def uri
88
+ @uri ||= if @href then
89
+ begin
90
+ URI.parse @href
91
+ rescue URI::InvalidURIError
92
+ URI.parse WEBrick::HTTPUtils.escape @href
93
+ end
94
+ end
95
+ end
96
+
97
+ end
98
+
@@ -0,0 +1,68 @@
1
+ ##
2
+ # This class encapsulates a meta element with a refresh http-equiv. Mechanize
3
+ # treats meta refresh elements just like 'a' tags. MetaRefresh objects will
4
+ # contain links, but most likely will have no text.
5
+
6
+ class Mechanize::Page::MetaRefresh < Mechanize::Page::Link
7
+
8
+ ##
9
+ # Time to wait before next refresh
10
+
11
+ attr_reader :delay
12
+
13
+ ##
14
+ # This MetaRefresh links did not contain a url= in the content attribute and
15
+ # links to itself.
16
+
17
+ attr_reader :link_self
18
+
19
+ ##
20
+ # Matches the content attribute of a meta refresh element. After the match:
21
+ #
22
+ # $1:: delay
23
+ # $3:: url
24
+
25
+ CONTENT_REGEXP = /^\s*(\d+\.?\d*)(;|;\s*url=\s*['"]?(\S*?)['"]?)?\s*$/i
26
+
27
+ ##
28
+ # Parses the delay and url from the content attribute of a meta refresh
29
+ # element. Parse requires the uri of the current page to infer a url when
30
+ # no url is specified.
31
+ #
32
+ # Returns an array of [delay, url]. (both in string)
33
+ #
34
+ # Returns nil if the delay and url cannot be parsed.
35
+
36
+ def self.parse content, base_uri
37
+ return unless content =~ CONTENT_REGEXP
38
+
39
+ link_self = $3.nil? || $3.empty?
40
+ delay, refresh_uri = $1, $3
41
+
42
+ dest = base_uri
43
+ dest += refresh_uri if refresh_uri
44
+
45
+ return delay, dest, link_self
46
+ end
47
+
48
+ def self.from_node node, page, uri
49
+ http_equiv = node['http-equiv']
50
+ return unless http_equiv and http_equiv.downcase == 'refresh'
51
+
52
+ delay, uri, link_self = parse node['content'], uri
53
+
54
+ return unless delay
55
+
56
+ new node, page, delay, uri.to_s, link_self
57
+ end
58
+
59
+ def initialize node, page, delay, href, link_self = false
60
+ super node, page.mech, page
61
+
62
+ @delay = delay =~ /\./ ? delay.to_f : delay.to_i
63
+ @href = href
64
+ @link_self = link_self
65
+ end
66
+
67
+ end
68
+
@@ -0,0 +1,173 @@
1
+ ##
2
+ # The parser module provides standard methods for accessing the headers and
3
+ # content of a response that are shared across pluggable parsers.
4
+
5
+ module Mechanize::Parser
6
+
7
+ extend Forwardable
8
+
9
+ special_filenames = Regexp.union %w[
10
+ AUX
11
+ COM1
12
+ COM2
13
+ COM3
14
+ COM4
15
+ COM5
16
+ COM6
17
+ COM7
18
+ COM8
19
+ COM9
20
+ CON
21
+ LPT1
22
+ LPT2
23
+ LPT3
24
+ LPT4
25
+ LPT5
26
+ LPT6
27
+ LPT7
28
+ LPT8
29
+ LPT9
30
+ NUL
31
+ PRN
32
+ ]
33
+
34
+ ##
35
+ # Special filenames that must be escaped
36
+
37
+ SPECIAL_FILENAMES = /\A#{special_filenames}/i
38
+
39
+ ##
40
+ # The URI this file was retrieved from
41
+
42
+ attr_accessor :uri
43
+
44
+ ##
45
+ # The Mechanize::Headers for this file
46
+
47
+ attr_accessor :response
48
+
49
+ alias header response
50
+
51
+ ##
52
+ # The HTTP response code
53
+
54
+ attr_accessor :code
55
+
56
+ ##
57
+ # :method: [](header)
58
+ #
59
+ # Access HTTP +header+ by name
60
+
61
+ def_delegator :header, :[], :[]
62
+
63
+ ##
64
+ # :method: []=(header, value)
65
+ #
66
+ # Set HTTP +header+ to +value+
67
+
68
+ def_delegator :header, :[]=, :[]=
69
+
70
+ ##
71
+ # :method: key?(header)
72
+ #
73
+ # Is the named +header+ present?
74
+
75
+ def_delegator :header, :key?, :key?
76
+
77
+ ##
78
+ # :method: each
79
+ #
80
+ # Enumerate HTTP headers
81
+
82
+ def_delegator :header, :each, :each
83
+
84
+ ##
85
+ # :method: each
86
+ #
87
+ # Enumerate HTTP headers in capitalized (canonical) form
88
+
89
+ def_delegator :header, :canonical_each, :canonical_each
90
+
91
+ ##
92
+ # Extracts the filename from a Content-Disposition header in the #response
93
+ # or from the URI. If +full_path+ is true the filename will include the
94
+ # host name and path to the resource, otherwise a filename in the current
95
+ # directory is given.
96
+
97
+ def extract_filename full_path = @full_path
98
+ handled = false
99
+
100
+ if @uri then
101
+ uri = @uri
102
+ uri += 'index.html' if uri.path.end_with? '/'
103
+
104
+ path = uri.path.split(/\//)
105
+ filename = path.pop || 'index.html'
106
+ else
107
+ path = []
108
+ filename = 'index.html'
109
+ end
110
+
111
+ # Set the filename
112
+ if disposition = @response['content-disposition'] then
113
+ content_disposition =
114
+ Mechanize::HTTP::ContentDispositionParser.parse disposition
115
+
116
+ if content_disposition then
117
+ filename = content_disposition.filename
118
+ filename = filename.split(/[\\\/]/).last
119
+ handled = true
120
+ end
121
+ end
122
+
123
+ if not handled and @uri then
124
+ filename << '.html' unless filename =~ /\./
125
+ filename << "?#{@uri.query}" if @uri.query
126
+ end
127
+
128
+ if SPECIAL_FILENAMES =~ filename then
129
+ filename = "_#{filename}"
130
+ end
131
+
132
+ filename = filename.tr "\x00-\x20<>:\"/\\|?*", '_'
133
+
134
+ @filename = if full_path then
135
+ File.join @uri.host, path, filename
136
+ else
137
+ filename
138
+ end
139
+ end
140
+
141
+ ##
142
+ # Creates a Mechanize::Header from the Net::HTTPResponse +response+.
143
+ #
144
+ # This allows the Net::HTTPResponse to be garbage collected sooner.
145
+
146
+ def fill_header response
147
+ @response = Mechanize::Headers.new
148
+
149
+ response.each { |k,v|
150
+ @response[k] = v
151
+ } if response
152
+
153
+ @response
154
+ end
155
+
156
+ ##
157
+ # Finds a free filename based on +filename+, but is not race-free
158
+
159
+ def find_free_name filename
160
+ filename = @filename unless filename
161
+
162
+ number = 1
163
+
164
+ while File.exist? filename do
165
+ filename = "#{@filename}.#{number}"
166
+ number += 1
167
+ end
168
+
169
+ filename
170
+ end
171
+
172
+ end
173
+
@@ -0,0 +1,144 @@
1
+ require 'mechanize/file'
2
+ require 'mechanize/file_saver'
3
+ require 'mechanize/page'
4
+
5
+ ##
6
+ # This class is used to register and maintain pluggable parsers for Mechanize
7
+ # to use.
8
+ #
9
+ # Mechanize allows different parsers for different content types. Mechanize
10
+ # uses PluggableParser to determine which parser to use for any content type.
11
+ # To use your own pluggable parser or to change the default pluggable parsers,
12
+ # register them with this class.
13
+ #
14
+ # The default parser for unregistered content types is Mechanize::File.
15
+ #
16
+ # The module Mechanize::Parser provides basic functionality for any content
17
+ # type, so you may use it in custom parsers you write. For small files you
18
+ # wish to perform in-memory operations on, you should subclass
19
+ # Mechanize::File. For large files you should subclass Mechanize::Download as
20
+ # the content is only loaded into memory in small chunks.
21
+ #
22
+ # == Example
23
+ #
24
+ # To create your own parser, just create a class that takes four parameters in
25
+ # the constructor. Here is an example of registering a pluggable parser that
26
+ # handles CSV files:
27
+ #
28
+ # require 'csv'
29
+ #
30
+ # class CSVParser < Mechanize::File
31
+ # attr_reader :csv
32
+ #
33
+ # def initialize uri = nil, response = nil, body = nil, code = nil
34
+ # super uri, response, body, code
35
+ # @csv = CSV.parse body
36
+ # end
37
+ # end
38
+ #
39
+ # agent = Mechanize.new
40
+ # agent.pluggable_parser.csv = CSVParser
41
+ # agent.get('http://example.com/test.csv') # => CSVParser
42
+ #
43
+ # Now any response with a content type of 'text/csv' will initialize a
44
+ # CSVParser and return that object to the caller.
45
+ #
46
+ # To register a pluggable parser for a content type that pluggable parser does
47
+ # not know about, use the hash syntax:
48
+ #
49
+ # agent.pluggable_parser['text/something'] = SomeClass
50
+ #
51
+ # To set the default parser, use #default:
52
+ #
53
+ # agent.pluggable_parser.default = Mechanize::Download
54
+ #
55
+ # Now all unknown content types will be saved to disk and not loaded into
56
+ # memory.
57
+
58
+ class Mechanize::PluggableParser
59
+
60
+ CONTENT_TYPES = {
61
+ :html => 'text/html',
62
+ :wap => 'application/vnd.wap.xhtml+xml',
63
+ :xhtml => 'application/xhtml+xml',
64
+ :pdf => 'application/pdf',
65
+ :csv => 'text/csv',
66
+ :xml => 'text/xml',
67
+ }
68
+
69
+ attr_accessor :default
70
+
71
+ def initialize
72
+ @parsers = {
73
+ CONTENT_TYPES[:html] => Mechanize::Page,
74
+ CONTENT_TYPES[:xhtml] => Mechanize::Page,
75
+ CONTENT_TYPES[:wap] => Mechanize::Page,
76
+ }
77
+
78
+ @default = Mechanize::File
79
+ end
80
+
81
+ ##
82
+ # Returns the parser registered for the given +content_type+
83
+
84
+ def parser(content_type)
85
+ content_type.nil? ? default : @parsers[content_type] || default
86
+ end
87
+
88
+ def register_parser(content_type, klass) # :nodoc:
89
+ @parsers[content_type] = klass
90
+ end
91
+
92
+ ##
93
+ # Registers +klass+ as the parser for text/html and application/xhtml+xml
94
+ # content
95
+
96
+ def html=(klass)
97
+ register_parser(CONTENT_TYPES[:html], klass)
98
+ register_parser(CONTENT_TYPES[:xhtml], klass)
99
+ end
100
+
101
+ ##
102
+ # Registers +klass+ as the parser for application/xhtml+xml content
103
+
104
+ def xhtml=(klass)
105
+ register_parser(CONTENT_TYPES[:xhtml], klass)
106
+ end
107
+
108
+ ##
109
+ # Registers +klass+ as the parser for application/pdf content
110
+
111
+ def pdf=(klass)
112
+ register_parser(CONTENT_TYPES[:pdf], klass)
113
+ end
114
+
115
+ ##
116
+ # Registers +klass+ as the parser for text/csv content
117
+
118
+ def csv=(klass)
119
+ register_parser(CONTENT_TYPES[:csv], klass)
120
+ end
121
+
122
+ ##
123
+ # Registers +klass+ as the parser for text/xml content
124
+
125
+ def xml=(klass)
126
+ register_parser(CONTENT_TYPES[:xml], klass)
127
+ end
128
+
129
+ ##
130
+ # Retrieves the parser for +content_type+ content
131
+
132
+ def [](content_type)
133
+ @parsers[content_type]
134
+ end
135
+
136
+ ##
137
+ # Sets the parser for +content_type+ content to +klass+
138
+
139
+ def []=(content_type, klass)
140
+ @parsers[content_type] = klass
141
+ end
142
+
143
+ end
144
+