flame 4.11.3.2 → 4.12.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|