flame 3.6.2 → 4.0.0
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.
- 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
|