merb-core 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
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,284 @@
1
+ require "optparse"
2
+ require "yaml"
3
+
4
+ module Merb
5
+
6
+ class Config
7
+
8
+ class << self
9
+
10
+ # ==== Returns
11
+ # Hash:: The defaults for the config.
12
+ def defaults
13
+ @defaults ||= {
14
+ :host => "0.0.0.0",
15
+ :port => "4000",
16
+ :adapter => "runner",
17
+ :reload_classes => true,
18
+ :environment => "development",
19
+ :merb_root => Dir.pwd,
20
+ :use_mutex => true,
21
+ :session_id_key => "_session_id",
22
+ :log_delimiter => " ~ ",
23
+ :log_auto_flush => false,
24
+ :disabled_components => []
25
+ }
26
+ end
27
+
28
+ # Yields the configuration.
29
+ #
30
+ # ==== Block parameters
31
+ # c<Hash>:: The configuration parameters.
32
+ #
33
+ # ==== Examples
34
+ # Merb::Config.use do |config|
35
+ # config[:exception_details] = false
36
+ # end
37
+ def use
38
+ @configuration ||= {}
39
+ yield @configuration
40
+ end
41
+
42
+ # ==== Parameters
43
+ # key<Object>:: The key to check.
44
+ #
45
+ # ==== Returns
46
+ # Boolean:: True if the key exists in the config.
47
+ def key?(key)
48
+ @configuration.key?(key)
49
+ end
50
+
51
+ # ==== Parameters
52
+ # key<Object>:: The key to retrieve the parameter for.
53
+ #
54
+ # ==== Returns
55
+ # Object:: The value of the configuration parameter.
56
+ def [](key)
57
+ (@configuration||={})[key]
58
+ end
59
+
60
+ # ==== Parameters
61
+ # key<Object>:: The key to set the parameter for.
62
+ # val<Object>:: The value of the parameter.
63
+ def []=(key,val)
64
+ @configuration[key] = val
65
+ end
66
+
67
+ # ==== Parameters
68
+ # key<Object>:: The key of the parameter to delete.
69
+ def delete(key)
70
+ @configuration.delete(key)
71
+ end
72
+
73
+ # ==== Parameters
74
+ # key<Object>:: The key to retrieve the parameter for.
75
+ # default<Object>::
76
+ # The default value to return if the parameter is not set.
77
+ #
78
+ # ==== Returns
79
+ # Object:: The value of the configuration parameter or the default.
80
+ def fetch(key, default)
81
+ @configuration.fetch(key, default)
82
+ end
83
+
84
+ # ==== Returns
85
+ # Hash:: The config as a hash.
86
+ def to_hash
87
+ @configuration
88
+ end
89
+
90
+ # ==== Returns
91
+ # String:: The config as YAML.
92
+ def to_yaml
93
+ @configuration.to_yaml
94
+ end
95
+
96
+ # Sets up the configuration by storing the given settings.
97
+ #
98
+ # ==== Parameters
99
+ # settings<Hash>::
100
+ # Configuration settings to use. These are merged with the defaults.
101
+ def setup(settings = {})
102
+ @configuration = defaults.merge(settings)
103
+ end
104
+
105
+ # Parses the command line arguments and stores them in the config.
106
+ #
107
+ # ==== Parameters
108
+ # argv<String>:: The command line arguments. Defaults to +ARGV+.
109
+ def parse_args(argv = ARGV)
110
+ @configuration ||= {}
111
+ # Our primary configuration hash for the length of this method
112
+ options = {}
113
+
114
+ # Environment variables always win
115
+ options[:environment] = ENV["MERB_ENV"] if ENV["MERB_ENV"]
116
+
117
+ # Build a parser for the command line arguments
118
+ opts = OptionParser.new do |opts|
119
+ opts.version = Merb::VERSION
120
+ opts.release = Merb::RELEASE
121
+
122
+ opts.banner = "Usage: merb [uGdcIpPhmailLerkKX] [argument]"
123
+ opts.define_head "Merb Mongrel+ Erb. Lightweight replacement for ActionPack."
124
+ opts.separator '*'*80
125
+ opts.separator 'If no flags are given, Merb starts in the foreground on port 4000.'
126
+ opts.separator '*'*80
127
+
128
+ opts.on("-u", "--user USER", "This flag is for having merb run as a user other than the one currently logged in. Note: if you set this you must also provide a --group option for it to take effect.") do |user|
129
+ options[:user] = user
130
+ end
131
+
132
+ opts.on("-G", "--group GROUP", "This flag is for having merb run as a group other than the one currently logged in. Note: if you set this you must also provide a --user option for it to take effect.") do |group|
133
+ options[:group] = group
134
+ end
135
+
136
+ opts.on("-d", "--daemonize", "This will run a single merb in the background.") do |daemon|
137
+ options[:daemonize] = true
138
+ end
139
+
140
+ opts.on("-c", "--cluster-nodes NUM_MERBS", "Number of merb daemons to run.") do |nodes|
141
+ options[:cluster] = nodes
142
+ end
143
+
144
+ opts.on("-I", "--init-file FILE", "Name of the file to load first") do |init_file|
145
+ options[:init_file] = init_file
146
+ end
147
+
148
+ opts.on("-p", "--port PORTNUM", "Port to run merb on, defaults to 4000.") do |port|
149
+ options[:port] = port
150
+ end
151
+
152
+ opts.on("-P", "--pid PIDFILE", "PID file, defaults to [Merb.root]/log/merb.[port_number].pid") do |pid_file|
153
+ options[:pid_file] = pid_file
154
+ end
155
+
156
+ opts.on("-h", "--host HOSTNAME", "Host to bind to (default is 0.0.0.0).") do |host|
157
+ options[:host] = host
158
+ end
159
+
160
+ opts.on("-m", "--merb-root /path/to/approot", "The path to the Merb.root for the app you want to run (default is current working dir).") do |root|
161
+ options[:merb_root] = File.expand_path(root)
162
+ end
163
+
164
+ opts.on("-a", "--adapter mongrel", "The rack adapter to use to run merb[mongrel, emongrel, thin, fastcgi, webrick, runner, irb]") do |adapter|
165
+ options[:adapter] = adapter
166
+ end
167
+
168
+ opts.on("-i", "--irb-console", "This flag will start merb in irb console mode. All your models and other classes will be available for you in an irb session.") do |console|
169
+ options[:adapter] = 'irb'
170
+ end
171
+
172
+ opts.on("-S", "--sandbox", "This flag will enable a sandboxed irb console. If your ORM supports transactions, all edits will be rolled back on exit.") do |sandbox|
173
+ options[:sandbox] = true
174
+ end
175
+
176
+ opts.on("-l", "--log-level LEVEL", "Log levels can be set to any of these options: debug < info < warn < error < fatal") do |log_level|
177
+ options[:log_level] = log_level.to_sym
178
+ end
179
+
180
+ opts.on("-L", "--log LOGFILE", "A string representing the logfile to use.") do |log_file|
181
+ options[:log_file] = log_file
182
+ end
183
+
184
+ opts.on("-e", "--environment STRING", "Run merb in the correct mode(development, production, testing)") do |env|
185
+ options[:environment] = env
186
+ end
187
+
188
+ opts.on("-r", "--script-runner ['RUBY CODE'| FULL_SCRIPT_PATH]",
189
+ "Command-line option to run scripts and/or code in the merb app.") do |code_or_file|
190
+ options[:runner_code] = code_or_file
191
+ options[:adapter] = 'runner'
192
+ end
193
+
194
+ opts.on("-K", "--graceful PORT or all", "Gracefully kill one merb proceses by port number. Use merb -K all to gracefully kill all merbs.") do |ports|
195
+ @configuration = defaults.merge(options)
196
+ Merb::Server.kill(ports, 1)
197
+ end
198
+
199
+ opts.on("-k", "--kill PORT or all", "Kill one merb proceses by port number. Use merb -k all to kill all merbs.") do |port|
200
+ @configuration = defaults.merge(options)
201
+ Merb::Server.kill(port, 9)
202
+ end
203
+
204
+ opts.on("-X", "--mutex on/off", "This flag is for turning the mutex lock on and off.") do |mutex|
205
+ if mutex == "off"
206
+ options[:use_mutex] = false
207
+ else
208
+ options[:use_mutex] = true
209
+ end
210
+ end
211
+
212
+ opts.on("-D", "--debugger", "Run merb using rDebug.") do
213
+ begin
214
+ require "ruby-debug"
215
+ Debugger.start
216
+ Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
217
+ puts "Debugger enabled"
218
+ rescue LoadError
219
+ puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'"
220
+ exit
221
+ end
222
+ end
223
+
224
+ opts.on("-?", "-H", "--help", "Show this help message") do
225
+ puts opts
226
+ exit
227
+ end
228
+ end
229
+
230
+ # Parse what we have on the command line
231
+ opts.parse!(argv)
232
+ Merb::Config.setup(options)
233
+ end
234
+
235
+ attr_accessor :configuration #:nodoc:
236
+
237
+ # Set configuration parameters from a code block, where each method
238
+ # evaluates to a config parameter.
239
+ #
240
+ # ==== Parameters
241
+ # &block:: Configuration parameter block.
242
+ #
243
+ # ==== Examples
244
+ # # Set environment and log level.
245
+ # Merb::Config.configure do
246
+ # environment "development"
247
+ # log_level "debug"
248
+ # end
249
+ def configure(&block)
250
+ ConfigBlock.new(self, &block) if block_given?
251
+ end
252
+
253
+ # Allows retrieval of single key config values via Merb.config.<key>
254
+ # Allows single key assignment via Merb.config.<key> = ...
255
+ #
256
+ # ==== Parameters
257
+ # method<~to_s>:: Method name as hash key value.
258
+ # *args:: Value to set the configuration parameter to.
259
+ def method_missing(method, *args) #:nodoc:
260
+ if method.to_s[-1,1] == '='
261
+ @configuration[method.to_s.tr('=','').to_sym] = *args
262
+ else
263
+ @configuration[method]
264
+ end
265
+ end
266
+
267
+ end # class << self
268
+
269
+ class ConfigBlock #:nodoc:
270
+
271
+ def initialize(klass, &block) #:nodoc:
272
+ @klass = klass
273
+ instance_eval(&block)
274
+ end
275
+
276
+ def method_missing(method, *args) #:nodoc:
277
+ @klass[method] = *args
278
+ end
279
+
280
+ end # class Configurator
281
+
282
+ end # Config
283
+
284
+ end # Merb
@@ -0,0 +1,43 @@
1
+ # Most of this list is simply constants frozen for efficiency
2
+ module Merb
3
+ module Const
4
+
5
+ DEFAULT_SEND_FILE_OPTIONS = {
6
+ :type => 'application/octet-stream'.freeze,
7
+ :disposition => 'attachment'.freeze
8
+ }.freeze
9
+
10
+ SET_COOKIE = " %s=%s; path=/; expires=%s".freeze
11
+ COOKIE_EXPIRATION_FORMAT = "%a, %d-%b-%Y %H:%M:%S GMT".freeze
12
+ COOKIE_SPLIT = /[;,] */n.freeze
13
+ COOKIE_REGEXP = /\s*(.+)=(.*)\s*/.freeze
14
+ COOKIE_EXPIRED_TIME = Time.at(0).freeze
15
+ HOUR = 60 * 60
16
+ DAY = HOUR * 24
17
+ WEEK = DAY * 7
18
+ MULTIPART_REGEXP = /\Amultipart\/form-data.*boundary=\"?([^\";,]+)/n.freeze
19
+ HTTP_COOKIE = 'HTTP_COOKIE'.freeze
20
+ QUERY_STRING = 'QUERY_STRING'.freeze
21
+ JSON_MIME_TYPE_REGEXP = %r{^application/json|^text/x-json}.freeze
22
+ XML_MIME_TYPE_REGEXP = %r{^application/xml|^text/xml}.freeze
23
+ FORM_URL_ENCODED_REGEXP = %r{^application/x-www-form-urlencoded}.freeze
24
+ UPCASE_CONTENT_TYPE = 'CONTENT_TYPE'.freeze
25
+ CONTENT_TYPE = "Content-Type".freeze
26
+ LAST_MODIFIED = "Last-Modified".freeze
27
+ SLASH = "/".freeze
28
+ REQUEST_METHOD = "REQUEST_METHOD".freeze
29
+ GET = "GET".freeze
30
+ POST = "POST".freeze
31
+ HEAD = "HEAD".freeze
32
+ CONTENT_LENGTH = "CONTENT_LENGTH".freeze
33
+ HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR".freeze
34
+ HTTP_IF_MODIFIED_SINCE = "HTTP_IF_MODIFIED_SINCE".freeze
35
+ HTTP_IF_NONE_MATCH = "HTTP_IF_NONE_MATCH".freeze
36
+ UPLOAD_ID = "upload_id".freeze
37
+ PATH_INFO = "PATH_INFO".freeze
38
+ SCRIPT_NAME = "SCRIPT_NAME".freeze
39
+ REQUEST_URI = "REQUEST_URI".freeze
40
+ REQUEST_PATH = "REQUEST_PATH".freeze
41
+ REMOTE_ADDR = "REMOTE_ADDR".freeze
42
+ end
43
+ end
@@ -0,0 +1,531 @@
1
+ # Note that the over-use of "_" in Controller methods is to avoid collisions
2
+ # with helpers, which will be pulled directly into controllers from now on.
3
+ #
4
+ # ==== Filters
5
+ # #before is a class method that allows you to specify before filters in
6
+ # your controllers. Filters can either be a symbol or string that
7
+ # corresponds to a method name to call, or a proc object. if it is a method
8
+ # name that method will be called and if it is a proc it will be called
9
+ # with an argument of self where self is the current controller object.
10
+ # When you use a proc as a filter it needs to take one parameter.
11
+ #
12
+ # #after is identical, but the filters are run after the action is invoked.
13
+ #
14
+ # ==== Examples
15
+ # before :some_filter
16
+ # before :authenticate, :exclude => [:login, :signup]
17
+ # before Proc.new {|c| c.some_method }, :only => :foo
18
+ # before :authorize, :unless => logged_in?
19
+ #
20
+ # You can use either :only => :actionname or :exclude => [:this, :that]
21
+ # but not both at once. :only will only run before the listed actions
22
+ # and :exclude will run for every action that is not listed.
23
+ #
24
+ # Merb's before filter chain is very flexible. To halt the filter chain you
25
+ # use throw :halt. If throw is called with only one argument of :halt the
26
+ # return of the method filters_halted will be what is rendered to the view.
27
+ # You can overide filters_halted in your own controllers to control what it
28
+ # outputs. But the throw construct is much more powerful then just that.
29
+ # throw :halt can also take a second argument. Here is what that second arg
30
+ # can be and the behavior each type can have:
31
+ #
32
+ # * +String+:
33
+ # when the second arg is a string then that string will be what
34
+ # is rendered to the browser. Since merb's render method returns
35
+ # a string you can render a template or just use a plain string:
36
+ #
37
+ # throw :halt, "You don't have permissions to do that!"
38
+ # throw :halt, render(:action => :access_denied)
39
+ #
40
+ # * +Symbol+:
41
+ # If the second arg is a symbol then the method named after that
42
+ # symbol will be called
43
+ #
44
+ # throw :halt, :must_click_disclaimer
45
+ #
46
+ # * +Proc+:
47
+ #
48
+ # If the second arg is a Proc, it will be called and its return
49
+ # value will be what is rendered to the browser:
50
+ #
51
+ # throw :halt, proc {|c| c.access_denied }
52
+ # throw :halt, proc {|c| Tidy.new(c.index) }
53
+ #
54
+ # ==== Filter Options (.before, .after, .add_filter, .if, .unless)
55
+ # :only<Symbol, Array[Symbol]>::
56
+ # A list of actions that this filter should apply to
57
+ #
58
+ # :exclude<Symbol, Array[Symbol]::
59
+ # A list of actions that this filter should *not* apply to
60
+ #
61
+ # :if<Symbol, Proc>::
62
+ # Only apply the filter if the method named after the symbol or calling the proc evaluates to true
63
+ #
64
+ # :unless<Symbol, Proc>::
65
+ # Only apply the filter if the method named after the symbol or calling the proc evaluates to false
66
+ #
67
+ # ==== Types (shortcuts for use in this file)
68
+ # Filter:: <Array[Symbol, (Symbol, String, Proc)]>
69
+ class Merb::AbstractController
70
+ include Merb::RenderMixin
71
+ include Merb::InlineTemplates
72
+
73
+ class_inheritable_accessor :_before_filters, :_after_filters, :_layout, :_template_root
74
+
75
+ # ==== Returns
76
+ # String:: The controller name in path form, e.g. "admin/items".
77
+ #---
78
+ # @public
79
+ def self.controller_name() @controller_name ||= self.name.to_const_path end
80
+
81
+ # ==== Returns
82
+ # String:: The controller name in path form, e.g. "admin/items".
83
+ def controller_name() self.class.controller_name end
84
+
85
+ self._before_filters, self._after_filters = [], []
86
+
87
+ # This is called after the controller is instantiated to figure out where to
88
+ # for templates under the _template_root. Override this to define a new
89
+ # structure for your app.
90
+ #
91
+ # ==== Parameters
92
+ # action<~to_s>:: The controller action.
93
+ # type<~to_s>:: The content type. Defaults to nil.
94
+ # controller<~to_s>::
95
+ # The name of the controller. Defaults to controller_name.
96
+ #
97
+ #
98
+ # ==== Returns
99
+ # String::
100
+ # Indicating where to look for the template for the current controller,
101
+ # action, and content-type.
102
+ #
103
+ # ==== Note
104
+ # The type is irrelevant for controller-types that don't support
105
+ # content-type negotiation, so we default to not include it in the
106
+ # superclass.
107
+ #
108
+ # ==== Examples
109
+ # def _template_location
110
+ # "#{params[:controller]}.#{params[:action]}.#{content_type}"
111
+ # end
112
+ #
113
+ # This would look for templates at controller.action.mime.type instead
114
+ # of controller/action.mime.type
115
+ #---
116
+ # @public
117
+ def _template_location(action, type = nil, controller = controller_name)
118
+ "#{controller}/#{action}"
119
+ end
120
+
121
+ # ==== Returns
122
+ # roots<Array[Array]>::
123
+ # Template roots as pairs of template root path and template location
124
+ # method.
125
+ def self._template_roots
126
+ read_inheritable_attribute(:template_roots) ||
127
+ write_inheritable_attribute(:template_roots, [[self._template_root, :_template_location]])
128
+ end
129
+
130
+ # ==== Parameters
131
+ # roots<Array[Array]>::
132
+ # Template roots as pairs of template root path and template location
133
+ # method.
134
+ def self._template_roots=(roots)
135
+ write_inheritable_attribute(:template_roots, roots)
136
+ end
137
+
138
+ cattr_accessor :_abstract_subclasses, :_template_path_cache
139
+ #---
140
+ # We're using abstract_subclasses so that Merb::Controller can have its
141
+ # own subclasses. We're using a Set so we don't have to worry about
142
+ # uniqueness.
143
+ self._abstract_subclasses = Set.new
144
+
145
+ # ==== Returns
146
+ # Set:: The subclasses.
147
+ def self.subclasses_list() _abstract_subclasses end
148
+
149
+ class << self
150
+ # ==== Parameters
151
+ # klass<Merb::AbstractController>::
152
+ # The controller that is being inherited from Merb::AbstractController
153
+ def inherited(klass)
154
+ _abstract_subclasses << klass.to_s
155
+ Object.make_module "Merb::#{klass}Helper" unless klass.to_s =~ /^Merb::/
156
+ klass.class_eval <<-HERE
157
+ include Object.full_const_get("Merb::#{klass}Helper") rescue nil
158
+ HERE
159
+ super
160
+ end
161
+
162
+ # ==== Parameters
163
+ # layout<~to_s>:: The layout that should be used for this class
164
+ #
165
+ # ==== Returns
166
+ # ~to_s:: The layout that was passed in
167
+ def layout(layout)
168
+ self._layout = layout
169
+ end
170
+ end
171
+
172
+ attr_accessor :_benchmarks, :_thrown_content
173
+
174
+ #---
175
+ # @semipublic
176
+ attr_accessor :body
177
+
178
+ attr_accessor :action_name
179
+
180
+ # ==== Parameters
181
+ # *args:: The args are ignored.
182
+ def initialize(*args)
183
+ @_benchmarks = {}
184
+ @_caught_content = {}
185
+ @_template_stack = []
186
+ end
187
+
188
+ # This will dispatch the request, calling setup_session and finalize_session
189
+ #
190
+ # ==== Parameters
191
+ # action<~to_s>::
192
+ # The action to dispatch to. This will be #send'ed in _call_action.
193
+ # Defaults to :to_s.
194
+ #
195
+ # ==== Raises
196
+ # MerbControllerError:: Invalid body content caught.
197
+ def _dispatch(action=:to_s)
198
+ setup_session
199
+ self.action_name = action
200
+
201
+ caught = catch(:halt) do
202
+ start = Time.now
203
+ result = _call_filters(_before_filters)
204
+ @_benchmarks[:before_filters_time] = Time.now - start if _before_filters
205
+ result
206
+ end
207
+
208
+ @body = case caught
209
+ when :filter_chain_completed then _call_action(action_name)
210
+ when String then caught
211
+ when nil then _filters_halted
212
+ when Symbol then __send__(caught)
213
+ when Proc then caught.call(self)
214
+ else
215
+ raise MerbControllerError, "The before filter chain is broken dude. wtf?"
216
+ end
217
+ start = Time.now
218
+ _call_filters(_after_filters)
219
+ @_benchmarks[:after_filters_time] = Time.now - start if _after_filters
220
+ finalize_session
221
+ end
222
+
223
+ # This method exists to provide an overridable hook for ActionArgs
224
+ #
225
+ # ==== Parameters
226
+ # action<~to_s>:: the action method to dispatch to
227
+ def _call_action(action)
228
+ send(action)
229
+ end
230
+
231
+ # ==== Parameters
232
+ # filter_set<Array[Filter]>::
233
+ # A set of filters in the form [[:filter, rule], [:filter, rule]]
234
+ #
235
+ # ==== Returns
236
+ # Symbol:: :filter_chain_completed.
237
+ #
238
+ # ==== Notes
239
+ # Filter rules can be Symbols, Strings, or Procs.
240
+ #
241
+ # Symbols or Strings::
242
+ # Call the method represented by the +Symbol+ or +String+.
243
+ # Procs::
244
+ # Execute the +Proc+, in the context of the controller (self will be the
245
+ # controller)
246
+ def _call_filters(filter_set)
247
+ (filter_set || []).each do |filter, rule|
248
+ if _call_filter_for_action?(rule, action_name) && _filter_condition_met?(rule)
249
+ case filter
250
+ when Symbol, String then send(filter)
251
+ when Proc then self.instance_eval(&filter)
252
+ end
253
+ end
254
+ end
255
+ return :filter_chain_completed
256
+ end
257
+
258
+ # ==== Parameters
259
+ # rule<Hash>:: Rules for the filter (see below).
260
+ # action_name<~to_s>:: The name of the action to be called.
261
+ #
262
+ # ==== Options (rule)
263
+ # :only<Array>::
264
+ # Optional list of actions to fire. If given, action_name must be a part of
265
+ # it for this function to return true.
266
+ # :exclude<Array>::
267
+ # Optional list of actions not to fire. If given, action_name must not be a
268
+ # part of it for this function to return true.
269
+ #
270
+ # ==== Returns
271
+ # Boolean:: True if the action should be called.
272
+ def _call_filter_for_action?(rule, action_name)
273
+ # Both:
274
+ # * no :only or the current action is in the :only list
275
+ # * no :exclude or the current action is not in the :exclude list
276
+ (!rule.key?(:only) || rule[:only].include?(action_name)) &&
277
+ (!rule.key?(:exclude) || !rule[:exclude].include?(action_name))
278
+ end
279
+
280
+ # ==== Parameters
281
+ # rule<Hash>:: Rules for the filter (see below).
282
+ #
283
+ # ==== Options (rule)
284
+ # :if<Array>:: Optional conditions that must be met for the filter to fire.
285
+ # :unless<Array>::
286
+ # Optional conditions that must not be met for the filter to fire.
287
+ #
288
+ # ==== Returns
289
+ # Boolean:: True if the conditions are met.
290
+ def _filter_condition_met?(rule)
291
+ # Both:
292
+ # * no :if or the if condition evaluates to true
293
+ # * no :unless or the unless condition evaluates to false
294
+ (!rule.key?(:if) || _evaluate_condition(rule[:if])) &&
295
+ (!rule.key?(:unless) || ! _evaluate_condition(rule[:unless]))
296
+ end
297
+
298
+ # ==== Parameters
299
+ # condition<Symbol, Proc>:: The condition to evaluate.
300
+ #
301
+ # ==== Raises
302
+ # ArgumentError:: condition not a Symbol or Proc.
303
+ #
304
+ # ==== Returns
305
+ # Boolean:: True if the condition is met.
306
+ #
307
+ # ==== Alternatives
308
+ # If condition is a symbol, it will be send'ed. If it is a Proc it will be
309
+ # called directly with self as an argument.
310
+ def _evaluate_condition(condition)
311
+ case condition
312
+ when Symbol : self.send(condition)
313
+ when Proc : condition.call(self)
314
+ else
315
+ raise ArgumentError,
316
+ 'Filter condtions need to be either a Symbol or a Proc'
317
+ end
318
+ end
319
+
320
+ # ==== Parameters
321
+ # filter<Symbol, Proc>:: The filter to add. Defaults to nil.
322
+ # opts<Hash>::
323
+ # Filter options (see class documentation under <tt>Filter Options</tt>).
324
+ # &block:: Currently ignored.
325
+ #
326
+ # ==== Note
327
+ # If the filter already exists, its options will be replaced with opts.
328
+ def self.after(filter = nil, opts = {}, &block)
329
+ add_filter(self._after_filters, filter, opts)
330
+ end
331
+
332
+ # ==== Parameters
333
+ # filter<Symbol, Proc>:: The filter to add. Defaults to nil.
334
+ # opts<Hash>::
335
+ # Filter options (see class documentation under <tt>Filter Options</tt>).
336
+ # &block:: A block to use as a filter if filter is nil.
337
+ #
338
+ # ==== Note
339
+ # If the filter already exists, its options will be replaced with opts.
340
+ def self.before(filter = nil, opts = {}, &block)
341
+ add_filter(self._before_filters, filter || block, opts)
342
+ end
343
+
344
+ # Skip an after filter that has been previously defined (perhaps in a
345
+ # superclass)
346
+ #
347
+ # ==== Parameters
348
+ # filter<Symbol>:: A filter name to skip.
349
+ def self.skip_after(filter)
350
+ skip_filter(self._after_filters, filter)
351
+ end
352
+
353
+ # Skip a before filter that has been previously defined (perhaps in a
354
+ # superclass).
355
+ #
356
+ # ==== Parameters
357
+ # filter<Symbol>:: A filter name to skip.
358
+ def self.skip_before(filter)
359
+ skip_filter(self._before_filters , filter)
360
+ end
361
+
362
+ #---
363
+ # Defaults that can be overridden by plugins, other mixins, or subclasses
364
+ def _filters_halted() "<html><body><h1>Filter Chain Halted!</h1></body></html>" end
365
+
366
+ # Method stub for setting up the session. This will be overriden by session
367
+ # modules.
368
+ def setup_session() end
369
+
370
+ # Method stub for finalizing up the session. This will be overriden by
371
+ # session modules.
372
+ def finalize_session() end
373
+
374
+ # Stub so content-type support in RenderMixin doesn't throw errors
375
+ attr_accessor :content_type
376
+
377
+ # Handles the template cache (which is used by BootLoader to cache the list
378
+ # of all templates).
379
+ #
380
+ # ==== Parameters
381
+ # template<String>::
382
+ # The full path to a template to add to the list of available templates
383
+ def self.add_path_to_template_cache(template)
384
+ return false if template.blank? || template.split("/").last.split(".").size != 3
385
+ key = template.match(/(.*)\.(.*)$/)[1]
386
+ self._template_path_cache[key] = template
387
+ end
388
+
389
+ # Resets the template_path_cache to an empty hash
390
+ def self.reset_template_path_cache!
391
+ self._template_path_cache = {}
392
+ end
393
+
394
+ # ==== Parameters
395
+ # name<~to_sym, Hash>:: The name of the URL to generate.
396
+ # rparams<Hash>:: Parameters for the route generation.
397
+ #
398
+ # ==== Returns
399
+ # String:: The generated URL.
400
+ #
401
+ # ==== Alternatives
402
+ # If a hash is used as the first argument, a default route will be
403
+ # generated based on it and rparams.
404
+ def url(name, rparams={})
405
+ uri = Merb::Router.generate(name, rparams,
406
+ { :controller => controller_name,
407
+ :action => action_name,
408
+ :format => params[:format]
409
+ }
410
+ )
411
+ uri = Merb::Config[:path_prefix] + uri if Merb::Config[:path_prefix]
412
+ uri
413
+ end
414
+ alias_method :relative_url, :url
415
+
416
+ # ==== Parameters
417
+ # name<~to_sym, Hash>:: The name of the URL to generate.
418
+ # rparams<Hash>:: Parameters for the route generation.
419
+ #
420
+ # ==== Returns
421
+ # String:: The generated url with protocol + hostname + URL.
422
+ #
423
+ # ==== Alternatives
424
+ # If a hash is used as the first argument, a default route will be
425
+ # generated based on it and rparams.
426
+ def absolute_url(name, rparams={})
427
+ request.protocol + request.host + url(name, rparams)
428
+ end
429
+
430
+ private
431
+ # ==== Parameters
432
+ # filters<Array[Filter]>:: The filter list that this should be added to.
433
+ # filter<Filter>:: A filter that should be added.
434
+ # opts<Hash>::
435
+ # Filter options (see class documentation under <tt>Filter Options</tt>).
436
+ #
437
+ # ==== Raises
438
+ # ArgumentError::
439
+ # Both :only and :exclude, or :if and :unless given, or filter is not a
440
+ # Symbol, String or Proc.
441
+ def self.add_filter(filters, filter, opts={})
442
+ raise(ArgumentError,
443
+ "You can specify either :only or :exclude but
444
+ not both at the same time for the same filter.") if opts.key?(:only) && opts.key?(:exclude)
445
+
446
+ raise(ArgumentError,
447
+ "You can specify either :if or :unless but
448
+ not both at the same time for the same filter.") if opts.key?(:if) && opts.key?(:unless)
449
+
450
+ opts = normalize_filters!(opts)
451
+
452
+ case filter
453
+ when Symbol, Proc, String
454
+ if existing_filter = filters.find {|f| f.first.to_s[filter.to_s]}
455
+ existing_filter.last.replace(opts)
456
+ else
457
+ filters << [filter, opts]
458
+ end
459
+ else
460
+ raise(ArgumentError,
461
+ 'Filters need to be either a Symbol, String or a Proc'
462
+ )
463
+ end
464
+ end
465
+
466
+ # Skip a filter that was previously added to the filter chain. Useful in
467
+ # inheritence hierarchies.
468
+ #
469
+ # ==== Parameters
470
+ # filters<Array[Filter]>:: The filter list that this should be removed from.
471
+ # filter<Filter>:: A filter that should be removed.
472
+ #
473
+ # ==== Raises
474
+ # ArgumentError:: filter not Symbol or String.
475
+ def self.skip_filter(filters, filter)
476
+ raise(ArgumentError, 'You can only skip filters that have a String or Symbol name.') unless
477
+ [Symbol, String].include? filter.class
478
+
479
+ Merb.logger.warn("Filter #{filter} was not found in your filter chain.") unless
480
+ filters.reject! {|f| f.first.to_s[filter.to_s] }
481
+ end
482
+
483
+ # Ensures that the passed in hash values are always arrays.
484
+ #
485
+ # ==== Parameters
486
+ # opts<Hash>:: Options for the filters (see below).
487
+ #
488
+ # ==== Options (opts)
489
+ # :only<Symbol, Array[Symbol]>:: A list of actions.
490
+ # :exclude<Symbol, Array[Symbol]>:: A list of actions.
491
+ #
492
+ # ==== Examples
493
+ # normalize_filters!(:only => :new) #=> {:only => [:new]}
494
+ def self.normalize_filters!(opts={})
495
+ opts[:only] = Array(opts[:only]).map {|x| x.to_s} if opts[:only]
496
+ opts[:exclude] = Array(opts[:exclude]).map {|x| x.to_s} if opts[:exclude]
497
+ return opts
498
+ end
499
+
500
+ # Calls the capture method for the selected template engine.
501
+ #
502
+ # ==== Parameters
503
+ # *args:: Arguments to pass to the block.
504
+ # &block:: The template block to call.
505
+ #
506
+ # ==== Returns
507
+ # String:: The output of the block.
508
+ def capture(*args, &block)
509
+ send("capture_#{@_engine}", *args, &block)
510
+ end
511
+
512
+ # Calls the concatenate method for the selected template engine.
513
+ #
514
+ # ==== Parameters
515
+ # str<String>:: The string to concatenate to the buffer.
516
+ # binding<Binding>:: The binding to use for the buffer.
517
+ def concat(str, binding)
518
+ send("concat_#{@_engine}", str, binding)
519
+ end
520
+
521
+ # Attempts to return the partial local variable corresponding to sym.
522
+ #
523
+ # ==== Paramteres
524
+ # sym<Symbol>:: Method name.
525
+ # *arg:: Arguments to pass to the method.
526
+ # &blk:: A block to pass to the method.
527
+ def method_missing(sym, *args, &blk)
528
+ return @_merb_partial_locals[sym] if @_merb_partial_locals && @_merb_partial_locals.key?(sym)
529
+ super
530
+ end
531
+ end