merb-core 0.9.2

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 (246) hide show
  1. data/LICENSE +20 -0
  2. data/README +21 -0
  3. data/Rakefile +285 -0
  4. data/TODO +0 -0
  5. data/bin/merb +8 -0
  6. data/bin/merb-specs +5 -0
  7. data/docs/bootloading.dox +57 -0
  8. data/docs/documentation_standards +40 -0
  9. data/docs/new_render_api +51 -0
  10. data/lib/merb-core.rb +304 -0
  11. data/lib/merb-core/autoload.rb +29 -0
  12. data/lib/merb-core/bootloader.rb +601 -0
  13. data/lib/merb-core/config.rb +284 -0
  14. data/lib/merb-core/constants.rb +43 -0
  15. data/lib/merb-core/controller/abstract_controller.rb +531 -0
  16. data/lib/merb-core/controller/exceptions.rb +257 -0
  17. data/lib/merb-core/controller/merb_controller.rb +214 -0
  18. data/lib/merb-core/controller/mime.rb +88 -0
  19. data/lib/merb-core/controller/mixins/controller.rb +262 -0
  20. data/lib/merb-core/controller/mixins/render.rb +324 -0
  21. data/lib/merb-core/controller/mixins/responder.rb +464 -0
  22. data/lib/merb-core/controller/template.rb +205 -0
  23. data/lib/merb-core/core_ext.rb +12 -0
  24. data/lib/merb-core/core_ext/class.rb +192 -0
  25. data/lib/merb-core/core_ext/hash.rb +422 -0
  26. data/lib/merb-core/core_ext/kernel.rb +304 -0
  27. data/lib/merb-core/core_ext/mash.rb +154 -0
  28. data/lib/merb-core/core_ext/object.rb +136 -0
  29. data/lib/merb-core/core_ext/object_space.rb +14 -0
  30. data/lib/merb-core/core_ext/rubygems.rb +28 -0
  31. data/lib/merb-core/core_ext/set.rb +41 -0
  32. data/lib/merb-core/core_ext/string.rb +69 -0
  33. data/lib/merb-core/dispatch/cookies.rb +92 -0
  34. data/lib/merb-core/dispatch/dispatcher.rb +233 -0
  35. data/lib/merb-core/dispatch/exceptions.html.erb +297 -0
  36. data/lib/merb-core/dispatch/request.rb +560 -0
  37. data/lib/merb-core/dispatch/router.rb +141 -0
  38. data/lib/merb-core/dispatch/router/behavior.rb +777 -0
  39. data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
  40. data/lib/merb-core/dispatch/router/route.rb +212 -0
  41. data/lib/merb-core/dispatch/session.rb +28 -0
  42. data/lib/merb-core/dispatch/session/cookie.rb +166 -0
  43. data/lib/merb-core/dispatch/session/memcached.rb +161 -0
  44. data/lib/merb-core/dispatch/session/memory.rb +234 -0
  45. data/lib/merb-core/gem_ext/erubis.rb +19 -0
  46. data/lib/merb-core/logger.rb +230 -0
  47. data/lib/merb-core/plugins.rb +25 -0
  48. data/lib/merb-core/rack.rb +15 -0
  49. data/lib/merb-core/rack/adapter.rb +42 -0
  50. data/lib/merb-core/rack/adapter/ebb.rb +22 -0
  51. data/lib/merb-core/rack/adapter/evented_mongrel.rb +24 -0
  52. data/lib/merb-core/rack/adapter/fcgi.rb +16 -0
  53. data/lib/merb-core/rack/adapter/irb.rb +108 -0
  54. data/lib/merb-core/rack/adapter/mongrel.rb +25 -0
  55. data/lib/merb-core/rack/adapter/runner.rb +27 -0
  56. data/lib/merb-core/rack/adapter/thin.rb +27 -0
  57. data/lib/merb-core/rack/adapter/webrick.rb +35 -0
  58. data/lib/merb-core/rack/application.rb +77 -0
  59. data/lib/merb-core/rack/handler/mongrel.rb +97 -0
  60. data/lib/merb-core/server.rb +184 -0
  61. data/lib/merb-core/test.rb +10 -0
  62. data/lib/merb-core/test/helpers.rb +9 -0
  63. data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
  64. data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
  65. data/lib/merb-core/test/helpers/request_helper.rb +257 -0
  66. data/lib/merb-core/test/helpers/route_helper.rb +33 -0
  67. data/lib/merb-core/test/helpers/view_helper.rb +121 -0
  68. data/lib/merb-core/test/matchers.rb +9 -0
  69. data/lib/merb-core/test/matchers/controller_matchers.rb +269 -0
  70. data/lib/merb-core/test/matchers/route_matchers.rb +136 -0
  71. data/lib/merb-core/test/matchers/view_matchers.rb +293 -0
  72. data/lib/merb-core/test/run_specs.rb +38 -0
  73. data/lib/merb-core/test/tasks/spectasks.rb +39 -0
  74. data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
  75. data/lib/merb-core/test/test_ext/object.rb +14 -0
  76. data/lib/merb-core/vendor/facets.rb +2 -0
  77. data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
  78. data/lib/merb-core/vendor/facets/inflect.rb +211 -0
  79. data/lib/merb-core/version.rb +11 -0
  80. data/spec/private/config/adapter_spec.rb +32 -0
  81. data/spec/private/config/config_spec.rb +139 -0
  82. data/spec/private/config/environment_spec.rb +13 -0
  83. data/spec/private/config/spec_helper.rb +1 -0
  84. data/spec/private/core_ext/hash_spec.rb +506 -0
  85. data/spec/private/core_ext/kernel_spec.rb +46 -0
  86. data/spec/private/core_ext/object_spec.rb +39 -0
  87. data/spec/private/core_ext/set_spec.rb +26 -0
  88. data/spec/private/core_ext/string_spec.rb +9 -0
  89. data/spec/private/dispatch/cookies_spec.rb +107 -0
  90. data/spec/private/dispatch/dispatch_spec.rb +26 -0
  91. data/spec/private/dispatch/fixture/app/controllers/application.rb +4 -0
  92. data/spec/private/dispatch/fixture/app/controllers/exceptions.rb +27 -0
  93. data/spec/private/dispatch/fixture/app/controllers/foo.rb +21 -0
  94. data/spec/private/dispatch/fixture/app/helpers/global_helpers.rb +8 -0
  95. data/spec/private/dispatch/fixture/app/views/exeptions/client_error.html.erb +37 -0
  96. data/spec/private/dispatch/fixture/app/views/exeptions/internal_server_error.html.erb +216 -0
  97. data/spec/private/dispatch/fixture/app/views/exeptions/not_acceptable.html.erb +38 -0
  98. data/spec/private/dispatch/fixture/app/views/exeptions/not_found.html.erb +40 -0
  99. data/spec/private/dispatch/fixture/app/views/foo/bar.html.erb +0 -0
  100. data/spec/private/dispatch/fixture/app/views/layout/application.html.erb +11 -0
  101. data/spec/private/dispatch/fixture/config/environments/development.rb +6 -0
  102. data/spec/private/dispatch/fixture/config/environments/production.rb +5 -0
  103. data/spec/private/dispatch/fixture/config/environments/test.rb +6 -0
  104. data/spec/private/dispatch/fixture/config/init.rb +45 -0
  105. data/spec/private/dispatch/fixture/config/rack.rb +1 -0
  106. data/spec/private/dispatch/fixture/config/router.rb +35 -0
  107. data/spec/private/dispatch/fixture/log/development.log +1 -0
  108. data/spec/private/dispatch/fixture/log/merb.4000.pid +1 -0
  109. data/spec/private/dispatch/fixture/log/merb_test.log +2040 -0
  110. data/spec/private/dispatch/fixture/log/production.log +1 -0
  111. data/spec/private/dispatch/fixture/merb.4000.pid +1 -0
  112. data/spec/private/dispatch/fixture/public/images/merb.jpg +0 -0
  113. data/spec/private/dispatch/fixture/public/merb.fcgi +4 -0
  114. data/spec/private/dispatch/fixture/public/stylesheets/master.css +119 -0
  115. data/spec/private/dispatch/route_params_spec.rb +24 -0
  116. data/spec/private/dispatch/spec_helper.rb +1 -0
  117. data/spec/private/plugins/plugin_spec.rb +81 -0
  118. data/spec/private/rack/application_spec.rb +43 -0
  119. data/spec/public/DEFINITIONS +11 -0
  120. data/spec/public/abstract_controller/controllers/alt_views/layout/application.erb +1 -0
  121. data/spec/public/abstract_controller/controllers/alt_views/layout/merb/test/fixtures/abstract/render_string_controller_layout.erb +1 -0
  122. data/spec/public/abstract_controller/controllers/alt_views/layout/merb/test/fixtures/abstract/render_template_controller_layout.erb +1 -0
  123. data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/display_object_with_multiple_roots/index.erb +1 -0
  124. data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/display_object_with_multiple_roots/show.erb +1 -0
  125. data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/render_template_multiple_roots/index.erb +1 -0
  126. data/spec/public/abstract_controller/controllers/alt_views/partial/basic_partial_with_multiple_roots/_partial.erb +1 -0
  127. data/spec/public/abstract_controller/controllers/alt_views/render_template_multiple_roots_and_custom_location/index.erb +1 -0
  128. data/spec/public/abstract_controller/controllers/alt_views/render_template_multiple_roots_inherited/index.erb +1 -0
  129. data/spec/public/abstract_controller/controllers/display.rb +54 -0
  130. data/spec/public/abstract_controller/controllers/filters.rb +167 -0
  131. data/spec/public/abstract_controller/controllers/helpers.rb +31 -0
  132. data/spec/public/abstract_controller/controllers/partial.rb +106 -0
  133. data/spec/public/abstract_controller/controllers/render.rb +86 -0
  134. data/spec/public/abstract_controller/controllers/views/helpers/capture/index.erb +1 -0
  135. data/spec/public/abstract_controller/controllers/views/helpers/concat/index.erb +1 -0
  136. data/spec/public/abstract_controller/controllers/views/layout/alt.erb +1 -0
  137. data/spec/public/abstract_controller/controllers/views/layout/custom.erb +1 -0
  138. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/display_object/index.erb +1 -0
  139. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/display_object_with_action/new.erb +1 -0
  140. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template/index.erb +1 -0
  141. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_app_layout/index.erb +0 -0
  142. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_custom_layout/index.erb +1 -0
  143. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_multiple_roots/index.erb +1 -0
  144. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_multiple_roots/show.erb +1 -0
  145. data/spec/public/abstract_controller/controllers/views/partial/another_directory/_partial.erb +1 -0
  146. data/spec/public/abstract_controller/controllers/views/partial/basic_partial/_partial.erb +1 -0
  147. data/spec/public/abstract_controller/controllers/views/partial/basic_partial/index.erb +1 -0
  148. data/spec/public/abstract_controller/controllers/views/partial/basic_partial_with_multiple_roots/index.erb +1 -0
  149. data/spec/public/abstract_controller/controllers/views/partial/nested_partial/_first.erb +1 -0
  150. data/spec/public/abstract_controller/controllers/views/partial/nested_partial/_second.erb +1 -0
  151. data/spec/public/abstract_controller/controllers/views/partial/nested_partial/index.erb +1 -0
  152. data/spec/public/abstract_controller/controllers/views/partial/partial_in_another_directory/index.erb +1 -0
  153. data/spec/public/abstract_controller/controllers/views/partial/partial_with_both/_collection.erb +1 -0
  154. data/spec/public/abstract_controller/controllers/views/partial/partial_with_both/index.erb +1 -0
  155. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections/_collection.erb +1 -0
  156. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections/index.erb +1 -0
  157. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_as/_collection.erb +1 -0
  158. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_as/index.erb +1 -0
  159. data/spec/public/abstract_controller/controllers/views/partial/partial_with_locals/_variables.erb +1 -0
  160. data/spec/public/abstract_controller/controllers/views/partial/partial_with_locals/index.erb +1 -0
  161. data/spec/public/abstract_controller/controllers/views/partial/partial_with_with_and_locals/_both.erb +1 -0
  162. data/spec/public/abstract_controller/controllers/views/partial/partial_with_with_and_locals/index.erb +1 -0
  163. data/spec/public/abstract_controller/controllers/views/partial/with_as_partial/_with_partial.erb +1 -0
  164. data/spec/public/abstract_controller/controllers/views/partial/with_as_partial/index.erb +1 -0
  165. data/spec/public/abstract_controller/controllers/views/partial/with_nil_partial/_with_partial.erb +1 -0
  166. data/spec/public/abstract_controller/controllers/views/partial/with_nil_partial/index.erb +1 -0
  167. data/spec/public/abstract_controller/controllers/views/partial/with_partial/_with_partial.erb +1 -0
  168. data/spec/public/abstract_controller/controllers/views/partial/with_partial/index.erb +1 -0
  169. data/spec/public/abstract_controller/controllers/views/test_display/foo.html.erb +1 -0
  170. data/spec/public/abstract_controller/controllers/views/test_render/foo.html.erb +0 -0
  171. data/spec/public/abstract_controller/controllers/views/wonderful/index.erb +1 -0
  172. data/spec/public/abstract_controller/display_spec.rb +33 -0
  173. data/spec/public/abstract_controller/filter_spec.rb +80 -0
  174. data/spec/public/abstract_controller/helper_spec.rb +13 -0
  175. data/spec/public/abstract_controller/partial_spec.rb +53 -0
  176. data/spec/public/abstract_controller/render_spec.rb +70 -0
  177. data/spec/public/abstract_controller/spec_helper.rb +27 -0
  178. data/spec/public/boot_loader/boot_loader_spec.rb +33 -0
  179. data/spec/public/boot_loader/spec_helper.rb +1 -0
  180. data/spec/public/controller/base_spec.rb +31 -0
  181. data/spec/public/controller/controllers/base.rb +41 -0
  182. data/spec/public/controller/controllers/display.rb +40 -0
  183. data/spec/public/controller/controllers/responder.rb +67 -0
  184. data/spec/public/controller/controllers/url.rb +7 -0
  185. data/spec/public/controller/controllers/views/layout/custom.html.erb +1 -0
  186. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.html.erb +1 -0
  187. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.xml.erb +1 -0
  188. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/display_with_template/index.html.erb +1 -0
  189. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/html_default/index.html.erb +1 -0
  190. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/layout/custom.html.erb +1 -0
  191. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/local_provides/index.html.erb +1 -0
  192. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/local_provides/index.xml.erb +1 -0
  193. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/multi_provides/index.html.erb +1 -0
  194. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/multi_provides/index.js.erb +1 -0
  195. data/spec/public/controller/display_spec.rb +34 -0
  196. data/spec/public/controller/log/merb.4000.pid +1 -0
  197. data/spec/public/controller/responder_spec.rb +95 -0
  198. data/spec/public/controller/spec_helper.rb +9 -0
  199. data/spec/public/controller/url_spec.rb +152 -0
  200. data/spec/public/directory_structure/directory/app/controllers/application.rb +3 -0
  201. data/spec/public/directory_structure/directory/app/controllers/base.rb +13 -0
  202. data/spec/public/directory_structure/directory/app/controllers/custom.rb +19 -0
  203. data/spec/public/directory_structure/directory/app/views/base/template.html.erb +1 -0
  204. data/spec/public/directory_structure/directory/app/views/wonderful/template.erb +1 -0
  205. data/spec/public/directory_structure/directory/config/router.rb +3 -0
  206. data/spec/public/directory_structure/directory/log/merb.4000.pid +1 -0
  207. data/spec/public/directory_structure/directory/log/merb_test.log +265 -0
  208. data/spec/public/directory_structure/directory/merb.4000.pid +1 -0
  209. data/spec/public/directory_structure/directory_spec.rb +44 -0
  210. data/spec/public/logger/logger_spec.rb +175 -0
  211. data/spec/public/logger/spec_helper.rb +1 -0
  212. data/spec/public/reloading/directory/app/controllers/application.rb +3 -0
  213. data/spec/public/reloading/directory/app/controllers/reload.rb +6 -0
  214. data/spec/public/reloading/directory/config/init.rb +2 -0
  215. data/spec/public/reloading/directory/log/merb.4000.pid +1 -0
  216. data/spec/public/reloading/directory/log/merb_test.log +59 -0
  217. data/spec/public/reloading/directory/merb.4000.pid +1 -0
  218. data/spec/public/reloading/reload_spec.rb +80 -0
  219. data/spec/public/request/multipart_spec.rb +15 -0
  220. data/spec/public/request/request_spec.rb +207 -0
  221. data/spec/public/router/default_spec.rb +21 -0
  222. data/spec/public/router/deferred_spec.rb +22 -0
  223. data/spec/public/router/namespace_spec.rb +113 -0
  224. data/spec/public/router/nested_resources_spec.rb +34 -0
  225. data/spec/public/router/resource_spec.rb +45 -0
  226. data/spec/public/router/resources_spec.rb +57 -0
  227. data/spec/public/router/spec_helper.rb +72 -0
  228. data/spec/public/router/special_spec.rb +44 -0
  229. data/spec/public/router/string_spec.rb +61 -0
  230. data/spec/public/template/template_spec.rb +92 -0
  231. data/spec/public/template/templates/error.html.erb +2 -0
  232. data/spec/public/template/templates/template.html.erb +1 -0
  233. data/spec/public/template/templates/template.html.myt +1 -0
  234. data/spec/public/test/controller_matchers_spec.rb +378 -0
  235. data/spec/public/test/controllers/controller_assertion_mock.rb +7 -0
  236. data/spec/public/test/controllers/dispatch_controller.rb +11 -0
  237. data/spec/public/test/controllers/spec_helper_controller.rb +30 -0
  238. data/spec/public/test/multipart_request_helper_spec.rb +159 -0
  239. data/spec/public/test/multipart_upload_text_file.txt +1 -0
  240. data/spec/public/test/request_helper_spec.rb +153 -0
  241. data/spec/public/test/route_helper_spec.rb +54 -0
  242. data/spec/public/test/route_matchers_spec.rb +133 -0
  243. data/spec/public/test/view_helper_spec.rb +96 -0
  244. data/spec/public/test/view_matchers_spec.rb +107 -0
  245. data/spec/spec_helper.rb +71 -0
  246. metadata +488 -0
@@ -0,0 +1,257 @@
1
+ require 'tempfile'
2
+
3
+ module Merb
4
+ module Test
5
+ module RequestHelper
6
+ # FakeRequest sets up a default enviroment which can be overridden either
7
+ # by passing and env into initialize or using request['HTTP_VAR'] = 'foo'
8
+ class FakeRequest < Request
9
+
10
+ # ==== Parameters
11
+ # env<Hash>:: Environment options that override the defaults.
12
+ # req<StringIO>:: The request to set as input for Rack.
13
+ def initialize(env = {}, req = StringIO.new)
14
+ env.environmentize_keys!
15
+ env['rack.input'] = req
16
+ super(DEFAULT_ENV.merge(env))
17
+ end
18
+
19
+ private
20
+ DEFAULT_ENV = Mash.new({
21
+ 'SERVER_NAME' => 'localhost',
22
+ 'PATH_INFO' => '/',
23
+ 'HTTP_ACCEPT_ENCODING' => 'gzip,deflate',
24
+ 'HTTP_USER_AGENT' => 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.0.1) Gecko/20060214 Camino/1.0',
25
+ 'SCRIPT_NAME' => '/',
26
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
27
+ 'HTTP_CACHE_CONTROL' => 'max-age=0',
28
+ 'HTTP_ACCEPT_LANGUAGE' => 'en,ja;q=0.9,fr;q=0.9,de;q=0.8,es;q=0.7,it;q=0.7,nl;q=0.6,sv;q=0.5,nb;q=0.5,da;q=0.4,fi;q=0.3,pt;q=0.3,zh-Hans;q=0.2,zh-Hant;q=0.1,ko;q=0.1',
29
+ 'HTTP_HOST' => 'localhost',
30
+ 'REMOTE_ADDR' => '127.0.0.1',
31
+ 'SERVER_SOFTWARE' => 'Mongrel 1.1',
32
+ 'HTTP_KEEP_ALIVE' => '300',
33
+ 'HTTP_REFERER' => 'http://localhost/',
34
+ 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
35
+ 'HTTP_VERSION' => 'HTTP/1.1',
36
+ 'REQUEST_URI' => '/',
37
+ 'SERVER_PORT' => '80',
38
+ 'GATEWAY_INTERFACE' => 'CGI/1.2',
39
+ 'HTTP_ACCEPT' => 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
40
+ 'HTTP_CONNECTION' => 'keep-alive',
41
+ 'REQUEST_METHOD' => 'GET'
42
+ }) unless defined?(DEFAULT_ENV)
43
+ end
44
+
45
+ # ==== Parameters
46
+ # env<Hash>:: A hash of environment keys to be merged into the default list.
47
+ # opt<Hash>:: A hash of options (see below).
48
+ #
49
+ # ==== Options (opt)
50
+ # :post_body<String>:: The post body for the request.
51
+ # :req<String>::
52
+ # The request string. This will only be used if :post_body is left out.
53
+ #
54
+ # ==== Returns
55
+ # FakeRequest:: A Request object that is built based on the parameters.
56
+ #
57
+ # ==== Note
58
+ # If you pass a post body, the content-type will be set to URL-encoded.
59
+ #
60
+ #---
61
+ # @public
62
+ def fake_request(env = {}, opt = {})
63
+ if opt[:post_body]
64
+ req = opt[:post_body]
65
+ env[:content_type] ||= "application/x-www-form-urlencoded"
66
+ else
67
+ req = opt[:req]
68
+ end
69
+ FakeRequest.new(env, StringIO.new(req || ''))
70
+ end
71
+
72
+ # Dispatches an action to the given class. This bypasses the router and is
73
+ # suitable for unit testing of controllers.
74
+ #
75
+ # ==== Parameters
76
+ # controller_klass<Controller>::
77
+ # The controller class object that the action should be dispatched to.
78
+ # action<Symbol>:: The action name, as a symbol.
79
+ # params<Hash>::
80
+ # An optional hash that will end up as params in the controller instance.
81
+ # env<Hash>::
82
+ # An optional hash that is passed to the fake request. Any request options
83
+ # should go here (see +fake_request+), including :req or :post_body
84
+ # for setting the request body itself.
85
+ # &blk::
86
+ # The controller is yielded to the block provided for actions *prior* to
87
+ # the action being dispatched.
88
+ #
89
+ # ==== Example
90
+ # dispatch_to(MyController, :create, :name => 'Homer' ) do
91
+ # self.stub!(:current_user).and_return(@user)
92
+ # end
93
+ #
94
+ # ==== Note
95
+ # Does not use routes.
96
+ #
97
+ #---
98
+ # @public
99
+ def dispatch_to(controller_klass, action, params = {}, env = {}, &blk)
100
+ action = action.to_s
101
+ request_body = { :post_body => env[:post_body], :req => env[:req] }
102
+ request = fake_request(env.merge(
103
+ :query_string => Merb::Request.params_to_query_string(params)), request_body)
104
+
105
+ dispatch_request(request, controller_klass, action, &blk)
106
+ end
107
+
108
+ # An HTTP GET request that operates through the router.
109
+ #
110
+ # ==== Parameters
111
+ # path<String>:: The path that should go to the router as the request uri.
112
+ # params<Hash>::
113
+ # An optional hash that will end up as params in the controller instance.
114
+ # env<Hash>::
115
+ # An optional hash that is passed to the fake request. Any request options
116
+ # should go here (see +fake_request+).
117
+ # &block:: The block is executed in the context of the controller.
118
+ def get(path, params = {}, env = {}, &block)
119
+ env[:request_method] = "GET"
120
+ request(path, params, env, &block)
121
+ end
122
+
123
+ # An HTTP POST request that operates through the router.
124
+ #
125
+ # ==== Parameters
126
+ # path<String>:: The path that should go to the router as the request uri.
127
+ # params<Hash>::
128
+ # An optional hash that will end up as params in the controller instance.
129
+ # env<Hash>::
130
+ # An optional hash that is passed to the fake request. Any request options
131
+ # should go here (see fake_request).
132
+ # &block:: The block is executed in the context of the controller.
133
+ def post(path, params = {}, env = {}, &block)
134
+ env[:request_method] = "POST"
135
+ request(path, params, env, &block)
136
+ end
137
+
138
+ # An HTTP PUT request that operates through the router.
139
+ #
140
+ # ==== Parameters
141
+ # path<String>:: The path that should go to the router as the request uri.
142
+ # params<Hash>::
143
+ # An optional hash that will end up as params in the controller instance.
144
+ # env<Hash>::
145
+ # An optional hash that is passed to the fake request. Any request options
146
+ # should go here (see fake_request).
147
+ # &block:: The block is executed in the context of the controller.
148
+ def put(path, params = {}, env = {}, &block)
149
+ env[:request_method] = "PUT"
150
+ request(path, params, env, &block)
151
+ end
152
+
153
+ # An HTTP DELETE request that operates through the router
154
+ #
155
+ # ==== Parameters
156
+ # path<String>:: The path that should go to the router as the request uri.
157
+ # params<Hash>::
158
+ # An optional hash that will end up as params in the controller instance.
159
+ # env<Hash>::
160
+ # An optional hash that is passed to the fake request. Any request options
161
+ # should go here (see fake_request).
162
+ # &block:: The block is executed in the context of the controller.
163
+ def delete(path, params = {}, env = {}, &block)
164
+ env[:request_method] = "DELETE"
165
+ request(path, params, env, &block)
166
+ end
167
+
168
+ # A generic request that checks the router for the controller and action.
169
+ # This request goes through the Merb::Router and finishes at the controller.
170
+ #
171
+ # ==== Parameters
172
+ # path<String>:: The path that should go to the router as the request uri.
173
+ # params<Hash>::
174
+ # An optional hash that will end up as params in the controller instance.
175
+ # env<Hash>::
176
+ # An optional hash that is passed to the fake request. Any request options
177
+ # should go here (see +fake_request+).
178
+ # blk<Proc>:: The block is executed in the context of the controller.
179
+ #
180
+ # ==== Example
181
+ # request(path, :create, :name => 'Homer' ) do
182
+ # self.stub!(:current_user).and_return(@user)
183
+ # end
184
+ #
185
+ # ==== Note
186
+ # Uses Routes.
187
+ #
188
+ #---
189
+ # @semi-public
190
+ def request(path, params = {}, env= {}, &block)
191
+ env[:request_method] ||= "GET"
192
+ env[:request_uri] = path
193
+ multipart = env.delete(:test_with_multipart)
194
+
195
+ request = fake_request(env)
196
+
197
+ opts = check_request_for_route(request) # Check that the request will be routed correctly
198
+ klass = Object.full_const_get(opts.delete(:controller).to_const_string)
199
+ action = opts.delete(:action).to_s
200
+ params.merge!(opts)
201
+
202
+ multipart.nil? ? dispatch_to(klass, action, params, env, &block) : dispatch_multipart_to(klass, action, params, env, &block)
203
+ end
204
+
205
+
206
+ # The workhorse for the dispatch*to helpers.
207
+ #
208
+ # ==== Parameters
209
+ # request<Merb::Test::FakeRequest, Merb::Request>::
210
+ # A request object that has been setup for testing.
211
+ # controller_klass<Merb::Controller>::
212
+ # The class object off the controller to dispatch the action to.
213
+ # action<Symbol>:: The action to dispatch the request to.
214
+ # blk<Proc>:: The block will execute in the context of the controller itself.
215
+ #
216
+ # ==== Returns
217
+ # An instance of +controller_klass+ based on the parameters.
218
+ #
219
+ # ==== Note
220
+ # Does not use routes.
221
+ #
222
+ #---
223
+ # @private
224
+ def dispatch_request(request, controller_klass, action, &blk)
225
+ controller = controller_klass.new(request)
226
+ yield controller if block_given?
227
+ controller._dispatch(action)
228
+
229
+ Merb.logger.info controller._benchmarks.inspect
230
+ Merb.logger.flush
231
+
232
+ controller
233
+ end
234
+
235
+ # Checks to see that a request is routable.
236
+ #
237
+ # ==== Parameters
238
+ # request<Merb::Test::FakeRequest, Merb::Request>::
239
+ # The request object to inspect.
240
+ #
241
+ # ==== Raises
242
+ # Merb::ControllerExceptions::BadRequest::
243
+ # No matching route was found.
244
+ #
245
+ # ==== Returns
246
+ # Hash:: The parameters built based on the matching route.
247
+ def check_request_for_route(request)
248
+ match = ::Merb::Router.match(request)
249
+ if match[0].nil?
250
+ raise ::Merb::ControllerExceptions::BadRequest, "No routes match the request"
251
+ else
252
+ match[1]
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,33 @@
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(name, params={})
15
+ Merb::Router.generate(name, params)
16
+ end
17
+
18
+ # ==== Parameters
19
+ # path<~to_string>:: The URL of the request.
20
+ # method<~to_sym>:: HTTP request method.
21
+ # env<Hash>:: Additional parameters for the request.
22
+ #
23
+ # ==== Returns
24
+ # Hash:: A hash containing the controller and action along with any parameters
25
+ def request_to(path, method = :get, env = {})
26
+ env[:request_method] ||= method.to_s.upcase
27
+ env[:request_uri] = path
28
+
29
+ check_request_for_route(fake_request(env))
30
+ end
31
+ end
32
+ end
33
+ 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 it's 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,269 @@
1
+ module Merb::Test::Rspec::ControllerMatchers
2
+
3
+ class BeRedirect
4
+
5
+ # ==== Parameters
6
+ # target<Fixnum, ~status>::
7
+ # Either the status code or a controller with a status code.
8
+ #
9
+ # ==== Returns
10
+ # Boolean:: True if the status code is in the range 300..305 or 307.
11
+ def matches?(target)
12
+ @target = target
13
+ [307, *(300..305)].include?(target.respond_to?(:status) ? target.status : target)
14
+ end
15
+
16
+ # ==== Returns
17
+ # String:: The failure message.
18
+ def failure_message
19
+ "expected#{inspect_target} to redirect"
20
+ end
21
+
22
+ # ==== Returns
23
+ # String:: The failure message to be displayed in negative matches.
24
+ def negative_failure_message
25
+ "expected#{inspect_target} not to redirect"
26
+ end
27
+
28
+ # ==== Returns
29
+ # String:: The controller and action name.
30
+ def inspect_target
31
+ " #{@target.controller_name}##{@target.action_name}" if @target.respond_to?(:controller_name) && @target.respond_to?(:action_name)
32
+ end
33
+ end
34
+
35
+ class RedirectTo
36
+
37
+ # === Parameters
38
+ # String:: The expected location
39
+ def initialize(expected)
40
+ @expected = expected
41
+ end
42
+
43
+ # ==== Parameters
44
+ # target<Merb::Controller>:: The controller to match
45
+ #
46
+ # ==== Returns
47
+ # Boolean::
48
+ # True if the controller status is redirect and the locations match.
49
+ def matches?(target)
50
+ @target, @location = target, target.headers['Location']
51
+ @redirected = BeRedirect.new.matches?(target.status)
52
+ @location == @expected && @redirected
53
+ end
54
+
55
+ # ==== Returns
56
+ # String:: The failure message.
57
+ def failure_message
58
+ msg = "expected #{inspect_target} to redirect to <#{@expected}>, but "
59
+ if @redirected
60
+ msg << "was <#{target_location}>"
61
+ else
62
+ msg << "there was no redirection"
63
+ end
64
+ end
65
+
66
+ # ==== Returns
67
+ # String:: The failure message to be displayed in negative matches.
68
+ def negative_failure_message
69
+ "expected #{inspect_target} not to redirect to <#{@expected}>, but did anyway"
70
+ end
71
+
72
+ # ==== Returns
73
+ # String:: The controller and action name.
74
+ def inspect_target
75
+ "#{@target.controller_name}##{@target.action_name}"
76
+ end
77
+
78
+ # ==== Returns
79
+ # String:: Either the target's location header or the target itself.
80
+ def target_location
81
+ @target.respond_to?(:headers) ? @target.headers['Location'] : @target
82
+ end
83
+ end
84
+
85
+ class BeSuccess
86
+
87
+ # ==== Parameters
88
+ # target<Fixnum, ~status>::
89
+ # Either the status code or a controller with a status code.
90
+ #
91
+ # ==== Returns
92
+ # Boolean:: True if the status code is in the range 200..207.
93
+ def matches?(target)
94
+ @target = target
95
+ (200..207).include?(status_code)
96
+ end
97
+
98
+ # ==== Returns
99
+ # String:: The failure message.
100
+ def failure_message
101
+ "expected#{inspect_target} to be successful but was #{status_code}"
102
+ end
103
+
104
+ # ==== Returns
105
+ # String:: The failure message to be displayed in negative matches.
106
+ def negative_failure_message
107
+ "expected#{inspect_target} not to be successful but it was #{status_code}"
108
+ end
109
+
110
+ # ==== Returns
111
+ # String:: The controller and action name.
112
+ def inspect_target
113
+ " #{@target.controller_name}##{@target.action_name}" if @target.respond_to?(:controller_name) && @target.respond_to?(:action_name)
114
+ end
115
+
116
+ # ==== Returns
117
+ # Fixnum:: Either the target's status or the target itself.
118
+ def status_code
119
+ @target.respond_to?(:status) ? @target.status : @target
120
+ end
121
+ end
122
+
123
+ class BeMissing
124
+
125
+ # ==== Parameters
126
+ # target<Fixnum, ~status>::
127
+ # Either the status code or a controller with a status code.
128
+ #
129
+ # ==== Returns
130
+ # Boolean:: True if the status code is in the range 400..417.
131
+ def matches?(target)
132
+ @target = target
133
+ (400..417).include?(status_code)
134
+ end
135
+
136
+ # ==== Returns
137
+ # String:: The failure message.
138
+ def failure_message
139
+ "expected#{inspect_target} to be missing but was #{status_code}"
140
+ end
141
+
142
+ # ==== Returns
143
+ # String:: The failure message to be displayed in negative matches.
144
+ def negative_failure_message
145
+ "expected#{inspect_target} not to be missing but it was #{status_code}"
146
+ end
147
+
148
+ # ==== Returns
149
+ # String:: The controller and action name.
150
+ def inspect_target
151
+ " #{@target.controller_name}##{@target.action_name}" if @target.respond_to?(:controller_name) && @target.respond_to?(:action_name)
152
+ end
153
+
154
+ # ==== Returns
155
+ # Fixnum:: Either the target's status or the target itself.
156
+ def status_code
157
+ @target.respond_to?(:status) ? @target.status : @target
158
+ end
159
+ end
160
+
161
+ # Passes if the target was redirected, or the target is a redirection (300
162
+ # level) response code.
163
+ #
164
+ # ==== Examples
165
+ # # Passes if the controller was redirected
166
+ # controller.should redirect
167
+ #
168
+ # # Also works if the target is the response code
169
+ # controller.status.should redirect
170
+ #
171
+ # ==== Notes
172
+ # valid HTTP Redirection codes:
173
+ # * 300: Multiple Choices
174
+ # * 301: Moved Permanently
175
+ # * 302: Moved Temporarily (HTTP/1.0)
176
+ # * 302: Found (HTTP/1.1)
177
+ # * 303: See Other (HTTP/1.1)
178
+ # * 304: Not Modified
179
+ # * 305: Use Proxy
180
+ # * 307: Temporary Redirect
181
+ #--
182
+ # status codes based on: http://cheat.errtheblog.com/s/http_status_codes/
183
+ def redirect
184
+ BeRedirect.new
185
+ end
186
+
187
+ alias_method :be_redirection, :redirect
188
+
189
+ # Passes if the target was redirected to the expected location.
190
+ #
191
+ # ==== Paramters
192
+ # expected<String>:: A relative or absolute url.
193
+ #
194
+ # ==== Examples
195
+ # # Passes if the controller was redirected to http://example.com/
196
+ # controller.should redirect_to('http://example.com/')
197
+ def redirect_to(expected)
198
+ RedirectTo.new(expected)
199
+ end
200
+
201
+ alias_method :be_redirection_to, :redirect_to
202
+
203
+ # Passes if the request that generated the target was successful, or the
204
+ # target is a success (200 level) response code.
205
+ #
206
+ # ==== Examples
207
+ # # Passes if the controller call was successful
208
+ # controller.should respond_successfully
209
+ #
210
+ # # Also works if the target is the response code
211
+ # controller.status.should respond_successfully
212
+ #
213
+ # ==== Notes
214
+ # valid HTTP Success codes:
215
+ # * 200: OK
216
+ # * 201: Created
217
+ # * 202: Accepted
218
+ # * 203: Non-Authoritative Information
219
+ # * 204: No Content
220
+ # * 205: Reset Content
221
+ # * 206: Partial Content
222
+ # * 207: Multi-Status
223
+ #--
224
+ # status codes based on: http://cheat.errtheblog.com/s/http_status_codes/
225
+ def respond_successfully
226
+ BeSuccess.new
227
+ end
228
+
229
+ alias_method :be_successful, :respond_successfully
230
+
231
+ # Passes if the request that generated the target was missing, or the target
232
+ # is a client-side error (400 level) response code.
233
+ #
234
+ # ==== Examples
235
+ # # Passes if the controller call was unknown or not understood
236
+ # controller.should be_missing
237
+ #
238
+ # # Also passes if the target is a response code
239
+ # controller.status.should be_missing
240
+ #
241
+ # ==== Notes
242
+ # valid HTTP Client Error codes:
243
+ # * 400: Bad Request
244
+ # * 401: Unauthorized
245
+ # * 402: Payment Required
246
+ # * 403: Forbidden
247
+ # * 404: Not Found
248
+ # * 405: Method Not Allowed
249
+ # * 406: Not Acceptable
250
+ # * 407: Proxy Authentication Required
251
+ # * 408: Request Timeout
252
+ # * 409: Conflict
253
+ # * 410: Gone
254
+ # * 411: Length Required
255
+ # * 412: Precondition Failed
256
+ # * 413: Request Entity Too Large
257
+ # * 414: Request-URI Too Long
258
+ # * 415: Unsupported Media Type
259
+ # * 416: Requested Range Not Satisfiable
260
+ # * 417: Expectation Failed
261
+ # * 422: Unprocessable Entity
262
+ #--
263
+ # status codes based on: http://cheat.errtheblog.com/s/http_status_codes/
264
+ def be_missing
265
+ BeMissing.new
266
+ end
267
+
268
+ alias_method :be_client_error, :be_missing
269
+ end