wycats-merb-core 0.9.8

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 (98) hide show
  1. data/CHANGELOG +992 -0
  2. data/CONTRIBUTORS +94 -0
  3. data/LICENSE +20 -0
  4. data/PUBLIC_CHANGELOG +142 -0
  5. data/README +21 -0
  6. data/Rakefile +458 -0
  7. data/TODO +0 -0
  8. data/bin/merb +11 -0
  9. data/bin/merb-specs +5 -0
  10. data/lib/merb-core.rb +598 -0
  11. data/lib/merb-core/autoload.rb +31 -0
  12. data/lib/merb-core/bootloader.rb +717 -0
  13. data/lib/merb-core/config.rb +305 -0
  14. data/lib/merb-core/constants.rb +45 -0
  15. data/lib/merb-core/controller/abstract_controller.rb +568 -0
  16. data/lib/merb-core/controller/exceptions.rb +315 -0
  17. data/lib/merb-core/controller/merb_controller.rb +256 -0
  18. data/lib/merb-core/controller/mime.rb +107 -0
  19. data/lib/merb-core/controller/mixins/authentication.rb +123 -0
  20. data/lib/merb-core/controller/mixins/conditional_get.rb +83 -0
  21. data/lib/merb-core/controller/mixins/controller.rb +319 -0
  22. data/lib/merb-core/controller/mixins/render.rb +513 -0
  23. data/lib/merb-core/controller/mixins/responder.rb +469 -0
  24. data/lib/merb-core/controller/template.rb +254 -0
  25. data/lib/merb-core/core_ext.rb +9 -0
  26. data/lib/merb-core/core_ext/hash.rb +7 -0
  27. data/lib/merb-core/core_ext/kernel.rb +340 -0
  28. data/lib/merb-core/dispatch/cookies.rb +130 -0
  29. data/lib/merb-core/dispatch/default_exception/default_exception.rb +93 -0
  30. data/lib/merb-core/dispatch/default_exception/views/_css.html.erb +198 -0
  31. data/lib/merb-core/dispatch/default_exception/views/_javascript.html.erb +73 -0
  32. data/lib/merb-core/dispatch/default_exception/views/index.html.erb +94 -0
  33. data/lib/merb-core/dispatch/dispatcher.rb +176 -0
  34. data/lib/merb-core/dispatch/request.rb +729 -0
  35. data/lib/merb-core/dispatch/router.rb +151 -0
  36. data/lib/merb-core/dispatch/router/behavior.rb +566 -0
  37. data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
  38. data/lib/merb-core/dispatch/router/resources.rb +191 -0
  39. data/lib/merb-core/dispatch/router/route.rb +511 -0
  40. data/lib/merb-core/dispatch/session.rb +222 -0
  41. data/lib/merb-core/dispatch/session/container.rb +74 -0
  42. data/lib/merb-core/dispatch/session/cookie.rb +173 -0
  43. data/lib/merb-core/dispatch/session/memcached.rb +68 -0
  44. data/lib/merb-core/dispatch/session/memory.rb +99 -0
  45. data/lib/merb-core/dispatch/session/store_container.rb +150 -0
  46. data/lib/merb-core/dispatch/worker.rb +28 -0
  47. data/lib/merb-core/gem_ext/erubis.rb +77 -0
  48. data/lib/merb-core/logger.rb +203 -0
  49. data/lib/merb-core/plugins.rb +67 -0
  50. data/lib/merb-core/rack.rb +25 -0
  51. data/lib/merb-core/rack/adapter.rb +44 -0
  52. data/lib/merb-core/rack/adapter/ebb.rb +25 -0
  53. data/lib/merb-core/rack/adapter/evented_mongrel.rb +26 -0
  54. data/lib/merb-core/rack/adapter/fcgi.rb +17 -0
  55. data/lib/merb-core/rack/adapter/irb.rb +118 -0
  56. data/lib/merb-core/rack/adapter/mongrel.rb +26 -0
  57. data/lib/merb-core/rack/adapter/runner.rb +28 -0
  58. data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +26 -0
  59. data/lib/merb-core/rack/adapter/thin.rb +39 -0
  60. data/lib/merb-core/rack/adapter/thin_turbo.rb +24 -0
  61. data/lib/merb-core/rack/adapter/webrick.rb +36 -0
  62. data/lib/merb-core/rack/application.rb +32 -0
  63. data/lib/merb-core/rack/handler/mongrel.rb +97 -0
  64. data/lib/merb-core/rack/middleware.rb +20 -0
  65. data/lib/merb-core/rack/middleware/conditional_get.rb +29 -0
  66. data/lib/merb-core/rack/middleware/content_length.rb +18 -0
  67. data/lib/merb-core/rack/middleware/csrf.rb +73 -0
  68. data/lib/merb-core/rack/middleware/path_prefix.rb +31 -0
  69. data/lib/merb-core/rack/middleware/profiler.rb +19 -0
  70. data/lib/merb-core/rack/middleware/static.rb +45 -0
  71. data/lib/merb-core/rack/middleware/tracer.rb +20 -0
  72. data/lib/merb-core/server.rb +284 -0
  73. data/lib/merb-core/tasks/audit.rake +68 -0
  74. data/lib/merb-core/tasks/gem_management.rb +229 -0
  75. data/lib/merb-core/tasks/merb.rb +1 -0
  76. data/lib/merb-core/tasks/merb_rake_helper.rb +80 -0
  77. data/lib/merb-core/tasks/stats.rake +71 -0
  78. data/lib/merb-core/test.rb +11 -0
  79. data/lib/merb-core/test/helpers.rb +9 -0
  80. data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
  81. data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
  82. data/lib/merb-core/test/helpers/request_helper.rb +393 -0
  83. data/lib/merb-core/test/helpers/route_helper.rb +39 -0
  84. data/lib/merb-core/test/helpers/view_helper.rb +121 -0
  85. data/lib/merb-core/test/matchers.rb +9 -0
  86. data/lib/merb-core/test/matchers/controller_matchers.rb +351 -0
  87. data/lib/merb-core/test/matchers/route_matchers.rb +137 -0
  88. data/lib/merb-core/test/matchers/view_matchers.rb +375 -0
  89. data/lib/merb-core/test/run_specs.rb +49 -0
  90. data/lib/merb-core/test/tasks/spectasks.rb +68 -0
  91. data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
  92. data/lib/merb-core/test/test_ext/object.rb +14 -0
  93. data/lib/merb-core/test/test_ext/string.rb +14 -0
  94. data/lib/merb-core/vendor/facets.rb +2 -0
  95. data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
  96. data/lib/merb-core/vendor/facets/inflect.rb +342 -0
  97. data/lib/merb-core/version.rb +3 -0
  98. metadata +253 -0
@@ -0,0 +1,39 @@
1
+ module Merb
2
+ module Test
3
+ module RouteHelper
4
+ include RequestHelper
5
+
6
+ # Mimics the url method available to controllers.
7
+ #
8
+ # ==== Parameters
9
+ # name<~to_sym>:: The name of the URL to generate.
10
+ # params<Hash>:: Parameters for the route generation.
11
+ #
12
+ # ==== Returns
13
+ # String:: The generated URL.
14
+ def url(*args)
15
+ name = args.first.is_a?(Symbol) ? args.shift : :default
16
+
17
+ unless route = Merb::Router.named_routes[name]
18
+ raise Merb::Router::GenerationError, "Named route not found: #{name}"
19
+ end
20
+
21
+ route.generate(args, @request_params || {})
22
+ end
23
+
24
+ # ==== Parameters
25
+ # path<~to_string>:: The URL of the request.
26
+ # method<~to_sym>:: HTTP request method.
27
+ # env<Hash>:: Additional parameters for the request.
28
+ #
29
+ # ==== Returns
30
+ # Hash:: A hash containing the controller and action along with any parameters
31
+ def request_to(path, method = :get, env = {})
32
+ env[:request_method] ||= method.to_s.upcase
33
+ env[:request_uri] = path
34
+
35
+ check_request_for_route(build_request({}, env))
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,121 @@
1
+ module Merb
2
+ module Test
3
+ module ViewHelper
4
+
5
+ # Small utility class for working with the Hpricot parser class
6
+ class DocumentOutput
7
+
8
+ # ==== Parameters
9
+ # response_body<String>:: The response body to parse with Hpricot.
10
+ def initialize(response_body)
11
+ @parser = Hpricot.parse(response_body)
12
+ end
13
+
14
+ # ==== Parameters
15
+ # css_query<String>::
16
+ # A CSS query to find the element for, e.g. "ul.links".
17
+ #
18
+ # ==== Returns
19
+ # String:: The content of the first tag matching the query.
20
+ def content_for(css_query)
21
+ match = @parser.search(css_query).first
22
+ match.inner_text unless match.nil?
23
+ end
24
+
25
+ # ==== Parameters
26
+ # css_query<String>:: A CSS query to find the elements for.
27
+ #
28
+ # ==== Returns
29
+ # Array[String]:: Content of all tags matching the query.
30
+ def content_for_all(css_query)
31
+ matches = @parser.search(css_query).collect{|ele| ele.inner_text}
32
+ end
33
+
34
+ # ==== Parameters
35
+ # css_query<String>:: A CSS query to find the elements for.
36
+ #
37
+ # ==== Returns
38
+ # Hpricot::Elements:: All tags matching the query.
39
+ def [](css_query)
40
+ @parser.search(css_query)
41
+ end
42
+ end
43
+
44
+ # ==== Parameters
45
+ # css_query<String>:: A CSS query to find the element for.
46
+ # output<DocumentOutput>::
47
+ # The output to look for the element in. Defaults to process_output.
48
+ #
49
+ # ==== Returns
50
+ # String:: The content of the first tag matching the query.
51
+ def tag(css_query, output = process_output)
52
+ output.content_for(css_query)
53
+ end
54
+
55
+ # ==== Parameters
56
+ # css_query<String>:: A CSS query to find the elements for.
57
+ # output<DocumentOutput>::
58
+ # The output to look for the element in. Defaults to process_output.
59
+ #
60
+ # ==== Returns
61
+ # Array[String]:: Content of all tags matching the query.
62
+ def tags(css_query, output = process_output)
63
+ output.content_for_all(css_query)
64
+ end
65
+
66
+ # ==== Parameters
67
+ # css_query<String>:: A CSS query to find the element for.
68
+ # output<DocumentOutput>::
69
+ # The output to look for the element in. Defaults to process_output.
70
+ #
71
+ # ==== Returns
72
+ # Hpricot::Elem:: The first tag matching the query.
73
+ def element(css_query, output = process_output)
74
+ output[css_query].first
75
+ end
76
+
77
+ # ==== Parameters
78
+ # css_query<String>:: A CSS query to find the elements for.
79
+ # output<DocumentOutput>::
80
+ # The output to look for the elements in. Defaults to process_output.
81
+ #
82
+ # ==== Returns
83
+ # Array[Hpricot::Elem]:: All tags matching the query.
84
+ def elements(css_query, output = process_output)
85
+ Hpricot::Elements[*css_query.to_s.split(",").map{|s| s.strip}.map do |query|
86
+ output[query]
87
+ end.flatten]
88
+ end
89
+
90
+ # ==== Parameters
91
+ # css_query<String>:: A CSS query to find the elements for.
92
+ # text<String, Regexp>:: A pattern to match tag contents for.
93
+ # output<DocumentOutput>::
94
+ # The output to look for the elements in. Defaults to process_output.
95
+ #
96
+ # ==== Returns
97
+ # Array[Hpricot::Elem]:: All tags matching the query and pattern.
98
+ def get_elements(css_query, text, output = nil)
99
+ els = elements(*[css_query, output].compact)
100
+ case text
101
+ when String then els.reject {|t| !t.contains?(text) }
102
+ when Regexp then els.reject {|t| !t.matches?(text) }
103
+ else []
104
+ end
105
+ end
106
+
107
+ protected
108
+
109
+ # ==== Returns
110
+ # DocumentOutput:: Document output from the response body.
111
+ def process_output
112
+ return @output unless @output.nil?
113
+ return @output = DocumentOutput.new(@response_output) unless @response_output.nil?
114
+
115
+ raise "The response output was not in its usual places, please provide the output" if @controller.nil? || @controller.body.empty?
116
+ @response_output = @controller.body
117
+ @output = DocumentOutput.new(@controller.body)
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,9 @@
1
+ module Merb::Test::Rspec; end
2
+
3
+ require "merb-core/test/matchers/controller_matchers"
4
+ require "merb-core/test/matchers/route_matchers"
5
+ require "merb-core/test/matchers/view_matchers"
6
+
7
+ Merb::Test::ControllerHelper.send(:include, Merb::Test::Rspec::ControllerMatchers)
8
+ Merb::Test::RouteHelper.send(:include, Merb::Test::Rspec::RouteMatchers)
9
+ Merb::Test::ViewHelper.send(:include, Merb::Test::Rspec::ViewMatchers)
@@ -0,0 +1,351 @@
1
+ module Merb::Test::Rspec::ControllerMatchers
2
+
3
+ class BeRedirect
4
+ # ==== Parameters
5
+ # target<Fixnum, ~status>::
6
+ # Either the status code or a controller with a status code.
7
+ #
8
+ # ==== Returns
9
+ # Boolean:: True if the status code is in the range 300..305 or 307.
10
+ def matches?(target)
11
+ @target = target
12
+ [307, *(300..305)].include?(target.respond_to?(:status) ? target.status : target)
13
+ end
14
+
15
+ # ==== Returns
16
+ # String:: The failure message.
17
+ def failure_message
18
+ "expected#{inspect_target} to redirect"
19
+ end
20
+
21
+ # ==== Returns
22
+ # String:: The failure message to be displayed in negative matches.
23
+ def negative_failure_message
24
+ "expected#{inspect_target} not to redirect"
25
+ end
26
+
27
+ # ==== Returns
28
+ # String:: The controller and action name.
29
+ def inspect_target
30
+ " #{@target.controller_name}##{@target.action_name}" if @target.respond_to?(:controller_name) && @target.respond_to?(:action_name)
31
+ end
32
+ end
33
+
34
+ class RedirectTo
35
+
36
+ # === Parameters
37
+ # String:: The expected location
38
+ # Hash:: Optional hash of options (currently only :message)
39
+ def initialize(expected, options = {})
40
+ @expected = expected
41
+ @options = options
42
+ end
43
+
44
+ # ==== Parameters
45
+ # target<Merb::Controller>:: The controller to match
46
+ #
47
+ # ==== Returns
48
+ # Boolean::
49
+ # True if the controller status is redirect and the locations match.
50
+ def matches?(target)
51
+ @target, @location = target, target.headers['Location']
52
+ @redirected = BeRedirect.new.matches?(target.status)
53
+
54
+ if @options[:message]
55
+ msg = Merb::Request.escape([Marshal.dump(@options[:message])].pack("m"))
56
+ @expected << "?_message=#{msg}"
57
+ end
58
+
59
+ @location == @expected && @redirected
60
+ end
61
+
62
+ # ==== Returns
63
+ # String:: The failure message.
64
+ def failure_message
65
+ msg = "expected #{inspect_target} to redirect to <#{@expected}>, but "
66
+ if @redirected
67
+ msg << "was <#{target_location}>"
68
+ else
69
+ msg << "there was no redirection"
70
+ end
71
+ end
72
+
73
+ # ==== Returns
74
+ # String:: The failure message to be displayed in negative matches.
75
+ def negative_failure_message
76
+ "expected #{inspect_target} not to redirect to <#{@expected}>, but did anyway"
77
+ end
78
+
79
+ # ==== Returns
80
+ # String:: The controller and action name.
81
+ def inspect_target
82
+ "#{@target.controller_name}##{@target.action_name}"
83
+ end
84
+
85
+ # ==== Returns
86
+ # String:: Either the target's location header or the target itself.
87
+ def target_location
88
+ @target.respond_to?(:headers) ? @target.headers['Location'] : @target
89
+ end
90
+ end
91
+
92
+ class BeSuccess
93
+
94
+ # ==== Parameters
95
+ # target<Fixnum, ~status>::
96
+ # Either the status code or a controller with a status code.
97
+ #
98
+ # ==== Returns
99
+ # Boolean:: True if the status code is in the range 200..207.
100
+ def matches?(target)
101
+ @target = target
102
+ (200..207).include?(status_code)
103
+ end
104
+
105
+ # ==== Returns
106
+ # String:: The failure message.
107
+ def failure_message
108
+ "expected#{inspect_target} to be successful but was #{status_code}"
109
+ end
110
+
111
+ # ==== Returns
112
+ # String:: The failure message to be displayed in negative matches.
113
+ def negative_failure_message
114
+ "expected#{inspect_target} not to be successful but it was #{status_code}"
115
+ end
116
+
117
+ # ==== Returns
118
+ # String:: The controller and action name.
119
+ def inspect_target
120
+ " #{@target.controller_name}##{@target.action_name}" if @target.respond_to?(:controller_name) && @target.respond_to?(:action_name)
121
+ end
122
+
123
+ # ==== Returns
124
+ # Fixnum:: Either the target's status or the target itself.
125
+ def status_code
126
+ @target.respond_to?(:status) ? @target.status : @target
127
+ end
128
+ end
129
+
130
+ class BeMissing
131
+
132
+ # ==== Parameters
133
+ # target<Fixnum, ~status>::
134
+ # Either the status code or a controller with a status code.
135
+ #
136
+ # ==== Returns
137
+ # Boolean:: True if the status code is in the range 400..417.
138
+ def matches?(target)
139
+ @target = target
140
+ (400..417).include?(status_code)
141
+ end
142
+
143
+ # ==== Returns
144
+ # String:: The failure message.
145
+ def failure_message
146
+ "expected#{inspect_target} to be missing but was #{status_code}"
147
+ end
148
+
149
+ # ==== Returns
150
+ # String:: The failure message to be displayed in negative matches.
151
+ def negative_failure_message
152
+ "expected#{inspect_target} not to be missing but it was #{status_code}"
153
+ end
154
+
155
+ # ==== Returns
156
+ # String:: The controller and action name.
157
+ def inspect_target
158
+ " #{@target.controller_name}##{@target.action_name}" if @target.respond_to?(:controller_name) && @target.respond_to?(:action_name)
159
+ end
160
+
161
+ # ==== Returns
162
+ # Fixnum:: Either the target's status or the target itself.
163
+ def status_code
164
+ @target.respond_to?(:status) ? @target.status : @target
165
+ end
166
+ end
167
+
168
+ class BeError
169
+ def initialize(expected)
170
+ @expected = expected
171
+ end
172
+
173
+ def matches?(target)
174
+ @target = target
175
+ @target.request.exceptions &&
176
+ @target.request.exceptions.first.is_a?(@expected)
177
+ end
178
+
179
+ def failure_message
180
+ "expected #{@target} to be a #{@expected} error, but it was " <<
181
+ @target.request.exceptions.first.inspect
182
+ end
183
+
184
+ def negative_failure_message
185
+ "expected #{@target} not to be a #{@expected} error, but it was"
186
+ end
187
+ end
188
+
189
+ class Provide
190
+
191
+ # === Parameters
192
+ # expected<Symbol>:: A format to check
193
+ def initialize(expected)
194
+ @expected = expected
195
+ end
196
+
197
+ # ==== Parameters
198
+ # target<Symbol>::
199
+ # A ControllerClass or controller_instance
200
+ #
201
+ # ==== Returns
202
+ # Boolean:: True if the formats provided by the target controller/class include the expected
203
+ def matches?(target)
204
+ @target = target
205
+ provided_formats.include?( @expected )
206
+ end
207
+
208
+ # ==== Returns
209
+ # String:: The failure message.
210
+ def failure_message
211
+ "expected #{@target.name} to provide #{@expected}, but it doesn't"
212
+ end
213
+
214
+ # ==== Returns
215
+ # String:: The failure message to be displayed in negative matches.
216
+ def negative_failure_message
217
+ "expected #{@target.name} not to provide #{@expected}, but it does"
218
+ end
219
+
220
+ # ==== Returns
221
+ # Array[Symbol]:: The formats the expected provides
222
+ def provided_formats
223
+ @target.class_provided_formats
224
+ end
225
+ end
226
+
227
+ # Passes if the target was redirected, or the target is a redirection (300
228
+ # level) response code.
229
+ #
230
+ # ==== Examples
231
+ # # Passes if the controller was redirected
232
+ # controller.should redirect
233
+ #
234
+ # # Also works if the target is the response code
235
+ # controller.status.should redirect
236
+ #
237
+ # ==== Notes
238
+ # valid HTTP Redirection codes:
239
+ # * 300: Multiple Choices
240
+ # * 301: Moved Permanently
241
+ # * 302: Moved Temporarily (HTTP/1.0)
242
+ # * 302: Found (HTTP/1.1)
243
+ # * 303: See Other (HTTP/1.1)
244
+ # * 304: Not Modified
245
+ # * 305: Use Proxy
246
+ # * 307: Temporary Redirect
247
+ #--
248
+ # status codes based on: http://cheat.errtheblog.com/s/http_status_codes/
249
+ def redirect
250
+ BeRedirect.new
251
+ end
252
+
253
+ alias_method :be_redirection, :redirect
254
+
255
+ # Passes if the target was redirected to the expected location.
256
+ #
257
+ # ==== Paramters
258
+ # expected<String>:: A relative or absolute url.
259
+ #
260
+ # ==== Examples
261
+ # # Passes if the controller was redirected to http://example.com/
262
+ # controller.should redirect_to('http://example.com/')
263
+ def redirect_to(expected, options = {})
264
+ RedirectTo.new(expected, options)
265
+ end
266
+
267
+ alias_method :be_redirection_to, :redirect_to
268
+
269
+ # Passes if the request that generated the target was successful, or the
270
+ # target is a success (200 level) response code.
271
+ #
272
+ # ==== Examples
273
+ # # Passes if the controller call was successful
274
+ # controller.should respond_successfully
275
+ #
276
+ # # Also works if the target is the response code
277
+ # controller.status.should respond_successfully
278
+ #
279
+ # ==== Notes
280
+ # valid HTTP Success codes:
281
+ # * 200: OK
282
+ # * 201: Created
283
+ # * 202: Accepted
284
+ # * 203: Non-Authoritative Information
285
+ # * 204: No Content
286
+ # * 205: Reset Content
287
+ # * 206: Partial Content
288
+ # * 207: Multi-Status
289
+ #--
290
+ # status codes based on: http://cheat.errtheblog.com/s/http_status_codes/
291
+ def respond_successfully
292
+ BeSuccess.new
293
+ end
294
+
295
+ alias_method :be_successful, :respond_successfully
296
+
297
+ # Passes if the request that generated the target was missing, or the target
298
+ # is a client-side error (400 level) response code.
299
+ #
300
+ # ==== Examples
301
+ # # Passes if the controller call was unknown or not understood
302
+ # controller.should be_missing
303
+ #
304
+ # # Also passes if the target is a response code
305
+ # controller.status.should be_missing
306
+ #
307
+ # ==== Notes
308
+ # valid HTTP Client Error codes:
309
+ # * 400: Bad Request
310
+ # * 401: Unauthorized
311
+ # * 402: Payment Required
312
+ # * 403: Forbidden
313
+ # * 404: Not Found
314
+ # * 405: Method Not Allowed
315
+ # * 406: Not Acceptable
316
+ # * 407: Proxy Authentication Required
317
+ # * 408: Request Timeout
318
+ # * 409: Conflict
319
+ # * 410: Gone
320
+ # * 411: Length Required
321
+ # * 412: Precondition Failed
322
+ # * 413: Request Entity Too Large
323
+ # * 414: Request-URI Too Long
324
+ # * 415: Unsupported Media Type
325
+ # * 416: Requested Range Not Satisfiable
326
+ # * 417: Expectation Failed
327
+ # * 422: Unprocessable Entity
328
+ #--
329
+ # status codes based on: http://cheat.errtheblog.com/s/http_status_codes/
330
+ def be_missing
331
+ BeMissing.new
332
+ end
333
+
334
+ def be_error(expected)
335
+ BeError.new(expected)
336
+ end
337
+
338
+ alias_method :be_client_error, :be_missing
339
+
340
+ # Passes if the controller actually provides the target format
341
+ #
342
+ # === Parameters
343
+ # expected<Symbol>:: A format to check
344
+ #
345
+ # ==== Examples
346
+ # ControllerClass.should provide( :html )
347
+ # controller_instance.should provide( :xml )
348
+ def provide( expected )
349
+ Provide.new( expected )
350
+ end
351
+ end