thorero 0.9.4.4 → 0.9.4.5

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 (298) hide show
  1. data/LICENSE +1 -1
  2. data/README +21 -0
  3. data/Rakefile +275 -108
  4. data/TODO +0 -0
  5. data/bin/merb +12 -0
  6. data/bin/merb-specs +5 -0
  7. data/docs/bootloading.dox +58 -0
  8. data/docs/documentation_standards +40 -0
  9. data/docs/merb-core-call-stack-diagram.mmap +0 -0
  10. data/docs/merb-core-call-stack-diagram.pdf +0 -0
  11. data/docs/merb-core-call-stack-diagram.png +0 -0
  12. data/docs/new_render_api +51 -0
  13. data/lib/merb-core.rb +603 -0
  14. data/lib/merb-core/autoload.rb +32 -0
  15. data/lib/merb-core/bootloader.rb +708 -0
  16. data/lib/merb-core/config.rb +303 -0
  17. data/lib/merb-core/constants.rb +43 -0
  18. data/lib/merb-core/controller/abstract_controller.rb +578 -0
  19. data/lib/merb-core/controller/exceptions.rb +302 -0
  20. data/lib/merb-core/controller/merb_controller.rb +256 -0
  21. data/lib/merb-core/controller/mime.rb +106 -0
  22. data/lib/merb-core/controller/mixins/authentication.rb +87 -0
  23. data/lib/merb-core/controller/mixins/controller.rb +290 -0
  24. data/lib/merb-core/controller/mixins/render.rb +481 -0
  25. data/lib/merb-core/controller/mixins/responder.rb +472 -0
  26. data/lib/merb-core/controller/template.rb +254 -0
  27. data/lib/merb-core/core_ext.rb +8 -0
  28. data/lib/merb-core/core_ext/kernel.rb +319 -0
  29. data/lib/merb-core/dispatch/cookies.rb +91 -0
  30. data/lib/merb-core/dispatch/dispatcher.rb +278 -0
  31. data/lib/merb-core/dispatch/exceptions.html.erb +303 -0
  32. data/lib/merb-core/dispatch/request.rb +603 -0
  33. data/lib/merb-core/dispatch/router.rb +179 -0
  34. data/lib/merb-core/dispatch/router/behavior.rb +867 -0
  35. data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
  36. data/lib/merb-core/dispatch/router/route.rb +321 -0
  37. data/lib/merb-core/dispatch/session.rb +78 -0
  38. data/lib/merb-core/dispatch/session/cookie.rb +168 -0
  39. data/lib/merb-core/dispatch/session/memcached.rb +184 -0
  40. data/lib/merb-core/dispatch/session/memory.rb +241 -0
  41. data/lib/merb-core/dispatch/worker.rb +28 -0
  42. data/lib/merb-core/gem_ext/erubis.rb +77 -0
  43. data/lib/{extlib → merb-core}/logger.rb +2 -2
  44. data/lib/merb-core/plugins.rb +59 -0
  45. data/lib/merb-core/rack.rb +21 -0
  46. data/lib/merb-core/rack/adapter.rb +44 -0
  47. data/lib/merb-core/rack/adapter/ebb.rb +25 -0
  48. data/lib/merb-core/rack/adapter/evented_mongrel.rb +26 -0
  49. data/lib/merb-core/rack/adapter/fcgi.rb +17 -0
  50. data/lib/merb-core/rack/adapter/irb.rb +118 -0
  51. data/lib/merb-core/rack/adapter/mongrel.rb +26 -0
  52. data/lib/merb-core/rack/adapter/runner.rb +28 -0
  53. data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +26 -0
  54. data/lib/merb-core/rack/adapter/thin.rb +39 -0
  55. data/lib/merb-core/rack/adapter/thin_turbo.rb +24 -0
  56. data/lib/merb-core/rack/adapter/webrick.rb +36 -0
  57. data/lib/merb-core/rack/application.rb +18 -0
  58. data/lib/merb-core/rack/handler/mongrel.rb +97 -0
  59. data/lib/merb-core/rack/middleware.rb +26 -0
  60. data/lib/merb-core/rack/middleware/path_prefix.rb +31 -0
  61. data/lib/merb-core/rack/middleware/profiler.rb +19 -0
  62. data/lib/merb-core/rack/middleware/static.rb +45 -0
  63. data/lib/merb-core/server.rb +252 -0
  64. data/lib/merb-core/tasks/audit.rake +68 -0
  65. data/lib/merb-core/tasks/merb.rb +1 -0
  66. data/lib/merb-core/tasks/merb_rake_helper.rb +12 -0
  67. data/lib/merb-core/test.rb +11 -0
  68. data/lib/merb-core/test/helpers.rb +9 -0
  69. data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
  70. data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
  71. data/lib/merb-core/test/helpers/request_helper.rb +344 -0
  72. data/lib/merb-core/test/helpers/route_helper.rb +33 -0
  73. data/lib/merb-core/test/helpers/view_helper.rb +121 -0
  74. data/lib/merb-core/test/matchers.rb +9 -0
  75. data/lib/merb-core/test/matchers/controller_matchers.rb +319 -0
  76. data/lib/merb-core/test/matchers/route_matchers.rb +136 -0
  77. data/lib/merb-core/test/matchers/view_matchers.rb +335 -0
  78. data/lib/merb-core/test/run_specs.rb +47 -0
  79. data/lib/merb-core/test/tasks/spectasks.rb +68 -0
  80. data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
  81. data/lib/merb-core/test/test_ext/object.rb +14 -0
  82. data/lib/merb-core/test/test_ext/string.rb +14 -0
  83. data/lib/merb-core/vendor/facets.rb +2 -0
  84. data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
  85. data/lib/merb-core/vendor/facets/inflect.rb +345 -0
  86. data/lib/merb-core/version.rb +11 -0
  87. data/spec/private/config/adapter_spec.rb +32 -0
  88. data/spec/private/config/config_spec.rb +202 -0
  89. data/spec/private/config/environment_spec.rb +13 -0
  90. data/spec/private/config/spec_helper.rb +1 -0
  91. data/spec/private/core_ext/kernel_spec.rb +169 -0
  92. data/spec/private/dispatch/bootloader_spec.rb +24 -0
  93. data/spec/private/dispatch/cookies_spec.rb +107 -0
  94. data/spec/private/dispatch/dispatch_spec.rb +35 -0
  95. data/spec/private/dispatch/fixture/app/controllers/application.rb +4 -0
  96. data/spec/private/dispatch/fixture/app/controllers/exceptions.rb +27 -0
  97. data/spec/private/dispatch/fixture/app/controllers/foo.rb +21 -0
  98. data/spec/private/dispatch/fixture/app/helpers/global_helpers.rb +8 -0
  99. data/spec/private/dispatch/fixture/app/views/exeptions/client_error.html.erb +37 -0
  100. data/spec/private/dispatch/fixture/app/views/exeptions/internal_server_error.html.erb +216 -0
  101. data/spec/private/dispatch/fixture/app/views/exeptions/not_acceptable.html.erb +38 -0
  102. data/spec/private/dispatch/fixture/app/views/exeptions/not_found.html.erb +40 -0
  103. data/spec/private/dispatch/fixture/app/views/foo/bar.html.erb +0 -0
  104. data/spec/private/dispatch/fixture/app/views/layout/application.html.erb +11 -0
  105. data/spec/private/dispatch/fixture/config/black_hole.rb +12 -0
  106. data/spec/private/dispatch/fixture/config/environments/development.rb +6 -0
  107. data/spec/private/dispatch/fixture/config/environments/production.rb +5 -0
  108. data/spec/private/dispatch/fixture/config/environments/test.rb +6 -0
  109. data/spec/private/dispatch/fixture/config/init.rb +45 -0
  110. data/spec/private/dispatch/fixture/config/rack.rb +11 -0
  111. data/spec/private/dispatch/fixture/config/router.rb +35 -0
  112. data/spec/private/dispatch/fixture/log/merb_test.log +1874 -0
  113. data/spec/private/dispatch/fixture/public/images/merb.jpg +0 -0
  114. data/spec/private/dispatch/fixture/public/merb.fcgi +4 -0
  115. data/spec/private/dispatch/fixture/public/stylesheets/master.css +119 -0
  116. data/spec/private/dispatch/route_params_spec.rb +24 -0
  117. data/spec/private/dispatch/session_mixin_spec.rb +47 -0
  118. data/spec/private/dispatch/spec_helper.rb +1 -0
  119. data/spec/private/plugins/plugin_spec.rb +166 -0
  120. data/spec/private/rack/application_spec.rb +49 -0
  121. data/spec/private/router/behavior_spec.rb +60 -0
  122. data/spec/private/router/fixture/log/merb_test.log +139 -0
  123. data/spec/private/router/route_spec.rb +414 -0
  124. data/spec/private/router/router_spec.rb +175 -0
  125. data/spec/private/vendor/facets/plural_spec.rb +564 -0
  126. data/spec/private/vendor/facets/singular_spec.rb +489 -0
  127. data/spec/public/DEFINITIONS +11 -0
  128. data/spec/public/abstract_controller/controllers/alt_views/layout/application.erb +1 -0
  129. data/spec/public/abstract_controller/controllers/alt_views/layout/merb/test/fixtures/abstract/render_string_controller_layout.erb +1 -0
  130. data/spec/public/abstract_controller/controllers/alt_views/layout/merb/test/fixtures/abstract/render_template_controller_layout.erb +1 -0
  131. data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/display_object_with_multiple_roots/index.erb +1 -0
  132. data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/display_object_with_multiple_roots/show.erb +1 -0
  133. data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/render_template_multiple_roots/index.erb +1 -0
  134. data/spec/public/abstract_controller/controllers/alt_views/partial/basic_partial_with_multiple_roots/_partial.erb +1 -0
  135. data/spec/public/abstract_controller/controllers/alt_views/render_template_multiple_roots_and_custom_location/index.erb +1 -0
  136. data/spec/public/abstract_controller/controllers/alt_views/render_template_multiple_roots_inherited/index.erb +1 -0
  137. data/spec/public/abstract_controller/controllers/cousins.rb +41 -0
  138. data/spec/public/abstract_controller/controllers/display.rb +54 -0
  139. data/spec/public/abstract_controller/controllers/filters.rb +193 -0
  140. data/spec/public/abstract_controller/controllers/helpers.rb +41 -0
  141. data/spec/public/abstract_controller/controllers/partial.rb +121 -0
  142. data/spec/public/abstract_controller/controllers/render.rb +113 -0
  143. data/spec/public/abstract_controller/controllers/views/helpers/capture/index.erb +1 -0
  144. data/spec/public/abstract_controller/controllers/views/helpers/capture_eq/index.erb +1 -0
  145. data/spec/public/abstract_controller/controllers/views/helpers/capture_with_args/index.erb +1 -0
  146. data/spec/public/abstract_controller/controllers/views/helpers/concat/index.erb +1 -0
  147. data/spec/public/abstract_controller/controllers/views/layout/alt.erb +1 -0
  148. data/spec/public/abstract_controller/controllers/views/layout/custom.erb +1 -0
  149. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/display_object/index.erb +1 -0
  150. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/display_object_with_action/new.erb +1 -0
  151. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template/index.erb +1 -0
  152. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_app_layout/index.erb +0 -0
  153. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_custom_layout/index.erb +1 -0
  154. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_multiple_roots/index.erb +1 -0
  155. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_multiple_roots/show.erb +1 -0
  156. data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_two_throw_contents/index.erb +1 -0
  157. data/spec/public/abstract_controller/controllers/views/partial/another_directory/_partial.erb +1 -0
  158. data/spec/public/abstract_controller/controllers/views/partial/basic_partial/_partial.erb +1 -0
  159. data/spec/public/abstract_controller/controllers/views/partial/basic_partial/index.erb +1 -0
  160. data/spec/public/abstract_controller/controllers/views/partial/basic_partial_with_multiple_roots/index.erb +1 -0
  161. data/spec/public/abstract_controller/controllers/views/partial/nested_partial/_first.erb +1 -0
  162. data/spec/public/abstract_controller/controllers/views/partial/nested_partial/_second.erb +1 -0
  163. data/spec/public/abstract_controller/controllers/views/partial/nested_partial/index.erb +1 -0
  164. data/spec/public/abstract_controller/controllers/views/partial/partial_in_another_directory/index.erb +1 -0
  165. data/spec/public/abstract_controller/controllers/views/partial/partial_with_both/_collection.erb +1 -0
  166. data/spec/public/abstract_controller/controllers/views/partial/partial_with_both/index.erb +1 -0
  167. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections/_collection.erb +1 -0
  168. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections/index.erb +1 -0
  169. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_as/_collection.erb +1 -0
  170. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_as/index.erb +1 -0
  171. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_counter/_collection.erb +1 -0
  172. data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_counter/index.erb +1 -0
  173. data/spec/public/abstract_controller/controllers/views/partial/partial_with_locals/_variables.erb +1 -0
  174. data/spec/public/abstract_controller/controllers/views/partial/partial_with_locals/index.erb +1 -0
  175. data/spec/public/abstract_controller/controllers/views/partial/partial_with_with_and_locals/_both.erb +1 -0
  176. data/spec/public/abstract_controller/controllers/views/partial/partial_with_with_and_locals/index.erb +1 -0
  177. data/spec/public/abstract_controller/controllers/views/partial/with_absolute_partial/_partial.erb +1 -0
  178. data/spec/public/abstract_controller/controllers/views/partial/with_absolute_partial/index.erb +1 -0
  179. data/spec/public/abstract_controller/controllers/views/partial/with_as_partial/_with_partial.erb +1 -0
  180. data/spec/public/abstract_controller/controllers/views/partial/with_as_partial/index.erb +1 -0
  181. data/spec/public/abstract_controller/controllers/views/partial/with_nil_partial/_with_partial.erb +1 -0
  182. data/spec/public/abstract_controller/controllers/views/partial/with_nil_partial/index.erb +1 -0
  183. data/spec/public/abstract_controller/controllers/views/partial/with_partial/_with_partial.erb +1 -0
  184. data/spec/public/abstract_controller/controllers/views/partial/with_partial/index.erb +1 -0
  185. data/spec/public/abstract_controller/controllers/views/test_display/foo.html.erb +1 -0
  186. data/spec/public/abstract_controller/controllers/views/test_render/foo.html.erb +0 -0
  187. data/spec/public/abstract_controller/controllers/views/wonderful/index.erb +1 -0
  188. data/spec/public/abstract_controller/display_spec.rb +33 -0
  189. data/spec/public/abstract_controller/filter_spec.rb +106 -0
  190. data/spec/public/abstract_controller/helper_spec.rb +21 -0
  191. data/spec/public/abstract_controller/partial_spec.rb +61 -0
  192. data/spec/public/abstract_controller/render_spec.rb +90 -0
  193. data/spec/public/abstract_controller/spec_helper.rb +31 -0
  194. data/spec/public/boot_loader/boot_loader_spec.rb +33 -0
  195. data/spec/public/boot_loader/spec_helper.rb +1 -0
  196. data/spec/public/controller/authentication_spec.rb +103 -0
  197. data/spec/public/controller/base_spec.rb +36 -0
  198. data/spec/public/controller/controllers/authentication.rb +45 -0
  199. data/spec/public/controller/controllers/base.rb +36 -0
  200. data/spec/public/controller/controllers/display.rb +118 -0
  201. data/spec/public/controller/controllers/redirect.rb +30 -0
  202. data/spec/public/controller/controllers/responder.rb +93 -0
  203. data/spec/public/controller/controllers/url.rb +7 -0
  204. data/spec/public/controller/controllers/views/layout/custom.html.erb +1 -0
  205. data/spec/public/controller/controllers/views/layout/custom_arg.html.erb +1 -0
  206. data/spec/public/controller/controllers/views/layout/custom_arg.json.erb +1 -0
  207. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_and_local_provides/index.html.erb +1 -0
  208. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_and_local_provides/index.xml.erb +1 -0
  209. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.html.erb +1 -0
  210. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.xml.erb +1 -0
  211. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/display_with_template/index.html.erb +1 -0
  212. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/display_with_template/no_layout.html.erb +1 -0
  213. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/display_with_template_argument/index.html.erb +1 -0
  214. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/html_default/index.html.erb +1 -0
  215. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/layout/custom.html.erb +1 -0
  216. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/local_provides/index.html.erb +1 -0
  217. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/local_provides/index.xml.erb +1 -0
  218. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/multi_provides/index.html.erb +1 -0
  219. data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/multi_provides/index.js.erb +1 -0
  220. data/spec/public/controller/display_spec.rb +84 -0
  221. data/spec/public/controller/redirect_spec.rb +27 -0
  222. data/spec/public/controller/responder_spec.rb +163 -0
  223. data/spec/public/controller/spec_helper.rb +11 -0
  224. data/spec/public/controller/url_spec.rb +180 -0
  225. data/spec/public/core/merb_core_spec.rb +45 -0
  226. data/spec/public/core_ext/class_spec.rb +91 -0
  227. data/spec/public/core_ext/fixtures/core_ext_dependency.rb +2 -0
  228. data/spec/public/core_ext/kernel_spec.rb +9 -0
  229. data/spec/public/core_ext/spec_helper.rb +1 -0
  230. data/spec/public/directory_structure/directory/app/controllers/application.rb +3 -0
  231. data/spec/public/directory_structure/directory/app/controllers/base.rb +13 -0
  232. data/spec/public/directory_structure/directory/app/controllers/custom.rb +19 -0
  233. data/spec/public/directory_structure/directory/app/views/base/template.html.erb +1 -0
  234. data/spec/public/directory_structure/directory/app/views/wonderful/template.erb +1 -0
  235. data/spec/public/directory_structure/directory/config/router.rb +3 -0
  236. data/spec/public/directory_structure/directory/log/merb_test.log +562 -0
  237. data/spec/public/directory_structure/directory_spec.rb +44 -0
  238. data/spec/public/logger/logger_spec.rb +181 -0
  239. data/spec/public/logger/spec_helper.rb +1 -0
  240. data/spec/public/reloading/directory/app/controllers/application.rb +3 -0
  241. data/spec/public/reloading/directory/app/controllers/reload.rb +6 -0
  242. data/spec/public/reloading/directory/config/init.rb +2 -0
  243. data/spec/public/reloading/directory/log/merb_test.log +138 -0
  244. data/spec/public/reloading/reload_spec.rb +103 -0
  245. data/spec/public/request/multipart_spec.rb +41 -0
  246. data/spec/public/request/request_spec.rb +228 -0
  247. data/spec/public/router/default_spec.rb +21 -0
  248. data/spec/public/router/deferred_spec.rb +22 -0
  249. data/spec/public/router/fixation_spec.rb +27 -0
  250. data/spec/public/router/fixture/log/merb_test.log +1556 -0
  251. data/spec/public/router/namespace_spec.rb +113 -0
  252. data/spec/public/router/nested_matches_spec.rb +97 -0
  253. data/spec/public/router/nested_resources_spec.rb +41 -0
  254. data/spec/public/router/resource_spec.rb +37 -0
  255. data/spec/public/router/resources_spec.rb +82 -0
  256. data/spec/public/router/spec_helper.rb +90 -0
  257. data/spec/public/router/special_spec.rb +61 -0
  258. data/spec/public/router/string_spec.rb +61 -0
  259. data/spec/public/template/template_spec.rb +104 -0
  260. data/spec/public/template/templates/error.html.erb +2 -0
  261. data/spec/public/template/templates/template.html.erb +1 -0
  262. data/spec/public/template/templates/template.html.myt +1 -0
  263. data/spec/public/test/controller_matchers_spec.rb +402 -0
  264. data/spec/public/test/controllers/controller_assertion_mock.rb +7 -0
  265. data/spec/public/test/controllers/dispatch_controller.rb +11 -0
  266. data/spec/public/test/controllers/spec_helper_controller.rb +38 -0
  267. data/spec/public/test/multipart_request_helper_spec.rb +159 -0
  268. data/spec/public/test/multipart_upload_text_file.txt +1 -0
  269. data/spec/public/test/request_helper_spec.rb +221 -0
  270. data/spec/public/test/route_helper_spec.rb +71 -0
  271. data/spec/public/test/route_matchers_spec.rb +162 -0
  272. data/spec/public/test/view_helper_spec.rb +96 -0
  273. data/spec/public/test/view_matchers_spec.rb +183 -0
  274. data/spec/spec_helper.rb +68 -0
  275. metadata +493 -41
  276. data/README.txt +0 -3
  277. data/lib/extlib.rb +0 -32
  278. data/lib/extlib/assertions.rb +0 -8
  279. data/lib/extlib/blank.rb +0 -42
  280. data/lib/extlib/class.rb +0 -175
  281. data/lib/extlib/hash.rb +0 -410
  282. data/lib/extlib/hook.rb +0 -366
  283. data/lib/extlib/inflection.rb +0 -141
  284. data/lib/extlib/lazy_array.rb +0 -106
  285. data/lib/extlib/mash.rb +0 -143
  286. data/lib/extlib/module.rb +0 -37
  287. data/lib/extlib/object.rb +0 -165
  288. data/lib/extlib/object_space.rb +0 -13
  289. data/lib/extlib/pathname.rb +0 -5
  290. data/lib/extlib/pooling.rb +0 -233
  291. data/lib/extlib/rubygems.rb +0 -38
  292. data/lib/extlib/simple_set.rb +0 -39
  293. data/lib/extlib/string.rb +0 -132
  294. data/lib/extlib/struct.rb +0 -8
  295. data/lib/extlib/tasks/release.rb +0 -9
  296. data/lib/extlib/time.rb +0 -12
  297. data/lib/extlib/version.rb +0 -3
  298. data/lib/extlib/virtual_file.rb +0 -10
@@ -0,0 +1,168 @@
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
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
+ if @original_session != new_session
23
+ options = {:expires => (Time.now + _session_expiry)}
24
+ options[:domain] = _session_cookie_domain if _session_cookie_domain
25
+ cookies.set_cookie(_session_id_key, new_session, options)
26
+ end
27
+ end
28
+
29
+ # ==== Returns
30
+ # String:: The session store type, i.e. "cookie".
31
+ def session_store_type
32
+ "cookie"
33
+ end
34
+ end
35
+
36
+ # If you have more than 4K of session data or don't want your data to be
37
+ # visible to the user, pick another session store.
38
+ #
39
+ # CookieOverflow is raised if you attempt to store more than 4K of data.
40
+ # TamperedWithCookie is raised if the data integrity check fails.
41
+ #
42
+ # A message digest is included with the cookie to ensure data integrity:
43
+ # a user cannot alter session data without knowing the secret key included
44
+ # in the hash.
45
+ #
46
+ # To use Cookie Sessions, set in config/merb.yml
47
+ # :session_secret_key - your secret digest key
48
+ # :session_store: cookie
49
+ class CookieSession
50
+ # TODO (maybe):
51
+ # include request ip address
52
+ # AES encrypt marshaled data
53
+
54
+ # Raised when storing more than 4K of session data.
55
+ class CookieOverflow < StandardError; end
56
+
57
+ # Raised when the cookie fails its integrity check.
58
+ class TamperedWithCookie < StandardError; end
59
+
60
+ # Cookies can typically store 4096 bytes.
61
+ MAX = 4096
62
+ DIGEST = OpenSSL::Digest::Digest.new('SHA1') # or MD5, RIPEMD160, SHA256?
63
+
64
+ attr_reader :data
65
+
66
+ # ==== Parameters
67
+ # cookie<String>:: The cookie.
68
+ # secret<String>:: A session secret.
69
+ #
70
+ # ==== Raises
71
+ # ArgumentError:: Nil or blank secret.
72
+ def initialize(cookie, secret)
73
+ if secret.nil? or secret.blank?
74
+ raise ArgumentError, 'A secret is required to generate an integrity hash for cookie session data.'
75
+ end
76
+ @secret = secret
77
+ @data = unmarshal(cookie) || Hash.new
78
+ end
79
+
80
+ # ==== Returns
81
+ # String:: Cookie value.
82
+ #
83
+ # ==== Raises
84
+ # CookieOverflow:: Session contains too much information.
85
+ def read_cookie
86
+ unless @data.nil?
87
+ updated = marshal(@data)
88
+ raise CookieOverflow if updated.size > MAX
89
+ updated
90
+ end
91
+ end
92
+
93
+ # ==== Parameters
94
+ # k<~to_s>:: The key of the session parameter to set.
95
+ # v<~to_s>:: The value of the session parameter to set.
96
+ def []=(k, v)
97
+ @data[k] = v
98
+ end
99
+
100
+ # ==== Parameters
101
+ # k<~to_s>:: The key of the session parameter to retrieve.
102
+ #
103
+ # ==== Returns
104
+ # String:: The value of the session parameter.
105
+ def [](k)
106
+ @data[k]
107
+ end
108
+
109
+ # Yields the session data to an each block.
110
+ #
111
+ # ==== Parameter
112
+ # &b:: The block to pass to each.
113
+ def each(&b)
114
+ @data.each(&b)
115
+ end
116
+
117
+ # Deletes the session by emptying stored data.
118
+ def delete
119
+ @data = {}
120
+ end
121
+
122
+ private
123
+
124
+ # Attempts to redirect any messages to the data object.
125
+ def method_missing(name, *args, &block)
126
+ @data.send(name, *args, &block)
127
+ end
128
+
129
+ # Generate the HMAC keyed message digest. Uses SHA1.
130
+ def generate_digest(data)
131
+ OpenSSL::HMAC.hexdigest(DIGEST, @secret, data)
132
+ end
133
+
134
+ # Marshal a session hash into safe cookie data. Include an integrity hash.
135
+ #
136
+ # ==== Parameters
137
+ # session<Hash>:: The session to store in the cookie.
138
+ #
139
+ # ==== Returns
140
+ # String:: The cookie to be stored.
141
+ def marshal(session)
142
+ data = Base64.encode64(Marshal.dump(session)).chop
143
+ Merb::Request.escape "#{data}--#{generate_digest(data)}"
144
+ end
145
+
146
+ # Unmarshal cookie data to a hash and verify its integrity.
147
+ #
148
+ # ==== Parameters
149
+ # cookie<~to_s>:: The cookie to unmarshal.
150
+ #
151
+ # ==== Raises
152
+ # TamperedWithCookie:: The digests don't match.
153
+ #
154
+ # ==== Returns
155
+ # Hash:: The stored session data.
156
+ def unmarshal(cookie)
157
+ if cookie
158
+ data, digest = cookie.split('--')
159
+ return {} if data.blank?
160
+ unless digest == generate_digest(data)
161
+ delete
162
+ raise TamperedWithCookie, "Maybe the site's session_secret_key has changed?"
163
+ end
164
+ Marshal.load(Base64.decode64(data))
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,184 @@
1
+ module Merb
2
+
3
+ module SessionMixin
4
+
5
+ # Adds a before and after dispatch hook for setting up the memcached
6
+ # session store.
7
+ #
8
+ # ==== Parameters
9
+ # base<Class>:: The class to which the SessionMixin is mixed into.
10
+ def setup_session
11
+ orig_key = cookies[_session_id_key]
12
+ session, key = Merb::MemCacheSession.persist(orig_key)
13
+ request.session = session
14
+ @_fingerprint = Marshal.dump(request.session.data).hash
15
+ if key != orig_key
16
+ set_session_id_cookie(key)
17
+ end
18
+ end
19
+
20
+ # Finalizes the session by storing the session ID in a cookie, if the
21
+ # session has changed.
22
+ def finalize_session
23
+ if @_fingerprint != Marshal.dump(request.session.data).hash
24
+ begin
25
+ CACHE.set("session:#{request.session.session_id}", request.session.data)
26
+ rescue => err
27
+ Merb.logger.debug("MemCache Error: #{err.message}")
28
+ Merb::SessionMixin::finalize_session_exception_callbacks.each {|x| x.call(err) }
29
+ end
30
+ end
31
+ if request.session.needs_new_cookie or @_new_cookie
32
+ set_session_id_cookie(request.session.session_id)
33
+ end
34
+ end
35
+
36
+ # ==== Returns
37
+ # String:: The session store type, i.e. "memcache".
38
+ def session_store_type
39
+ "memcache"
40
+ end
41
+ end
42
+
43
+ ##
44
+ # Sessions stored in memcached.
45
+ #
46
+ # Requires setup in your +init.rb+.
47
+ #
48
+ # require 'memcache'
49
+ # CACHE = MemCache.new('127.0.0.1:11211', { :namespace => 'my_app' })
50
+ #
51
+ # And a setting in +init.rb+:
52
+ #
53
+ # c[:session_store] = 'memcache'
54
+ #
55
+ # If you are using the memcached gem instead of memcache-client, you must setup like this:
56
+ #
57
+ # require 'memcached'
58
+ # CACHE = Memcached.new('127.0.0.1:11211', { :namespace => 'my_app' })
59
+ #
60
+ class MemCacheSession
61
+
62
+ attr_accessor :session_id
63
+ attr_accessor :data
64
+ attr_accessor :needs_new_cookie
65
+
66
+ # ==== Parameters
67
+ # session_id<String>:: A unique identifier for this session.
68
+ def initialize(session_id)
69
+ @session_id = session_id
70
+ @data = {}
71
+ end
72
+
73
+ class << self
74
+
75
+ # Generates a new session ID and creates a new session.
76
+ #
77
+ # ==== Returns
78
+ # MemCacheSession:: The new session.
79
+ def generate
80
+ sid = Merb::SessionMixin::rand_uuid
81
+ new(sid)
82
+ end
83
+
84
+ # ==== Parameters
85
+ # session_id<String:: The ID of the session to retrieve.
86
+ #
87
+ # ==== Returns
88
+ # Array::
89
+ # A pair consisting of a MemCacheSession and the session's ID. If no
90
+ # sessions matched session_id, a new MemCacheSession will be generated.
91
+ #
92
+ # ==== Notes
93
+ # If there are persiste exceptions callbacks to execute, they all get executed
94
+ # when Memcache library raises an exception.
95
+ def persist(session_id)
96
+ unless session_id.blank?
97
+ begin
98
+ session = CACHE.get("session:#{session_id}")
99
+ rescue => err
100
+ Merb.logger.warn!("Could not persist session to MemCache: #{err.message}")
101
+ Merb::SessionMixin::persist_exception_callbacks.each {|x| x.call(err) }
102
+ end
103
+ if session.nil?
104
+ # Not in memcached, but assume that cookie exists
105
+ session = new(session_id)
106
+ end
107
+ else
108
+ # No cookie...make a new session_id
109
+ session = generate
110
+ end
111
+ if session.is_a?(MemCacheSession)
112
+ [session, session.session_id]
113
+ else
114
+ # recreate using the rails session as the data
115
+ session_object = MemCacheSession.new(session_id)
116
+ session_object.data = session
117
+ [session_object, session_object.session_id]
118
+ end
119
+ end
120
+
121
+ # Don't try to reload in dev mode.
122
+ def reloadable?
123
+ false
124
+ end
125
+
126
+ end
127
+
128
+ # Regenerate the session ID.
129
+ def regenerate
130
+ @session_id = Merb::SessionMixin::rand_uuid
131
+ self.needs_new_cookie=true
132
+ end
133
+
134
+ # Recreates the cookie with the default expiration time. Useful during log
135
+ # in for pushing back the expiration date.
136
+ def refresh_expiration
137
+ self.needs_new_cookie=true
138
+ end
139
+
140
+ # Deletes the session by emptying stored data.
141
+ def delete
142
+ @data = {}
143
+ end
144
+
145
+ # ==== Returns
146
+ # Boolean:: True if session has been loaded already.
147
+ def loaded?
148
+ !! @data
149
+ end
150
+
151
+ # ==== Parameters
152
+ # k<~to_s>:: The key of the session parameter to set.
153
+ # v<~to_s>:: The value of the session parameter to set.
154
+ def []=(k, v)
155
+ @data[k] = v
156
+ end
157
+
158
+ # ==== Parameters
159
+ # k<~to_s>:: The key of the session parameter to retrieve.
160
+ #
161
+ # ==== Returns
162
+ # String:: The value of the session parameter.
163
+ def [](k)
164
+ @data[k]
165
+ end
166
+
167
+ # Yields the session data to an each block.
168
+ #
169
+ # ==== Parameter
170
+ # &b:: The block to pass to each.
171
+ def each(&b)
172
+ @data.each(&b)
173
+ end
174
+
175
+ private
176
+
177
+ # Attempts to redirect any messages to the data object.
178
+ def method_missing(name, *args, &block)
179
+ @data.send(name, *args, &block)
180
+ end
181
+
182
+ end
183
+
184
+ end
@@ -0,0 +1,241 @@
1
+ module Merb
2
+
3
+ module SessionMixin
4
+
5
+ # Adds a before and after dispatch hook for setting up the memory session
6
+ # store.
7
+ #
8
+ # ==== Parameters
9
+ # base<Class>:: The class to which the SessionMixin is mixed into.
10
+ def setup_session
11
+ orig_key = cookies[_session_id_key]
12
+ session, key = Merb::MemorySession.persist(orig_key)
13
+ request.session = session
14
+ if key != orig_key
15
+ set_session_id_cookie(key)
16
+ end
17
+ end
18
+
19
+ # Finalizes the session by storing the session ID in a cookie, if the
20
+ # session has changed.
21
+ def finalize_session
22
+ if request.session.needs_new_cookie or @_new_cookie
23
+ set_session_id_cookie(request.session.session_id)
24
+ end
25
+ end
26
+
27
+ # ==== Returns
28
+ # String:: The session store type, i.e. "memory".
29
+ def session_store_type
30
+ "memory"
31
+ end
32
+ end
33
+
34
+ ##
35
+ # Sessions stored in memory.
36
+ #
37
+ # Set it up by adding the following to your init file:
38
+ #
39
+ # Merb::Config.use do |c|
40
+ # c[:session_store] = :memory
41
+ # c[:memory_session_ttl] = 3600 # in seconds, one hour
42
+ # end
43
+ #
44
+ # Sessions will remain in memory until the server is stopped or the time
45
+ # as set in :memory_session_ttl expires.
46
+ class MemorySession
47
+
48
+ attr_accessor :session_id
49
+ attr_accessor :data
50
+ attr_accessor :needs_new_cookie
51
+
52
+ # ==== Parameters
53
+ # session_id<String>:: A unique identifier for this session.
54
+ def initialize(session_id)
55
+ @session_id = session_id
56
+ @data = {}
57
+ end
58
+
59
+ class << self
60
+
61
+ # Generates a new session ID and creates a new session.
62
+ #
63
+ # ==== Returns
64
+ # MemorySession:: The new session.
65
+ def generate
66
+ sid = Merb::SessionMixin::rand_uuid
67
+ MemorySessionContainer[sid] = new(sid)
68
+ end
69
+
70
+ # ==== Parameters
71
+ # session_id<String:: The ID of the session to retrieve.
72
+ #
73
+ # ==== Returns
74
+ # Array::
75
+ # A pair consisting of a MemorySession and the session's ID. If no
76
+ # sessions matched session_id, a new MemorySession will be generated.
77
+ def persist(session_id)
78
+ if session_id
79
+ session = MemorySessionContainer[session_id]
80
+ end
81
+ unless session
82
+ session = generate
83
+ end
84
+ [session, session.session_id]
85
+ end
86
+
87
+ end
88
+
89
+ # Regenerate the Session ID
90
+ def regenerate
91
+ new_sid = Merb::SessionMixin::rand_uuid
92
+ old_sid = @session_id
93
+ MemorySessionContainer[new_sid] = MemorySessionContainer[old_sid]
94
+ @session_id = new_sid
95
+ MemorySessionContainer.delete(old_sid)
96
+ self.needs_new_cookie=true
97
+ end
98
+
99
+ # Recreates the cookie with the default expiration time. Useful during log
100
+ # in for pushing back the expiration date.
101
+ def refresh_expiration
102
+ self.needs_new_cookie=true
103
+ end
104
+
105
+ # Deletes the session by emptying stored data.
106
+ def delete
107
+ @data = {}
108
+ end
109
+
110
+ # ==== Returns
111
+ # Boolean:: True if session has been loaded already.
112
+ def loaded?
113
+ !! @data
114
+ end
115
+
116
+ # ==== Parameters
117
+ # k<~to_s>:: The key of the session parameter to set.
118
+ # v<~to_s>:: The value of the session parameter to set.
119
+ def []=(k, v)
120
+ @data[k] = v
121
+ end
122
+
123
+ # ==== Parameters
124
+ # k<~to_s>:: The key of the session parameter to retrieve.
125
+ #
126
+ # ==== Returns
127
+ # String:: The value of the session parameter.
128
+ def [](k)
129
+ @data[k]
130
+ end
131
+
132
+ # Yields the session data to an each block.
133
+ #
134
+ # ==== Parameter
135
+ # &b:: The block to pass to each.
136
+ def each(&b)
137
+ @data.each(&b)
138
+ end
139
+
140
+ private
141
+
142
+ # Attempts to redirect any messages to the data object.
143
+ def method_missing(name, *args, &block)
144
+ @data.send(name, *args, &block)
145
+ end
146
+
147
+ end
148
+
149
+ # Used for handling multiple sessions stored in memory.
150
+ class MemorySessionContainer
151
+ class << self
152
+
153
+ # ==== Parameters
154
+ # ttl<Fixnum>:: Session validity time in seconds. Defaults to 1 hour.
155
+ #
156
+ # ==== Returns
157
+ # MemorySessionContainer:: The new session container.
158
+ def setup(ttl=nil)
159
+ @sessions = Hash.new
160
+ @timestamps = Hash.new
161
+ @mutex = Mutex.new
162
+ @session_ttl = ttl || 60*60 # default 1 hour
163
+ start_timer
164
+ self
165
+ end
166
+
167
+ # Creates a new session based on the options.
168
+ #
169
+ # ==== Parameters
170
+ # opts<Hash>:: The session options (see below).
171
+ #
172
+ # ==== Options (opts)
173
+ # :session_id<String>:: ID of the session to create in the container.
174
+ # :data<MemorySession>:: The session to create in the container.
175
+ def create(opts={})
176
+ self[opts[:session_id]] = opts[:data]
177
+ end
178
+
179
+ # ==== Parameters
180
+ # key<String>:: ID of the session to retrieve.
181
+ #
182
+ # ==== Returns
183
+ # MemorySession:: The session corresponding to the ID.
184
+ def [](key)
185
+ @mutex.synchronize {
186
+ @timestamps[key] = Time.now
187
+ @sessions[key]
188
+ }
189
+ end
190
+
191
+ # ==== Parameters
192
+ # key<String>:: ID of the session to set.
193
+ # val<MemorySession>:: The session to set.
194
+ def []=(key, val)
195
+ @mutex.synchronize {
196
+ @timestamps[key] = Time.now
197
+ @sessions[key] = val
198
+ }
199
+ end
200
+
201
+ # ==== Parameters
202
+ # key<String>:: ID of the session to delete.
203
+ def delete(key)
204
+ @mutex.synchronize {
205
+ @sessions.delete(key)
206
+ @timestamps.delete(key)
207
+ }
208
+ end
209
+
210
+ # Deletes any sessions that have reached their maximum validity.
211
+ def reap_old_sessions
212
+ @timestamps.each do |key,stamp|
213
+ if stamp + @session_ttl < Time.now
214
+ delete(key)
215
+ end
216
+ end
217
+ GC.start
218
+ end
219
+
220
+ # Starts the timer that will eventually reap outdated sessions.
221
+ def start_timer
222
+ Thread.new do
223
+ loop {
224
+ sleep @session_ttl
225
+ reap_old_sessions
226
+ }
227
+ end
228
+ end
229
+
230
+ # ==== Returns
231
+ # Array:: The sessions stored in this container.
232
+ def sessions
233
+ @sessions
234
+ end
235
+
236
+ end # end singleton class
237
+
238
+ end # end MemorySessionContainer
239
+ end
240
+
241
+ Merb::MemorySessionContainer.setup(Merb::Config[:memory_session_ttl])