rocketio 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -5
- data/.pryrc +2 -0
- data/.travis.yml +3 -0
- data/README.md +22 -5
- data/Rakefile +7 -1
- data/bin/console +14 -0
- data/bin/setup +7 -0
- data/lib/rocketio.rb +131 -3
- data/lib/rocketio/application.rb +31 -0
- data/lib/rocketio/controller.rb +288 -0
- data/lib/rocketio/controller/authentication.rb +141 -0
- data/lib/rocketio/controller/authorization.rb +53 -0
- data/lib/rocketio/controller/cookies.rb +59 -0
- data/lib/rocketio/controller/error_handlers.rb +89 -0
- data/lib/rocketio/controller/filters.rb +119 -0
- data/lib/rocketio/controller/flash.rb +21 -0
- data/lib/rocketio/controller/helpers.rb +438 -0
- data/lib/rocketio/controller/middleware.rb +32 -0
- data/lib/rocketio/controller/render.rb +148 -0
- data/lib/rocketio/controller/render/engine.rb +76 -0
- data/lib/rocketio/controller/render/layout.rb +27 -0
- data/lib/rocketio/controller/render/layouts.rb +85 -0
- data/lib/rocketio/controller/render/templates.rb +83 -0
- data/lib/rocketio/controller/request.rb +115 -0
- data/lib/rocketio/controller/response.rb +84 -0
- data/lib/rocketio/controller/sessions.rb +64 -0
- data/lib/rocketio/controller/token_auth.rb +118 -0
- data/lib/rocketio/controller/websocket.rb +21 -0
- data/lib/rocketio/error_templates/404.html +3 -0
- data/lib/rocketio/error_templates/409.html +7 -0
- data/lib/rocketio/error_templates/500.html +3 -0
- data/lib/rocketio/error_templates/501.html +6 -0
- data/lib/rocketio/error_templates/layout.html +1 -0
- data/lib/rocketio/exceptions.rb +4 -0
- data/lib/rocketio/router.rb +65 -0
- data/lib/rocketio/util.rb +122 -0
- data/lib/rocketio/version.rb +2 -2
- data/rocketio.gemspec +21 -17
- data/test/aliases_test.rb +54 -0
- data/test/authentication_test.rb +307 -0
- data/test/authorization_test.rb +91 -0
- data/test/cache_control_test.rb +268 -0
- data/test/content_type_test.rb +124 -0
- data/test/cookies_test.rb +49 -0
- data/test/error_handlers_test.rb +125 -0
- data/test/etag_test.rb +445 -0
- data/test/filters_test.rb +177 -0
- data/test/halt_test.rb +73 -0
- data/test/helpers_test.rb +171 -0
- data/test/middleware_test.rb +57 -0
- data/test/redirect_test.rb +135 -0
- data/test/render/engine_test.rb +71 -0
- data/test/render/get.erb +1 -0
- data/test/render/items.erb +1 -0
- data/test/render/layout.erb +1 -0
- data/test/render/layout_test.rb +104 -0
- data/test/render/layouts/master.erb +1 -0
- data/test/render/layouts_test.rb +145 -0
- data/test/render/master.erb +1 -0
- data/test/render/post.erb +1 -0
- data/test/render/put.erb +1 -0
- data/test/render/render_test.rb +101 -0
- data/test/render/setup.rb +14 -0
- data/test/render/templates/a/get.erb +1 -0
- data/test/render/templates/master.erb +1 -0
- data/test/render/templates_test.rb +146 -0
- data/test/request_test.rb +105 -0
- data/test/response_test.rb +119 -0
- data/test/routes_test.rb +70 -0
- data/test/sendfile_test.rb +209 -0
- data/test/sessions_test.rb +176 -0
- data/test/setup.rb +59 -0
- metadata +144 -9
- data/LICENSE.txt +0 -22
@@ -0,0 +1,141 @@
|
|
1
|
+
module RocketIO
|
2
|
+
class Controller
|
3
|
+
|
4
|
+
# easily restrict access to controller using basic auth
|
5
|
+
#
|
6
|
+
# @example protect all request methods
|
7
|
+
# basic_auth do |user,pass|
|
8
|
+
# user == 'admin' && pass == 'super secret password'
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# @example protect only POST, PUT and DELETE request methods
|
12
|
+
# basic_auth :post, :put, :delete do |user,pass|
|
13
|
+
# user == 'admin' && pass == 'super secret password'
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# @example use different credentials for GET and POST
|
17
|
+
# basic_auth :get do |user,pass|
|
18
|
+
# user == 'reader' && pass == 'readPass'
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# basic_auth :post do |user,pass|
|
22
|
+
# user == 'poster' && pass == 'writePass'
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# @note authorization is composable, that's it, if superclass is protecting :get method
|
26
|
+
# and current controller protects :post method,
|
27
|
+
# both :get and :post will be protected in current controller
|
28
|
+
#
|
29
|
+
# @params *args [Array]
|
30
|
+
# @param block [Proc]
|
31
|
+
#
|
32
|
+
def self.basic_auth *args, &block
|
33
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
34
|
+
rqms = args.any? ? args.map!(&:to_sym) : RocketIO::REQUEST_METHODS.values
|
35
|
+
rqms.each do |rm|
|
36
|
+
(@__basic_auth__ ||= {})[rm] = {
|
37
|
+
class: Rack::Auth::Basic,
|
38
|
+
arguments: [opts[:realm] || RocketIO::DEFAULT_AUTH_REALM].freeze,
|
39
|
+
block: block,
|
40
|
+
mock: RocketIO::HTTP_AUTHORIZATION_MOCKS[:basic]
|
41
|
+
}.freeze
|
42
|
+
end
|
43
|
+
define_basic_auth_methods
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.define_basic_auth_methods source = self
|
47
|
+
prompts = (source.instance_variable_get(:@__basic_auth__) || {}).each_with_object(allocate.basic_auth.dup) do |(rm,p),o|
|
48
|
+
method = :"__basic_auth__#{rm}__"
|
49
|
+
define_method(method, &p[:block])
|
50
|
+
o[rm] = p.merge(method: method).freeze
|
51
|
+
end.freeze
|
52
|
+
return if prompts.empty?
|
53
|
+
define_method(:basic_auth) {prompts}
|
54
|
+
end
|
55
|
+
|
56
|
+
def basic_auth; RocketIO::EMPTY_HASH end
|
57
|
+
|
58
|
+
# easily restrict access to controller using digest auth
|
59
|
+
#
|
60
|
+
# @example protect all request methods using hashed passwords
|
61
|
+
# # hash the password somewhere in irb:
|
62
|
+
# # ::Digest::MD5.hexdigest 'admin:AccessRestricted:somePassword'
|
63
|
+
# # username ^ realm ^ password ^
|
64
|
+
#
|
65
|
+
# #=> 9d77d54decc22cdcfb670b7b79ee0ef0
|
66
|
+
#
|
67
|
+
# digest_auth :passwords_hashed => true, :realm => 'AccessRestricted' do |user|
|
68
|
+
# {'admin' => '9d77d54decc22cdcfb670b7b79ee0ef0'}[user]
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# @example protect all request methods using plain passwords
|
72
|
+
# digest_auth do |user|
|
73
|
+
# {'admin' => 'password'}[user]
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# @example protect only POST, PUT and DELETE request methods
|
77
|
+
# digest_auth :post, :put, :delete do |user|
|
78
|
+
# {'admin' => 'password'}[user]
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# @example use different credentials for GET and POST
|
82
|
+
# digest_auth :get do |user|
|
83
|
+
# {'user' => 'readPass'}[user]
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# digest_auth :post do |user|
|
87
|
+
# {'poster' => 'writePass'}[user]
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# @params *args [Array]
|
91
|
+
# @param block [Proc]
|
92
|
+
#
|
93
|
+
def self.digest_auth *args, &block
|
94
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
95
|
+
opts[:realm] ||= RocketIO::DEFAULT_AUTH_REALM
|
96
|
+
opts[:opaque] ||= opts[:realm]
|
97
|
+
rqms = args.any? ? args.map!(&:to_sym) : RocketIO::REQUEST_METHODS.values
|
98
|
+
rqms.each do |rm|
|
99
|
+
(@__digest_auth__ ||= {})[rm] = {
|
100
|
+
class: Rack::Auth::Digest::MD5,
|
101
|
+
arguments: [opts].freeze,
|
102
|
+
block: block,
|
103
|
+
mock: RocketIO::HTTP_AUTHORIZATION_MOCKS[:digest]
|
104
|
+
}.freeze
|
105
|
+
end
|
106
|
+
define_digest_auth_methods
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.define_digest_auth_methods source = self
|
110
|
+
prompts = (source.instance_variable_get(:@__digest_auth__) || {}).each_with_object(allocate.digest_auth.dup) do |(rm,p),o|
|
111
|
+
method = :"__digest_auth__#{rm}__"
|
112
|
+
define_method(method, &p[:block])
|
113
|
+
o[rm] = p.merge(method: method).freeze
|
114
|
+
end.freeze
|
115
|
+
return if prompts.empty?
|
116
|
+
define_method(:digest_auth) {prompts}
|
117
|
+
end
|
118
|
+
|
119
|
+
def digest_auth; RocketIO::EMPTY_HASH end
|
120
|
+
|
121
|
+
def user?
|
122
|
+
env[RocketIO::REMOTE_USER]
|
123
|
+
end
|
124
|
+
|
125
|
+
# checks whether authentication is required and
|
126
|
+
# send an authorization request if credentials not present or invalid
|
127
|
+
def validate_or_request_authentication_if_needed
|
128
|
+
return unless auth = digest_auth[requested_method] || basic_auth[requested_method]
|
129
|
+
return unless prompt = auth[:class].new(proc {}, *auth[:arguments]) do |*a|
|
130
|
+
self.__send__(auth[:method], *a)
|
131
|
+
end.call(
|
132
|
+
if RocketIO::HTTP_AUTHORIZATION_KEYS.detect {|key| env.has_key?(key)}
|
133
|
+
env
|
134
|
+
else
|
135
|
+
env.merge(RocketIO::HTTP_AUTHORIZATION_KEYS.first => auth[:mock])
|
136
|
+
end
|
137
|
+
)
|
138
|
+
throw(:__response__, prompt)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rocketio/controller/token_auth'
|
2
|
+
|
3
|
+
module RocketIO
|
4
|
+
class Controller
|
5
|
+
|
6
|
+
# easily restrict access to controller using token auth
|
7
|
+
#
|
8
|
+
# @example simple Token example
|
9
|
+
#
|
10
|
+
# class User < RocketIO::Controller
|
11
|
+
# token_auth { |token| token == 'secret' }
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
def self.token_auth *args, &block
|
15
|
+
opts = args.last.is_a?(Hash) ? args.pop : {}
|
16
|
+
(args.any? ? args.map!(&:to_sym) : RocketIO::REQUEST_METHODS.values).each do |rm|
|
17
|
+
(@__token_auth__ ||= {})[rm] = {
|
18
|
+
realm: opts[:realm] || RocketIO::DEFAULT_TOKEN_AUTH_REALM.freeze,
|
19
|
+
block: block
|
20
|
+
}
|
21
|
+
end
|
22
|
+
define_token_auth_methods
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.define_token_auth_methods source = self
|
26
|
+
prompts = allocate.token_auth.merge(source.instance_variable_get(:@__token_auth__) || {}).freeze
|
27
|
+
return if prompts.empty?
|
28
|
+
define_method(:token_auth) {prompts}
|
29
|
+
end
|
30
|
+
|
31
|
+
def token_auth; RocketIO::EMPTY_HASH end
|
32
|
+
|
33
|
+
def validate_or_request_authorization_if_needed
|
34
|
+
return unless auth = token_auth[requested_method]
|
35
|
+
return if validate_token_auth(&auth[:block])
|
36
|
+
throw(:__response__, request_token_auth(auth[:realm]))
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate_or_request_token_auth realm = RocketIO::DEFAULT_TOKEN_AUTH_REALM, &block
|
40
|
+
validate_token_auth(&block) || request_token_auth(realm)
|
41
|
+
end
|
42
|
+
|
43
|
+
def validate_token_auth &block
|
44
|
+
RocketIO::TokenAuth.authenticate(env, &block)
|
45
|
+
end
|
46
|
+
alias valid_token_auth? validate_token_auth
|
47
|
+
|
48
|
+
def request_token_auth realm = RocketIO::DEFAULT_TOKEN_AUTH_REALM
|
49
|
+
RocketIO::TokenAuth.authentication_request(realm)
|
50
|
+
end
|
51
|
+
alias request_token_auth! request_token_auth
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module RocketIO
|
2
|
+
class Controller
|
3
|
+
|
4
|
+
# shorthand for `request.cookies`, `response.set_cookie` and `response.delete_cookie`
|
5
|
+
#
|
6
|
+
# @example Setting a cookie
|
7
|
+
# cookies['cookie-name'] = 'value'
|
8
|
+
#
|
9
|
+
# @example Reading a cookie
|
10
|
+
# cookies['cookie-name']
|
11
|
+
#
|
12
|
+
# @example Setting a cookie with custom options
|
13
|
+
# cookies['question_of_the_day'] = {
|
14
|
+
# value: 'who is not who?',
|
15
|
+
# expires: Date.today + 1,
|
16
|
+
# secure: true
|
17
|
+
# }
|
18
|
+
#
|
19
|
+
# @example Deleting a cookie
|
20
|
+
# cookies.delete('cookie-name')
|
21
|
+
#
|
22
|
+
def cookies
|
23
|
+
@__cookies__ ||= RocketIO::Cookies.new(request.cookies, response)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module RocketIO
|
29
|
+
class Cookies
|
30
|
+
|
31
|
+
def initialize cookies, response
|
32
|
+
@cookies = RocketIO.indifferent_params(cookies)
|
33
|
+
@response = response
|
34
|
+
end
|
35
|
+
|
36
|
+
# set cookie header
|
37
|
+
#
|
38
|
+
# @param [String, Symbol] key
|
39
|
+
# @param [String, Hash] val
|
40
|
+
#
|
41
|
+
def []= key, val
|
42
|
+
@response.set_cookie(key, val)
|
43
|
+
end
|
44
|
+
|
45
|
+
# get cookie by key
|
46
|
+
def [] key
|
47
|
+
@cookies[key]
|
48
|
+
end
|
49
|
+
|
50
|
+
# instruct browser to delete a cookie
|
51
|
+
#
|
52
|
+
# @param [String, Symbol] key
|
53
|
+
# @param [Hash] opts
|
54
|
+
#
|
55
|
+
def delete key, opts ={}
|
56
|
+
@response.delete_cookie(key, opts)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module RocketIO
|
2
|
+
class Controller
|
3
|
+
|
4
|
+
# define error handlers
|
5
|
+
#
|
6
|
+
# @example define a handler that will process 404 errors
|
7
|
+
# class Pages < RocketIO::Controller
|
8
|
+
#
|
9
|
+
# error 404 do |id|
|
10
|
+
# "Sorry, looks like item with ID #{id.to_i} does not exists"
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# def get id
|
14
|
+
# item = Item.find_by(id: id) || error(404, id)
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# @example define a handler that will process fatal errors
|
19
|
+
# class Pages < RocketIO::Controller
|
20
|
+
#
|
21
|
+
# error 500 do |exception|
|
22
|
+
# "Fatal error occurred: " + html_escape(exception.message)
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def get
|
26
|
+
# # any exception raised here will be handled by the handler above
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
def self.error code, &block
|
31
|
+
code = code.to_i
|
32
|
+
code > 0 || raise(ArgumentError, 'Error code should be a number')
|
33
|
+
block || raise(ArgumentError, 'block missing')
|
34
|
+
(@__error_handlers__ ||= {})[code] = block
|
35
|
+
define_error_handlers_methods
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.define_error_handlers_methods source = self
|
39
|
+
handlers = (source.instance_variable_get(:@__error_handlers__) || {}).each_with_object({}) do |(code,proc),o|
|
40
|
+
o[code] = :"__#{code}_error_handler__"
|
41
|
+
define_method(o[code], &proc)
|
42
|
+
end
|
43
|
+
handlers.update(allocate.error_handlers)
|
44
|
+
return if handlers.empty?
|
45
|
+
handlers.freeze
|
46
|
+
define_method(:error_handlers) {handlers}
|
47
|
+
end
|
48
|
+
|
49
|
+
def error_handlers; RocketIO::EMPTY_HASH end
|
50
|
+
|
51
|
+
# if there is a handler defined for given code it will be executed and the result used as body.
|
52
|
+
# otherwise the `error` behaves exactly as `halt`.
|
53
|
+
#
|
54
|
+
# given args will be passed either to handler(if any defined) or to `halt`
|
55
|
+
#
|
56
|
+
def error code, *args
|
57
|
+
error_handlers[code] || halt(code, *args)
|
58
|
+
halt(code, __send__(error_handlers[code], *args))
|
59
|
+
end
|
60
|
+
alias error! error
|
61
|
+
|
62
|
+
# 404: Not Found
|
63
|
+
error 404 do
|
64
|
+
RocketIO.error_renderer(404, xhr?, env: env)
|
65
|
+
end
|
66
|
+
|
67
|
+
# 409: Wrong number of arguments received
|
68
|
+
error 409 do
|
69
|
+
RocketIO.error_renderer(409, xhr?, {
|
70
|
+
env: env,
|
71
|
+
controller: self.class,
|
72
|
+
resolved_path: url,
|
73
|
+
expected_parameters: parameters_policy[env[RocketIO::REQUEST_METHOD]],
|
74
|
+
received_parameters: path_params
|
75
|
+
})
|
76
|
+
end
|
77
|
+
|
78
|
+
# 500: Fatal Error
|
79
|
+
error 500 do |error|
|
80
|
+
error = StandardError.new(error) unless Exception === error
|
81
|
+
RocketIO.error_renderer(500, xhr?, env: env, error: error)
|
82
|
+
end
|
83
|
+
|
84
|
+
# 501: Not Implemented
|
85
|
+
error 501 do
|
86
|
+
RocketIO.error_renderer(501, xhr?, env: env)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module RocketIO
|
2
|
+
class Controller
|
3
|
+
|
4
|
+
# define blocks to run before, around, after called method.
|
5
|
+
# if no methods given, given block will run on any called method.
|
6
|
+
#
|
7
|
+
# @note call it without a block to define a void filter.
|
8
|
+
# useful to override inherited filters.
|
9
|
+
#
|
10
|
+
# @note sub-controllers will inherit all filters from parent controller
|
11
|
+
# and can override them selectively, by name
|
12
|
+
#
|
13
|
+
# @note wildcard filters will run before/around/after any methods,
|
14
|
+
# that's it, if defining `before {}` and `before(:get) {}` filters
|
15
|
+
# `get` method will run `before` filter then `before(:get)`
|
16
|
+
#
|
17
|
+
# @example run before any requested method, being it REST or websocket
|
18
|
+
# before do
|
19
|
+
# # some logic here
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# @example run only before GET
|
23
|
+
# before :get do
|
24
|
+
# # some logic here
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# @example run only around PUT and POST
|
28
|
+
# around :put, :post do |app|
|
29
|
+
# # some logic here
|
30
|
+
# app.call
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# @example run only after :register websocket call
|
34
|
+
# after :register do
|
35
|
+
# # some logic here
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @example define a filter that does nothing. useful to override inherited filters.
|
39
|
+
# before :get
|
40
|
+
#
|
41
|
+
# @example run 2 blocks before GET
|
42
|
+
# before do # wildcard filter, will run before any method
|
43
|
+
# @user = User.find...
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# before :get do # named filter, will run only before `get`
|
47
|
+
# # wildcard filter already executed so we have `@user` variable here
|
48
|
+
# @photo = @user.photos.find...
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# # before calling `get` two filters will be executed:
|
52
|
+
# # - wildcard one
|
53
|
+
# # - named one
|
54
|
+
# def get
|
55
|
+
# # both @user and @photo variables available here
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
{
|
59
|
+
before: proc {},
|
60
|
+
around: proc {|app| app.call},
|
61
|
+
after: proc {}
|
62
|
+
}.each_pair do |filter,default_block|
|
63
|
+
define_methods = :"define_#{filter}_methods"
|
64
|
+
var = :"@__#{filter}_filters__"
|
65
|
+
|
66
|
+
define_singleton_method filter do |*methods,&block|
|
67
|
+
instance_variable_get(var) || instance_variable_set(var, {})
|
68
|
+
methods = [:*] if methods.empty?
|
69
|
+
methods.each do |meth|
|
70
|
+
instance_variable_get(var)[meth.to_sym] = block || default_block
|
71
|
+
end
|
72
|
+
__send__(define_methods)
|
73
|
+
end
|
74
|
+
|
75
|
+
define_singleton_method define_methods do |source = self|
|
76
|
+
filters = (source.instance_variable_get(var) || {}).each_with_object({}) do |(meth,proc),o|
|
77
|
+
o[meth] = :"__#{filter}_#{meth}__"
|
78
|
+
define_method(o[meth], &proc)
|
79
|
+
end
|
80
|
+
filters.update(allocate.__send__(filter))
|
81
|
+
return unless filters.any?
|
82
|
+
filters.freeze
|
83
|
+
define_method(filter) {filters}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def before; RocketIO::EMPTY_HASH end
|
88
|
+
def around; RocketIO::EMPTY_HASH end
|
89
|
+
def after; RocketIO::EMPTY_HASH end
|
90
|
+
|
91
|
+
def invoke_before_filter method = requested_method
|
92
|
+
__send__(before[:*]) if before[:*]
|
93
|
+
__send__(before[method]) if before[method]
|
94
|
+
end
|
95
|
+
|
96
|
+
# passing blocks somehow tends to add some overhead
|
97
|
+
# so passing the proc as a common argument
|
98
|
+
def invoke_around_filter method = requested_method, block
|
99
|
+
if around[:*]
|
100
|
+
__send__ around[:*], proc {
|
101
|
+
if around[method]
|
102
|
+
__send__(around[method], block)
|
103
|
+
else
|
104
|
+
block.call
|
105
|
+
end
|
106
|
+
}
|
107
|
+
elsif around[method]
|
108
|
+
__send__(around[method], block)
|
109
|
+
else
|
110
|
+
block.call
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def invoke_after_filter method = requested_method
|
115
|
+
__send__(after[:*]) if after[:*]
|
116
|
+
__send__(after[method]) if after[method]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|