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.
- data/LICENSE +1 -1
- data/README +21 -0
- data/Rakefile +275 -108
- data/TODO +0 -0
- data/bin/merb +12 -0
- data/bin/merb-specs +5 -0
- data/docs/bootloading.dox +58 -0
- data/docs/documentation_standards +40 -0
- data/docs/merb-core-call-stack-diagram.mmap +0 -0
- data/docs/merb-core-call-stack-diagram.pdf +0 -0
- data/docs/merb-core-call-stack-diagram.png +0 -0
- data/docs/new_render_api +51 -0
- data/lib/merb-core.rb +603 -0
- data/lib/merb-core/autoload.rb +32 -0
- data/lib/merb-core/bootloader.rb +708 -0
- data/lib/merb-core/config.rb +303 -0
- data/lib/merb-core/constants.rb +43 -0
- data/lib/merb-core/controller/abstract_controller.rb +578 -0
- data/lib/merb-core/controller/exceptions.rb +302 -0
- data/lib/merb-core/controller/merb_controller.rb +256 -0
- data/lib/merb-core/controller/mime.rb +106 -0
- data/lib/merb-core/controller/mixins/authentication.rb +87 -0
- data/lib/merb-core/controller/mixins/controller.rb +290 -0
- data/lib/merb-core/controller/mixins/render.rb +481 -0
- data/lib/merb-core/controller/mixins/responder.rb +472 -0
- data/lib/merb-core/controller/template.rb +254 -0
- data/lib/merb-core/core_ext.rb +8 -0
- data/lib/merb-core/core_ext/kernel.rb +319 -0
- data/lib/merb-core/dispatch/cookies.rb +91 -0
- data/lib/merb-core/dispatch/dispatcher.rb +278 -0
- data/lib/merb-core/dispatch/exceptions.html.erb +303 -0
- data/lib/merb-core/dispatch/request.rb +603 -0
- data/lib/merb-core/dispatch/router.rb +179 -0
- data/lib/merb-core/dispatch/router/behavior.rb +867 -0
- data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
- data/lib/merb-core/dispatch/router/route.rb +321 -0
- data/lib/merb-core/dispatch/session.rb +78 -0
- data/lib/merb-core/dispatch/session/cookie.rb +168 -0
- data/lib/merb-core/dispatch/session/memcached.rb +184 -0
- data/lib/merb-core/dispatch/session/memory.rb +241 -0
- data/lib/merb-core/dispatch/worker.rb +28 -0
- data/lib/merb-core/gem_ext/erubis.rb +77 -0
- data/lib/{extlib → merb-core}/logger.rb +2 -2
- data/lib/merb-core/plugins.rb +59 -0
- data/lib/merb-core/rack.rb +21 -0
- data/lib/merb-core/rack/adapter.rb +44 -0
- data/lib/merb-core/rack/adapter/ebb.rb +25 -0
- data/lib/merb-core/rack/adapter/evented_mongrel.rb +26 -0
- data/lib/merb-core/rack/adapter/fcgi.rb +17 -0
- data/lib/merb-core/rack/adapter/irb.rb +118 -0
- data/lib/merb-core/rack/adapter/mongrel.rb +26 -0
- data/lib/merb-core/rack/adapter/runner.rb +28 -0
- data/lib/merb-core/rack/adapter/swiftiplied_mongrel.rb +26 -0
- data/lib/merb-core/rack/adapter/thin.rb +39 -0
- data/lib/merb-core/rack/adapter/thin_turbo.rb +24 -0
- data/lib/merb-core/rack/adapter/webrick.rb +36 -0
- data/lib/merb-core/rack/application.rb +18 -0
- data/lib/merb-core/rack/handler/mongrel.rb +97 -0
- data/lib/merb-core/rack/middleware.rb +26 -0
- data/lib/merb-core/rack/middleware/path_prefix.rb +31 -0
- data/lib/merb-core/rack/middleware/profiler.rb +19 -0
- data/lib/merb-core/rack/middleware/static.rb +45 -0
- data/lib/merb-core/server.rb +252 -0
- data/lib/merb-core/tasks/audit.rake +68 -0
- data/lib/merb-core/tasks/merb.rb +1 -0
- data/lib/merb-core/tasks/merb_rake_helper.rb +12 -0
- data/lib/merb-core/test.rb +11 -0
- data/lib/merb-core/test/helpers.rb +9 -0
- data/lib/merb-core/test/helpers/controller_helper.rb +8 -0
- data/lib/merb-core/test/helpers/multipart_request_helper.rb +175 -0
- data/lib/merb-core/test/helpers/request_helper.rb +344 -0
- data/lib/merb-core/test/helpers/route_helper.rb +33 -0
- data/lib/merb-core/test/helpers/view_helper.rb +121 -0
- data/lib/merb-core/test/matchers.rb +9 -0
- data/lib/merb-core/test/matchers/controller_matchers.rb +319 -0
- data/lib/merb-core/test/matchers/route_matchers.rb +136 -0
- data/lib/merb-core/test/matchers/view_matchers.rb +335 -0
- data/lib/merb-core/test/run_specs.rb +47 -0
- data/lib/merb-core/test/tasks/spectasks.rb +68 -0
- data/lib/merb-core/test/test_ext/hpricot.rb +32 -0
- data/lib/merb-core/test/test_ext/object.rb +14 -0
- data/lib/merb-core/test/test_ext/string.rb +14 -0
- data/lib/merb-core/vendor/facets.rb +2 -0
- data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
- data/lib/merb-core/vendor/facets/inflect.rb +345 -0
- data/lib/merb-core/version.rb +11 -0
- data/spec/private/config/adapter_spec.rb +32 -0
- data/spec/private/config/config_spec.rb +202 -0
- data/spec/private/config/environment_spec.rb +13 -0
- data/spec/private/config/spec_helper.rb +1 -0
- data/spec/private/core_ext/kernel_spec.rb +169 -0
- data/spec/private/dispatch/bootloader_spec.rb +24 -0
- data/spec/private/dispatch/cookies_spec.rb +107 -0
- data/spec/private/dispatch/dispatch_spec.rb +35 -0
- data/spec/private/dispatch/fixture/app/controllers/application.rb +4 -0
- data/spec/private/dispatch/fixture/app/controllers/exceptions.rb +27 -0
- data/spec/private/dispatch/fixture/app/controllers/foo.rb +21 -0
- data/spec/private/dispatch/fixture/app/helpers/global_helpers.rb +8 -0
- data/spec/private/dispatch/fixture/app/views/exeptions/client_error.html.erb +37 -0
- data/spec/private/dispatch/fixture/app/views/exeptions/internal_server_error.html.erb +216 -0
- data/spec/private/dispatch/fixture/app/views/exeptions/not_acceptable.html.erb +38 -0
- data/spec/private/dispatch/fixture/app/views/exeptions/not_found.html.erb +40 -0
- data/spec/private/dispatch/fixture/app/views/foo/bar.html.erb +0 -0
- data/spec/private/dispatch/fixture/app/views/layout/application.html.erb +11 -0
- data/spec/private/dispatch/fixture/config/black_hole.rb +12 -0
- data/spec/private/dispatch/fixture/config/environments/development.rb +6 -0
- data/spec/private/dispatch/fixture/config/environments/production.rb +5 -0
- data/spec/private/dispatch/fixture/config/environments/test.rb +6 -0
- data/spec/private/dispatch/fixture/config/init.rb +45 -0
- data/spec/private/dispatch/fixture/config/rack.rb +11 -0
- data/spec/private/dispatch/fixture/config/router.rb +35 -0
- data/spec/private/dispatch/fixture/log/merb_test.log +1874 -0
- data/spec/private/dispatch/fixture/public/images/merb.jpg +0 -0
- data/spec/private/dispatch/fixture/public/merb.fcgi +4 -0
- data/spec/private/dispatch/fixture/public/stylesheets/master.css +119 -0
- data/spec/private/dispatch/route_params_spec.rb +24 -0
- data/spec/private/dispatch/session_mixin_spec.rb +47 -0
- data/spec/private/dispatch/spec_helper.rb +1 -0
- data/spec/private/plugins/plugin_spec.rb +166 -0
- data/spec/private/rack/application_spec.rb +49 -0
- data/spec/private/router/behavior_spec.rb +60 -0
- data/spec/private/router/fixture/log/merb_test.log +139 -0
- data/spec/private/router/route_spec.rb +414 -0
- data/spec/private/router/router_spec.rb +175 -0
- data/spec/private/vendor/facets/plural_spec.rb +564 -0
- data/spec/private/vendor/facets/singular_spec.rb +489 -0
- data/spec/public/DEFINITIONS +11 -0
- data/spec/public/abstract_controller/controllers/alt_views/layout/application.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/layout/merb/test/fixtures/abstract/render_string_controller_layout.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/layout/merb/test/fixtures/abstract/render_template_controller_layout.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/display_object_with_multiple_roots/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/display_object_with_multiple_roots/show.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/merb/test/fixtures/abstract/render_template_multiple_roots/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/partial/basic_partial_with_multiple_roots/_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/render_template_multiple_roots_and_custom_location/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/alt_views/render_template_multiple_roots_inherited/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/cousins.rb +41 -0
- data/spec/public/abstract_controller/controllers/display.rb +54 -0
- data/spec/public/abstract_controller/controllers/filters.rb +193 -0
- data/spec/public/abstract_controller/controllers/helpers.rb +41 -0
- data/spec/public/abstract_controller/controllers/partial.rb +121 -0
- data/spec/public/abstract_controller/controllers/render.rb +113 -0
- data/spec/public/abstract_controller/controllers/views/helpers/capture/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/helpers/capture_eq/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/helpers/capture_with_args/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/helpers/concat/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/layout/alt.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/layout/custom.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/display_object/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/display_object_with_action/new.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_app_layout/index.erb +0 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_custom_layout/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_multiple_roots/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_template_multiple_roots/show.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/merb/test/fixtures/abstract/render_two_throw_contents/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/another_directory/_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/basic_partial/_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/basic_partial/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/basic_partial_with_multiple_roots/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/nested_partial/_first.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/nested_partial/_second.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/nested_partial/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_in_another_directory/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_both/_collection.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_both/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections/_collection.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_as/_collection.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_as/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_counter/_collection.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_collections_and_counter/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_locals/_variables.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_locals/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_with_and_locals/_both.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/partial_with_with_and_locals/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_absolute_partial/_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_absolute_partial/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_as_partial/_with_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_as_partial/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_nil_partial/_with_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_nil_partial/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_partial/_with_partial.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/partial/with_partial/index.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/test_display/foo.html.erb +1 -0
- data/spec/public/abstract_controller/controllers/views/test_render/foo.html.erb +0 -0
- data/spec/public/abstract_controller/controllers/views/wonderful/index.erb +1 -0
- data/spec/public/abstract_controller/display_spec.rb +33 -0
- data/spec/public/abstract_controller/filter_spec.rb +106 -0
- data/spec/public/abstract_controller/helper_spec.rb +21 -0
- data/spec/public/abstract_controller/partial_spec.rb +61 -0
- data/spec/public/abstract_controller/render_spec.rb +90 -0
- data/spec/public/abstract_controller/spec_helper.rb +31 -0
- data/spec/public/boot_loader/boot_loader_spec.rb +33 -0
- data/spec/public/boot_loader/spec_helper.rb +1 -0
- data/spec/public/controller/authentication_spec.rb +103 -0
- data/spec/public/controller/base_spec.rb +36 -0
- data/spec/public/controller/controllers/authentication.rb +45 -0
- data/spec/public/controller/controllers/base.rb +36 -0
- data/spec/public/controller/controllers/display.rb +118 -0
- data/spec/public/controller/controllers/redirect.rb +30 -0
- data/spec/public/controller/controllers/responder.rb +93 -0
- data/spec/public/controller/controllers/url.rb +7 -0
- data/spec/public/controller/controllers/views/layout/custom.html.erb +1 -0
- data/spec/public/controller/controllers/views/layout/custom_arg.html.erb +1 -0
- data/spec/public/controller/controllers/views/layout/custom_arg.json.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_and_local_provides/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_and_local_provides/index.xml.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/class_provides/index.xml.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/display_with_template/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/display_with_template/no_layout.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/display_with_template_argument/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/html_default/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/layout/custom.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/local_provides/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/local_provides/index.xml.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/multi_provides/index.html.erb +1 -0
- data/spec/public/controller/controllers/views/merb/test/fixtures/controllers/multi_provides/index.js.erb +1 -0
- data/spec/public/controller/display_spec.rb +84 -0
- data/spec/public/controller/redirect_spec.rb +27 -0
- data/spec/public/controller/responder_spec.rb +163 -0
- data/spec/public/controller/spec_helper.rb +11 -0
- data/spec/public/controller/url_spec.rb +180 -0
- data/spec/public/core/merb_core_spec.rb +45 -0
- data/spec/public/core_ext/class_spec.rb +91 -0
- data/spec/public/core_ext/fixtures/core_ext_dependency.rb +2 -0
- data/spec/public/core_ext/kernel_spec.rb +9 -0
- data/spec/public/core_ext/spec_helper.rb +1 -0
- data/spec/public/directory_structure/directory/app/controllers/application.rb +3 -0
- data/spec/public/directory_structure/directory/app/controllers/base.rb +13 -0
- data/spec/public/directory_structure/directory/app/controllers/custom.rb +19 -0
- data/spec/public/directory_structure/directory/app/views/base/template.html.erb +1 -0
- data/spec/public/directory_structure/directory/app/views/wonderful/template.erb +1 -0
- data/spec/public/directory_structure/directory/config/router.rb +3 -0
- data/spec/public/directory_structure/directory/log/merb_test.log +562 -0
- data/spec/public/directory_structure/directory_spec.rb +44 -0
- data/spec/public/logger/logger_spec.rb +181 -0
- data/spec/public/logger/spec_helper.rb +1 -0
- data/spec/public/reloading/directory/app/controllers/application.rb +3 -0
- data/spec/public/reloading/directory/app/controllers/reload.rb +6 -0
- data/spec/public/reloading/directory/config/init.rb +2 -0
- data/spec/public/reloading/directory/log/merb_test.log +138 -0
- data/spec/public/reloading/reload_spec.rb +103 -0
- data/spec/public/request/multipart_spec.rb +41 -0
- data/spec/public/request/request_spec.rb +228 -0
- data/spec/public/router/default_spec.rb +21 -0
- data/spec/public/router/deferred_spec.rb +22 -0
- data/spec/public/router/fixation_spec.rb +27 -0
- data/spec/public/router/fixture/log/merb_test.log +1556 -0
- data/spec/public/router/namespace_spec.rb +113 -0
- data/spec/public/router/nested_matches_spec.rb +97 -0
- data/spec/public/router/nested_resources_spec.rb +41 -0
- data/spec/public/router/resource_spec.rb +37 -0
- data/spec/public/router/resources_spec.rb +82 -0
- data/spec/public/router/spec_helper.rb +90 -0
- data/spec/public/router/special_spec.rb +61 -0
- data/spec/public/router/string_spec.rb +61 -0
- data/spec/public/template/template_spec.rb +104 -0
- data/spec/public/template/templates/error.html.erb +2 -0
- data/spec/public/template/templates/template.html.erb +1 -0
- data/spec/public/template/templates/template.html.myt +1 -0
- data/spec/public/test/controller_matchers_spec.rb +402 -0
- data/spec/public/test/controllers/controller_assertion_mock.rb +7 -0
- data/spec/public/test/controllers/dispatch_controller.rb +11 -0
- data/spec/public/test/controllers/spec_helper_controller.rb +38 -0
- data/spec/public/test/multipart_request_helper_spec.rb +159 -0
- data/spec/public/test/multipart_upload_text_file.txt +1 -0
- data/spec/public/test/request_helper_spec.rb +221 -0
- data/spec/public/test/route_helper_spec.rb +71 -0
- data/spec/public/test/route_matchers_spec.rb +162 -0
- data/spec/public/test/view_helper_spec.rb +96 -0
- data/spec/public/test/view_matchers_spec.rb +183 -0
- data/spec/spec_helper.rb +68 -0
- metadata +493 -41
- data/README.txt +0 -3
- data/lib/extlib.rb +0 -32
- data/lib/extlib/assertions.rb +0 -8
- data/lib/extlib/blank.rb +0 -42
- data/lib/extlib/class.rb +0 -175
- data/lib/extlib/hash.rb +0 -410
- data/lib/extlib/hook.rb +0 -366
- data/lib/extlib/inflection.rb +0 -141
- data/lib/extlib/lazy_array.rb +0 -106
- data/lib/extlib/mash.rb +0 -143
- data/lib/extlib/module.rb +0 -37
- data/lib/extlib/object.rb +0 -165
- data/lib/extlib/object_space.rb +0 -13
- data/lib/extlib/pathname.rb +0 -5
- data/lib/extlib/pooling.rb +0 -233
- data/lib/extlib/rubygems.rb +0 -38
- data/lib/extlib/simple_set.rb +0 -39
- data/lib/extlib/string.rb +0 -132
- data/lib/extlib/struct.rb +0 -8
- data/lib/extlib/tasks/release.rb +0 -9
- data/lib/extlib/time.rb +0 -12
- data/lib/extlib/version.rb +0 -3
- 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])
|