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,136 @@
1
+ class Object
2
+ # Extracts the singleton class, so that metaprogramming can be done on it.
3
+ #
4
+ # ==== Returns
5
+ # Class:: The meta class.
6
+ #
7
+ # ==== Examples
8
+ # class MyString < String; end
9
+ #
10
+ # MyString.instance_eval do
11
+ # define_method :foo do
12
+ # puts self
13
+ # end
14
+ # end
15
+ #
16
+ # MyString.meta_class.instance_eval do
17
+ # define_method :bar do
18
+ # puts self
19
+ # end
20
+ # end
21
+ #
22
+ # def String.add_meta_var(var)
23
+ # self.meta_class.instance_eval do
24
+ # define_method var do
25
+ # puts "HELLO"
26
+ # end
27
+ # end
28
+ # end
29
+ #
30
+ # MyString.new("Hello").foo #=> "Hello"
31
+ # MyString.new("Hello").bar
32
+ # #=> NoMethodError: undefined method `bar' for "Hello":MyString
33
+ # MyString.foo
34
+ # #=> NoMethodError: undefined method `foo' for MyString:Class
35
+ # MyString.bar
36
+ # #=> MyString
37
+ # String.bar
38
+ # #=> NoMethodError: undefined method `bar' for String:Class
39
+ #
40
+ # MyString.add_meta_var(:x)
41
+ # MyString.x #=> HELLO
42
+ #
43
+ # As you can see, using #meta_class allows you to execute code (and here,
44
+ # define a method) on the metaclass itself. It also allows you to define
45
+ # class methods that can be run on subclasses, and then be able to execute
46
+ # code on the metaclass of the subclass (here MyString).
47
+ #
48
+ # In this case, we were able to define a class method (add_meta_var) on
49
+ # String that was executable by the MyString subclass. It was then able to
50
+ # define a method on the subclass by adding it to the MyString metaclass.
51
+ #
52
+ # For more information, you can check out _why's excellent article at:
53
+ # http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
54
+ def meta_class() class << self; self end end
55
+
56
+ # ==== Returns
57
+ # Boolean::
58
+ # True if the empty? is true or if the object responds to strip (e.g. a
59
+ # String) and strip.empty? is true, or if !self is true.
60
+ #
61
+ # ==== Examples
62
+ # [].blank? #=> true
63
+ # [1].blank? #=> false
64
+ # [nil].blank? #=> false
65
+ # nil.blank? #=> true
66
+ # true.blank? #=> false
67
+ # false.blank? #=> true
68
+ # "".blank? #=> true
69
+ # " ".blank? #=> true
70
+ # " hey ho ".blank? #=> false
71
+ def blank?
72
+ if respond_to?(:empty?) && respond_to?(:strip)
73
+ empty? or strip.empty?
74
+ elsif respond_to?(:empty?)
75
+ empty?
76
+ else
77
+ !self
78
+ end
79
+ end
80
+
81
+ # ==== Parameters
82
+ # name<String>:: The name of the constant to get, e.g. "Merb::Router".
83
+ #
84
+ # ==== Returns
85
+ # Object:: The constant corresponding to the name.
86
+ def full_const_get(name)
87
+ list = name.split("::")
88
+ obj = Object
89
+ list.each {|x| obj = obj.const_get(x) }
90
+ obj
91
+ end
92
+
93
+ # Makes a module from a string (e.g. Foo::Bar::Baz)
94
+ #
95
+ # ==== Parameters
96
+ # name<String>:: The name of the full module name to make
97
+ #
98
+ # ==== Returns
99
+ # nil
100
+ def make_module(str)
101
+ mod = str.split("::")
102
+ start = mod.map {|x| "module #{x}"}.join("; ")
103
+ ender = (["end"] * mod.size).join("; ")
104
+ self.class_eval <<-HERE
105
+ #{start}
106
+ #{ender}
107
+ HERE
108
+ end
109
+
110
+ # ==== Parameters
111
+ # duck<Symbol, Class, Array>:: The thing to compare the object to.
112
+ #
113
+ # ==== Notes
114
+ # The behavior of the method depends on the type of duck as follows:
115
+ # Symbol:: Check whether the object respond_to?(duck).
116
+ # Class:: Check whether the object is_a?(duck).
117
+ # Array::
118
+ # Check whether the object quacks_like? at least one of the options in the
119
+ # array.
120
+ #
121
+ # ==== Returns
122
+ # Boolean:: True if the object quacks like duck.
123
+ def quacks_like?(duck)
124
+ case duck
125
+ when Symbol
126
+ self.respond_to?(duck)
127
+ when Class
128
+ self.is_a?(duck)
129
+ when Array
130
+ duck.any? {|d| self.quacks_like?(d) }
131
+ else
132
+ false
133
+ end
134
+ end
135
+
136
+ end
@@ -0,0 +1,14 @@
1
+ module ObjectSpace
2
+
3
+ class << self
4
+
5
+ # ==== Returns
6
+ # Array[Class]:: All the classes in the object space.
7
+ def classes
8
+ klasses = []
9
+ ObjectSpace.each_object(Class) {|o| klasses << o}
10
+ klasses
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,28 @@
1
+ # this is a temporary workaround until rubygems Does the Right thing here
2
+ require 'rubygems'
3
+ module Gem
4
+ class SourceIndex
5
+
6
+ # Overwrite this so that a gem of the same name and version won't push one
7
+ # from the gems directory out entirely.
8
+ #
9
+ # ==== Parameters
10
+ # gem_spec<Gem::Specification>:: The specification of the gem to add.
11
+ def add_spec(gem_spec)
12
+ @gems[gem_spec.full_name] = gem_spec unless @gems[gem_spec.full_name].is_a?(Gem::Specification) && @gems[gem_spec.full_name].installation_path == File.join(defined?(Merb) && Merb.respond_to?(:root) ? Merb.root : Dir.pwd,"gems")
13
+ end
14
+ end
15
+
16
+ class Specification
17
+
18
+ # Overwrite this so that gems in the gems directory get preferred over gems
19
+ # from any other location. If there are two gems of different versions in
20
+ # the gems directory, the later one will load as usual.
21
+ #
22
+ # ==== Returns
23
+ # Array[Array]:: The object used for sorting gem specs.
24
+ def sort_obj
25
+ [@name, installation_path == File.join(defined?(Merb) && Merb.respond_to?(:root) ? Merb.root : Dir.pwd,"gems") ? 1 : -1, @version.to_ints, @new_platform == Gem::Platform::RUBY ? -1 : 1]
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,41 @@
1
+ module Merb
2
+ class SimpleSet < Hash
3
+
4
+ # ==== Parameters
5
+ # arr<Array>:: Initial set values.
6
+ #
7
+ # ==== Returns
8
+ # Array:: The array the Set was initialized with
9
+ def initialize(arr = [])
10
+ arr.each {|x| self[x] = true}
11
+ end
12
+
13
+ # ==== Parameters
14
+ # value<Object>:: Value to add to set.
15
+ #
16
+ # ==== Returns
17
+ # true
18
+ def <<(value)
19
+ self[value] = true
20
+ end
21
+
22
+ # ==== Parameters
23
+ # arr<Array>:: Values to merge with set.
24
+ #
25
+ # ==== Returns
26
+ # SimpleSet:: The set after the Array was merged in.
27
+ def merge(arr)
28
+ super(arr.inject({}) {|s,x| s[x] = true; s })
29
+ end
30
+
31
+ # ==== Returns
32
+ # String:: A human readable version of the set.
33
+ def inspect
34
+ "#<SimpleSet: {#{keys.map {|x| x.inspect}.join(", ")}}>"
35
+ end
36
+
37
+ # def to_a
38
+ alias_method :to_a, :keys
39
+
40
+ end
41
+ end
@@ -0,0 +1,69 @@
1
+ require "pathname"
2
+
3
+ class String
4
+
5
+ class InvalidPathConversion < Exception; end
6
+
7
+ # ==== Returns
8
+ # String:: The string with all regexp special characters escaped.
9
+ #
10
+ # ==== Examples
11
+ # "\*?{}.".escape_regexp #=> "\\*\\?\\{\\}\\."
12
+ def escape_regexp
13
+ Regexp.escape self
14
+ end
15
+
16
+ # ==== Returns
17
+ # String:: The string converted to snake case.
18
+ #
19
+ # ==== Examples
20
+ # "FooBar".snake_case #=> "foo_bar"
21
+ def snake_case
22
+ gsub(/\B[A-Z]/, '_\&').downcase
23
+ end
24
+
25
+ # ==== Returns
26
+ # String:: The string converted to camel case.
27
+ #
28
+ # ==== Examples
29
+ # "foo_bar".camel_case #=> "FooBar"
30
+ def camel_case
31
+ split('_').map{|e| e.capitalize}.join
32
+ end
33
+
34
+ # ==== Returns
35
+ # String:: The path string converted to a constant name.
36
+ #
37
+ # ==== Examples
38
+ # "merb/core_ext/string".to_const_string #=> "Merb::CoreExt::String"
39
+ def to_const_string
40
+ gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
41
+ end
42
+
43
+ # ==== Returns
44
+ # String::
45
+ # The path that is associated with the constantized string, assuming a
46
+ # conventional structure.
47
+ #
48
+ # ==== Examples
49
+ # "FooBar::Baz".to_const_path # => "foo_bar/baz"
50
+ def to_const_path
51
+ snake_case.gsub(/::/, "/")
52
+ end
53
+
54
+ # ==== Parameters
55
+ # o<String>:: The path component to join with the string.
56
+ #
57
+ # ==== Returns
58
+ # String:: The original path concatenated with o.
59
+ #
60
+ # ==== Examples
61
+ # "merb"/"core_ext" #=> "merb/core_ext"
62
+ def /(o)
63
+ File.join(self, o.to_s)
64
+ end
65
+
66
+ def relative_path_from(other)
67
+ Pathname.new(self).relative_path_from(Pathname.new(other)).to_s
68
+ end
69
+ end
@@ -0,0 +1,92 @@
1
+ module Merb
2
+
3
+ # Cookies are read and written through Merb::Controller#cookies. The cookies
4
+ # you read are those received in request along with those that have been set
5
+ # during the current request. The cookies you write will be sent out with the
6
+ # response. Cookies are read by value (so you won't get the cookie object
7
+ # itself back -- just the value it holds).
8
+ class Cookies
9
+
10
+ # ==== Parameters
11
+ # request_cookies<Hash>:: Initial cookie store.
12
+ # headers<Hash>:: The response headers.
13
+ def initialize(request_cookies, headers)
14
+ @_cookies = request_cookies
15
+ @_headers = headers
16
+ end
17
+
18
+ # ==== Parameters
19
+ # name<~to_s>:: Name of the cookie.
20
+ #
21
+ # ==== Returns
22
+ # String:: Value of the cookie.
23
+ def [](name)
24
+ @_cookies[name]
25
+ end
26
+
27
+ # ==== Parameters
28
+ # name<~to_s>:: Name of the cookie.
29
+ # options<Hash, ~to_s>:: Options for the cookie being set (see below).
30
+ #
31
+ # ==== Options (options)
32
+ # :value<~to_s>:: Value of the cookie
33
+ # :path<String>:: The path for which this cookie applies. Defaults to "/".
34
+ # :expires<Time>:: Cookie expiry date.
35
+ #
36
+ # ==== Alternatives
37
+ # If options is not a hash, it will be used as the cookie value directly.
38
+ #
39
+ # ==== Examples
40
+ # cookies[:user] = "dave" # => Sets a simple session cookie
41
+ # cookies[:token] = { :value => user.token, :expires => Time.now + 2.weeks }
42
+ # # => Will set a cookie that expires in 2 weeks
43
+ def []=(name, options)
44
+ value = ''
45
+ if options.is_a?(Hash)
46
+ options = Mash.new(options)
47
+ value = options.delete(:value)
48
+ else
49
+ value = options
50
+ options = Mash.new
51
+ end
52
+ @_cookies[name] = value
53
+ set_cookie(name, Merb::Request.escape(value), options)
54
+ Merb.logger.info("Cookie set: #{name} => #{value} -- #{options.inspect}")
55
+ options
56
+ end
57
+
58
+ # Removes the cookie on the client machine by setting the value to an empty
59
+ # string and setting its expiration date into the past.
60
+ #
61
+ # ==== Parameters
62
+ # name<~to_s>:: Name of the cookie to delete.
63
+ # options<Hash>:: Additional options to pass to +set_cookie+.
64
+ def delete(name, options = {})
65
+ cookie = @_cookies.delete(name)
66
+ options = Mash.new(options)
67
+ options[:expires] = Time.at(0)
68
+ set_cookie(name, "", options)
69
+ Merb.logger.info("Cookie deleted: #{name} => #{cookie.inspect}")
70
+ cookie
71
+ end
72
+
73
+ private
74
+ # ==== Parameters
75
+ # name<~to_s>:: Name of the cookie.
76
+ # value<~to_s>:: Value of the cookie.
77
+ # options<Hash>:: Additional options for the cookie (see below).
78
+ #
79
+ # ==== Options (options)
80
+ # :path<String>:: The path for which this cookie applies. Defaults to "/".
81
+ # :expires<Time>:: Cookie expiry date.
82
+ def set_cookie(name, value, options)
83
+ options[:path] = '/' unless options[:path]
84
+ if expiry = options[:expires]
85
+ options[:expires] = expiry.gmtime.strftime(Merb::Const::COOKIE_EXPIRATION_FORMAT)
86
+ end
87
+ # options are sorted for testing purposes
88
+ (@_headers['Set-Cookie'] ||=[]) << "#{name}=#{value}; " +
89
+ options.map{|k, v| "#{k}=#{v};"}.sort.join(' ')
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,233 @@
1
+ class Merb::Dispatcher
2
+ DEFAULT_ERROR_TEMPLATE = File.expand_path(File.dirname(__FILE__) / 'exceptions.html')
3
+
4
+ class << self
5
+
6
+ attr_accessor :use_mutex
7
+
8
+ @@mutex = Mutex.new
9
+ Merb::Dispatcher.use_mutex = ::Merb::Config[:use_mutex]
10
+
11
+ # This is where we grab the incoming request REQUEST_URI and use that in
12
+ # the merb RouteMatcher to determine which controller and method to run.
13
+ # Returns a 2 element tuple of: [controller, action]
14
+ #
15
+ # ControllerExceptions are rescued here and redispatched.
16
+ #
17
+ # ==== Parameters
18
+ # rack_env<Rack::Environment>::
19
+ # The rack environment, which is used to instantiate a Merb::Request
20
+ # response<IO>::
21
+ # An IO object to hold the response
22
+ #
23
+ # ==== Returns
24
+ # Array[Merb::Controller, Symbol]::
25
+ # An array containing the Merb::Controller and the action that was
26
+ # dispatched to.
27
+ def handle(rack_env)
28
+ start = Time.now
29
+ request = Merb::Request.new(rack_env)
30
+
31
+ route_index, route_params = Merb::Router.match(request)
32
+
33
+ if route_params.empty?
34
+ raise ::Merb::ControllerExceptions::NotFound, "No routes match the request, #{request.uri}"
35
+ end
36
+ request.route_params = route_params
37
+ request.params.merge! route_params
38
+
39
+ Merb.logger.info("Params: #{request.params.inspect}")
40
+
41
+ controller_name = (route_params[:namespace] ? route_params[:namespace] + '/' : '') + route_params[:controller]
42
+
43
+ unless controller_name
44
+ raise Merb::ControllerExceptions::NotFound, "Route matched, but route did not specify a controller"
45
+ end
46
+
47
+ Merb.logger.debug("Routed to: #{request.route_params.inspect}")
48
+
49
+ cnt = controller_name.snake_case.to_const_string
50
+
51
+ if !Merb::Controller._subclasses.include?(cnt)
52
+ raise Merb::ControllerExceptions::NotFound, "Controller '#{cnt}' not found"
53
+ end
54
+ if cnt == "Application"
55
+ raise Merb::ControllerExceptions::NotFound, "The 'Application' controller has no public actions"
56
+ end
57
+
58
+ begin
59
+ klass = Object.full_const_get(cnt)
60
+ rescue NameError
61
+ raise Merb::ControllerExceptions::NotFound
62
+ end
63
+
64
+ action = route_params[:action]
65
+
66
+ controller = dispatch_action(klass, action, request)
67
+ controller._benchmarks[:dispatch_time] = Time.now - start
68
+ controller.route = Merb::Router.routes[route_index] if route_index
69
+ Merb.logger.info controller._benchmarks.inspect
70
+ Merb.logger.flush
71
+
72
+ controller
73
+ # this is the custom dispatch_exception; it allows failures to still be dispatched
74
+ # to the error controller
75
+ rescue => exception
76
+ Merb.logger.error(Merb.exception(exception))
77
+ unless request.xhr?
78
+ exception = controller_exception(exception)
79
+ dispatch_exception(request, exception)
80
+ else
81
+ Struct.new(:headers, :status, :body).new({}, 500,
82
+ <<-HERE
83
+ #{exception.message}
84
+
85
+ Params:
86
+ #{(request.params || {}).map { |p,v| " #{p}: #{v}\n"}.join("\n")}
87
+
88
+ Session:
89
+ #{(request.session || {}).map { |p,v| " #{p}: #{v}\n"}.join("\n")}
90
+
91
+ Cookies:
92
+ #{(request.cookies || {}).map { |p,v| " #{p}: #{v}\n"}.join("\n")}
93
+
94
+ Stacktrace:
95
+ #{exception.backtrace.join("\n")}
96
+ HERE
97
+ )
98
+ end
99
+ end
100
+
101
+ private
102
+ # Setup the controller and call the chosen action
103
+ #
104
+ # ==== Parameters
105
+ # klass<Merb::Controller>:: The controller class to dispatch to.
106
+ # action<Symbol>:: The action to dispatch.
107
+ # request<Merb::Request>::
108
+ # The Merb::Request object that was created in #handle
109
+ # response<IO>:: The response object passed in from Mongrel
110
+ # status<Integer>:: The status code to respond with.
111
+ #
112
+ # ==== Returns
113
+ # Array[Merb::Controller, Symbol]::
114
+ # An array containing the Merb::Controller and the action that was
115
+ # dispatched to.
116
+ def dispatch_action(klass, action, request, status=200)
117
+ # build controller
118
+ controller = klass.new(request, status)
119
+ if use_mutex
120
+ @@mutex.synchronize { controller._dispatch(action) }
121
+ else
122
+ controller._dispatch(action)
123
+ end
124
+ controller
125
+ end
126
+
127
+ # Re-route the current request to the Exception controller if it is
128
+ # available, and try to render the exception nicely.
129
+ #
130
+ # You can handle exceptions by implementing actions for specific
131
+ # exceptions such as not_found or for entire classes of exceptions
132
+ # such as client_error
133
+ #
134
+ # If it is not available then just render a simple text error.
135
+ #
136
+ # ==== Parameters
137
+ # request<Merb::Request>::
138
+ # The request object associated with the failed request.
139
+ # response<IO>::
140
+ # The response object to put the response into.
141
+ # exception<Object>::
142
+ # The exception object that was created when trying to dispatch the
143
+ # original controller.
144
+ #
145
+ # ==== Returns
146
+ # Array[Merb::Controller, String]::
147
+ # An array containing the Merb::Controller and the name of the exception
148
+ # that triggrered #dispatch_exception. For instance, a NotFound exception
149
+ # will be "not_found".
150
+ def dispatch_exception(request, exception)
151
+ exception_klass = exception.class
152
+ begin
153
+ klass = ::Exceptions rescue Merb::Controller
154
+ request.params[:original_params] = request.params.dup rescue {}
155
+ request.params[:original_session] = request.session.dup rescue {}
156
+ request.params[:original_cookies] = request.cookies.dup rescue {}
157
+ request.params[:exception] = exception
158
+ request.params[:action] = exception_klass.name
159
+
160
+ dispatch_action(klass, exception_klass.name, request, exception.class::STATUS)
161
+ rescue => dispatch_issue
162
+ dispatch_issue = controller_exception(dispatch_issue)
163
+ # when no action/template exist for an exception, or an
164
+ # exception occurs on an InternalServerError the message is
165
+ # rendered as simple text.
166
+
167
+ # ControllerExceptions raised from exception actions are
168
+ # dispatched back into the Exceptions controller
169
+ if dispatch_issue.is_a?(Merb::ControllerExceptions::NotFound)
170
+ # If a handler for a specific exception is not found, keep retrying
171
+ # with the more general cases until we reach the base exception.
172
+ unless exception_klass == Merb::ControllerExceptions::Base
173
+ exception_klass = exception_klass.superclass
174
+ retry
175
+ else
176
+ dispatch_default_exception(klass, request, exception)
177
+ end
178
+ elsif dispatch_issue.is_a?(Merb::ControllerExceptions::InternalServerError)
179
+ dispatch_default_exception(klass, request, dispatch_issue)
180
+ else
181
+ exception = dispatch_issue
182
+ retry
183
+ end
184
+ end
185
+ end
186
+
187
+ # If no custom actions are available to render an exception then the errors
188
+ # will end up here for processing
189
+ #
190
+ # ==== Parameters
191
+ # klass<Merb::Controller>::
192
+ # The class of the controller to use for exception dispatch.
193
+ # request<Merb::Request>::
194
+ # The Merb request that produced the original error.
195
+ # response<IO>::
196
+ # The response object that the response will be put into.
197
+ # e<Exception>::
198
+ # The exception that caused #dispatch_exception to be called.
199
+ #
200
+ # ==== Returns
201
+ # Array[Merb::Controller, String]::
202
+ # An array containing the Merb::Controller that was dispatched to and the
203
+ # error's name. For instance, a NotFound error's name is "not_found".
204
+ def dispatch_default_exception(klass, request, e)
205
+ controller = klass.new(request, e.class::STATUS)
206
+ if e.is_a? Merb::ControllerExceptions::Redirection
207
+ controller.headers.merge!('Location' => e.message)
208
+ controller.body = %{ } #fix
209
+ else
210
+ controller.instance_variable_set("@exception", e) # for ERB
211
+ controller.instance_variable_set("@exception_name", e.name.split("_").map {|x| x.capitalize}.join(" "))
212
+ controller.body = controller.send(Merb::Template.template_for(DEFAULT_ERROR_TEMPLATE))
213
+ end
214
+ controller
215
+ end
216
+
217
+ # Wraps any non-ControllerException errors in an InternalServerError ready
218
+ # for displaying over HTTP.
219
+ #
220
+ # ==== Parameters
221
+ # e<Exception>::
222
+ # The exception that caused #dispatch_exception to be called.
223
+ #
224
+ # ==== Returns
225
+ # Merb::InternalServerError::
226
+ # An internal server error wrapper for the exception.
227
+ def controller_exception(e)
228
+ e.kind_of?(Merb::ControllerExceptions::Base) ?
229
+ e : Merb::ControllerExceptions::InternalServerError.new(e)
230
+ end
231
+
232
+ end
233
+ end