flame 4.11.3.2 → 4.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/flame +1 -0
- data/lib/flame.rb +2 -0
- data/lib/flame/application.rb +4 -2
- data/lib/flame/controller.rb +11 -12
- data/lib/flame/cookies.rb +2 -0
- data/lib/flame/dispatcher.rb +22 -28
- data/lib/flame/errors.rb +29 -67
- data/lib/flame/render.rb +3 -1
- data/lib/flame/request.rb +2 -0
- data/lib/flame/response.rb +13 -0
- data/lib/flame/route.rb +47 -18
- data/lib/flame/router.rb +12 -11
- data/lib/flame/static.rb +3 -1
- data/lib/flame/validators.rb +41 -67
- data/lib/flame/version.rb +1 -1
- data/template/Gemfile +2 -0
- data/template/Rakefile.erb +2 -0
- data/template/app.rb.erb +2 -0
- data/template/config.ru.erb +2 -0
- data/template/config/sequel.rb.erb +2 -0
- data/template/controllers/_base_controller.rb.erb +2 -0
- data/template/server +1 -0
- metadata +115 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3b3c22d674ae570e3f8f2fd8874ec54f87dfb39b
|
|
4
|
+
data.tar.gz: a8ab56284dc70eb6babcf86a2dd80281757278dd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: affe6a9c49a9eb2bd31ea0ed63540451e66a9986a35ed5a2d65cfd66363b13e0452eef7f8f194a00552203dfabfde31cd06550ac033256d04421b14d9398fe72
|
|
7
|
+
data.tar.gz: c40433fa6ef393e1d3630ea4817aeb490639cdb2c16934849947c88705b063c18ca452db88d5b5ee8091ab5acd3e7dee37c3e2bae946e6d74b0dc82914a95e37
|
data/bin/flame
CHANGED
data/lib/flame.rb
CHANGED
data/lib/flame/application.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'router'
|
|
2
4
|
require_relative 'dispatcher'
|
|
3
5
|
|
|
@@ -56,7 +58,7 @@ module Flame
|
|
|
56
58
|
## defaults
|
|
57
59
|
## end
|
|
58
60
|
def self.mount(ctrl, path = nil, &block)
|
|
59
|
-
router.add_controller(ctrl, path, block)
|
|
61
|
+
router.add_controller(ctrl, path, &block)
|
|
60
62
|
end
|
|
61
63
|
|
|
62
64
|
## Router for routing
|
|
@@ -71,7 +73,7 @@ module Flame
|
|
|
71
73
|
## Initialize default for config directories
|
|
72
74
|
def self.default_config_dirs(root_dir:)
|
|
73
75
|
result = { root_dir: File.realpath(root_dir) }
|
|
74
|
-
%i
|
|
76
|
+
%i[public views config tmp].each do |key|
|
|
75
77
|
result[:"#{key}_dir"] = proc { File.join(config[:root_dir], key.to_s) }
|
|
76
78
|
end
|
|
77
79
|
result
|
data/lib/flame/controller.rb
CHANGED
|
@@ -17,7 +17,7 @@ module Flame
|
|
|
17
17
|
def_delegators(
|
|
18
18
|
:@dispatcher,
|
|
19
19
|
:config, :request, :params, :halt, :session, :response, :status, :body,
|
|
20
|
-
:default_body
|
|
20
|
+
:default_body
|
|
21
21
|
)
|
|
22
22
|
|
|
23
23
|
## Initialize the controller for request execution
|
|
@@ -58,12 +58,12 @@ module Flame
|
|
|
58
58
|
# Set the Content-Disposition to "attachment" with the specified filename,
|
|
59
59
|
# instructing the user agents to prompt to save.
|
|
60
60
|
def attachment(filename = nil, disposition = :attachment)
|
|
61
|
-
content_dis = 'Content-Disposition'
|
|
61
|
+
content_dis = 'Content-Disposition'
|
|
62
62
|
response[content_dis] = disposition.to_s
|
|
63
63
|
return unless filename
|
|
64
64
|
response[content_dis] << "; filename=\"#{File.basename(filename)}\""
|
|
65
65
|
ext = File.extname(filename)
|
|
66
|
-
content_type
|
|
66
|
+
response.content_type = ext unless ext.empty?
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
## Render a template with `Flame::Render` (based on Tilt-engine)
|
|
@@ -88,7 +88,7 @@ module Flame
|
|
|
88
88
|
## @param method [Symbol] name of the controller method
|
|
89
89
|
def execute(method)
|
|
90
90
|
# send method
|
|
91
|
-
body send(method, *select_args(method))
|
|
91
|
+
body send(method, *select_args(method).values)
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
## Default method for Internal Server Error, can be inherited
|
|
@@ -98,14 +98,13 @@ module Flame
|
|
|
98
98
|
|
|
99
99
|
private
|
|
100
100
|
|
|
101
|
-
def select_args(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
def select_args(action)
|
|
102
|
+
# p action, params, parameters
|
|
103
|
+
method(action).parameters.each_with_object({}) do |parameter, result|
|
|
104
|
+
key = parameter.last
|
|
105
|
+
next if params[key].nil?
|
|
106
|
+
result[key] = params[key]
|
|
107
107
|
end
|
|
108
|
-
params_select.call(:req) + params_select.call(:opt).compact
|
|
109
108
|
end
|
|
110
109
|
|
|
111
110
|
def add_controller_class(args)
|
|
@@ -119,7 +118,7 @@ module Flame
|
|
|
119
118
|
## Default root path of the controller for requests
|
|
120
119
|
def default_path
|
|
121
120
|
modules = name.underscore.split('/')
|
|
122
|
-
parts = modules[-1].split('_') - %w
|
|
121
|
+
parts = modules[-1].split('_') - %w[index controller ctrl]
|
|
123
122
|
return modules[-2] if parts.empty?
|
|
124
123
|
parts.join('_')
|
|
125
124
|
end
|
data/lib/flame/cookies.rb
CHANGED
data/lib/flame/dispatcher.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'gorilla-patch/symbolize'
|
|
2
4
|
|
|
3
5
|
require_relative 'cookies'
|
|
@@ -25,9 +27,9 @@ module Flame
|
|
|
25
27
|
## Start of execution the request
|
|
26
28
|
def run!
|
|
27
29
|
catch :halt do
|
|
28
|
-
|
|
29
|
-
try_static ||
|
|
30
|
+
try_static ||
|
|
30
31
|
try_static(File.join(__dir__, '..', '..', 'public')) ||
|
|
32
|
+
try_route ||
|
|
31
33
|
halt(404)
|
|
32
34
|
end
|
|
33
35
|
response.write body
|
|
@@ -76,12 +78,6 @@ module Flame
|
|
|
76
78
|
@app.config
|
|
77
79
|
end
|
|
78
80
|
|
|
79
|
-
## Access to Content-Type header of response
|
|
80
|
-
def content_type(ext = nil)
|
|
81
|
-
return response[Rack::CONTENT_TYPE] unless ext
|
|
82
|
-
response[Rack::CONTENT_TYPE] = Rack::Mime.mime_type(ext)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
81
|
## Build a path to the given controller and action, with any expected params
|
|
86
82
|
##
|
|
87
83
|
## @param ctrl [Flame::Controller] class of controller
|
|
@@ -96,32 +92,30 @@ module Flame
|
|
|
96
92
|
def path_to(ctrl, action = :index, args = {})
|
|
97
93
|
route = @app.class.router.find_route(controller: ctrl, action: action)
|
|
98
94
|
raise Errors::RouteNotFoundError.new(ctrl, action) unless route
|
|
99
|
-
|
|
95
|
+
query = Rack::Utils.build_nested_query args.delete(:params)
|
|
96
|
+
query = nil if query&.empty?
|
|
100
97
|
path = route.assign_arguments(args)
|
|
101
98
|
path = '/' if path.empty?
|
|
102
|
-
path
|
|
99
|
+
URI::Generic.build(path: path, query: query).to_s
|
|
103
100
|
end
|
|
104
101
|
|
|
105
102
|
## Interrupt the execution of route, and set new optional data
|
|
106
103
|
## (otherwise using existing)
|
|
107
|
-
## @param
|
|
108
|
-
## set new HTTP status code
|
|
109
|
-
## @param new_body [String] set new body
|
|
110
|
-
## @param new_headers [
|
|
104
|
+
## @param new_status [Integer, nil]
|
|
105
|
+
## set new HTTP status code
|
|
106
|
+
## @param new_body [String, nil] set new body
|
|
107
|
+
## @param new_headers [Hash, nil] merge new headers
|
|
108
|
+
## @example Halt, no change status or body
|
|
109
|
+
## halt
|
|
111
110
|
## @example Halt with 500, no change body
|
|
112
111
|
## halt 500
|
|
113
112
|
## @example Halt with 404, render template
|
|
114
113
|
## halt 404, render('errors/404')
|
|
115
114
|
## @example Halt with 200, set new headers
|
|
116
115
|
## halt 200, 'Cats!', 'Content-Type' => 'animal/cat'
|
|
117
|
-
def halt(
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
when Integer then status new_status_or_body
|
|
121
|
-
end
|
|
122
|
-
# new_status.is_a?(String) ? () : (status new_status)
|
|
123
|
-
body new_body if new_body
|
|
124
|
-
default_body_of_nearest_route if body.empty?
|
|
116
|
+
def halt(new_status = nil, new_body = nil, new_headers = {})
|
|
117
|
+
status new_status if new_status
|
|
118
|
+
body new_body || (default_body_of_nearest_route if body.empty?)
|
|
125
119
|
response.headers.merge!(new_headers)
|
|
126
120
|
throw :halt
|
|
127
121
|
end
|
|
@@ -134,7 +128,7 @@ module Flame
|
|
|
134
128
|
"#{error.class} - #{error.message}:",
|
|
135
129
|
*error.backtrace
|
|
136
130
|
].join("\n\t")
|
|
137
|
-
@env[
|
|
131
|
+
@env[Rack::RACK_ERRORS].puts(error_message)
|
|
138
132
|
end
|
|
139
133
|
|
|
140
134
|
## Generate default body of error page
|
|
@@ -152,17 +146,17 @@ module Flame
|
|
|
152
146
|
path_parts: request.path_parts
|
|
153
147
|
)
|
|
154
148
|
return nil unless route
|
|
149
|
+
status 200
|
|
155
150
|
execute_route(route)
|
|
156
151
|
end
|
|
157
152
|
|
|
158
153
|
## Execute route
|
|
159
154
|
## @param route [Flame::Route] route that must be executed
|
|
160
|
-
def execute_route(route)
|
|
161
|
-
status 200
|
|
155
|
+
def execute_route(route, action = route.action)
|
|
162
156
|
params.merge!(route.arguments(request.path_parts))
|
|
163
157
|
# route.execute(self)
|
|
164
158
|
controller = route.controller.new(self)
|
|
165
|
-
controller.send(:execute,
|
|
159
|
+
controller.send(:execute, action)
|
|
166
160
|
rescue => exception
|
|
167
161
|
# p 'rescue from dispatcher'
|
|
168
162
|
dump_error(exception)
|
|
@@ -182,8 +176,8 @@ module Flame
|
|
|
182
176
|
## or it's `default_body` method not defined
|
|
183
177
|
return default_body unless route
|
|
184
178
|
## Execute `default_body` method for the founded route
|
|
185
|
-
route
|
|
186
|
-
|
|
179
|
+
execute_route(route, :default_body)
|
|
180
|
+
default_body if body.empty?
|
|
187
181
|
end
|
|
188
182
|
end
|
|
189
183
|
end
|
data/lib/flame/errors.rb
CHANGED
|
@@ -1,60 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Flame
|
|
2
4
|
module Errors
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def message
|
|
15
|
-
"Controller '#{@ctrl}' has no methods" \
|
|
16
|
-
" '#{@extra_actions.join(', ')}' from routes"
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
## Error if controller has not assigned in routes actions
|
|
21
|
-
class ExtraControllerActionsError < ActionsError
|
|
22
|
-
def message
|
|
23
|
-
"Routes for '#{@ctrl}' has no methods" \
|
|
24
|
-
" '#{@extra_actions.join(', ')}'"
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
## Error for Flame::Router::RouteRefine.arguments_valid?
|
|
29
|
-
class ArgumentsError < StandardError
|
|
30
|
-
def initialize(ctrl, action, path, extra_args)
|
|
31
|
-
@ctrl = ctrl
|
|
32
|
-
@action = action
|
|
33
|
-
@path = path
|
|
34
|
-
@extra_args = extra_args
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
## Error if path has more arguments, than controller's method
|
|
39
|
-
class ExtraPathArgumentsError < ArgumentsError
|
|
40
|
-
def message
|
|
41
|
-
"Method '#{@action}' from controller '#{@ctrl}'" \
|
|
42
|
-
" does not know arguments '#{@extra_args.join(', ')}'" \
|
|
43
|
-
" from path '#{@path}'"
|
|
44
|
-
end
|
|
5
|
+
## Error for Route initialization
|
|
6
|
+
class RouteArgumentsError < StandardError
|
|
7
|
+
def initialize(ctrl, action, path, extra)
|
|
8
|
+
@ctrl = ctrl
|
|
9
|
+
@action = action
|
|
10
|
+
@path = path
|
|
11
|
+
@extra = extra
|
|
12
|
+
@extra[:type_name] = {
|
|
13
|
+
req: 'required',
|
|
14
|
+
opt: 'optional'
|
|
15
|
+
}[@extra[:type]]
|
|
45
16
|
end
|
|
46
17
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
" '#{@
|
|
52
|
-
|
|
18
|
+
def message
|
|
19
|
+
case @extra[:place]
|
|
20
|
+
when :ctrl
|
|
21
|
+
## Error if path has no arguments, that controller's method has
|
|
22
|
+
"Path '#{@path}' has no #{@extra[:type_name]}" \
|
|
23
|
+
" arguments #{@extra[:args].inspect}"
|
|
24
|
+
when :path
|
|
25
|
+
## Error if path has more arguments, than controller's method
|
|
26
|
+
"Action '#{@ctrl}##{@action}' has no #{@extra[:type_name]}" \
|
|
27
|
+
" arguments #{@extra[:args].inspect}"
|
|
53
28
|
end
|
|
54
29
|
end
|
|
55
30
|
end
|
|
56
31
|
|
|
57
|
-
## Error for Flame::
|
|
32
|
+
## Error for Flame::Dispatcher.path_to
|
|
58
33
|
class RouteNotFoundError < StandardError
|
|
59
34
|
def initialize(ctrl, method)
|
|
60
35
|
@ctrl = ctrl
|
|
@@ -63,32 +38,19 @@ module Flame
|
|
|
63
38
|
|
|
64
39
|
def message
|
|
65
40
|
"Route with controller '#{@ctrl}' and method '#{@method}'" \
|
|
66
|
-
|
|
41
|
+
' not found in application routes'
|
|
67
42
|
end
|
|
68
43
|
end
|
|
69
44
|
|
|
70
|
-
## Error for Flame::
|
|
45
|
+
## Error for Flame::Dispatcher.path_to
|
|
71
46
|
class ArgumentNotAssignedError < StandardError
|
|
72
|
-
def initialize(path,
|
|
47
|
+
def initialize(path, argument)
|
|
73
48
|
@path = path
|
|
74
|
-
@
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def message
|
|
78
|
-
"Argument '#{@path_part}' for path '#{@path}' is not assigned"
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
## Error for Flame::Router.find_path
|
|
83
|
-
class UnexpectedTypeOfHookError < StandardError
|
|
84
|
-
def initialize(hook, route)
|
|
85
|
-
@hook = hook
|
|
86
|
-
@route = route
|
|
49
|
+
@argument = argument
|
|
87
50
|
end
|
|
88
51
|
|
|
89
52
|
def message
|
|
90
|
-
"
|
|
91
|
-
" in route '#{@route}'"
|
|
53
|
+
"Argument '#{@argument}' for path '#{@path}' is not assigned"
|
|
92
54
|
end
|
|
93
55
|
end
|
|
94
56
|
end
|
data/lib/flame/render.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'pathname'
|
|
2
4
|
|
|
3
5
|
require 'tilt'
|
|
@@ -94,7 +96,7 @@ module Flame
|
|
|
94
96
|
## Find possible directories for the controller
|
|
95
97
|
def controller_dirs
|
|
96
98
|
parts = @ctrl.class.underscore.split('/').map do |part|
|
|
97
|
-
(part.split('_') - %w
|
|
99
|
+
(part.split('_') - %w[controller controllers ctrl]).join('_')
|
|
98
100
|
end
|
|
99
101
|
combine_parts(parts).map! { |path| path.join('/') }
|
|
100
102
|
end
|
data/lib/flame/request.rb
CHANGED
data/lib/flame/response.rb
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Flame
|
|
2
4
|
## Class for responses
|
|
3
5
|
class Response < Rack::Response
|
|
6
|
+
## Set Content-Type header directly or by extension
|
|
7
|
+
## @param value [String] value for header or extension of file
|
|
8
|
+
## @return [String] setted value
|
|
9
|
+
## @example Set value directly
|
|
10
|
+
## content_type = 'text/css'
|
|
11
|
+
## @example Set value by file extension
|
|
12
|
+
## content_type = '.css'
|
|
13
|
+
def content_type=(value)
|
|
14
|
+
value = Rack::Mime.mime_type(value) if value.start_with? '.'
|
|
15
|
+
set_header Rack::CONTENT_TYPE, value
|
|
16
|
+
end
|
|
4
17
|
end
|
|
5
18
|
end
|
data/lib/flame/route.rb
CHANGED
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative 'validators'
|
|
4
|
+
|
|
3
5
|
module Flame
|
|
4
6
|
class Router
|
|
5
|
-
ARG_CHAR = ':'
|
|
6
|
-
ARG_CHAR_OPT = '?'
|
|
7
|
+
ARG_CHAR = ':'
|
|
8
|
+
ARG_CHAR_OPT = '?'
|
|
7
9
|
|
|
8
10
|
## Class for Route in Router.routes
|
|
9
11
|
class Route
|
|
10
12
|
attr_reader :method, :controller, :action, :path, :path_parts
|
|
11
13
|
|
|
12
|
-
def initialize(controller, action, method,
|
|
14
|
+
def initialize(controller, action, method, ctrl_path, action_path)
|
|
15
|
+
## Merge action path with controller path
|
|
16
|
+
path = self.class.path_merge(ctrl_path, action_path)
|
|
13
17
|
@controller = controller
|
|
14
18
|
@action = action
|
|
15
19
|
@method = method.to_sym.upcase
|
|
16
20
|
## MAKE PATH
|
|
17
21
|
@path = path
|
|
18
|
-
Validators::
|
|
22
|
+
Validators::RouteArgumentsValidator.new(
|
|
23
|
+
@controller, action_path, @action
|
|
24
|
+
).valid?
|
|
19
25
|
@path_parts = @path.to_s.split('/').reject(&:empty?)
|
|
20
26
|
freeze
|
|
21
27
|
end
|
|
@@ -38,24 +44,38 @@ module Flame
|
|
|
38
44
|
## Assign arguments to path for `Controller.path_to`
|
|
39
45
|
## @param args [Hash] arguments for assigning
|
|
40
46
|
def assign_arguments(args = {})
|
|
41
|
-
parts = @path_parts.map { |part| assign_argument(part, args) }
|
|
42
|
-
self.class.path_merge(parts.unshift(
|
|
47
|
+
parts = @path_parts.map { |part| assign_argument(part, args) }.compact
|
|
48
|
+
self.class.path_merge(parts.unshift(nil))
|
|
43
49
|
end
|
|
44
50
|
|
|
45
51
|
## Extract arguments from request_parts for `execute`
|
|
46
52
|
## @param request_parts [Array] parts of the request (Array of String)
|
|
47
53
|
def arguments(request_parts)
|
|
48
54
|
@path_parts.each_with_index.with_object({}) do |(path_part, i), args|
|
|
49
|
-
request_part = request_parts[i]
|
|
55
|
+
request_part = request_parts[i].to_s
|
|
50
56
|
path_part_opt = path_part[1] == ARG_CHAR_OPT
|
|
51
57
|
next args unless path_part[0] == ARG_CHAR
|
|
52
|
-
break args if path_part_opt && request_part.
|
|
58
|
+
break args if path_part_opt && request_part.empty?
|
|
53
59
|
args[
|
|
54
60
|
path_part[(path_part_opt ? 2 : 1)..-1].to_sym
|
|
55
61
|
] = URI.decode(request_part)
|
|
56
62
|
end
|
|
57
63
|
end
|
|
58
64
|
|
|
65
|
+
## Method for Routes comparison
|
|
66
|
+
def ==(other)
|
|
67
|
+
%i[controller action method path_parts].reduce(true) do |result, method|
|
|
68
|
+
result && (
|
|
69
|
+
public_send(method) == other.public_send(method)
|
|
70
|
+
)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
## Compare by path parts count (more is matter)
|
|
75
|
+
def <=>(other)
|
|
76
|
+
other.path_parts.size <=> path_parts.size
|
|
77
|
+
end
|
|
78
|
+
|
|
59
79
|
def self.path_merge(*parts)
|
|
60
80
|
parts.join('/').gsub(%r{\/{2,}}, '/')
|
|
61
81
|
end
|
|
@@ -79,19 +99,28 @@ module Flame
|
|
|
79
99
|
end
|
|
80
100
|
|
|
81
101
|
def compare_path_parts(request_parts)
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
# compare_parts(request_parts, self[:path_parts])
|
|
86
|
-
request_parts.each_with_index do |request_part, i|
|
|
87
|
-
path_part = @path_parts[i]
|
|
88
|
-
# p request_part, path_part
|
|
89
|
-
break false unless path_part
|
|
90
|
-
next if path_part[0] == ARG_CHAR
|
|
91
|
-
break false unless request_part == path_part
|
|
102
|
+
return false unless request_contain_required_path_parts?(request_parts)
|
|
103
|
+
[request_parts.size, @path_parts.size].max.times do |i|
|
|
104
|
+
break false unless compare_parts request_parts[i], @path_parts[i]
|
|
92
105
|
end
|
|
93
106
|
end
|
|
94
107
|
|
|
108
|
+
def compare_parts(request_part, path_part)
|
|
109
|
+
# p request_part, path_part
|
|
110
|
+
return unless path_part
|
|
111
|
+
return if request_part.nil? && path_part[1] != ARG_CHAR_OPT
|
|
112
|
+
# p request_part, path_part
|
|
113
|
+
return true if path_part[0] == ARG_CHAR
|
|
114
|
+
return true if request_part == path_part
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def request_contain_required_path_parts?(request_parts)
|
|
118
|
+
req_path_parts = @path_parts.reject { |part| part[1] == ARG_CHAR_OPT }
|
|
119
|
+
fixed_path_parts = @path_parts.reject { |part| part[0] == ARG_CHAR }
|
|
120
|
+
(request_parts & fixed_path_parts) == fixed_path_parts &&
|
|
121
|
+
request_parts.count >= req_path_parts.count
|
|
122
|
+
end
|
|
123
|
+
|
|
95
124
|
## Helpers for `assign_arguments`
|
|
96
125
|
def assign_argument(path_part, args = {})
|
|
97
126
|
## Not argument
|
data/lib/flame/router.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'route'
|
|
2
|
-
require_relative 'validators'
|
|
3
4
|
|
|
4
5
|
module Flame
|
|
5
6
|
## Router class for routing
|
|
@@ -13,14 +14,13 @@ module Flame
|
|
|
13
14
|
|
|
14
15
|
## Add the controller with it's methods to routes
|
|
15
16
|
## @param ctrl [Flame::Controller] class of the controller which will be added
|
|
16
|
-
## @param path [String] root path for controller's methods
|
|
17
|
-
## @
|
|
18
|
-
def add_controller(ctrl, path
|
|
17
|
+
## @param path [String, nil] root path for controller's methods
|
|
18
|
+
## @yield block for routes refine
|
|
19
|
+
def add_controller(ctrl, path = nil, &block)
|
|
19
20
|
## @todo Add Regexp paths
|
|
20
21
|
|
|
21
22
|
## Add routes from controller to glob array
|
|
22
23
|
route_refine = RouteRefine.new(self, ctrl, path, block)
|
|
23
|
-
return unless Validators::ActionsValidator.new(route_refine).valid?
|
|
24
24
|
concat_routes(route_refine)
|
|
25
25
|
end
|
|
26
26
|
|
|
@@ -76,7 +76,7 @@ module Flame
|
|
|
76
76
|
execute(&block)
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
%i
|
|
79
|
+
%i[GET POST PUT PATCH DELETE].each do |request_method|
|
|
80
80
|
## Define refine methods for all HTTP methods
|
|
81
81
|
## @overload post(path, action)
|
|
82
82
|
## Execute action on requested path and HTTP method
|
|
@@ -98,10 +98,11 @@ module Flame
|
|
|
98
98
|
end
|
|
99
99
|
## Make path by controller method with parameners
|
|
100
100
|
path = default_action_path(action, path) if prefix || path.nil?
|
|
101
|
-
##
|
|
102
|
-
|
|
103
|
-
route
|
|
101
|
+
## Init new Route
|
|
102
|
+
route = Route.new(@ctrl, action, method, @path, path)
|
|
103
|
+
## Try to find route with the same action
|
|
104
104
|
index = find_route_index(action: action)
|
|
105
|
+
## Overwrite route if needed
|
|
105
106
|
index ? @routes[index] = route : @routes.push(route)
|
|
106
107
|
end
|
|
107
108
|
end
|
|
@@ -132,7 +133,7 @@ module Flame
|
|
|
132
133
|
## @yield Block of code for routes refine
|
|
133
134
|
def mount(ctrl, path = nil, &block)
|
|
134
135
|
path = Route.path_merge(@path, path || ctrl.default_path)
|
|
135
|
-
@router.add_controller(ctrl, path, block)
|
|
136
|
+
@router.add_controller(ctrl, path, &block)
|
|
136
137
|
end
|
|
137
138
|
|
|
138
139
|
private
|
|
@@ -141,7 +142,7 @@ module Flame
|
|
|
141
142
|
def execute(&block)
|
|
142
143
|
instance_exec(&block) if block
|
|
143
144
|
defaults
|
|
144
|
-
@routes.sort!
|
|
145
|
+
@routes.sort!
|
|
145
146
|
end
|
|
146
147
|
|
|
147
148
|
def find_route_index(attrs)
|
data/lib/flame/static.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Flame
|
|
2
4
|
class Dispatcher
|
|
3
5
|
## Module for working with static files
|
|
@@ -19,7 +21,7 @@ module Flame
|
|
|
19
21
|
def return_static(file)
|
|
20
22
|
file_time = File.mtime(file)
|
|
21
23
|
halt 304 if static_cached?(file_time)
|
|
22
|
-
content_type File.extname(file)
|
|
24
|
+
response.content_type = File.extname(file)
|
|
23
25
|
response[Rack::CACHE_CONTROL] = 'no-cache'
|
|
24
26
|
response['Last-Modified'] = file_time.httpdate
|
|
25
27
|
body File.read(file)
|
data/lib/flame/validators.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
require_relative 'errors'
|
|
3
4
|
|
|
4
5
|
module Flame
|
|
5
6
|
module Validators
|
|
6
7
|
## Compare arguments from path and from controller's action
|
|
7
|
-
class
|
|
8
|
+
class RouteArgumentsValidator
|
|
8
9
|
def initialize(ctrl, path, action)
|
|
9
10
|
@ctrl = ctrl
|
|
10
11
|
@path = path
|
|
@@ -12,81 +13,54 @@ module Flame
|
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def valid?
|
|
15
|
-
##
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
## Get hash of any extra arguments
|
|
17
|
+
extra = %i[req opt].find do |type|
|
|
18
|
+
found = extra_arguments(type).find do |place, args|
|
|
19
|
+
break { place: place, type: type, args: args } if args.any?
|
|
20
|
+
end
|
|
21
|
+
break found if found
|
|
22
|
+
end
|
|
23
|
+
## Return true if no any extra argument
|
|
24
|
+
return true unless extra
|
|
25
|
+
## Raise error with extra arguments
|
|
26
|
+
raise Errors::RouteArgumentsError.new(@ctrl, @action, @path, extra)
|
|
21
27
|
end
|
|
22
28
|
|
|
23
29
|
private
|
|
24
30
|
|
|
25
31
|
## Split path to args array
|
|
26
|
-
def path_arguments
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
def path_arguments
|
|
33
|
+
@path_arguments ||= @path.split('/')
|
|
34
|
+
.each_with_object(req: [], opt: []) do |part, hash|
|
|
35
|
+
## Take only argument parts
|
|
36
|
+
next if part[0] != Router::ARG_CHAR
|
|
37
|
+
## Clean argument from special chars
|
|
38
|
+
clean_part = part.delete(
|
|
39
|
+
Router::ARG_CHAR + Router::ARG_CHAR_OPT
|
|
40
|
+
).to_sym
|
|
41
|
+
## Memorize arguments
|
|
42
|
+
hash[part[1] != Router::ARG_CHAR_OPT ? :req : :opt] << clean_part
|
|
43
|
+
end
|
|
32
44
|
end
|
|
33
45
|
|
|
34
46
|
## Take args from controller's action
|
|
35
|
-
def action_arguments
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
req:
|
|
41
|
-
|
|
47
|
+
def action_arguments
|
|
48
|
+
return @action_arguments if @action_arguments
|
|
49
|
+
## Get all parameters (arguments) from method
|
|
50
|
+
## Than collect and sort parameters into hash
|
|
51
|
+
@ctrl.instance_method(@action).parameters
|
|
52
|
+
.each_with_object(req: [], opt: []) do |param, hash|
|
|
53
|
+
## Only required parameters must be in `:req`
|
|
54
|
+
hash[param[0]] << param[1]
|
|
55
|
+
end
|
|
42
56
|
end
|
|
43
57
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def no_extra_action_arguments?
|
|
54
|
-
## Subtraction path args from action required args
|
|
55
|
-
extra_action_args = @action_args[:req] - @path_args
|
|
56
|
-
return true if extra_action_args.empty?
|
|
57
|
-
raise Errors::RouterError::ExtraActionArgumentsError.new(
|
|
58
|
-
@ctrl, @action, @path, extra_action_args
|
|
59
|
-
)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
## Compare actions from routes and from controller
|
|
64
|
-
class ActionsValidator
|
|
65
|
-
def initialize(route_refine)
|
|
66
|
-
@routes_actions = route_refine.routes.map(&:action)
|
|
67
|
-
@ctrl = route_refine.ctrl
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def valid?
|
|
71
|
-
no_extra_routes_actions? && no_extra_controller_actions?
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
private
|
|
75
|
-
|
|
76
|
-
def no_extra_routes_actions?
|
|
77
|
-
extra_routes_actions = @routes_actions - @ctrl.actions
|
|
78
|
-
return true if extra_routes_actions.empty?
|
|
79
|
-
raise Errors::RouterError::ExtraRoutesActionsError.new(
|
|
80
|
-
@ctrl, extra_routes_actions
|
|
81
|
-
)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def no_extra_controller_actions?
|
|
85
|
-
extra_ctrl_actions = @ctrl.actions - @routes_actions
|
|
86
|
-
return true if extra_ctrl_actions.empty?
|
|
87
|
-
raise Errors::RouterError::ExtraControllerActionsError.new(
|
|
88
|
-
@ctrl, extra_ctrl_actions
|
|
89
|
-
)
|
|
58
|
+
## Calculate path and action extra arguments
|
|
59
|
+
def extra_arguments(type)
|
|
60
|
+
{
|
|
61
|
+
ctrl: action_arguments[type] - path_arguments[type],
|
|
62
|
+
path: path_arguments[type] - action_arguments[type]
|
|
63
|
+
}
|
|
90
64
|
end
|
|
91
65
|
end
|
|
92
66
|
end
|
data/lib/flame/version.rb
CHANGED
data/template/Gemfile
CHANGED
data/template/Rakefile.erb
CHANGED
data/template/app.rb.erb
CHANGED
data/template/config.ru.erb
CHANGED
data/template/server
CHANGED
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.
|
|
4
|
+
version: 4.12.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexander Popov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2017-
|
|
11
|
+
date: 2017-05-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rack
|
|
@@ -80,6 +80,104 @@ dependencies:
|
|
|
80
80
|
version: '0'
|
|
81
81
|
- !ruby/object:Gem::Dependency
|
|
82
82
|
name: rubocop
|
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - "~>"
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '0.48'
|
|
88
|
+
type: :development
|
|
89
|
+
prerelease: false
|
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - "~>"
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '0.48'
|
|
95
|
+
- !ruby/object:Gem::Dependency
|
|
96
|
+
name: rake
|
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
|
98
|
+
requirements:
|
|
99
|
+
- - "~>"
|
|
100
|
+
- !ruby/object:Gem::Version
|
|
101
|
+
version: '12'
|
|
102
|
+
type: :development
|
|
103
|
+
prerelease: false
|
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
105
|
+
requirements:
|
|
106
|
+
- - "~>"
|
|
107
|
+
- !ruby/object:Gem::Version
|
|
108
|
+
version: '12'
|
|
109
|
+
- !ruby/object:Gem::Dependency
|
|
110
|
+
name: bacon
|
|
111
|
+
requirement: !ruby/object:Gem::Requirement
|
|
112
|
+
requirements:
|
|
113
|
+
- - "~>"
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: '1'
|
|
116
|
+
type: :development
|
|
117
|
+
prerelease: false
|
|
118
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
119
|
+
requirements:
|
|
120
|
+
- - "~>"
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: '1'
|
|
123
|
+
- !ruby/object:Gem::Dependency
|
|
124
|
+
name: bacon-colored_output
|
|
125
|
+
requirement: !ruby/object:Gem::Requirement
|
|
126
|
+
requirements:
|
|
127
|
+
- - "~>"
|
|
128
|
+
- !ruby/object:Gem::Version
|
|
129
|
+
version: '1.1'
|
|
130
|
+
type: :development
|
|
131
|
+
prerelease: false
|
|
132
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
133
|
+
requirements:
|
|
134
|
+
- - "~>"
|
|
135
|
+
- !ruby/object:Gem::Version
|
|
136
|
+
version: '1.1'
|
|
137
|
+
- !ruby/object:Gem::Dependency
|
|
138
|
+
name: rack-test
|
|
139
|
+
requirement: !ruby/object:Gem::Requirement
|
|
140
|
+
requirements:
|
|
141
|
+
- - "~>"
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: '0'
|
|
144
|
+
type: :development
|
|
145
|
+
prerelease: false
|
|
146
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
147
|
+
requirements:
|
|
148
|
+
- - "~>"
|
|
149
|
+
- !ruby/object:Gem::Version
|
|
150
|
+
version: '0'
|
|
151
|
+
- !ruby/object:Gem::Dependency
|
|
152
|
+
name: simplecov
|
|
153
|
+
requirement: !ruby/object:Gem::Requirement
|
|
154
|
+
requirements:
|
|
155
|
+
- - "~>"
|
|
156
|
+
- !ruby/object:Gem::Version
|
|
157
|
+
version: '0'
|
|
158
|
+
type: :development
|
|
159
|
+
prerelease: false
|
|
160
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
161
|
+
requirements:
|
|
162
|
+
- - "~>"
|
|
163
|
+
- !ruby/object:Gem::Version
|
|
164
|
+
version: '0'
|
|
165
|
+
- !ruby/object:Gem::Dependency
|
|
166
|
+
name: codecov
|
|
167
|
+
requirement: !ruby/object:Gem::Requirement
|
|
168
|
+
requirements:
|
|
169
|
+
- - "~>"
|
|
170
|
+
- !ruby/object:Gem::Version
|
|
171
|
+
version: '0'
|
|
172
|
+
type: :development
|
|
173
|
+
prerelease: false
|
|
174
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
175
|
+
requirements:
|
|
176
|
+
- - "~>"
|
|
177
|
+
- !ruby/object:Gem::Version
|
|
178
|
+
version: '0'
|
|
179
|
+
- !ruby/object:Gem::Dependency
|
|
180
|
+
name: pry
|
|
83
181
|
requirement: !ruby/object:Gem::Requirement
|
|
84
182
|
requirements:
|
|
85
183
|
- - "~>"
|
|
@@ -92,6 +190,20 @@ dependencies:
|
|
|
92
190
|
- - "~>"
|
|
93
191
|
- !ruby/object:Gem::Version
|
|
94
192
|
version: '0'
|
|
193
|
+
- !ruby/object:Gem::Dependency
|
|
194
|
+
name: pry-byebug
|
|
195
|
+
requirement: !ruby/object:Gem::Requirement
|
|
196
|
+
requirements:
|
|
197
|
+
- - "~>"
|
|
198
|
+
- !ruby/object:Gem::Version
|
|
199
|
+
version: '3'
|
|
200
|
+
type: :development
|
|
201
|
+
prerelease: false
|
|
202
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
203
|
+
requirements:
|
|
204
|
+
- - "~>"
|
|
205
|
+
- !ruby/object:Gem::Version
|
|
206
|
+
version: '3'
|
|
95
207
|
description: Use controller's classes with instance methods as routing actions, mounting
|
|
96
208
|
its in application class.
|
|
97
209
|
email:
|
|
@@ -155,7 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
155
267
|
version: '0'
|
|
156
268
|
requirements: []
|
|
157
269
|
rubyforge_project:
|
|
158
|
-
rubygems_version: 2.6.
|
|
270
|
+
rubygems_version: 2.6.11
|
|
159
271
|
signing_key:
|
|
160
272
|
specification_version: 4
|
|
161
273
|
summary: Web-framework, based on MVC-pattern
|