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,52 @@
1
+ module Merb
2
+
3
+ class Router
4
+ # Cache procs for future reference in eval statement
5
+ class CachedProc
6
+ @@index = 0
7
+ @@list = []
8
+
9
+ attr_accessor :cache, :index
10
+
11
+ # ==== Parameters
12
+ # cache<Proc>:: The block of code to cache.
13
+ def initialize(cache)
14
+ @cache, @index = cache, CachedProc.register(self)
15
+ end
16
+
17
+ # ==== Returns
18
+ # String:: The CachedProc object in a format embeddable within a string.
19
+ def to_s
20
+ "CachedProc[#{@index}].cache"
21
+ end
22
+
23
+ class << self
24
+
25
+ # ==== Parameters
26
+ # cached_code<CachedProc>:: The cached code to register.
27
+ #
28
+ # ==== Returns
29
+ # Fixnum:: The index of the newly registered CachedProc.
30
+ def register(cached_code)
31
+ CachedProc[@@index] = cached_code
32
+ @@index += 1
33
+ @@index - 1
34
+ end
35
+
36
+ # Sets the cached code for a specific index.
37
+ #
38
+ # ==== Parameters
39
+ # index<Fixnum>:: The index of the cached code to set.
40
+ # code<CachedProc>:: The cached code to set.
41
+ def []=(index, code) @@list[index] = code end
42
+
43
+ # ==== Parameters
44
+ # index<Fixnum>:: The index of the cached code to retrieve.
45
+ #
46
+ # ==== Returns
47
+ # CachedProc:: The cached code at index.
48
+ def [](index) @@list[index] end
49
+ end
50
+ end # CachedProc
51
+ end
52
+ end
@@ -0,0 +1,212 @@
1
+ require 'merb-core/controller/mixins/responder'
2
+ module Merb
3
+
4
+ class Router
5
+
6
+ class Route
7
+ attr_reader :conditions, :conditional_block
8
+ attr_reader :params, :behavior, :segments, :index, :symbol
9
+
10
+ # ==== Parameters
11
+ # conditions<Hash>:: Conditions for the route.
12
+ # params<Hash>:: Parameters for the route.
13
+ # behavior<Merb::Router::Behavior>::
14
+ # The associated behavior. Defaults to nil.
15
+ # &conditional_block::
16
+ # A block with the conditions to be met for the route to take effect.
17
+ def initialize(conditions, params, behavior = nil, &conditional_block)
18
+ @conditions, @params, @behavior = conditions, params, behavior
19
+ @conditional_block = conditional_block
20
+ if @behavior && (path = @behavior.merged_original_conditions[:path])
21
+ @segments = segments_from_path(path)
22
+ end
23
+ end
24
+
25
+ # ==== Returns
26
+ # Boolean:: True if fixation is allowed.
27
+ def allow_fixation?
28
+ @fixation
29
+ end
30
+
31
+ # ==== Parameters
32
+ # enabled<Boolean>:: True enables fixation on the route.
33
+ def fixatable(enable=true)
34
+ @fixation = enable
35
+ self
36
+ end
37
+
38
+ # ==== Returns
39
+ # String:: The route as a string, e.g. "admin/:controller/:id".
40
+ def to_s
41
+ segments.inject('') do |str,seg|
42
+ str << (seg.is_a?(Symbol) ? ":#{seg}" : seg)
43
+ end
44
+ end
45
+
46
+ # Registers the route in the Router.routes array.
47
+ def register
48
+ @index = Router.routes.size
49
+ Router.routes << self
50
+ self
51
+ end
52
+
53
+ # ==== Returns
54
+ # Array:: All the symbols in the segments array.
55
+ def symbol_segments
56
+ segments.select{ |s| s.is_a?(Symbol) }
57
+ end
58
+
59
+ # Turn a path into string and symbol segments so it can be reconstructed,
60
+ # as in the case of a named route.
61
+ #
62
+ # ==== Parameters
63
+ # path<String>:: The path to split into segments.
64
+ #
65
+ # ==== Returns
66
+ # Array:: The Symbol and String segments for the path.
67
+ def segments_from_path(path)
68
+ # Remove leading ^ and trailing $ from each segment (left-overs from regexp joining)
69
+ strip = proc { |str| str.gsub(/^\^/, '').gsub(/\$$/, '') }
70
+ segments = []
71
+ while match = (path.match(SEGMENT_REGEXP))
72
+ segments << strip[match.pre_match] unless match.pre_match.empty?
73
+ segments << match[2].intern
74
+ path = strip[match.post_match]
75
+ end
76
+ segments << strip[path] unless path.empty?
77
+ segments
78
+ end
79
+
80
+ # Names this route in Router.
81
+ #
82
+ # ==== Parameters
83
+ # symbol<Symbol>:: The name of the route.
84
+ #
85
+ # ==== Raises
86
+ # ArgumentError:: symbol is not a Symbol.
87
+ def name(symbol = nil)
88
+ raise ArgumentError unless (@symbol = symbol).is_a?(Symbol)
89
+ Router.named_routes[@symbol] = self
90
+ end
91
+
92
+ # ==== Returns
93
+ # Boolean::
94
+ # True if this route is a regexp, i.e. its behavior or one of the
95
+ # behaviors ancestors is a regexp.
96
+ def regexp?
97
+ behavior.regexp? || behavior.send(:ancestors).any? { |a| a.regexp? }
98
+ end
99
+
100
+ # ==== Parameters
101
+ # params<Hash>:: Optional parameters for the route.
102
+ # fallback<Hash>:: Optional parameters for the fallback route.
103
+ #
104
+ # ==== Returns
105
+ # String::
106
+ # The URL corresponding to the params, using the stored route segments
107
+ # for reconstruction of the URL.
108
+ def generate(params = {}, fallback = {})
109
+ raise "Cannot generate regexp Routes" if regexp?
110
+ query_params = params.dup if params.is_a? Hash
111
+ url = @segments.map do |segment|
112
+ value =
113
+ if segment.is_a? Symbol
114
+ if params.is_a? Hash
115
+ params[segment] || fallback[segment]
116
+ query_params.delete segment
117
+ else
118
+ if segment == :id && params.respond_to?(:to_param)
119
+ params.to_param
120
+ elsif segment == :id && params.is_a?(Fixnum)
121
+ params
122
+ elsif params.respond_to?(segment)
123
+ params.send(segment)
124
+ else
125
+ fallback[segment]
126
+ end
127
+ end
128
+ elsif segment.respond_to? :to_s
129
+ segment
130
+ else
131
+ raise "Segment type '#{segment.class}' can't be converted to a string"
132
+ end
133
+ (value.respond_to?(:to_param) ? value.to_param : value).to_s
134
+ end.join
135
+ if query_params && !query_params.empty?
136
+ url += "?" + Merb::Request.params_to_query_string(query_params)
137
+ end
138
+ url
139
+ end
140
+
141
+ # ==== Params
142
+ # params_as_string<String>::
143
+ # The params hash as a string, e.g. ":foo => 'bar'".
144
+ #
145
+ # ==== Returns
146
+ # Array:: All the conditions as eval'able strings.
147
+ def if_conditions(params_as_string)
148
+ cond = []
149
+ condition_string = proc do |key, value, regexp_string|
150
+ max = Behavior.count_parens_up_to(value.source, value.source.size)
151
+ captures = max == 0 ? "" : (1..max).to_a.map{ |n| "#{key}#{n}" }.join(", ") + " = " +
152
+ (1..max).to_a.map{ |n| "$#{n}"}.join(", ")
153
+ " (#{value.inspect} =~ #{regexp_string}) #{" && (" + captures + ")" unless captures.empty?}"
154
+ end
155
+ @conditions.each_pair do |key, value|
156
+
157
+ # Note: =~ is slightly faster than .match
158
+ cond << case key
159
+ when :path then condition_string[key, value, "cached_path"]
160
+ when :method then condition_string[key, value, "cached_method"]
161
+ else condition_string[key, value, "request.#{key}.to_s"]
162
+ end
163
+ end
164
+ if @conditional_block
165
+ str = " # #{@conditional_block.inspect.scan(/@([^>]+)/).flatten.first}\n"
166
+ str << " (block_result = #{CachedProc.new(@conditional_block)}.call(request, params.merge({#{params_as_string}})))" if @conditional_block
167
+ cond << str
168
+ end
169
+ cond
170
+ end
171
+
172
+ # Compiles the route to a form used by Merb::Router.
173
+ #
174
+ # ==== Parameters
175
+ # first<Boolean>::
176
+ # True if this is the first route in set of routes. Defaults to false.
177
+ #
178
+ # ==== Returns
179
+ # String:: The code corresponding to the route in a form suited for eval.
180
+ def compile(first = false)
181
+ code = ""
182
+ default_params = { :action => "index" }
183
+ get_value = proc do |key|
184
+ if default_params.has_key?(key) && params[key][0] != ?"
185
+ "#{params[key]} || \"#{default_params[key]}\""
186
+ else
187
+ "#{params[key]}"
188
+ end
189
+ end
190
+ params_as_string = params.keys.map { |k| "#{k.inspect} => #{get_value[k]}" }.join(', ')
191
+ code << " els" unless first
192
+ code << "if # #{@behavior.merged_original_conditions.inspect} \n"
193
+ code << if_conditions(params_as_string).join(" && ") << "\n"
194
+ code << " # then\n"
195
+ if @conditional_block
196
+ code << " [#{@index.inspect}, block_result]\n"
197
+ else
198
+ code << " [#{@index.inspect}, {#{params_as_string}}]\n"
199
+ end
200
+ end
201
+
202
+ # Prints a trace of the behavior for this route.
203
+ def behavior_trace
204
+ if @behavior
205
+ puts @behavior.send(:ancestors).reverse.map{|a| a.inspect}.join("\n"); puts @behavior.inspect; puts
206
+ else
207
+ puts "No behavior to trace #{self}"
208
+ end
209
+ end
210
+ end # Route
211
+ end
212
+ end
@@ -0,0 +1,28 @@
1
+ module Merb
2
+
3
+ module SessionMixin
4
+
5
+ # ==== Returns
6
+ # String:: A random 32 character string for use as a unique session ID.
7
+ def rand_uuid
8
+ values = [
9
+ rand(0x0010000),
10
+ rand(0x0010000),
11
+ rand(0x0010000),
12
+ rand(0x0010000),
13
+ rand(0x0010000),
14
+ rand(0x1000000),
15
+ rand(0x1000000),
16
+ ]
17
+ "%04x%04x%04x%04x%04x%06x%06x" % values
18
+ end
19
+
20
+ # Marks this session as needing a new cookie.
21
+ def needs_new_cookie!
22
+ @_new_cookie = true
23
+ end
24
+
25
+ module_function :rand_uuid, :needs_new_cookie!
26
+ end
27
+
28
+ end
@@ -0,0 +1,166 @@
1
+ require 'base64' # to convert Marshal.dump to ASCII
2
+ require 'openssl' # to generate the HMAC message digest
3
+ # Most of this code is taken from bitsweat's implementation in rails
4
+ module Merb
5
+
6
+ module SessionMixin #:nodoc:
7
+
8
+ # Adds a before and after dispatch hook for setting up the cookie session
9
+ # store.
10
+ #
11
+ # ==== Parameters
12
+ # base<Class>:: The class to which the SessionMixin is mixed into.
13
+ def setup_session
14
+ request.session = Merb::CookieSession.new(cookies[_session_id_key], _session_secret_key)
15
+ @original_session = request.session.read_cookie
16
+ end
17
+
18
+ # Finalizes the session by storing the session in a cookie, if the session
19
+ # has changed.
20
+ def finalize_session
21
+ new_session = request.session.read_cookie
22
+
23
+ if @original_session != new_session
24
+ set_cookie(_session_id_key, new_session, Time.now + _session_expiry)
25
+ end
26
+ end
27
+
28
+ # ==== Returns
29
+ # String:: The session store type, i.e. "cookie".
30
+ def session_store_type
31
+ "cookie"
32
+ end
33
+ end
34
+
35
+ # If you have more than 4K of session data or don't want your data to be
36
+ # visible to the user, pick another session store.
37
+ #
38
+ # CookieOverflow is raised if you attempt to store more than 4K of data.
39
+ # TamperedWithCookie is raised if the data integrity check fails.
40
+ #
41
+ # A message digest is included with the cookie to ensure data integrity:
42
+ # a user cannot alter session data without knowing the secret key included
43
+ # in the hash.
44
+ #
45
+ # To use Cookie Sessions, set in config/merb.yml
46
+ # :session_secret_key - your secret digest key
47
+ # :session_store: cookie
48
+ class CookieSession
49
+ # TODO (maybe):
50
+ # include request ip address
51
+ # AES encrypt marshaled data
52
+
53
+ # Raised when storing more than 4K of session data.
54
+ class CookieOverflow < StandardError; end
55
+
56
+ # Raised when the cookie fails its integrity check.
57
+ class TamperedWithCookie < StandardError; end
58
+
59
+ # Cookies can typically store 4096 bytes.
60
+ MAX = 4096
61
+ DIGEST = OpenSSL::Digest::Digest.new('SHA1') # or MD5, RIPEMD160, SHA256?
62
+
63
+ attr_reader :data
64
+
65
+ # ==== Parameters
66
+ # cookie<String>:: The cookie.
67
+ # secret<String>:: A session secret.
68
+ #
69
+ # ==== Raises
70
+ # ArgumentError:: Nil or blank secret.
71
+ def initialize(cookie, secret)
72
+ if secret.nil? or secret.blank?
73
+ raise ArgumentError, 'A secret is required to generate an integrity hash for cookie session data.'
74
+ end
75
+ @secret = secret
76
+ @data = unmarshal(cookie) || Hash.new
77
+ end
78
+
79
+ # ==== Returns
80
+ # String:: Cookie value.
81
+ #
82
+ # ==== Raises
83
+ # CookieOverflow:: Session contains too much information.
84
+ def read_cookie
85
+ unless @data.nil? or @data.empty?
86
+ updated = marshal(@data)
87
+ raise CookieOverflow if updated.size > MAX
88
+ updated
89
+ end
90
+ end
91
+
92
+ # ==== Parameters
93
+ # k<~to_s>:: The key of the session parameter to set.
94
+ # v<~to_s>:: The value of the session parameter to set.
95
+ def []=(k, v)
96
+ @data[k] = v
97
+ end
98
+
99
+ # ==== Parameters
100
+ # k<~to_s>:: The key of the session parameter to retrieve.
101
+ #
102
+ # ==== Returns
103
+ # String:: The value of the session parameter.
104
+ def [](k)
105
+ @data[k]
106
+ end
107
+
108
+ # Yields the session data to an each block.
109
+ #
110
+ # ==== Parameter
111
+ # &b:: The block to pass to each.
112
+ def each(&b)
113
+ @data.each(&b)
114
+ end
115
+
116
+ # Deletes the session by emptying stored data.
117
+ def delete
118
+ @data = {}
119
+ end
120
+
121
+ private
122
+
123
+ # Attempts to redirect any messages to the data object.
124
+ def method_missing(name, *args, &block)
125
+ @data.send(name, *args, &block)
126
+ end
127
+
128
+ # Generate the HMAC keyed message digest. Uses SHA1.
129
+ def generate_digest(data)
130
+ OpenSSL::HMAC.hexdigest(DIGEST, @secret, data)
131
+ end
132
+
133
+ # Marshal a session hash into safe cookie data. Include an integrity hash.
134
+ #
135
+ # ==== Parameters
136
+ # session<Hash>:: The session to store in the cookie.
137
+ #
138
+ # ==== Returns
139
+ # String:: The cookie to be stored.
140
+ def marshal(session)
141
+ data = Base64.encode64(Marshal.dump(session)).chop
142
+ Merb::Request.escape "#{data}--#{generate_digest(data)}"
143
+ end
144
+
145
+ # Unmarshal cookie data to a hash and verify its integrity.
146
+ #
147
+ # ==== Parameters
148
+ # cookie<~to_s>:: The cookie to unmarshal.
149
+ #
150
+ # ==== Raises
151
+ # TamperedWithCookie:: The digests don't match.
152
+ #
153
+ # ==== Returns
154
+ # Hash:: The stored session data.
155
+ def unmarshal(cookie)
156
+ if cookie
157
+ data, digest = Merb::Request.unescape(cookie).split('--')
158
+ return {} if data.blank?
159
+ unless digest == generate_digest(data)
160
+ raise TamperedWithCookie, "Maybe the site's session_secret_key has changed?"
161
+ end
162
+ Marshal.load(Base64.decode64(data))
163
+ end
164
+ end
165
+ end
166
+ end