flame 4.18.1 → 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 -4
- data/lib/flame/application.rb +93 -40
- data/lib/flame/config.rb +73 -0
- data/lib/flame/controller.rb +62 -98
- data/lib/flame/controller/actions.rb +122 -0
- data/lib/flame/controller/cookies.rb +44 -0
- data/lib/flame/controller/path_to.rb +63 -0
- data/lib/flame/dispatcher.rb +44 -73
- data/lib/flame/dispatcher/request.rb +33 -4
- data/lib/flame/dispatcher/routes.rb +66 -0
- data/lib/flame/dispatcher/static.rb +26 -15
- data/lib/flame/errors/argument_not_assigned_error.rb +7 -6
- 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 +9 -8
- data/lib/flame/errors/route_extra_arguments_error.rb +18 -18
- data/lib/flame/errors/route_not_found_error.rb +8 -7
- data/lib/flame/errors/template_not_found_error.rb +6 -6
- data/lib/flame/path.rb +141 -55
- data/lib/flame/render.rb +46 -15
- data/lib/flame/router.rb +41 -127
- data/lib/flame/router/controller_finder.rb +56 -0
- data/lib/flame/router/route.rb +16 -54
- data/lib/flame/router/routes.rb +136 -0
- data/lib/flame/router/routes_refine.rb +144 -0
- data/lib/flame/router/routes_refine/mounting.rb +57 -0
- data/lib/flame/validators.rb +21 -11
- data/lib/flame/version.rb +1 -1
- metadata +139 -84
- data/bin/flame +0 -71
- data/lib/flame/application/config.rb +0 -43
- data/lib/flame/dispatcher/cookies.rb +0 -31
- data/template/.gitignore +0 -11
- data/template/Gemfile +0 -15
- data/template/Rakefile.erb +0 -64
- data/template/app.rb.erb +0 -7
- data/template/config.ru.erb +0 -20
- data/template/config/config.rb.erb +0 -14
- data/template/config/database.example.yml +0 -5
- data/template/config/sequel.rb.erb +0 -15
- data/template/config/thin.example.yml +0 -18
- data/template/controllers/_base_controller.rb.erb +0 -13
- data/template/db/.keep +0 -0
- 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 -49
- data/template/views/.keep +0 -0
data/lib/flame/render.rb
CHANGED
@@ -6,38 +6,54 @@ require 'tilt'
|
|
6
6
|
require 'tilt/plain'
|
7
7
|
require 'tilt/erb'
|
8
8
|
|
9
|
-
require '
|
9
|
+
require 'gorilla_patch/inflections'
|
10
10
|
|
11
11
|
require_relative 'errors/template_not_found_error'
|
12
12
|
|
13
13
|
module Flame
|
14
14
|
## Helper for render functionality
|
15
15
|
class Render
|
16
|
+
## Create a new instance from controller, by path and with options
|
17
|
+
## @param controller [Flame::Controller]
|
18
|
+
## controller for default scope, views directory and cache
|
19
|
+
## @param path [Symbol, String] path (full or the last part) for view search
|
20
|
+
## @param options [Hash] options for template
|
21
|
+
## @option options [Object] :scope (controller)
|
22
|
+
## scope of visibility in rendering
|
23
|
+
## @option options [Symbol, String, false] :layout ('layout.*')
|
24
|
+
## name of the layout file
|
25
|
+
## @option options [Hash] :tilt options for Tilt
|
26
|
+
## @option options [Hash] :locals ({}) local variables for rendering
|
16
27
|
def initialize(controller, path, options = {})
|
17
28
|
## Take options for rendering
|
18
29
|
@controller = controller
|
19
|
-
@scope = options.delete(:scope)
|
20
|
-
@layout = options.delete(:layout)
|
21
|
-
|
30
|
+
@scope = options.delete(:scope) { @controller }
|
31
|
+
@layout = options.delete(:layout) { 'layout.*' }
|
32
|
+
|
33
|
+
## Options for Tilt Template
|
34
|
+
@tilt_options = options.delete(:tilt)
|
35
|
+
|
22
36
|
## And get the rest variables to locals
|
23
|
-
@locals = options.merge(options.delete(:locals)
|
37
|
+
@locals = options.merge(options.delete(:locals) { {} })
|
38
|
+
|
24
39
|
## Find filename
|
25
40
|
@filename = find_file(path)
|
26
|
-
unless @filename
|
27
|
-
|
28
|
-
end
|
41
|
+
raise Flame::Errors::TemplateNotFoundError.new(controller, path) unless @filename
|
42
|
+
|
29
43
|
@layout = nil if File.basename(@filename)[0] == '_'
|
30
44
|
end
|
31
45
|
|
32
|
-
## Render template
|
46
|
+
## Render template with layout
|
33
47
|
## @param cache [Boolean] cache compiles or not
|
34
|
-
|
48
|
+
## @return [String] compiled template
|
49
|
+
def render(cache: true, &block)
|
35
50
|
@cache = cache
|
36
51
|
## Compile Tilt to instance hash
|
37
52
|
return unless @filename
|
53
|
+
|
38
54
|
tilt = compile_file
|
39
55
|
## Render Tilt from instance hash with new options
|
40
|
-
layout_render tilt.render(@scope, @locals)
|
56
|
+
layout_render tilt.render(@scope, @locals, &block)
|
41
57
|
end
|
42
58
|
|
43
59
|
private
|
@@ -51,7 +67,8 @@ module Flame
|
|
51
67
|
def compile_file(filename = @filename)
|
52
68
|
cached = @controller.cached_tilts[filename]
|
53
69
|
return cached if @cache && cached
|
54
|
-
|
70
|
+
|
71
|
+
compiled = Tilt.new(filename, nil, @tilt_options)
|
55
72
|
@controller.cached_tilts[filename] ||= compiled if @cache
|
56
73
|
compiled
|
57
74
|
end
|
@@ -76,7 +93,17 @@ module Flame
|
|
76
93
|
|
77
94
|
## Find template-file by path
|
78
95
|
def find_file(path)
|
79
|
-
|
96
|
+
caller_path = caller_locations(4..4).first.path
|
97
|
+
|
98
|
+
caller_dir =
|
99
|
+
begin
|
100
|
+
File.dirname(caller_path).sub(views_dir, '') if Tilt[caller_path]
|
101
|
+
rescue LoadError
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
find_files(path, controller_dirs | Array(caller_dir))
|
106
|
+
.find { |file| Tilt[file] }
|
80
107
|
end
|
81
108
|
|
82
109
|
## Find layout-files by path
|
@@ -88,11 +115,13 @@ module Flame
|
|
88
115
|
|
89
116
|
using GorillaPatch::Inflections
|
90
117
|
|
118
|
+
CONTROLLER_SUFFIXES = %w[_controller _ctrl].freeze
|
119
|
+
private_constant :CONTROLLER_SUFFIXES
|
120
|
+
|
91
121
|
## Find possible directories for the controller
|
92
122
|
def controller_dirs
|
93
123
|
parts = @controller.class.underscore.split('/').map do |part|
|
94
|
-
|
95
|
-
.find { |suffix| part.chomp! suffix }
|
124
|
+
CONTROLLER_SUFFIXES.find { |suffix| part.chomp! suffix }
|
96
125
|
part
|
97
126
|
## Alternative, but slower by ~50%:
|
98
127
|
# part.sub(/_(controller|ctrl)$/, '')
|
@@ -128,8 +157,10 @@ module Flame
|
|
128
157
|
## @param result [String] result of template rendering
|
129
158
|
def layout_render(content)
|
130
159
|
return content unless @layout
|
160
|
+
|
131
161
|
layout_files = find_layouts(@layout)
|
132
162
|
return content if layout_files.empty?
|
163
|
+
|
133
164
|
layout_files.each_with_object(content.dup) do |layout_file, result|
|
134
165
|
layout = compile_file(layout_file)
|
135
166
|
result.replace layout.render(@scope, @locals) { result }
|
data/lib/flame/router.rb
CHANGED
@@ -1,153 +1,67 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'gorilla_patch/deep_merge'
|
4
|
+
require 'gorilla_patch/inflections'
|
5
|
+
require 'gorilla_patch/namespace'
|
6
|
+
|
7
|
+
require_relative 'router/controller_finder'
|
8
|
+
require_relative 'errors/controller_not_found_error'
|
4
9
|
|
5
10
|
module Flame
|
6
11
|
## Router class for routing
|
7
12
|
class Router
|
8
|
-
|
13
|
+
HTTP_METHODS = %i[GET POST PUT PATCH DELETE].freeze
|
14
|
+
|
15
|
+
require_relative 'router/route'
|
16
|
+
require_relative 'router/routes'
|
17
|
+
require_relative 'router/routes_refine'
|
18
|
+
|
19
|
+
extend Forwardable
|
20
|
+
def_delegators :routes, :navigate
|
21
|
+
|
22
|
+
attr_reader :app, :routes, :reverse_routes
|
9
23
|
|
24
|
+
## @param app [Flame::Application] host application
|
10
25
|
def initialize(app)
|
11
26
|
@app = app
|
12
|
-
@routes =
|
27
|
+
@routes = Flame::Router::Routes.new
|
28
|
+
@reverse_routes = {}
|
13
29
|
end
|
14
30
|
|
15
|
-
|
16
|
-
## @param ctrl [Flame::Controller] class of the controller which will be added
|
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)
|
20
|
-
## @todo Add Regexp paths
|
31
|
+
using GorillaPatch::DeepMerge
|
21
32
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
## Find route by any attributes
|
28
|
-
## @param attrs [Hash] attributes for comparing
|
29
|
-
## @return [Flame::Route, nil] return the found route, otherwise `nil`
|
30
|
-
def find_route(attrs)
|
31
|
-
route = routes.find { |r| r.compare_attributes(attrs) }
|
32
|
-
route.dup if route
|
33
|
+
## Add RoutesRefine to Router
|
34
|
+
## @param routes_refine [Flame::Router::RoutesRefine] refined routes
|
35
|
+
def add(routes_refine)
|
36
|
+
routes.deep_merge! routes_refine.routes
|
37
|
+
reverse_routes.merge! routes_refine.reverse_routes
|
33
38
|
end
|
34
39
|
|
35
40
|
## Find the nearest route by path
|
36
41
|
## @param path [Flame::Path] path for route finding
|
37
42
|
## @return [Flame::Route, nil] return the found nearest route or `nil`
|
38
43
|
def find_nearest_route(path)
|
39
|
-
path = Flame::Path.new(path) if path.is_a? String
|
40
44
|
path_parts = path.parts.dup
|
41
|
-
|
42
|
-
route =
|
43
|
-
break if route || path_parts.
|
44
|
-
path_parts.pop
|
45
|
+
loop do
|
46
|
+
route = routes.navigate(*path_parts)&.first_route
|
47
|
+
break route if route || path_parts.pop.nil?
|
45
48
|
end
|
46
|
-
route
|
47
49
|
end
|
48
50
|
|
49
|
-
|
50
|
-
|
51
|
-
##
|
52
|
-
## @param
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
## Defaults REST routes (methods, pathes, controllers actions)
|
63
|
-
def rest_routes
|
64
|
-
@rest_routes ||= [
|
65
|
-
{ method: :GET, path: '/', action: :index },
|
66
|
-
{ method: :POST, path: '/', action: :create },
|
67
|
-
{ method: :GET, path: '/', action: :show },
|
68
|
-
{ method: :PUT, path: '/', action: :update },
|
69
|
-
{ method: :DELETE, path: '/', action: :delete }
|
70
|
-
]
|
71
|
-
end
|
72
|
-
|
73
|
-
def initialize(router, ctrl, path, block)
|
74
|
-
@router = router
|
75
|
-
@ctrl = ctrl
|
76
|
-
@path = path || @ctrl.default_path
|
77
|
-
@routes = []
|
78
|
-
execute(&block)
|
79
|
-
end
|
80
|
-
|
81
|
-
%i[GET POST PUT PATCH DELETE].each do |request_method|
|
82
|
-
## Define refine methods for all HTTP methods
|
83
|
-
## @overload post(path, action)
|
84
|
-
## Execute action on requested path and HTTP method
|
85
|
-
## @param path [String] path of method for the request
|
86
|
-
## @param action [Symbol] name of method for the request
|
87
|
-
## @example Set path to '/bye' and method to :POST for action `goodbye`
|
88
|
-
## post '/bye', :goodbye
|
89
|
-
## @overload post(action)
|
90
|
-
## Execute action on requested HTTP method
|
91
|
-
## @param action [Symbol] name of method for the request
|
92
|
-
## @example Set method to :POST for action `goodbye`
|
93
|
-
## post :goodbye
|
94
|
-
method = request_method.downcase
|
95
|
-
define_method(method) do |path, action = nil|
|
96
|
-
## Swap arguments if action in path variable
|
97
|
-
unless action
|
98
|
-
action = path.to_sym
|
99
|
-
path = nil
|
100
|
-
end
|
101
|
-
## Init new Route
|
102
|
-
route = Route.new(@ctrl, action, method, @path, path)
|
103
|
-
## Try to find route with the same action
|
104
|
-
index = find_route_index(action: action)
|
105
|
-
## Overwrite route if needed
|
106
|
-
index ? @routes[index] = route : @routes.push(route)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
## Assign remaining methods of the controller
|
111
|
-
## to defaults pathes and HTTP methods
|
112
|
-
def defaults
|
113
|
-
rest
|
114
|
-
@ctrl.actions.each do |action|
|
115
|
-
next if find_route_index(action: action)
|
116
|
-
send(:GET.downcase, action)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
## Assign methods of the controller to REST architecture
|
121
|
-
def rest
|
122
|
-
rest_routes.each do |rest_route|
|
123
|
-
action = rest_route[:action]
|
124
|
-
next if !@ctrl.actions.include?(action) ||
|
125
|
-
find_route_index(action: action)
|
126
|
-
send(*rest_route.values.map(&:downcase))
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
## Mount controller inside other (parent) controller
|
131
|
-
## @param ctrl [Flame::Controller] class of mounting controller
|
132
|
-
## @param path [String, nil] root path for mounting controller
|
133
|
-
## @yield Block of code for routes refine
|
134
|
-
def mount(ctrl, path = nil, &block)
|
135
|
-
path = Flame::Path.merge(@path, path || ctrl.default_path)
|
136
|
-
@router.add_controller(ctrl, path, &block)
|
137
|
-
end
|
138
|
-
|
139
|
-
private
|
140
|
-
|
141
|
-
## Execute block of refinings end sorting routes
|
142
|
-
def execute(&block)
|
143
|
-
instance_exec(&block) if block
|
144
|
-
defaults
|
145
|
-
@routes.sort!
|
146
|
-
end
|
147
|
-
|
148
|
-
def find_route_index(attrs)
|
149
|
-
@routes.find_index { |route| route.compare_attributes(attrs) }
|
51
|
+
## Find the path of route
|
52
|
+
## @param route_or_controller [Flame::Router::Route, Flame::Controller]
|
53
|
+
## route or controller
|
54
|
+
## @param action [Symbol, nil] action (or not for route)
|
55
|
+
## @return [Flame::Path] mounted path to action of controller
|
56
|
+
def path_of(route_or_controller, action = nil)
|
57
|
+
if route_or_controller.is_a?(Flame::Router::Route)
|
58
|
+
route = route_or_controller
|
59
|
+
controller = route.controller
|
60
|
+
action = route.action
|
61
|
+
else
|
62
|
+
controller = route_or_controller
|
150
63
|
end
|
64
|
+
reverse_routes.dig(controller.to_s, action)
|
151
65
|
end
|
152
66
|
end
|
153
67
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Flame
|
4
|
+
## Comment due to `private_constant`
|
5
|
+
class Router
|
6
|
+
## Class for controller constant finding in namespace by names
|
7
|
+
class ControllerFinder
|
8
|
+
attr_reader :controller
|
9
|
+
|
10
|
+
def initialize(namespace_name, controller_or_name)
|
11
|
+
@namespace =
|
12
|
+
namespace_name.empty? ? Object : Object.const_get(namespace_name)
|
13
|
+
|
14
|
+
if controller_or_name.is_a?(Class)
|
15
|
+
@controller = controller_or_name
|
16
|
+
@controller_name = controller_or_name.name
|
17
|
+
else
|
18
|
+
@controller_name = controller_or_name
|
19
|
+
@controller = find
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def find
|
26
|
+
found_controller_name = controller_name_variations.find do |variation|
|
27
|
+
@namespace.const_defined?(variation) unless variation.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
raise_controller_not_found_error unless found_controller_name
|
31
|
+
|
32
|
+
controller = @namespace.const_get(found_controller_name)
|
33
|
+
return controller if controller < Flame::Controller
|
34
|
+
|
35
|
+
controller::IndexController
|
36
|
+
end
|
37
|
+
|
38
|
+
using GorillaPatch::Inflections
|
39
|
+
|
40
|
+
TRASNFORMATION_METHODS = %i[camelize upcase].freeze
|
41
|
+
|
42
|
+
def controller_name_variations
|
43
|
+
TRASNFORMATION_METHODS.each_with_object([]) do |method, result|
|
44
|
+
transformed = @controller_name.to_s.send(method)
|
45
|
+
result.push transformed, "#{transformed}Controller"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def raise_controller_not_found_error
|
50
|
+
raise Errors::ControllerNotFoundError.new(@controller_name, @namespace)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private_constant :ControllerFinder
|
55
|
+
end
|
56
|
+
end
|
data/lib/flame/router/route.rb
CHANGED
@@ -7,73 +7,35 @@ module Flame
|
|
7
7
|
class Router
|
8
8
|
## Class for Route in Router.routes
|
9
9
|
class Route
|
10
|
-
|
10
|
+
extend Forwardable
|
11
11
|
|
12
|
-
|
13
|
-
@controller = controller
|
14
|
-
@action = action
|
15
|
-
@method = method.to_sym.upcase
|
16
|
-
## Make path by controller method with parameners
|
17
|
-
action_path = Flame::Path.new(action_path).adapt(controller, action)
|
18
|
-
## Merge action path with controller path
|
19
|
-
@path = Flame::Path.new(ctrl_path, action_path)
|
20
|
-
Validators::RouteArgumentsValidator.new(
|
21
|
-
@controller, action_path, @action
|
22
|
-
).valid?
|
23
|
-
freeze
|
24
|
-
end
|
12
|
+
def_delegators :to_s, :inspect
|
25
13
|
|
26
|
-
|
27
|
-
@path.freeze
|
28
|
-
super
|
29
|
-
end
|
14
|
+
attr_reader :controller, :action
|
30
15
|
|
31
|
-
##
|
32
|
-
## @param
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
16
|
+
## Create a new instance
|
17
|
+
## @param controller [Flame::Controller] controller
|
18
|
+
## @param action [Symbol] action
|
19
|
+
def initialize(controller, action)
|
20
|
+
@controller = controller
|
21
|
+
@action = action
|
38
22
|
end
|
39
23
|
|
40
24
|
## Method for Routes comparison
|
25
|
+
## @param other [Flame::Router::Route] other route
|
26
|
+
## @return [true, false] equal or not
|
41
27
|
def ==(other)
|
42
|
-
|
28
|
+
return false unless other.is_a? self.class
|
29
|
+
|
30
|
+
%i[controller action].reduce(true) do |result, method|
|
43
31
|
result && (
|
44
32
|
public_send(method) == other.public_send(method)
|
45
33
|
)
|
46
34
|
end
|
47
35
|
end
|
48
36
|
|
49
|
-
|
50
|
-
|
51
|
-
## 2. args position (father is matter);
|
52
|
-
## 3. HTTP-method (default).
|
53
|
-
def <=>(other)
|
54
|
-
path_result = other.path <=> path
|
55
|
-
return path_result unless path_result.zero?
|
56
|
-
method <=> other.method
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
## Helpers for `compare_attributes`
|
62
|
-
def compare_attribute(name, value)
|
63
|
-
case name
|
64
|
-
when :method
|
65
|
-
compare_method(value)
|
66
|
-
when :path
|
67
|
-
path.match? value
|
68
|
-
else
|
69
|
-
send(name) == value
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def compare_method(request_method)
|
74
|
-
request_method = request_method.upcase.to_sym
|
75
|
-
request_method = :GET if request_method == :HEAD
|
76
|
-
method.upcase.to_sym == request_method
|
37
|
+
def to_s
|
38
|
+
"#{controller}##{action}"
|
77
39
|
end
|
78
40
|
end
|
79
41
|
end
|