pakyow-core 0.10.2 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/pakyow-core/CHANGELOG.md +23 -0
- data/pakyow-core/lib/pakyow/core/app.rb +81 -0
- data/pakyow-core/lib/{core → pakyow/core}/app_context.rb +0 -0
- data/pakyow-core/lib/pakyow/core/base.rb +61 -0
- data/pakyow-core/lib/pakyow/core/call_context.rb +171 -0
- data/pakyow-core/lib/{core → pakyow/core}/config/app.rb +10 -30
- data/pakyow-core/lib/pakyow/core/config/cookies.rb +4 -0
- data/pakyow-core/lib/{core → pakyow/core}/config/logger.rb +11 -16
- data/pakyow-core/lib/pakyow/core/config/reloader.rb +10 -0
- data/pakyow-core/lib/{core → pakyow/core}/config/server.rb +2 -4
- data/pakyow-core/lib/pakyow/core/config/session.rb +41 -0
- data/pakyow-core/lib/{core → pakyow/core}/config.rb +2 -0
- data/pakyow-core/lib/{core → pakyow/core}/errors.rb +0 -0
- data/pakyow-core/lib/pakyow/core/helpers/configuring.rb +142 -0
- data/pakyow-core/lib/pakyow/core/helpers/hooks.rb +106 -0
- data/pakyow-core/lib/pakyow/core/helpers/running.rb +124 -0
- data/pakyow-core/lib/{core → pakyow/core}/helpers.rb +21 -5
- data/pakyow-core/lib/{core → pakyow/core}/loader.rb +1 -1
- data/pakyow-core/lib/{core → pakyow/core}/middleware/logger.rb +8 -2
- data/pakyow-core/lib/pakyow/core/middleware/override.rb +3 -0
- data/pakyow-core/lib/pakyow/core/middleware/reloader.rb +23 -0
- data/pakyow-core/lib/pakyow/core/middleware/req_path_normalizer.rb +49 -0
- data/pakyow-core/lib/pakyow/core/middleware/session.rb +5 -0
- data/pakyow-core/lib/pakyow/core/middleware/static.rb +76 -0
- data/pakyow-core/lib/{core → pakyow/core}/multilog.rb +0 -0
- data/pakyow-core/lib/{core → pakyow/core}/request.rb +7 -3
- data/pakyow-core/lib/{core → pakyow/core}/response.rb +4 -2
- data/pakyow-core/lib/{core → pakyow/core}/route_eval.rb +0 -0
- data/pakyow-core/lib/{core → pakyow/core}/route_expansion_eval.rb +1 -1
- data/pakyow-core/lib/{core → pakyow/core}/route_lookup.rb +0 -0
- data/pakyow-core/lib/{core → pakyow/core}/route_merger.rb +0 -0
- data/pakyow-core/lib/{core → pakyow/core}/route_module.rb +0 -0
- data/pakyow-core/lib/{core → pakyow/core}/route_set.rb +2 -2
- data/pakyow-core/lib/{core → pakyow/core}/route_template_defaults.rb +0 -0
- data/pakyow-core/lib/{core → pakyow/core}/route_template_eval.rb +0 -0
- data/pakyow-core/lib/{core → pakyow/core}/router.rb +4 -0
- data/pakyow-core/lib/pakyow/core.rb +8 -0
- data/pakyow-core/lib/pakyow-core.rb +1 -11
- metadata +41 -34
- data/pakyow-core/lib/core/app.rb +0 -469
- data/pakyow-core/lib/core/base.rb +0 -56
- data/pakyow-core/lib/core/config/cookies.rb +0 -4
- data/pakyow-core/lib/core/middleware/reloader.rb +0 -14
- data/pakyow-core/lib/core/middleware/static.rb +0 -40
- data/pakyow-core/lib/views/errors/404.html +0 -13
- data/pakyow-core/lib/views/errors/500.html +0 -15
@@ -0,0 +1,106 @@
|
|
1
|
+
module Pakyow
|
2
|
+
module Helpers
|
3
|
+
# Methods to register and call hooks before and after particular triggers.
|
4
|
+
#
|
5
|
+
# @api public
|
6
|
+
module Hooks
|
7
|
+
TYPES = %i(before after)
|
8
|
+
TRIGGERS = %i(init load process route match error configure reload)
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
protected
|
12
|
+
|
13
|
+
def hook_around(trigger)
|
14
|
+
call_hooks :before, trigger
|
15
|
+
yield
|
16
|
+
call_hooks :after, trigger
|
17
|
+
end
|
18
|
+
|
19
|
+
def call_hooks(type, trigger)
|
20
|
+
self.class.hook(type, trigger).each do |block|
|
21
|
+
instance_exec(&block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.extended(object)
|
27
|
+
object.send(:include, InstanceMethods)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Registers a before hook for a particular trigger.
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def before(trigger, &block)
|
34
|
+
register_hook(:before, trigger, block)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Registers an after hook for a particular trigger.
|
38
|
+
#
|
39
|
+
# @api public
|
40
|
+
def after(trigger, &block)
|
41
|
+
register_hook(:after, trigger, block)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Fetches a hook by type (before | after) and trigger.
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
def hook(type, trigger)
|
48
|
+
check_hook_type(type)
|
49
|
+
check_trigger(trigger)
|
50
|
+
|
51
|
+
hooks[type.to_sym][trigger.to_sym]
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def hooks
|
57
|
+
return @hooks unless @hooks.nil?
|
58
|
+
|
59
|
+
@hooks = {
|
60
|
+
before: {},
|
61
|
+
after: {}
|
62
|
+
}
|
63
|
+
|
64
|
+
TRIGGERS.each do |name|
|
65
|
+
@hooks[:before][name.to_sym] = []
|
66
|
+
@hooks[:after][name.to_sym] = []
|
67
|
+
end
|
68
|
+
|
69
|
+
@hooks
|
70
|
+
end
|
71
|
+
|
72
|
+
def register_hook(type, trigger, block)
|
73
|
+
raise ArgumentError, 'Expected a block' if block.nil?
|
74
|
+
|
75
|
+
trigger = trigger.to_sym
|
76
|
+
|
77
|
+
check_trigger(trigger)
|
78
|
+
check_hook_type(type)
|
79
|
+
|
80
|
+
hooks[type][trigger] << block
|
81
|
+
end
|
82
|
+
|
83
|
+
def hook_around(trigger)
|
84
|
+
call_hooks :before, trigger
|
85
|
+
yield
|
86
|
+
call_hooks :after, trigger
|
87
|
+
end
|
88
|
+
|
89
|
+
def call_hooks(type, trigger)
|
90
|
+
hook(type, trigger).each do |block|
|
91
|
+
instance_exec(&block)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def check_trigger(trigger)
|
96
|
+
return true if TRIGGERS.include?(trigger)
|
97
|
+
raise ArgumentError, "Hook trigger #{trigger} doesn't exist"
|
98
|
+
end
|
99
|
+
|
100
|
+
def check_hook_type(type)
|
101
|
+
return true if TYPES.include?(type)
|
102
|
+
raise ArgumentError, "Hook type #{type} doesn't exist"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'rack/builder'
|
2
|
+
require 'rack/handler'
|
3
|
+
|
4
|
+
module Pakyow
|
5
|
+
module Helpers
|
6
|
+
# Methods run running an app.
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
module Running
|
10
|
+
STOP_METHODS = ['stop!', 'stop']
|
11
|
+
HANDLERS = ['puma', 'thin', 'mongrel', 'webrick']
|
12
|
+
SIGNALS = [:INT, :TERM]
|
13
|
+
|
14
|
+
# Prepares the app for being staged in one or more environments by
|
15
|
+
# loading config(s), middleware, and setting the load path.
|
16
|
+
#
|
17
|
+
# @api public
|
18
|
+
def prepare(*env_or_envs)
|
19
|
+
return true if prepared?
|
20
|
+
|
21
|
+
# load config for one or more environments
|
22
|
+
load_env_config(*env_or_envs)
|
23
|
+
|
24
|
+
# load each block from middleware stack
|
25
|
+
load_middleware
|
26
|
+
|
27
|
+
# add app/lib to load path
|
28
|
+
$LOAD_PATH.unshift config.app.src_dir
|
29
|
+
|
30
|
+
@prepared = true
|
31
|
+
end
|
32
|
+
|
33
|
+
# Stages the app by preparing and returning an instance. This is
|
34
|
+
# essentially everything short of running it.
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def stage(*env_or_envs)
|
38
|
+
prepare(*env_or_envs)
|
39
|
+
self.new
|
40
|
+
end
|
41
|
+
|
42
|
+
# Runs the staged app.
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
def run(*env_or_envs)
|
46
|
+
return true if running?
|
47
|
+
@running = true
|
48
|
+
builder.run(stage(*env_or_envs))
|
49
|
+
detect_handler.run(builder, Host: config.server.host, Port: config.server.port) do |server|
|
50
|
+
SIGNALS.each do |signal|
|
51
|
+
trap(signal) { stop(server) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns true if the application is prepared.
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
def prepared?
|
60
|
+
@prepared == true
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns true if the application is running.
|
64
|
+
#
|
65
|
+
# @api public
|
66
|
+
def running?
|
67
|
+
@running == true
|
68
|
+
end
|
69
|
+
|
70
|
+
# Returns true if the application is staged.
|
71
|
+
#
|
72
|
+
# @api public
|
73
|
+
def staged?
|
74
|
+
!Pakyow.app.nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
# Returns a rack builder instance.
|
78
|
+
#
|
79
|
+
# @api public
|
80
|
+
def builder
|
81
|
+
@builder ||= Rack::Builder.new
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns an instance of the rack handler.
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
def detect_handler
|
88
|
+
if config.server.handler
|
89
|
+
HANDLERS.unshift(config.server.handler).uniq!
|
90
|
+
end
|
91
|
+
|
92
|
+
HANDLERS.each do |handler_name|
|
93
|
+
begin
|
94
|
+
handler = Rack::Handler.get(handler_name)
|
95
|
+
return handler unless handler.nil?
|
96
|
+
rescue LoadError
|
97
|
+
rescue NameError
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
raise 'No handler found'
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
def stop(server)
|
107
|
+
STOP_METHODS.each do |method|
|
108
|
+
if server.respond_to?(method)
|
109
|
+
return server.send(method)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# exit ungracefully
|
114
|
+
Process.exit!
|
115
|
+
end
|
116
|
+
|
117
|
+
def load_middleware
|
118
|
+
middleware.each do |block|
|
119
|
+
instance_exec(builder, &block)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -1,12 +1,14 @@
|
|
1
1
|
module Pakyow
|
2
|
-
#
|
2
|
+
# Helpers available anywhere
|
3
|
+
#
|
4
|
+
# @api public
|
3
5
|
module Helpers
|
4
6
|
def context
|
5
7
|
@context or raise NoContextError
|
6
8
|
end
|
7
9
|
|
8
10
|
def logger
|
9
|
-
request.logger
|
11
|
+
request.logger || Pakyow.logger
|
10
12
|
end
|
11
13
|
|
12
14
|
def router
|
@@ -38,8 +40,22 @@ module Pakyow
|
|
38
40
|
def config
|
39
41
|
Pakyow::Config
|
40
42
|
end
|
41
|
-
end
|
42
43
|
|
43
|
-
|
44
|
-
|
44
|
+
# Returns the primary app environment.
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
def env
|
48
|
+
config.env
|
49
|
+
end
|
50
|
+
|
51
|
+
# Helpers for Pakyow::App
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
module App; end
|
55
|
+
|
56
|
+
# Helpers for Pakyow::CallContext
|
57
|
+
#
|
58
|
+
# @api public
|
59
|
+
module Context; end
|
60
|
+
end
|
45
61
|
end
|
@@ -17,7 +17,7 @@ module Pakyow
|
|
17
17
|
next if FileTest.directory?(path)
|
18
18
|
next if path.split('.')[-1] != 'rb'
|
19
19
|
|
20
|
-
if Config.
|
20
|
+
if Config.reloader.enabled
|
21
21
|
if !@times[path] || (@times[path] && File.mtime(path) - @times[path] > 0)
|
22
22
|
load(path)
|
23
23
|
@times[path] = File.mtime(path)
|
@@ -41,7 +41,7 @@ module Pakyow
|
|
41
41
|
|
42
42
|
def <<(msg = nil, severity = :unknown)
|
43
43
|
return if @log.nil?
|
44
|
-
msg << "\n"
|
44
|
+
(msg || "") << "\n"
|
45
45
|
|
46
46
|
msg = format(msg, severity) if @format
|
47
47
|
@mutex.synchronize do
|
@@ -83,7 +83,7 @@ module Pakyow
|
|
83
83
|
|
84
84
|
def format(msg, level)
|
85
85
|
return msg unless color = level_color(level)
|
86
|
-
return COLOR_SEQ % (30 + COLOR_TABLE.index(color)) + msg + RESET_SEQ
|
86
|
+
return COLOR_SEQ % (30 + COLOR_TABLE.index(color)) + (msg || "") + RESET_SEQ
|
87
87
|
end
|
88
88
|
|
89
89
|
def level_color(level)
|
@@ -99,6 +99,12 @@ end
|
|
99
99
|
|
100
100
|
module Pakyow
|
101
101
|
module Middleware
|
102
|
+
Pakyow::App.middleware do |builder|
|
103
|
+
if Pakyow::Config.logger.enabled
|
104
|
+
builder.use Pakyow::Middleware::Logger
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
102
108
|
class Logger
|
103
109
|
# handles logging after an error occurs
|
104
110
|
Pakyow::App.after(:error) {
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Pakyow
|
2
|
+
module Middleware
|
3
|
+
Pakyow::App.middleware do |builder|
|
4
|
+
if Pakyow::Config.reloader.enabled
|
5
|
+
builder.use Pakyow::Middleware::Reloader
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
# Rack compatible middleware that tells app to reload on each request.
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
class Reloader
|
13
|
+
def initialize(app)
|
14
|
+
@app = app
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
Pakyow.app.reload
|
19
|
+
@app.call(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'pakyow/core/call_context'
|
2
|
+
|
3
|
+
module Pakyow
|
4
|
+
module Middleware
|
5
|
+
Pakyow::App.middleware do |builder|
|
6
|
+
builder.use Pakyow::Middleware::ReqPathNormalizer
|
7
|
+
end
|
8
|
+
|
9
|
+
# Rack compatible middleware that normalize the path if contains '//'
|
10
|
+
# or has a trailing '/', it replace '//' with '/', remove trailing `/`
|
11
|
+
# and issue a 301 redirect to the normalized path.
|
12
|
+
#
|
13
|
+
# @api public
|
14
|
+
class ReqPathNormalizer
|
15
|
+
TAIL_SLASH_REPLACE_REGEX = /(\/)+$/
|
16
|
+
TAIL_SLASH_REGEX = /(.)+(\/)+$/
|
17
|
+
|
18
|
+
def initialize(app)
|
19
|
+
@app = app
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(env)
|
23
|
+
path = env['PATH_INFO']
|
24
|
+
|
25
|
+
if double_slash?(path) || tail_slash?(path)
|
26
|
+
catch :halt do
|
27
|
+
CallContext.new(env).redirect(normalize_path(path), 301)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
@app.call(env)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def normalize_path(path)
|
35
|
+
normalized = path
|
36
|
+
.gsub('//', '/')
|
37
|
+
.gsub(TAIL_SLASH_REPLACE_REGEX, '')
|
38
|
+
end
|
39
|
+
|
40
|
+
def double_slash?(path)
|
41
|
+
path.include?('//')
|
42
|
+
end
|
43
|
+
|
44
|
+
def tail_slash?(path)
|
45
|
+
(TAIL_SLASH_REGEX =~ path).nil? ? false : true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'pakyow/core/config'
|
2
|
+
require 'pakyow/core/call_context'
|
3
|
+
|
4
|
+
module Pakyow
|
5
|
+
module Middleware
|
6
|
+
# Rack compatible middleware that serves static files from one or more configured resource stores.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# Pakyow::Config.app.resources = {
|
10
|
+
# default: './public'
|
11
|
+
# }
|
12
|
+
#
|
13
|
+
# Pakyow::App.builder.use Pakyow::Middleware::Static
|
14
|
+
#
|
15
|
+
# # Assuming './public/foo.png' exists, a GET request to '/foo.png' will
|
16
|
+
# # result in this middleware responding with the static file.
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
class Static
|
20
|
+
Pakyow::App.middleware do |builder|
|
21
|
+
if Pakyow::Config.app.static
|
22
|
+
builder.use Pakyow::Middleware::Static
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(app)
|
27
|
+
@app = app
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(env)
|
31
|
+
static, resource_path = self.class.static?(env)
|
32
|
+
return @app.call(env) unless static
|
33
|
+
|
34
|
+
catch :halt do
|
35
|
+
CallContext.new(env).send(File.open(resource_path))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class << self
|
40
|
+
STATIC_REGEX = /\.(.*)$/
|
41
|
+
STATIC_HTTP_METHODS = %w(GET)
|
42
|
+
|
43
|
+
# Checks if `path` can be found in any configured resource store.
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
def static?(env)
|
47
|
+
path, method = env.values_at('PATH_INFO', 'REQUEST_METHOD')
|
48
|
+
|
49
|
+
return false unless STATIC_HTTP_METHODS.include?(method)
|
50
|
+
return false unless static_path?(path)
|
51
|
+
|
52
|
+
resources_contain?(path)
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def static_path?(path)
|
58
|
+
path =~ STATIC_REGEX
|
59
|
+
end
|
60
|
+
|
61
|
+
def resources_contain?(path)
|
62
|
+
resources.each_pair do |_, resource_path|
|
63
|
+
full_path = File.join(resource_path, path)
|
64
|
+
return true, full_path if File.exists?(full_path)
|
65
|
+
end
|
66
|
+
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
def resources
|
71
|
+
Config.app.resources
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
File without changes
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'json'
|
2
|
+
require 'rack'
|
2
3
|
|
3
4
|
module Pakyow
|
4
5
|
|
@@ -58,9 +59,12 @@ module Pakyow
|
|
58
59
|
|
59
60
|
# Returns indifferent params (see {HashUtils.strhash} for more info on indifferent hashes).
|
60
61
|
def params
|
61
|
-
@params
|
62
|
-
|
63
|
-
@params =
|
62
|
+
return @params unless @params.nil?
|
63
|
+
|
64
|
+
@params = super
|
65
|
+
@params.merge!(env['pakyow.data']) if env['pakyow.data'].is_a?(Hash)
|
66
|
+
@params.merge!(JSON.parse(body.read.to_s)) if format == :json
|
67
|
+
@params = Hash.strhash(@params)
|
64
68
|
end
|
65
69
|
|
66
70
|
# Returns array of url components.
|
@@ -4,6 +4,8 @@ module Pakyow
|
|
4
4
|
class Response < Rack::Response
|
5
5
|
attr_reader :format
|
6
6
|
|
7
|
+
DEFAULT_CONTENT_TYPE = 'text/html;charset=utf-8'.freeze
|
8
|
+
|
7
9
|
STATUS_CODE_NAMES = {
|
8
10
|
100 => 'Continue',
|
9
11
|
101 => 'Switching Protocols',
|
@@ -117,13 +119,13 @@ module Pakyow
|
|
117
119
|
def initialize(*args)
|
118
120
|
super
|
119
121
|
|
120
|
-
self["Content-Type"] ||=
|
122
|
+
self["Content-Type"] ||= DEFAULT_CONTENT_TYPE
|
121
123
|
@format = Rack::Mime::MIME_TYPES.key(type)
|
122
124
|
end
|
123
125
|
|
124
126
|
def format=(format)
|
125
127
|
@format = format
|
126
|
-
self["Content-Type"] = Rack::Mime.mime_type(".#{format}")
|
128
|
+
self["Content-Type"] = format == :html ? DEFAULT_CONTENT_TYPE : Rack::Mime.mime_type(".#{format}")
|
127
129
|
end
|
128
130
|
|
129
131
|
def type
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -28,7 +28,7 @@ module Pakyow
|
|
28
28
|
method = method.to_sym
|
29
29
|
method = :get if method == :head
|
30
30
|
|
31
|
-
@routes[method].each
|
31
|
+
(@routes[method] || []).each do |r|
|
32
32
|
#TODO can we do this without conditionals? fall-through?
|
33
33
|
case r[0]
|
34
34
|
when Regexp
|
@@ -40,7 +40,7 @@ module Pakyow
|
|
40
40
|
return r, nil
|
41
41
|
end
|
42
42
|
end
|
43
|
-
|
43
|
+
end
|
44
44
|
|
45
45
|
nil
|
46
46
|
end
|
File without changes
|
File without changes
|
@@ -37,6 +37,10 @@ module Pakyow
|
|
37
37
|
raise MissingRoute, "Could not find route '#{name}'"
|
38
38
|
end
|
39
39
|
|
40
|
+
def exists?(context)
|
41
|
+
!match(context.request).empty?
|
42
|
+
end
|
43
|
+
|
40
44
|
# Performs the initial routing for a request.
|
41
45
|
#
|
42
46
|
def perform(context, app = Pakyow.app, &after_match)
|