flame 3.6.2 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/flame/application.rb +18 -10
- data/lib/flame/controller.rb +38 -1
- data/lib/flame/cookies.rb +9 -0
- data/lib/flame/dispatcher.rb +81 -39
- data/lib/flame/render.rb +14 -12
- data/lib/flame/request.rb +2 -0
- data/lib/flame/route.rb +10 -60
- data/lib/flame/router.rb +40 -35
- data/lib/flame/validators.rb +1 -13
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3b164f4b1817723648efb5412dc308265aaa30af
|
4
|
+
data.tar.gz: dc130470827a1bb8f1c22689c605381aa97ee78d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a482215bfd58d3ca783ac1c904f171d5e09f5a2cd42245b6324bf433ffb8611827602e551099076939969fe07b23ae0defb1be6a1c68d18708a270b4b97e3f4
|
7
|
+
data.tar.gz: 5afe0a7dcd19b8ee8eb9d674211f456ea0232932fd40bc0e24f86837dce011d81216c9ecaba9ab84bd4f5cc263a1c813efd1be6a28ceba7febb60dda3ce3341e
|
data/lib/flame/application.rb
CHANGED
@@ -13,6 +13,7 @@ module Flame
|
|
13
13
|
self.class.config
|
14
14
|
end
|
15
15
|
|
16
|
+
## Generating application config when inherited
|
16
17
|
def self.inherited(app)
|
17
18
|
app.config = Config.new(
|
18
19
|
app,
|
@@ -26,32 +27,38 @@ module Flame
|
|
26
27
|
|
27
28
|
def initialize(app = nil)
|
28
29
|
@app = app
|
29
|
-
router.routes.map! do |route|
|
30
|
-
route[:hooks] = router.find_hooks(route)
|
31
|
-
route.freeze
|
32
|
-
end
|
33
|
-
router.freeze
|
34
30
|
end
|
35
31
|
|
36
|
-
##
|
32
|
+
## Request recieving method
|
37
33
|
def call(env)
|
38
34
|
@app.call(env) if @app.respond_to? :call
|
39
35
|
Flame::Dispatcher.new(self, env).run!
|
40
36
|
end
|
41
37
|
|
38
|
+
## Make available `run Application` without `.new` for `rackup`
|
42
39
|
def self.call(env)
|
43
40
|
@app ||= new
|
44
41
|
@app.call env
|
45
42
|
end
|
46
43
|
|
44
|
+
## Mount controller in application class
|
45
|
+
## @param ctrl [Flame::Controller] the mounted controller class
|
46
|
+
## @param path [String, nil] root path for the mounted controller
|
47
|
+
## @yield refine defaults pathes for a methods of the mounted controller
|
48
|
+
## @example Mount controller with defaults
|
49
|
+
## mount ArticlesController
|
50
|
+
## @example Mount controller with specific path
|
51
|
+
## mount HomeController, '/welcome'
|
52
|
+
## @example Mount controller with specific path of methods
|
53
|
+
## mount HomeController do
|
54
|
+
## get '/bye', :goodbye
|
55
|
+
## post '/greetings', :new
|
56
|
+
## defaults
|
57
|
+
## end
|
47
58
|
def self.mount(ctrl, path = nil, &block)
|
48
59
|
router.add_controller(ctrl, path, block)
|
49
60
|
end
|
50
61
|
|
51
|
-
def self.helpers(*modules)
|
52
|
-
modules.empty? ? (@helpers ||= []) : helpers.concat(modules).uniq!
|
53
|
-
end
|
54
|
-
|
55
62
|
## Router for routing
|
56
63
|
def self.router
|
57
64
|
@router ||= Flame::Router.new(self)
|
@@ -61,6 +68,7 @@ module Flame
|
|
61
68
|
self.class.router
|
62
69
|
end
|
63
70
|
|
71
|
+
## Initialize default for config directories
|
64
72
|
def self.default_config_dirs(root_dir:)
|
65
73
|
{
|
66
74
|
root_dir: File.realpath(root_dir),
|
data/lib/flame/controller.rb
CHANGED
@@ -5,6 +5,8 @@ module Flame
|
|
5
5
|
## Class initialize when Dispatcher found route with it
|
6
6
|
## For new request and response
|
7
7
|
class Controller
|
8
|
+
## Initialize the controller for request execution
|
9
|
+
## @param dispatcher [Flame::Dispatcher] dispatcher object
|
8
10
|
def initialize(dispatcher)
|
9
11
|
@dispatcher = dispatcher
|
10
12
|
end
|
@@ -15,12 +17,27 @@ module Flame
|
|
15
17
|
@dispatcher.path_to(*args)
|
16
18
|
end
|
17
19
|
|
20
|
+
## Redirect for response
|
21
|
+
## @overload redirect(path)
|
22
|
+
## Redirect to the string path
|
23
|
+
## @param path [String] path
|
24
|
+
## @example Redirect to '/hello'
|
25
|
+
## redirect '/hello'
|
26
|
+
## @overload redirect(*args)
|
27
|
+
## Redirect to the path of `path_to` method
|
28
|
+
## @param args arguments for `path_to` method
|
29
|
+
## @example Redirect to `show` method of `ArticlesController` with id = 2
|
30
|
+
## redirect ArticlesController, :show, id: 2
|
18
31
|
def redirect(*params)
|
19
32
|
response.redirect(
|
20
33
|
params[0].is_a?(String) ? params[0] : path_to(*params)
|
21
34
|
)
|
22
35
|
end
|
23
36
|
|
37
|
+
## Render a template with `Flame::Render` (based on Tilt-engine)
|
38
|
+
## @param path [Symbol, nil] path to the template file
|
39
|
+
## @param options [Hash] options for the `Flame::Render` rendering
|
40
|
+
## @return [String] rendered template
|
24
41
|
def view(path = nil, options = {})
|
25
42
|
template = Flame::Render.new(
|
26
43
|
self,
|
@@ -31,7 +48,26 @@ module Flame
|
|
31
48
|
end
|
32
49
|
alias render view
|
33
50
|
|
34
|
-
##
|
51
|
+
## Execute the method of the controller with hooks (may be overloaded)
|
52
|
+
## @param method [Symbol] name of the controller method
|
53
|
+
def execute(method)
|
54
|
+
# send method
|
55
|
+
body send(
|
56
|
+
method,
|
57
|
+
*params.values_at(
|
58
|
+
*self.class.instance_method(method).parameters.map { |par| par[1] }
|
59
|
+
)
|
60
|
+
)
|
61
|
+
rescue => exception
|
62
|
+
# p 'rescue from controller'
|
63
|
+
status 500
|
64
|
+
dump_error(exception)
|
65
|
+
|
66
|
+
## Re-raise exception for inherited controllers or `Flame::Dispatcher`
|
67
|
+
raise exception
|
68
|
+
end
|
69
|
+
|
70
|
+
## Call helpers methods from `Flame::Dispatcher`
|
35
71
|
def method_missing(m, *args, &block)
|
36
72
|
return super unless @dispatcher.respond_to?(m)
|
37
73
|
@dispatcher.send(m, *args, &block)
|
@@ -40,6 +76,7 @@ module Flame
|
|
40
76
|
class << self
|
41
77
|
using GorillaPatch::StringExt
|
42
78
|
|
79
|
+
## Default root path of the controller for requests
|
43
80
|
def default_path(last = false)
|
44
81
|
(name.split('::').last.underscore.split('_') - %w(index controller ctrl))
|
45
82
|
.join('/').split('/')
|
data/lib/flame/cookies.rb
CHANGED
@@ -6,10 +6,19 @@ module Flame
|
|
6
6
|
@response = response
|
7
7
|
end
|
8
8
|
|
9
|
+
## Get request cookies
|
10
|
+
## @param key [String, Symbol] name of cookie
|
9
11
|
def [](key)
|
10
12
|
@request_cookies[key.to_s]
|
11
13
|
end
|
12
14
|
|
15
|
+
## Set (or delete) cookies for response
|
16
|
+
## @param key [String, Symbol] name of cookie
|
17
|
+
## @param new_value [Object, nil] value of cookie
|
18
|
+
## @example Set new value to `cat` cookie
|
19
|
+
## cookies['cat'] = 'nice cat'
|
20
|
+
## @example Delete `cat` cookie
|
21
|
+
## cookies['cat'] = nil
|
13
22
|
def []=(key, new_value)
|
14
23
|
return @response.delete_cookie(key.to_s, path: '/') if new_value.nil?
|
15
24
|
@response.set_cookie(key.to_s, value: new_value, path: '/')
|
data/lib/flame/dispatcher.rb
CHANGED
@@ -14,6 +14,9 @@ module Flame
|
|
14
14
|
|
15
15
|
include Flame::Dispatcher::Static
|
16
16
|
|
17
|
+
## Initialize Dispatcher from Application#call
|
18
|
+
## @param app [Flame::Application] application object
|
19
|
+
## @param env Rack-environment object
|
17
20
|
def initialize(app, env)
|
18
21
|
@app = app
|
19
22
|
@env = env
|
@@ -21,43 +24,66 @@ module Flame
|
|
21
24
|
@response = Flame::Response.new
|
22
25
|
end
|
23
26
|
|
27
|
+
## Start of execution the request
|
24
28
|
def run!
|
25
29
|
catch :halt do
|
26
30
|
try_route ||
|
27
31
|
try_static ||
|
28
32
|
try_static(File.join(__dir__, '..', '..', 'public')) ||
|
29
|
-
|
33
|
+
not_found
|
30
34
|
end
|
31
35
|
response.write body
|
32
36
|
response.finish
|
33
37
|
end
|
34
38
|
|
39
|
+
## Acccess to the status of response
|
40
|
+
## @param value [Ineger, nil] integer value for new status
|
41
|
+
## @return [Integer] current status
|
42
|
+
## @example Set status value
|
43
|
+
## status 200
|
35
44
|
def status(value = nil)
|
36
45
|
response.status ||= 200
|
37
46
|
response.headers['X-Cascade'] = 'pass' if value == 404
|
38
47
|
value ? response.status = value : response.status
|
39
48
|
end
|
40
49
|
|
50
|
+
## Acccess to the body of response
|
51
|
+
## @param value [String, nil] string value for new body
|
52
|
+
## @return [String] current body
|
53
|
+
## @example Set body value
|
54
|
+
## body 'Hello World!'
|
41
55
|
def body(value = nil)
|
42
56
|
value ? @body = value : @body ||= ''
|
43
57
|
end
|
44
58
|
|
59
|
+
## Parameters of the request
|
45
60
|
def params
|
46
61
|
@params ||= request.params.merge(request.params.keys_to_sym)
|
47
62
|
end
|
48
63
|
|
64
|
+
## Session object as Hash
|
49
65
|
def session
|
50
66
|
request.session
|
51
67
|
end
|
52
68
|
|
69
|
+
## Cookies object as Hash
|
53
70
|
def cookies
|
54
71
|
@cookies ||= Cookies.new(request.cookies, response)
|
55
72
|
end
|
56
73
|
|
74
|
+
## Application-config object as Hash
|
57
75
|
def config
|
58
76
|
@app.config
|
59
77
|
end
|
60
78
|
|
79
|
+
## Get path for controller and action.
|
80
|
+
##
|
81
|
+
## @param ctrl [Flame::Controller] class of controller
|
82
|
+
## @param action [Symbol] method of controller
|
83
|
+
## @param args [Hash] parameters for method of controller
|
84
|
+
## @return [String] path for requested method, controller and parameters
|
85
|
+
## @example Path for `show(id)` method of `ArticlesController` with `id: 2`
|
86
|
+
## path_to ArticlesController, :show, id: 2 # => "/articles/show/2"
|
61
87
|
def path_to(ctrl, action = :index, args = {})
|
62
88
|
route = @app.class.router.find_route(controller: ctrl, action: action)
|
63
89
|
fail RouteNotFoundError.new(ctrl, action) unless route
|
@@ -65,10 +91,22 @@ module Flame
|
|
65
91
|
path.empty? ? '/' : path
|
66
92
|
end
|
67
93
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
94
|
+
## Interrupt the execution of route, and set new optional data
|
95
|
+
## (otherwise using existing)
|
96
|
+
## @param new_status_or_body [Integer, String]
|
97
|
+
## set new HTTP status code or new body
|
98
|
+
## @param new_body [String] set new body
|
99
|
+
## @param new_headers [String] merge new headers
|
100
|
+
## @example Halt with 500, no change body
|
101
|
+
## halt 500
|
102
|
+
## @example Halt with 404, render template
|
103
|
+
## halt 404, render('errors/404')
|
104
|
+
## @example Halt with 200, set new headers
|
105
|
+
## halt 200, 'Cats!', 'Content-Type' => 'animal/cat'
|
106
|
+
def halt(new_status_or_body = nil, new_body = nil, new_headers = {})
|
107
|
+
case new_status_or_body
|
108
|
+
when String then new_body = new_status_or_body
|
109
|
+
when Integer then status new_status_or_body
|
72
110
|
end
|
73
111
|
# new_status.is_a?(String) ? () : (status new_status)
|
74
112
|
new_body = default_body if new_body.nil? && body.empty?
|
@@ -77,20 +115,27 @@ module Flame
|
|
77
115
|
throw :halt
|
78
116
|
end
|
79
117
|
|
118
|
+
## Add error's backtrace to @env['rack.errors'] (terminal or file)
|
119
|
+
## @param error [Exception] exception for class, message and backtrace
|
120
|
+
def dump_error(error)
|
121
|
+
msg = [
|
122
|
+
"#{Time.now.strftime('%Y-%m-%d %H:%M:%S')} - " \
|
123
|
+
"#{error.class} - #{error.message}:",
|
124
|
+
*error.backtrace
|
125
|
+
].join("\n\t")
|
126
|
+
@env['rack.errors'].puts(msg)
|
127
|
+
end
|
128
|
+
|
80
129
|
private
|
81
130
|
|
82
131
|
## Generate default body of error page
|
83
132
|
def default_body
|
133
|
+
## Return nil if must be no body for current HTTP status
|
84
134
|
return if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
|
85
135
|
response.headers[Rack::CONTENT_TYPE] = 'text/html'
|
86
136
|
"<h1>#{Rack::Utils::HTTP_STATUS_CODES[status]}</h1>"
|
87
137
|
end
|
88
138
|
|
89
|
-
## Find nearest route
|
90
|
-
def nearest_route_for_request
|
91
|
-
@app.router.find_nearest_route(request.path_parts)
|
92
|
-
end
|
93
|
-
|
94
139
|
## Find route and try execute it
|
95
140
|
def try_route
|
96
141
|
route = @app.class.router.find_route(
|
@@ -98,39 +143,36 @@ module Flame
|
|
98
143
|
path_parts: request.path_parts
|
99
144
|
)
|
100
145
|
return nil unless route
|
101
|
-
status 200
|
102
|
-
params.merge!(route.arguments(request.path_parts))
|
103
|
-
# route.execute(self)
|
104
146
|
execute_route(route)
|
105
147
|
end
|
106
148
|
|
149
|
+
## Execute route
|
150
|
+
## @param route [Flame::Route] route that must be executed
|
107
151
|
def execute_route(route)
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
#
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
].join("\n\t")
|
133
|
-
@env['rack.errors'].puts(msg)
|
152
|
+
status 200
|
153
|
+
params.merge!(route.arguments(request.path_parts))
|
154
|
+
# route.execute(self)
|
155
|
+
route[:controller].new(self).execute(route[:action])
|
156
|
+
rescue => _exception
|
157
|
+
# p 'rescue from dispatcher'
|
158
|
+
halt 500
|
159
|
+
|
160
|
+
# p 're raise exception from dispatcher'
|
161
|
+
# raise exception
|
162
|
+
end
|
163
|
+
|
164
|
+
## Generate a response if the route is not found
|
165
|
+
def not_found
|
166
|
+
# p 'not found from dispatcher'
|
167
|
+
## Change the status of response to 404
|
168
|
+
status 404
|
169
|
+
## Find the nearest route by the parts of requested path
|
170
|
+
route = @app.router.find_nearest_route(request.path_parts)
|
171
|
+
## Halt with default body if the route not found
|
172
|
+
## or it's `not_found` method not defined
|
173
|
+
return halt unless route && route[:controller].method_defined?(:not_found)
|
174
|
+
## Execute `not_found` method for the founded route
|
175
|
+
route[:controller].new(self).execute(:not_found)
|
134
176
|
end
|
135
177
|
end
|
136
178
|
end
|
data/lib/flame/render.rb
CHANGED
@@ -20,6 +20,8 @@ module Flame
|
|
20
20
|
@layout = nil if File.basename(@filename)[0] == '_'
|
21
21
|
end
|
22
22
|
|
23
|
+
## Render template
|
24
|
+
## @param cache [Boolean] cache compiles or not
|
23
25
|
def render(cache: true)
|
24
26
|
## Compile Tilt to instance hash
|
25
27
|
tilt = cache ? self.class.tilts[@filename] ||= compile : compile
|
@@ -39,19 +41,16 @@ module Flame
|
|
39
41
|
|
40
42
|
using GorillaPatch::StringExt
|
41
43
|
|
44
|
+
## Compile file with Tilt engine
|
45
|
+
## @param filename [String] filename
|
42
46
|
def compile(filename = @filename)
|
43
47
|
Tilt.new(filename)
|
44
48
|
end
|
45
49
|
|
46
|
-
##
|
47
|
-
##
|
50
|
+
## @todo Add `views_dir` for Application and Controller
|
51
|
+
## @todo Add `layout` method for Controller
|
48
52
|
def find_file(path)
|
49
53
|
## Get full filename
|
50
|
-
# p Dir[File.join(
|
51
|
-
# @ctrl.config[:views_dir],
|
52
|
-
# "{#{controller_dirs.join(',')},}",
|
53
|
-
# "#{path}.*"
|
54
|
-
# )].uniq
|
55
54
|
Dir[File.join(
|
56
55
|
@ctrl.config[:views_dir],
|
57
56
|
"{#{controller_dirs.join(',')},}",
|
@@ -61,18 +60,21 @@ module Flame
|
|
61
60
|
end
|
62
61
|
end
|
63
62
|
|
63
|
+
## Find possible directories for the controller
|
64
64
|
def controller_dirs
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
controller_dir_parts = controller_dir.split('/')
|
65
|
+
controller_dir_parts = @ctrl.class.name.underscore.split('/').map do |part|
|
66
|
+
(part.split('_') - %w(controller controllers ctrl)).join('_')
|
67
|
+
end
|
68
|
+
controller_dir = controller_dir_parts.join('/')
|
70
69
|
[controller_dir,
|
71
70
|
controller_dir_parts[1..-1].join('/'),
|
72
71
|
controller_dir_parts[1..-2].join('/'),
|
73
72
|
controller_dir_parts.last]
|
74
73
|
end
|
75
74
|
|
75
|
+
## Render the layout with template
|
76
|
+
## @param result [String] result of template rendering
|
77
|
+
## @param cache [Boolean] cache compiles or not
|
76
78
|
def layout_render(result, cache: true)
|
77
79
|
layout_file = find_file(@layout)
|
78
80
|
## Compile layout to hash
|
data/lib/flame/request.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
module Flame
|
2
2
|
## Class for requests
|
3
3
|
class Request < Rack::Request
|
4
|
+
## Split path of the request to parts (Array of String)
|
4
5
|
def path_parts
|
5
6
|
@path_parts ||= path_info.to_s.split('/').reject(&:empty?)
|
6
7
|
end
|
7
8
|
|
9
|
+
## Override HTTP-method of the request if the param '_method' found
|
8
10
|
def http_method
|
9
11
|
params['_method'] || request_method
|
10
12
|
end
|
data/lib/flame/route.rb
CHANGED
@@ -5,24 +5,26 @@ module Flame
|
|
5
5
|
|
6
6
|
def initialize(attrs = {})
|
7
7
|
@attributes = attrs.merge(
|
8
|
-
|
8
|
+
## Split path to parts (Array of String)
|
9
|
+
path_parts: attrs[:path].to_s.split('/').reject(&:empty?).freeze
|
9
10
|
)
|
10
11
|
end
|
11
12
|
|
13
|
+
## Get the attribute of route
|
14
|
+
## @param key [Symbol] name of attribute
|
12
15
|
def [](key)
|
13
16
|
@attributes[key]
|
14
17
|
end
|
15
18
|
|
19
|
+
## Set the attribute of route
|
20
|
+
## @param key [Symbol] name of attribute
|
21
|
+
## @param value [Object] value of attribute
|
16
22
|
def []=(key, value)
|
17
23
|
@attributes[key] = value
|
18
24
|
end
|
19
25
|
|
20
|
-
## Create Executable object (route)
|
21
|
-
def executable(dispatcher)
|
22
|
-
Executable.new(self, dispatcher)
|
23
|
-
end
|
24
|
-
|
25
26
|
## Compare attributes for `Router.find_route`
|
27
|
+
## @param attrs [Hash] Hash of attributes for comparing
|
26
28
|
def compare_attributes(attrs)
|
27
29
|
attrs.each do |name, value|
|
28
30
|
next true if compare_attribute(name, value)
|
@@ -31,6 +33,7 @@ module Flame
|
|
31
33
|
end
|
32
34
|
|
33
35
|
## Assign arguments to path for `Controller.path_to`
|
36
|
+
## @param args [Hash] arguments for assigning
|
34
37
|
def assign_arguments(args = {})
|
35
38
|
self[:path_parts]
|
36
39
|
.map { |path_part| assign_argument(path_part, args) }
|
@@ -38,6 +41,7 @@ module Flame
|
|
38
41
|
end
|
39
42
|
|
40
43
|
## Extract arguments from request_parts for `execute`
|
44
|
+
## @param request_parts [Array] parts of the request (Array of String)
|
41
45
|
def arguments(request_parts)
|
42
46
|
self[:path_parts].each_with_index.with_object({}) do |(path_part, i), args|
|
43
47
|
request_part = request_parts[i]
|
@@ -93,59 +97,5 @@ module Flame
|
|
93
97
|
## All is ok
|
94
98
|
param
|
95
99
|
end
|
96
|
-
|
97
|
-
## Class for Route execution
|
98
|
-
class Executable
|
99
|
-
## Create executable route with dispatcher
|
100
|
-
def initialize(route, dispatcher)
|
101
|
-
@route = route
|
102
|
-
@ctrl = @route[:controller].new(dispatcher)
|
103
|
-
end
|
104
|
-
|
105
|
-
## Execute route
|
106
|
-
def run!
|
107
|
-
execute_hooks(:before)
|
108
|
-
@ctrl.body @ctrl.send(@route[:action], *arranged_params)
|
109
|
-
execute_hooks(:after)
|
110
|
-
end
|
111
|
-
|
112
|
-
def execute_errors(status = 500)
|
113
|
-
execute_hooks(:error, status)
|
114
|
-
end
|
115
|
-
|
116
|
-
private
|
117
|
-
|
118
|
-
## Arguments in order as parameters of method of controller
|
119
|
-
def arranged_params
|
120
|
-
# action_parameters.each_with_object([]) do |par, arr|
|
121
|
-
# arr << @ctrl.params[par[1]] if par[0] == :req || @ctrl.params[par[1]]
|
122
|
-
# end
|
123
|
-
@ctrl.params.values_at(*action_parameters.map { |par| par[1] })
|
124
|
-
end
|
125
|
-
|
126
|
-
## Method parameters of route controller#action
|
127
|
-
def action_parameters
|
128
|
-
@route[:controller].instance_method(@route[:action]).parameters
|
129
|
-
end
|
130
|
-
|
131
|
-
## Execute before, after or error hook of Symbol, String or Proc
|
132
|
-
def execute_hook(hook)
|
133
|
-
case hook
|
134
|
-
when Symbol, String
|
135
|
-
@ctrl.send(hook.to_sym)
|
136
|
-
when Proc
|
137
|
-
@ctrl.instance_exec(&hook)
|
138
|
-
else
|
139
|
-
fail UnexpectedTypeOfHookError.new(hook, @route)
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
## Execute before, after or error hooks
|
144
|
-
def execute_hooks(*keys)
|
145
|
-
hooks = @route[:hooks].dig(*keys)
|
146
|
-
# p hooks
|
147
|
-
hooks.each { |hook| execute_hook(hook) } if hooks
|
148
|
-
end
|
149
|
-
end
|
150
100
|
end
|
151
101
|
end
|
data/lib/flame/router.rb
CHANGED
@@ -4,30 +4,36 @@ require_relative 'validators'
|
|
4
4
|
module Flame
|
5
5
|
## Router class for routing
|
6
6
|
class Router
|
7
|
-
attr_reader :app, :routes
|
7
|
+
attr_reader :app, :routes
|
8
8
|
|
9
9
|
def initialize(app)
|
10
10
|
@app = app
|
11
11
|
@routes = []
|
12
|
-
@hooks = {}
|
13
12
|
end
|
14
13
|
|
14
|
+
## Add the controller with it's methods to routes
|
15
|
+
## @param ctrl [Flame::Controller] class of the controller which will be added
|
16
|
+
## @param path [String] root path for controller's methods
|
17
|
+
## @param block [Proc, nil] block for routes refine
|
15
18
|
def add_controller(ctrl, path, block = nil)
|
16
|
-
##
|
19
|
+
## @todo Add Regexp paths
|
17
20
|
|
18
21
|
## Add routes from controller to glob array
|
19
|
-
ctrl.include(*@app.helpers)
|
20
22
|
route_refine = RouteRefine.new(self, ctrl, path, block)
|
21
23
|
concat_routes(route_refine) if ActionsValidator.new(route_refine).valid?
|
22
24
|
end
|
23
25
|
|
24
26
|
## Find route by any attributes
|
27
|
+
## @param attrs [Hash] attributes for comparing
|
28
|
+
## @return [Flame::Route, nil] return the found route, otherwise `nil`
|
25
29
|
def find_route(attrs)
|
26
30
|
route = routes.find { |r| r.compare_attributes(attrs) }
|
27
31
|
route.dup if route
|
28
32
|
end
|
29
33
|
|
30
34
|
## Find the nearest route by path parts
|
35
|
+
## @param path_parts [Array] parts of path for route finding
|
36
|
+
## @return [Flame::Route, nil] return the found nearest route, otherwise `nil`
|
31
37
|
def find_nearest_route(path_parts)
|
32
38
|
while path_parts.size >= 0
|
33
39
|
route = find_route(path_parts: path_parts)
|
@@ -37,38 +43,24 @@ module Flame
|
|
37
43
|
route
|
38
44
|
end
|
39
45
|
|
40
|
-
## Find hooks by Route
|
41
|
-
def find_hooks(route)
|
42
|
-
result = {}
|
43
|
-
hooks[route[:controller]].each do |type, hash|
|
44
|
-
if type == :error
|
45
|
-
result[type] = hash
|
46
|
-
else
|
47
|
-
result[type] = (hash[route[:action]] || []) | (hash[:*] || [])
|
48
|
-
end
|
49
|
-
end
|
50
|
-
# p result
|
51
|
-
result
|
52
|
-
end
|
53
|
-
|
54
46
|
private
|
55
47
|
|
48
|
+
## Add `RouteRefine` routes to the routes of `Flame::Router`
|
49
|
+
## @param route_refine [Flame::Router::RouteRefine] `RouteRefine` with routes
|
56
50
|
def concat_routes(route_refine)
|
57
51
|
routes.concat(route_refine.routes)
|
58
|
-
hooks[route_refine.ctrl] = route_refine.hooks
|
59
52
|
end
|
60
53
|
|
61
|
-
## Helper
|
54
|
+
## Helper class for controller routing refine
|
62
55
|
class RouteRefine
|
63
56
|
attr_accessor :rest_routes
|
64
|
-
attr_reader :ctrl, :routes
|
65
|
-
|
66
|
-
HOOK_TYPES = [:before, :after, :error].freeze
|
57
|
+
attr_reader :ctrl, :routes
|
67
58
|
|
68
59
|
def self.http_methods
|
69
60
|
[:GET, :POST, :PUT, :DELETE]
|
70
61
|
end
|
71
62
|
|
63
|
+
## Defaults REST routes (methods, pathes, controllers actions)
|
72
64
|
def rest_routes
|
73
65
|
@rest_routes ||= [
|
74
66
|
{ method: :GET, path: '/', action: :index },
|
@@ -84,11 +76,22 @@ module Flame
|
|
84
76
|
@ctrl = ctrl
|
85
77
|
@path = path || @ctrl.default_path
|
86
78
|
@routes = []
|
87
|
-
@hooks = HOOK_TYPES.each_with_object({}) { |type, hash| hash[type] = {} }
|
88
79
|
execute(&block)
|
89
80
|
end
|
90
81
|
|
91
82
|
http_methods.each do |request_method|
|
83
|
+
## Define refine methods for all HTTP methods
|
84
|
+
## @overload post(path, action)
|
85
|
+
## Execute action on requested path and HTTP method
|
86
|
+
## @param path [String] path of method for the request
|
87
|
+
## @param action [Symbol] name of method for the request
|
88
|
+
## @example Set path to '/bye' and method to :POST for action `goodbye`
|
89
|
+
## post '/bye', :goodbye
|
90
|
+
## @overload post(action)
|
91
|
+
## Execute action on requested HTTP method
|
92
|
+
## @param action [Symbol] name of method for the request
|
93
|
+
## @example Set method to :POST for action `goodbye`
|
94
|
+
## post :goodbye
|
92
95
|
define_method(request_method.downcase) do |path, action = nil|
|
93
96
|
if action.nil?
|
94
97
|
action = path.to_sym
|
@@ -99,14 +102,8 @@ module Flame
|
|
99
102
|
end
|
100
103
|
end
|
101
104
|
|
102
|
-
|
103
|
-
|
104
|
-
define_method(type) do |actions = default_actions, action = nil, &block|
|
105
|
-
actions = [actions] unless actions.is_a?(Array)
|
106
|
-
actions.each { |a| (@hooks[type][a] ||= []).push(action || block) }
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
105
|
+
## Assign remaining methods of the controller
|
106
|
+
## to defaults pathes and HTTP methods
|
110
107
|
def defaults
|
111
108
|
rest
|
112
109
|
@ctrl.public_instance_methods(false).each do |action|
|
@@ -115,6 +112,7 @@ module Flame
|
|
115
112
|
end
|
116
113
|
end
|
117
114
|
|
115
|
+
## Assign methods of the controller to REST architecture
|
118
116
|
def rest
|
119
117
|
rest_routes.each do |rest_route|
|
120
118
|
action = rest_route[:action]
|
@@ -125,6 +123,10 @@ module Flame
|
|
125
123
|
end
|
126
124
|
end
|
127
125
|
|
126
|
+
## Mount controller inside other (parent) controller
|
127
|
+
## @param ctrl [Flame::Controller] class of mounting controller
|
128
|
+
## @param path [String, nil] root path for mounting controller
|
129
|
+
## @yield Block of code for routes refine
|
128
130
|
def mount(ctrl, path = nil, &block)
|
129
131
|
path = path_merge(
|
130
132
|
@path,
|
@@ -135,15 +137,18 @@ module Flame
|
|
135
137
|
|
136
138
|
private
|
137
139
|
|
140
|
+
## Execute block of refinings end sorting routes
|
138
141
|
def execute(&block)
|
139
142
|
block.nil? ? defaults : instance_exec(&block)
|
140
|
-
@
|
141
|
-
|
142
|
-
|
143
|
+
# instance_exec(&@ctrl.mounted) if @ctrl.respond_to? :mounted
|
144
|
+
# @router.app.helpers.each do |helper|
|
145
|
+
# instance_exec(&helper.mount) if helper.respond_to?(:mount)
|
146
|
+
# end
|
143
147
|
# p @routes
|
144
148
|
@routes.sort! { |a, b| b[:path] <=> a[:path] }
|
145
149
|
end
|
146
150
|
|
151
|
+
## Build path for the action of controller
|
147
152
|
def make_path(path, action = nil, force_params = false)
|
148
153
|
## TODO: Add :arg:type support (:id:num, :name:str, etc.)
|
149
154
|
unshifted = force_params ? path : action_path(action)
|
data/lib/flame/validators.rb
CHANGED
@@ -59,8 +59,6 @@ module Flame
|
|
59
59
|
class ActionsValidator
|
60
60
|
def initialize(route_refine)
|
61
61
|
@routes_actions = route_refine.routes.map { |route| route[:action] }
|
62
|
-
@hooks_actions = route_refine.hooks.values.map(&:values).flatten
|
63
|
-
@hooks_actions.select! { |action| action.is_a? Symbol }
|
64
62
|
@ctrl = route_refine.ctrl
|
65
63
|
@ctrl_actions = {
|
66
64
|
public: @ctrl.public_instance_methods(false),
|
@@ -69,9 +67,7 @@ module Flame
|
|
69
67
|
end
|
70
68
|
|
71
69
|
def valid?
|
72
|
-
no_extra_routes_actions? &&
|
73
|
-
no_extra_hooks_actions? &&
|
74
|
-
no_extra_controller_actions?
|
70
|
+
no_extra_routes_actions? && no_extra_controller_actions?
|
75
71
|
end
|
76
72
|
|
77
73
|
private
|
@@ -84,14 +80,6 @@ module Flame
|
|
84
80
|
)
|
85
81
|
end
|
86
82
|
|
87
|
-
def no_extra_hooks_actions?
|
88
|
-
extra_hooks_actions = @hooks_actions - @ctrl_actions[:all]
|
89
|
-
return true if extra_hooks_actions.empty?
|
90
|
-
fail RouterError::ExtraRoutesActionsError.new(
|
91
|
-
@ctrl, extra_hooks_actions
|
92
|
-
)
|
93
|
-
end
|
94
|
-
|
95
83
|
def no_extra_controller_actions?
|
96
84
|
extra_ctrl_actions = @ctrl_actions[:public] - @routes_actions
|
97
85
|
return true if extra_ctrl_actions.empty?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flame
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Popov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|