flame 5.0.0.rc5 → 5.0.0.rc6
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 +5 -5
- data/CHANGELOG.md +921 -0
- data/LICENSE.txt +19 -0
- data/README.md +135 -0
- data/lib/flame.rb +12 -5
- data/lib/flame/application.rb +47 -46
- data/lib/flame/config.rb +73 -0
- data/lib/flame/controller.rb +45 -78
- data/lib/flame/controller/actions.rb +122 -0
- data/lib/flame/{dispatcher → controller}/cookies.rb +8 -3
- data/lib/flame/controller/path_to.rb +34 -10
- data/lib/flame/dispatcher.rb +14 -17
- data/lib/flame/dispatcher/request.rb +25 -6
- data/lib/flame/dispatcher/routes.rb +22 -14
- data/lib/flame/dispatcher/static.rb +13 -9
- data/lib/flame/errors/argument_not_assigned_error.rb +3 -8
- data/lib/flame/errors/config_file_not_found_error.rb +17 -0
- data/lib/flame/errors/controller_not_found_error.rb +19 -0
- data/lib/flame/errors/route_arguments_order_error.rb +5 -10
- data/lib/flame/errors/route_extra_arguments_error.rb +10 -20
- data/lib/flame/errors/route_not_found_error.rb +3 -8
- data/lib/flame/errors/template_not_found_error.rb +2 -8
- data/lib/flame/path.rb +36 -18
- data/lib/flame/render.rb +13 -5
- data/lib/flame/router.rb +7 -157
- data/lib/flame/router/controller_finder.rb +56 -0
- data/lib/flame/router/route.rb +9 -0
- data/lib/flame/router/routes.rb +58 -8
- data/lib/flame/router/routes_refine.rb +144 -0
- data/lib/flame/router/routes_refine/mounting.rb +57 -0
- data/lib/flame/validators.rb +14 -10
- data/lib/flame/version.rb +1 -1
- metadata +91 -99
- data/bin/flame +0 -16
- data/lib/flame/application/config.rb +0 -49
- data/template/.editorconfig +0 -15
- data/template/.gitignore +0 -28
- data/template/.rubocop.yml +0 -14
- data/template/Gemfile +0 -55
- data/template/Rakefile +0 -824
- data/template/application.rb.erb +0 -10
- data/template/config.ru.erb +0 -72
- data/template/config/config.rb.erb +0 -56
- data/template/config/database.example.yml +0 -5
- data/template/config/deploy.example.yml +0 -2
- data/template/config/puma.rb +0 -56
- data/template/config/sequel.rb.erb +0 -22
- data/template/config/server.example.yml +0 -32
- data/template/config/session.example.yml +0 -7
- data/template/controllers/_controller.rb.erb +0 -14
- data/template/controllers/site/_controller.rb.erb +0 -18
- data/template/controllers/site/index_controller.rb.erb +0 -12
- data/template/db/.keep +0 -0
- data/template/filewatchers.yml +0 -12
- data/template/helpers/.keep +0 -0
- data/template/lib/.keep +0 -0
- data/template/locales/en.yml +0 -0
- data/template/models/.keep +0 -0
- data/template/public/.keep +0 -0
- data/template/server +0 -200
- data/template/services/.keep +0 -0
- data/template/views/.keep +0 -0
- data/template/views/site/index.html.erb.erb +0 -1
- data/template/views/site/layout.html.erb.erb +0 -10
@@ -0,0 +1,122 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'gorilla_patch/slice'
|
4
|
+
|
5
|
+
module Flame
|
6
|
+
class Controller
|
7
|
+
## Module for work with actions
|
8
|
+
module Actions
|
9
|
+
include Memery
|
10
|
+
using GorillaPatch::Slice
|
11
|
+
|
12
|
+
## Shortcut for not-inherited public methods: actions
|
13
|
+
## @return [Array<Symbol>] array of actions (public instance methods)
|
14
|
+
def actions
|
15
|
+
public_instance_methods(false)
|
16
|
+
end
|
17
|
+
|
18
|
+
## Re-define public instance methods (actions) from parent
|
19
|
+
## @param actions [Array<Symbol>] Actions for inheritance
|
20
|
+
## @param exclude [Array<Symbol>] Actions for excluding from inheritance
|
21
|
+
## @param from [Module]
|
22
|
+
## Module (or Class) from which actions will be inherited
|
23
|
+
## @example Inherit all parent actions
|
24
|
+
## class MyController < BaseController
|
25
|
+
## inherit_actions
|
26
|
+
## end
|
27
|
+
## @example Inherit certain parent actions
|
28
|
+
## class MyController < BaseController
|
29
|
+
## inherit_actions %i[index show]
|
30
|
+
## end
|
31
|
+
## @example Inherit all parent actions exclude certain
|
32
|
+
## class MyController < BaseController
|
33
|
+
## inherit_actions exclude: %i[edit update]
|
34
|
+
## end
|
35
|
+
## @example Inherit certain actions from specific module
|
36
|
+
## class MyController < BaseController
|
37
|
+
## inherit_actions %i[index show], from: ModuleWithActions
|
38
|
+
## end
|
39
|
+
def inherit_actions(actions = nil, exclude: [], from: superclass)
|
40
|
+
actions = from.actions if actions.nil?
|
41
|
+
actions -= exclude
|
42
|
+
|
43
|
+
actions.each do |action|
|
44
|
+
define_method action, from.public_instance_method(action)
|
45
|
+
end
|
46
|
+
|
47
|
+
return unless from.respond_to?(:refined_http_methods)
|
48
|
+
|
49
|
+
refined_http_methods.merge!(
|
50
|
+
from.refined_http_methods.slice(*actions)
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
## Re-define public instance method from module
|
55
|
+
## @param mod [Module] Module for including to controller
|
56
|
+
## @param exclude [Array<Symbol>] Actions for excluding
|
57
|
+
## from module public instance methods
|
58
|
+
## @param only [Array<Symbol>] Actions for re-defining
|
59
|
+
## from module public instance methods
|
60
|
+
## @example Define actions from module in controller
|
61
|
+
## class MyController < BaseController
|
62
|
+
## include with_actions Module1
|
63
|
+
## include with_actions Module2
|
64
|
+
## ....
|
65
|
+
## end
|
66
|
+
## @example Define actions from module exclude some actions in controller
|
67
|
+
## class MyController < BaseController
|
68
|
+
## include with_actions Module1, exclude: %i[action1 action2 ...]
|
69
|
+
## include with_actions Module2, exclude: %i[action1 action2 ...]
|
70
|
+
## ....
|
71
|
+
## end
|
72
|
+
## @example Define actions from module according list in controller
|
73
|
+
## class MyController < BaseController
|
74
|
+
## include with_actions Module1, only: %i[action1 action2 ...]
|
75
|
+
## include with_actions Module2, only: %i[action1 action2 ...]
|
76
|
+
## ....
|
77
|
+
## end
|
78
|
+
def with_actions(mod, exclude: [], only: nil)
|
79
|
+
Module.new do
|
80
|
+
@mod = mod
|
81
|
+
@actions = only || (@mod.public_instance_methods(false) - exclude)
|
82
|
+
|
83
|
+
extend ModuleWithActions
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
memoize def refined_http_methods
|
88
|
+
{}
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
Flame::Router::HTTP_METHODS.each do |http_method|
|
94
|
+
downcased_http_method = http_method.downcase
|
95
|
+
define_method(
|
96
|
+
downcased_http_method
|
97
|
+
) do |action_or_action_path, action = nil|
|
98
|
+
action, action_path =
|
99
|
+
if action
|
100
|
+
[action, action_or_action_path]
|
101
|
+
else
|
102
|
+
[action_or_action_path, nil]
|
103
|
+
end
|
104
|
+
refined_http_methods[action] = [downcased_http_method, action_path]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
## Base module for module `with_actions`
|
109
|
+
module ModuleWithActions
|
110
|
+
using GorillaPatch::Slice
|
111
|
+
|
112
|
+
def included(ctrl)
|
113
|
+
ctrl.include @mod
|
114
|
+
|
115
|
+
ctrl.inherit_actions @actions, from: @mod
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
private_constant :ModuleWithActions
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Flame
|
4
|
-
class
|
4
|
+
class Controller
|
5
5
|
## Helper class for cookies
|
6
6
|
class Cookies
|
7
7
|
## Create an instance of Cookies
|
@@ -22,14 +22,19 @@ module Flame
|
|
22
22
|
|
23
23
|
## Set (or delete) cookies for response
|
24
24
|
## @param key [String, Symbol] name of cookie
|
25
|
-
## @param new_value [Object, nil] value of cookie
|
25
|
+
## @param new_value [Object, Hash, nil] value of cookie or Hash with `:value` and options
|
26
26
|
## @example Set new value to `cat` cookie
|
27
27
|
## cookies['cat'] = 'nice cat'
|
28
|
+
## @example Set new value to `cat` cookie with `Max-Age` 60 seconds
|
29
|
+
## cookies['cat'] = { value: 'nice cat', max_age: 60 }
|
28
30
|
## @example Delete `cat` cookie
|
29
31
|
## cookies['cat'] = nil
|
30
32
|
def []=(key, new_value)
|
31
|
-
|
33
|
+
case new_value
|
34
|
+
when NilClass
|
32
35
|
@response.delete_cookie(key.to_s, path: '/')
|
36
|
+
when Hash
|
37
|
+
@response.set_cookie(key.to_s, new_value)
|
33
38
|
else
|
34
39
|
@response.set_cookie(key.to_s, value: new_value, path: '/')
|
35
40
|
end
|
@@ -4,24 +4,19 @@ module Flame
|
|
4
4
|
class Controller
|
5
5
|
## Module with methods for path or URL building
|
6
6
|
module PathTo
|
7
|
+
include Memery
|
8
|
+
|
7
9
|
## Look documentation at {Flame::Dispatcher#path_to}
|
8
10
|
def path_to(*args)
|
9
11
|
add_controller_class(args)
|
12
|
+
add_controller_arguments(args)
|
10
13
|
@dispatcher.path_to(*args)
|
11
14
|
end
|
12
15
|
|
13
16
|
## Build a URI to the given controller and action, or path
|
14
17
|
def url_to(*args, **options)
|
15
|
-
|
16
|
-
path
|
17
|
-
if first_arg.is_a?(String) || first_arg.is_a?(Flame::Path)
|
18
|
-
find_static(first_arg).path(with_version: options[:version])
|
19
|
-
else
|
20
|
-
path_to(*args, **options)
|
21
|
-
end
|
22
|
-
Addressable::URI.new(
|
23
|
-
scheme: request.scheme, host: request.host_with_port, path: path
|
24
|
-
).to_s
|
18
|
+
path = build_path_for_url(*args, **options)
|
19
|
+
Addressable::URI.join(request.base_url, path).to_s
|
25
20
|
end
|
26
21
|
|
27
22
|
using GorillaPatch::Namespace
|
@@ -31,9 +26,38 @@ module Flame
|
|
31
26
|
def path_to_back
|
32
27
|
back_path = request.referer
|
33
28
|
return back_path if back_path && back_path != request.url
|
29
|
+
|
34
30
|
return path_to :index if self.class.actions.include?(:index)
|
31
|
+
|
35
32
|
'/'
|
36
33
|
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def add_controller_class(args)
|
38
|
+
args.unshift(self.class) if args[0].is_a?(Symbol)
|
39
|
+
args.insert(1, :index) if args[0].is_a?(Class) && !args[1].is_a?(Symbol)
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_controller_arguments(args)
|
43
|
+
if args[-1].is_a?(Hash)
|
44
|
+
args[-1] = controller_arguments.merge args[-1]
|
45
|
+
else
|
46
|
+
args.push(controller_arguments)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_path_for_url(*args, **options)
|
51
|
+
first_arg = args.first
|
52
|
+
if first_arg.is_a?(String) || first_arg.is_a?(Flame::Path)
|
53
|
+
find_static(first_arg).path(with_version: options[:version])
|
54
|
+
else
|
55
|
+
path_to(*args, **options)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
memoize :build_path_for_url,
|
60
|
+
condition: -> { config[:environment] == 'production' }
|
37
61
|
end
|
38
62
|
end
|
39
63
|
end
|
data/lib/flame/dispatcher.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'gorilla_patch/symbolize'
|
4
|
+
require 'rack'
|
4
5
|
|
5
6
|
require_relative 'dispatcher/request'
|
6
7
|
require_relative 'dispatcher/response'
|
7
|
-
require_relative 'dispatcher/cookies'
|
8
8
|
|
9
9
|
require_relative 'dispatcher/static'
|
10
10
|
require_relative 'dispatcher/routes'
|
@@ -14,6 +14,8 @@ require_relative 'errors/route_not_found_error'
|
|
14
14
|
module Flame
|
15
15
|
## Helpers for dispatch Flame::Application#call
|
16
16
|
class Dispatcher
|
17
|
+
include Memery
|
18
|
+
|
17
19
|
GEM_STATIC_FILES = File.join(__dir__, '../../public').freeze
|
18
20
|
|
19
21
|
extend Forwardable
|
@@ -73,34 +75,27 @@ module Flame
|
|
73
75
|
|
74
76
|
## Parameters of the request
|
75
77
|
def params
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
{}
|
82
|
-
end
|
78
|
+
request.params.symbolize_keys(deep: true)
|
79
|
+
rescue ArgumentError => e
|
80
|
+
raise unless e.message.include?('invalid %-encoding')
|
81
|
+
|
82
|
+
{}
|
83
83
|
end
|
84
|
+
memoize :params
|
84
85
|
|
85
86
|
## Session object as Hash
|
86
87
|
def session
|
87
88
|
request.session
|
88
89
|
end
|
89
90
|
|
90
|
-
## Cookies object as Hash
|
91
|
-
def cookies
|
92
|
-
@cookies ||= Cookies.new(request.cookies, response)
|
93
|
-
end
|
94
|
-
|
95
91
|
## Application-config object as Hash
|
96
92
|
def config
|
97
93
|
@app_class.config
|
98
94
|
end
|
99
95
|
|
100
96
|
## Available routes endpoint
|
101
|
-
def available_endpoint
|
102
|
-
|
103
|
-
@available_endpoint = router.navigate(*request.path.parts)
|
97
|
+
memoize def available_endpoint
|
98
|
+
router.navigate(*request.path.parts)
|
104
99
|
end
|
105
100
|
|
106
101
|
## Interrupt the execution of route, and set new optional data
|
@@ -138,7 +133,7 @@ module Flame
|
|
138
133
|
## Generate default body of error page
|
139
134
|
def default_body
|
140
135
|
# response.headers[Rack::CONTENT_TYPE] = 'text/html'
|
141
|
-
|
136
|
+
Rack::Utils::HTTP_STATUS_CODES[status]
|
142
137
|
end
|
143
138
|
|
144
139
|
## All cached tilts (views) for application by Flame::Render
|
@@ -153,12 +148,14 @@ module Flame
|
|
153
148
|
request.params
|
154
149
|
rescue ArgumentError => e
|
155
150
|
raise unless e.message.include?('invalid %-encoding')
|
151
|
+
|
156
152
|
halt 400
|
157
153
|
end
|
158
154
|
|
159
155
|
## Return response if HTTP-method is OPTIONS
|
160
156
|
def try_options
|
161
157
|
return unless request.http_method == :OPTIONS
|
158
|
+
|
162
159
|
allow = available_endpoint&.allow
|
163
160
|
halt 404 unless allow
|
164
161
|
response.headers['Allow'] = allow
|
@@ -4,15 +4,15 @@ module Flame
|
|
4
4
|
class Dispatcher
|
5
5
|
## Class for requests
|
6
6
|
class Request < Rack::Request
|
7
|
+
include Memery
|
8
|
+
|
7
9
|
## Initialize Flame::Path
|
8
|
-
def path
|
9
|
-
|
10
|
+
memoize def path
|
11
|
+
Flame::Path.new path_info
|
10
12
|
end
|
11
13
|
|
12
14
|
## Override HTTP-method of the request if the param '_method' found
|
13
|
-
def http_method
|
14
|
-
return @http_method if defined?(@http_method)
|
15
|
-
|
15
|
+
memoize def http_method
|
16
16
|
method_from_method =
|
17
17
|
begin
|
18
18
|
params['_method']
|
@@ -21,7 +21,26 @@ module Flame
|
|
21
21
|
raise unless e.message.include?('invalid %-encoding')
|
22
22
|
end
|
23
23
|
|
24
|
-
|
24
|
+
(method_from_method || request_method).upcase.to_sym
|
25
|
+
end
|
26
|
+
|
27
|
+
using GorillaPatch::Inflections
|
28
|
+
|
29
|
+
HEADER_PREFIX = 'HTTP_'
|
30
|
+
|
31
|
+
## Helper method for comfortable Camel-Cased Hash of headers
|
32
|
+
memoize def headers
|
33
|
+
env.each_with_object({}) do |(key, value), result|
|
34
|
+
next unless key.start_with?(HEADER_PREFIX)
|
35
|
+
|
36
|
+
## TODO: Replace `String#[]` with `#delete_prefix`
|
37
|
+
## after Ruby < 2.5 dropping
|
38
|
+
camelized_key =
|
39
|
+
key[HEADER_PREFIX.size..-1].downcase.tr('_', '/')
|
40
|
+
.camelize.gsub('::', '-')
|
41
|
+
|
42
|
+
result[camelized_key] = value
|
43
|
+
end
|
25
44
|
end
|
26
45
|
end
|
27
46
|
end
|
@@ -11,8 +11,10 @@ module Flame
|
|
11
11
|
http_method = request.http_method
|
12
12
|
http_method = :GET if http_method == :HEAD
|
13
13
|
return unless available_endpoint
|
14
|
+
|
14
15
|
route = available_endpoint[http_method]
|
15
16
|
return unless route || available_endpoint.allow
|
17
|
+
|
16
18
|
halt(405, nil, 'Allow' => available_endpoint.allow) unless route
|
17
19
|
status 200
|
18
20
|
execute_route route
|
@@ -26,32 +28,38 @@ module Flame
|
|
26
28
|
router.path_of(route).extract_arguments(request.path)
|
27
29
|
)
|
28
30
|
# route.execute(self)
|
29
|
-
|
30
|
-
|
31
|
-
rescue StandardError =>
|
31
|
+
@current_controller = route.controller.new(self)
|
32
|
+
@current_controller.send(:execute, action)
|
33
|
+
rescue StandardError, SyntaxError => e
|
32
34
|
# p 'rescue from dispatcher'
|
33
|
-
dump_error(
|
35
|
+
dump_error(e)
|
34
36
|
status 500
|
35
|
-
|
36
|
-
# p 're raise
|
37
|
-
# raise
|
37
|
+
@current_controller&.send(:server_error, e)
|
38
|
+
# p 're raise error from dispatcher'
|
39
|
+
# raise e
|
38
40
|
end
|
39
41
|
|
40
42
|
## Generate a default body of nearest route
|
41
43
|
def default_body_of_nearest_route
|
42
44
|
## Return nil if must be no body for current HTTP status
|
43
45
|
return if Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
|
46
|
+
|
44
47
|
## Find the nearest route by the parts of requested path
|
45
48
|
route = router.find_nearest_route(request.path)
|
46
49
|
## Return standard `default_body` if the route not found
|
47
50
|
return default_body unless route
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
|
52
|
+
return not_found_body(route) if response.not_found?
|
53
|
+
|
54
|
+
controller =
|
55
|
+
(@current_controller if defined?(@current_controller)) || route.controller.new(self)
|
56
|
+
controller.send :default_body
|
57
|
+
end
|
58
|
+
|
59
|
+
def not_found_body(route)
|
60
|
+
## Execute `not_found` method as action for the founded route
|
61
|
+
execute_route(route, :not_found)
|
62
|
+
body
|
55
63
|
end
|
56
64
|
end
|
57
65
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'cgi'
|
4
|
+
|
3
5
|
module Flame
|
4
6
|
class Dispatcher
|
5
7
|
## Module for working with static files
|
@@ -16,9 +18,11 @@ module Flame
|
|
16
18
|
private
|
17
19
|
|
18
20
|
## Find static files and try return it
|
19
|
-
def try_static(*args)
|
20
|
-
file = find_static(*args)
|
21
|
+
def try_static(*args, **kwargs)
|
22
|
+
file = find_static(*args, **kwargs)
|
21
23
|
return nil unless file.exist?
|
24
|
+
|
25
|
+
halt 400 unless file.within_directory
|
22
26
|
return_static(file)
|
23
27
|
end
|
24
28
|
|
@@ -32,9 +36,14 @@ module Flame
|
|
32
36
|
|
33
37
|
## Class for static files with helpers methods
|
34
38
|
class StaticFile
|
39
|
+
attr_reader :extname, :within_directory
|
40
|
+
|
35
41
|
def initialize(filename, dir)
|
36
42
|
@filename = filename.to_s
|
37
|
-
@
|
43
|
+
@directory = File.expand_path dir
|
44
|
+
@file_path = File.expand_path File.join dir, CGI.unescape(@filename)
|
45
|
+
@extname = File.extname(@file_path)
|
46
|
+
@within_directory = @file_path.start_with? @directory
|
38
47
|
end
|
39
48
|
|
40
49
|
def exist?
|
@@ -45,10 +54,6 @@ module Flame
|
|
45
54
|
File.mtime(@file_path)
|
46
55
|
end
|
47
56
|
|
48
|
-
def extname
|
49
|
-
File.extname(@file_path)
|
50
|
-
end
|
51
|
-
|
52
57
|
def newer?(http_since)
|
53
58
|
!http_since || mtime.to_i > Time.httpdate(http_since).to_i
|
54
59
|
end
|
@@ -58,8 +63,7 @@ module Flame
|
|
58
63
|
end
|
59
64
|
|
60
65
|
def path(with_version: false)
|
61
|
-
|
62
|
-
with_version ? "#{path}?v=#{mtime.to_i}" : path
|
66
|
+
with_version ? "#{@filename}?v=#{mtime.to_i}" : @filename
|
63
67
|
end
|
64
68
|
end
|
65
69
|
|