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.
- data/LICENSE +20 -0
- data/README +21 -0
- data/Rakefile +285 -0
- data/TODO +0 -0
- data/bin/merb +8 -0
- data/bin/merb-specs +5 -0
- data/docs/bootloading.dox +57 -0
- data/docs/documentation_standards +40 -0
- data/docs/new_render_api +51 -0
- data/lib/merb-core.rb +304 -0
- data/lib/merb-core/autoload.rb +29 -0
- data/lib/merb-core/bootloader.rb +601 -0
- data/lib/merb-core/config.rb +284 -0
- data/lib/merb-core/constants.rb +43 -0
- data/lib/merb-core/controller/abstract_controller.rb +531 -0
- data/lib/merb-core/controller/exceptions.rb +257 -0
- data/lib/merb-core/controller/merb_controller.rb +214 -0
- data/lib/merb-core/controller/mime.rb +88 -0
- data/lib/merb-core/controller/mixins/controller.rb +262 -0
- data/lib/merb-core/controller/mixins/render.rb +324 -0
- data/lib/merb-core/controller/mixins/responder.rb +464 -0
- data/lib/merb-core/controller/template.rb +205 -0
- data/lib/merb-core/core_ext.rb +12 -0
- data/lib/merb-core/core_ext/class.rb +192 -0
- data/lib/merb-core/core_ext/hash.rb +422 -0
- data/lib/merb-core/core_ext/kernel.rb +304 -0
- data/lib/merb-core/core_ext/mash.rb +154 -0
- data/lib/merb-core/core_ext/object.rb +136 -0
- data/lib/merb-core/core_ext/object_space.rb +14 -0
- data/lib/merb-core/core_ext/rubygems.rb +28 -0
- data/lib/merb-core/core_ext/set.rb +41 -0
- data/lib/merb-core/core_ext/string.rb +69 -0
- data/lib/merb-core/dispatch/cookies.rb +92 -0
- data/lib/merb-core/dispatch/dispatcher.rb +233 -0
- data/lib/merb-core/dispatch/exceptions.html.erb +297 -0
- data/lib/merb-core/dispatch/request.rb +560 -0
- data/lib/merb-core/dispatch/router.rb +141 -0
- data/lib/merb-core/dispatch/router/behavior.rb +777 -0
- data/lib/merb-core/dispatch/router/cached_proc.rb +52 -0
- data/lib/merb-core/dispatch/router/route.rb +212 -0
- data/lib/merb-core/dispatch/session.rb +28 -0
- data/lib/merb-core/dispatch/session/cookie.rb +166 -0
- data/lib/merb-core/dispatch/session/memcached.rb +161 -0
- data/lib/merb-core/dispatch/session/memory.rb +234 -0
- data/lib/merb-core/gem_ext/erubis.rb +19 -0
- data/lib/merb-core/logger.rb +230 -0
- data/lib/merb-core/plugins.rb +25 -0
- data/lib/merb-core/rack.rb +15 -0
- data/lib/merb-core/rack/adapter.rb +42 -0
- data/lib/merb-core/rack/adapter/ebb.rb +22 -0
- data/lib/merb-core/rack/adapter/evented_mongrel.rb +24 -0
- data/lib/merb-core/rack/adapter/fcgi.rb +16 -0
- data/lib/merb-core/rack/adapter/irb.rb +108 -0
- data/lib/merb-core/rack/adapter/mongrel.rb +25 -0
- data/lib/merb-core/rack/adapter/runner.rb +27 -0
- data/lib/merb-core/rack/adapter/thin.rb +27 -0
- data/lib/merb-core/rack/adapter/webrick.rb +35 -0
- data/lib/merb-core/rack/application.rb +77 -0
- data/lib/merb-core/rack/handler/mongrel.rb +97 -0
- data/lib/merb-core/server.rb +184 -0
- data/lib/merb-core/test.rb +10 -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 +257 -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 +269 -0
- data/lib/merb-core/test/matchers/route_matchers.rb +136 -0
- data/lib/merb-core/test/matchers/view_matchers.rb +293 -0
- data/lib/merb-core/test/run_specs.rb +38 -0
- data/lib/merb-core/test/tasks/spectasks.rb +39 -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/vendor/facets.rb +2 -0
- data/lib/merb-core/vendor/facets/dictionary.rb +433 -0
- data/lib/merb-core/vendor/facets/inflect.rb +211 -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 +139 -0
- data/spec/private/config/environment_spec.rb +13 -0
- data/spec/private/config/spec_helper.rb +1 -0
- data/spec/private/core_ext/hash_spec.rb +506 -0
- data/spec/private/core_ext/kernel_spec.rb +46 -0
- data/spec/private/core_ext/object_spec.rb +39 -0
- data/spec/private/core_ext/set_spec.rb +26 -0
- data/spec/private/core_ext/string_spec.rb +9 -0
- data/spec/private/dispatch/cookies_spec.rb +107 -0
- data/spec/private/dispatch/dispatch_spec.rb +26 -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/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 +1 -0
- data/spec/private/dispatch/fixture/config/router.rb +35 -0
- data/spec/private/dispatch/fixture/log/development.log +1 -0
- data/spec/private/dispatch/fixture/log/merb.4000.pid +1 -0
- data/spec/private/dispatch/fixture/log/merb_test.log +2040 -0
- data/spec/private/dispatch/fixture/log/production.log +1 -0
- data/spec/private/dispatch/fixture/merb.4000.pid +1 -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/spec_helper.rb +1 -0
- data/spec/private/plugins/plugin_spec.rb +81 -0
- data/spec/private/rack/application_spec.rb +43 -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/display.rb +54 -0
- data/spec/public/abstract_controller/controllers/filters.rb +167 -0
- data/spec/public/abstract_controller/controllers/helpers.rb +31 -0
- data/spec/public/abstract_controller/controllers/partial.rb +106 -0
- data/spec/public/abstract_controller/controllers/render.rb +86 -0
- data/spec/public/abstract_controller/controllers/views/helpers/capture/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/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_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_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 +80 -0
- data/spec/public/abstract_controller/helper_spec.rb +13 -0
- data/spec/public/abstract_controller/partial_spec.rb +53 -0
- data/spec/public/abstract_controller/render_spec.rb +70 -0
- data/spec/public/abstract_controller/spec_helper.rb +27 -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/base_spec.rb +31 -0
- data/spec/public/controller/controllers/base.rb +41 -0
- data/spec/public/controller/controllers/display.rb +40 -0
- data/spec/public/controller/controllers/responder.rb +67 -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/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/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 +34 -0
- data/spec/public/controller/log/merb.4000.pid +1 -0
- data/spec/public/controller/responder_spec.rb +95 -0
- data/spec/public/controller/spec_helper.rb +9 -0
- data/spec/public/controller/url_spec.rb +152 -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.4000.pid +1 -0
- data/spec/public/directory_structure/directory/log/merb_test.log +265 -0
- data/spec/public/directory_structure/directory/merb.4000.pid +1 -0
- data/spec/public/directory_structure/directory_spec.rb +44 -0
- data/spec/public/logger/logger_spec.rb +175 -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.4000.pid +1 -0
- data/spec/public/reloading/directory/log/merb_test.log +59 -0
- data/spec/public/reloading/directory/merb.4000.pid +1 -0
- data/spec/public/reloading/reload_spec.rb +80 -0
- data/spec/public/request/multipart_spec.rb +15 -0
- data/spec/public/request/request_spec.rb +207 -0
- data/spec/public/router/default_spec.rb +21 -0
- data/spec/public/router/deferred_spec.rb +22 -0
- data/spec/public/router/namespace_spec.rb +113 -0
- data/spec/public/router/nested_resources_spec.rb +34 -0
- data/spec/public/router/resource_spec.rb +45 -0
- data/spec/public/router/resources_spec.rb +57 -0
- data/spec/public/router/spec_helper.rb +72 -0
- data/spec/public/router/special_spec.rb +44 -0
- data/spec/public/router/string_spec.rb +61 -0
- data/spec/public/template/template_spec.rb +92 -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 +378 -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 +30 -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 +153 -0
- data/spec/public/test/route_helper_spec.rb +54 -0
- data/spec/public/test/route_matchers_spec.rb +133 -0
- data/spec/public/test/view_helper_spec.rb +96 -0
- data/spec/public/test/view_matchers_spec.rb +107 -0
- data/spec/spec_helper.rb +71 -0
- 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
|