flame 5.0.0.rc4 → 5.0.0.rc7
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 +929 -0
- data/LICENSE.txt +19 -0
- data/README.md +135 -0
- data/lib/flame/application.rb +47 -46
- data/lib/flame/config.rb +73 -0
- 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/controller.rb +45 -78
- data/lib/flame/dispatcher/request.rb +22 -6
- data/lib/flame/dispatcher/routes.rb +22 -14
- data/lib/flame/dispatcher/static.rb +13 -9
- data/lib/flame/dispatcher.rb +15 -18
- 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 +3 -10
- data/lib/flame/errors/route_extra_arguments_error.rb +7 -20
- data/lib/flame/errors/route_not_found_error.rb +4 -9
- 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/controller_finder.rb +56 -0
- data/lib/flame/router/route.rb +9 -0
- data/lib/flame/router/routes.rb +56 -9
- data/lib/flame/router/routes_refine/mounting.rb +57 -0
- data/lib/flame/router/routes_refine.rb +144 -0
- data/lib/flame/router.rb +7 -157
- data/lib/flame/validators.rb +14 -10
- data/lib/flame/version.rb +1 -1
- data/lib/flame.rb +12 -5
- metadata +107 -96
- 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/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/config.ru.erb +0 -72
- 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
data/lib/flame/path.rb
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'forwardable'
|
4
|
+
require 'memery'
|
4
5
|
|
5
6
|
require_relative 'errors/argument_not_assigned_error'
|
6
7
|
|
7
8
|
module Flame
|
8
9
|
## Class for working with paths
|
9
10
|
class Path
|
11
|
+
include Memery
|
12
|
+
|
13
|
+
extend Forwardable
|
14
|
+
def_delegators :to_s, :include?
|
15
|
+
|
10
16
|
## Merge parts of path to one path
|
11
17
|
## @param parts [Array<String, Flame::Path>] parts of expected path
|
12
18
|
## @return [Flame::Path] path from parts
|
@@ -24,8 +30,8 @@ module Flame
|
|
24
30
|
|
25
31
|
## Return parts of path, splitted by slash (`/`)
|
26
32
|
## @return [Array<Flame::Path::Part>] array of path parts
|
27
|
-
def parts
|
28
|
-
@
|
33
|
+
memoize def parts
|
34
|
+
@path.to_s.split('/').reject(&:empty?)
|
29
35
|
.map! { |part| self.class::Part.new(part) }
|
30
36
|
end
|
31
37
|
|
@@ -49,14 +55,10 @@ module Flame
|
|
49
55
|
## @return [-1, 0, 1] result of comparing
|
50
56
|
def <=>(other)
|
51
57
|
self_parts, other_parts = [self, other].map(&:parts)
|
52
|
-
|
53
|
-
return
|
54
|
-
|
55
|
-
|
56
|
-
break -1 if self_part.arg? && !other_part.arg?
|
57
|
-
break 1 if other_part.arg? && !self_part.arg?
|
58
|
-
result
|
59
|
-
end
|
58
|
+
by_parts_size = self_parts.size <=> other_parts.size
|
59
|
+
return by_parts_size unless by_parts_size.zero?
|
60
|
+
|
61
|
+
compare_by_args_in_parts self_parts.zip(other_parts)
|
60
62
|
end
|
61
63
|
|
62
64
|
## Compare with other path by parts
|
@@ -120,16 +122,32 @@ module Flame
|
|
120
122
|
## Not argument
|
121
123
|
return part unless part.arg?
|
122
124
|
## Not required argument
|
123
|
-
return args
|
125
|
+
return args.delete(part[2..].to_sym) if part.opt_arg?
|
126
|
+
|
124
127
|
## Required argument
|
125
|
-
param = args
|
128
|
+
param = args.delete(part[1..].to_sym)
|
126
129
|
## Required argument is nil
|
127
130
|
error = Errors::ArgumentNotAssignedError.new(@path, part)
|
128
131
|
raise error if param.nil?
|
132
|
+
|
129
133
|
## All is ok
|
130
134
|
param
|
131
135
|
end
|
132
136
|
|
137
|
+
def compare_by_args_in_parts(self_and_other_parts)
|
138
|
+
result = 0
|
139
|
+
|
140
|
+
self_and_other_parts.each do |self_part, other_part|
|
141
|
+
if self_part.arg?
|
142
|
+
break result = -1 unless other_part.arg?
|
143
|
+
elsif other_part.arg?
|
144
|
+
break result = 1
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
result
|
149
|
+
end
|
150
|
+
|
133
151
|
## Class for extracting arguments from other path
|
134
152
|
class Extractor
|
135
153
|
def initialize(parts, other_parts)
|
@@ -148,7 +166,7 @@ module Flame
|
|
148
166
|
|
149
167
|
break if part.opt_arg? && @other_parts.count <= @other_index
|
150
168
|
|
151
|
-
@args[part.
|
169
|
+
@args[part.to_sym] = extract
|
152
170
|
@index += 1
|
153
171
|
end
|
154
172
|
|
@@ -178,7 +196,7 @@ module Flame
|
|
178
196
|
class Part
|
179
197
|
extend Forwardable
|
180
198
|
|
181
|
-
def_delegators
|
199
|
+
def_delegators :to_s, :[], :hash, :size, :empty?, :b, :inspect
|
182
200
|
|
183
201
|
ARG_CHAR = ':'
|
184
202
|
ARG_CHAR_OPT = '?'
|
@@ -229,10 +247,10 @@ module Flame
|
|
229
247
|
# arg? && !opt_arg?
|
230
248
|
# end
|
231
249
|
|
232
|
-
## Path part as a
|
233
|
-
## @return [
|
234
|
-
def
|
235
|
-
@part.delete
|
250
|
+
## Path part as a Symbol without arguments characters
|
251
|
+
## @return [Symbol] clean Symbol
|
252
|
+
def to_sym
|
253
|
+
@part.delete(ARG_CHAR + ARG_CHAR_OPT).to_sym
|
236
254
|
end
|
237
255
|
end
|
238
256
|
end
|
data/lib/flame/render.rb
CHANGED
@@ -29,15 +29,17 @@ module Flame
|
|
29
29
|
@controller = controller
|
30
30
|
@scope = options.delete(:scope) { @controller }
|
31
31
|
@layout = options.delete(:layout) { 'layout.*' }
|
32
|
+
|
32
33
|
## Options for Tilt Template
|
33
34
|
@tilt_options = options.delete(:tilt)
|
35
|
+
|
34
36
|
## And get the rest variables to locals
|
35
37
|
@locals = options.merge(options.delete(:locals) { {} })
|
38
|
+
|
36
39
|
## Find filename
|
37
40
|
@filename = find_file(path)
|
38
|
-
unless @filename
|
39
|
-
|
40
|
-
end
|
41
|
+
raise Flame::Errors::TemplateNotFoundError.new(controller, path) unless @filename
|
42
|
+
|
41
43
|
@layout = nil if File.basename(@filename)[0] == '_'
|
42
44
|
end
|
43
45
|
|
@@ -48,6 +50,7 @@ module Flame
|
|
48
50
|
@cache = cache
|
49
51
|
## Compile Tilt to instance hash
|
50
52
|
return unless @filename
|
53
|
+
|
51
54
|
tilt = compile_file
|
52
55
|
## Render Tilt from instance hash with new options
|
53
56
|
layout_render tilt.render(@scope, @locals, &block)
|
@@ -64,6 +67,7 @@ module Flame
|
|
64
67
|
def compile_file(filename = @filename)
|
65
68
|
cached = @controller.cached_tilts[filename]
|
66
69
|
return cached if @cache && cached
|
70
|
+
|
67
71
|
compiled = Tilt.new(filename, nil, @tilt_options)
|
68
72
|
@controller.cached_tilts[filename] ||= compiled if @cache
|
69
73
|
compiled
|
@@ -111,11 +115,13 @@ module Flame
|
|
111
115
|
|
112
116
|
using GorillaPatch::Inflections
|
113
117
|
|
118
|
+
CONTROLLER_SUFFIXES = %w[_controller _ctrl].freeze
|
119
|
+
private_constant :CONTROLLER_SUFFIXES
|
120
|
+
|
114
121
|
## Find possible directories for the controller
|
115
122
|
def controller_dirs
|
116
123
|
parts = @controller.class.underscore.split('/').map do |part|
|
117
|
-
|
118
|
-
.find { |suffix| part.chomp! suffix }
|
124
|
+
CONTROLLER_SUFFIXES.find { |suffix| part.chomp! suffix }
|
119
125
|
part
|
120
126
|
## Alternative, but slower by ~50%:
|
121
127
|
# part.sub(/_(controller|ctrl)$/, '')
|
@@ -151,8 +157,10 @@ module Flame
|
|
151
157
|
## @param result [String] result of template rendering
|
152
158
|
def layout_render(content)
|
153
159
|
return content unless @layout
|
160
|
+
|
154
161
|
layout_files = find_layouts(@layout)
|
155
162
|
return content if layout_files.empty?
|
163
|
+
|
156
164
|
layout_files.each_with_object(content.dup) do |layout_file, result|
|
157
165
|
layout = compile_file(layout_file)
|
158
166
|
result.replace layout.render(@scope, @locals) { result }
|
@@ -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,6 +7,10 @@ module Flame
|
|
7
7
|
class Router
|
8
8
|
## Class for Route in Router.routes
|
9
9
|
class Route
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
def_delegators :to_s, :inspect
|
13
|
+
|
10
14
|
attr_reader :controller, :action
|
11
15
|
|
12
16
|
## Create a new instance
|
@@ -22,12 +26,17 @@ module Flame
|
|
22
26
|
## @return [true, false] equal or not
|
23
27
|
def ==(other)
|
24
28
|
return false unless other.is_a? self.class
|
29
|
+
|
25
30
|
%i[controller action].reduce(true) do |result, method|
|
26
31
|
result && (
|
27
32
|
public_send(method) == other.public_send(method)
|
28
33
|
)
|
29
34
|
end
|
30
35
|
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
"#{controller}##{action}"
|
39
|
+
end
|
31
40
|
end
|
32
41
|
end
|
33
42
|
end
|
data/lib/flame/router/routes.rb
CHANGED
@@ -12,10 +12,15 @@ module Flame
|
|
12
12
|
## Flame::Router::Routes.new('/foo/bar/baz')
|
13
13
|
## # => { 'foo' => { 'bar' => { 'baz' => {} } } }
|
14
14
|
def initialize(*path_parts)
|
15
|
+
super()
|
16
|
+
|
15
17
|
path = Flame::Path.new(*path_parts)
|
16
18
|
return if path.parts.empty?
|
17
|
-
|
18
|
-
|
19
|
+
|
20
|
+
nested_routes = self.class.new Flame::Path.new(*path.parts[1..])
|
21
|
+
# path.parts.reduce(result) do |hash, part|
|
22
|
+
# hash[part] ||= self.class.new
|
23
|
+
# end
|
19
24
|
self[path.parts.first] = nested_routes
|
20
25
|
end
|
21
26
|
|
@@ -38,6 +43,12 @@ module Flame
|
|
38
43
|
super
|
39
44
|
end
|
40
45
|
|
46
|
+
## Return the first available route (at the first level).
|
47
|
+
## @return [Flame::Router::Route] the first route
|
48
|
+
def first_route
|
49
|
+
values.find { |value| value.is_a?(Route) }
|
50
|
+
end
|
51
|
+
|
41
52
|
## Navigate to Routes or Route through static parts or arguments
|
42
53
|
## @param path_parts [Array<String, Flame::Path, Flame::Path::Part>]
|
43
54
|
## path or path parts as keys for navigating
|
@@ -48,31 +59,67 @@ module Flame
|
|
48
59
|
def navigate(*path_parts)
|
49
60
|
path_parts = Flame::Path.new(*path_parts).parts
|
50
61
|
return dig_through_opt_args if path_parts.empty?
|
62
|
+
|
51
63
|
endpoint =
|
52
|
-
self[path_parts.first] ||
|
53
|
-
|
54
|
-
|
55
|
-
|
64
|
+
self[path_parts.first] || dig(first_opt_arg_key, path_parts.first)
|
65
|
+
|
66
|
+
endpoint&.navigate(*path_parts[1..]) ||
|
67
|
+
find_among_arg_keys(path_parts[1..])
|
56
68
|
end
|
57
69
|
|
58
70
|
## Dig through optional arguments as keys
|
59
71
|
## @return [Flame::Router::Routes] return most nested end-point
|
60
72
|
## without optional arguments
|
61
73
|
def dig_through_opt_args
|
62
|
-
|
74
|
+
[
|
75
|
+
self[first_opt_arg_key]&.dig_through_opt_args,
|
76
|
+
self
|
77
|
+
]
|
78
|
+
.compact.find(&:first_route)
|
63
79
|
end
|
64
80
|
|
65
81
|
def allow
|
66
82
|
methods = keys.select { |key| key.is_a? Symbol }
|
67
83
|
return if methods.empty?
|
84
|
+
|
68
85
|
methods.push(:OPTIONS).join(', ')
|
69
86
|
end
|
70
87
|
|
88
|
+
PADDING_SIZE = Router::HTTP_METHODS.map(&:size).max
|
89
|
+
PADDING_FORMAT = "%#{PADDING_SIZE}.#{PADDING_SIZE}s"
|
90
|
+
|
91
|
+
## Output routes in human readable format
|
92
|
+
def to_s(prefix = '/')
|
93
|
+
sort.map do |key, value|
|
94
|
+
if key.is_a?(Symbol)
|
95
|
+
<<~OUTPUT
|
96
|
+
\e[1m#{format PADDING_FORMAT, key} #{prefix}\e[22m
|
97
|
+
#{' ' * PADDING_SIZE} \e[3m\e[36m#{value}\e[0m\e[23m
|
98
|
+
OUTPUT
|
99
|
+
else
|
100
|
+
value.to_s(Flame::Path.new(prefix, key))
|
101
|
+
end
|
102
|
+
end.join
|
103
|
+
end
|
104
|
+
|
105
|
+
## Sort routes for human readability
|
106
|
+
def sort
|
107
|
+
sort_by do |key, _value|
|
108
|
+
[
|
109
|
+
key.is_a?(Symbol) ? Router::HTTP_METHODS.index(key) : Float::INFINITY,
|
110
|
+
key.to_s
|
111
|
+
]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
71
115
|
private
|
72
116
|
|
73
|
-
def
|
117
|
+
def find_among_arg_keys(path_parts)
|
74
118
|
keys.find do |key|
|
75
|
-
key.is_a?(Flame::Path::Part) && key.arg?
|
119
|
+
next unless key.is_a?(Flame::Path::Part) && key.arg?
|
120
|
+
|
121
|
+
result = self[key].navigate(*path_parts)
|
122
|
+
break result if result
|
76
123
|
end
|
77
124
|
end
|
78
125
|
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Flame
|
4
|
+
class Router
|
5
|
+
class RoutesRefine
|
6
|
+
## Module for mounting in RoutesRefine
|
7
|
+
module Mounting
|
8
|
+
private
|
9
|
+
|
10
|
+
using GorillaPatch::DeepMerge
|
11
|
+
|
12
|
+
## Mount controller inside other (parent) controller
|
13
|
+
## @param controller [Flame::Controller] class of mounting controller
|
14
|
+
## @param path [String, nil] root path for mounting controller
|
15
|
+
## @yield Block of code for routes refine
|
16
|
+
def mount(controller_name, path = nil, &block)
|
17
|
+
routes_refine = self.class.new(
|
18
|
+
@namespace_name, controller_name, path, &block
|
19
|
+
)
|
20
|
+
|
21
|
+
@endpoint.deep_merge! routes_refine.routes
|
22
|
+
|
23
|
+
@reverse_routes.merge!(
|
24
|
+
routes_refine.reverse_routes.transform_values do |hash|
|
25
|
+
hash.transform_values { |action_path| @path + action_path }
|
26
|
+
end
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
using GorillaPatch::Namespace
|
31
|
+
|
32
|
+
def mount_nested_controllers
|
33
|
+
namespace = Object.const_get(@namespace_name)
|
34
|
+
|
35
|
+
namespace.constants.each do |constant_name|
|
36
|
+
constant = namespace.const_get(constant_name)
|
37
|
+
if constant < Flame::Controller || constant.instance_of?(Module)
|
38
|
+
mount_nested_controller constant
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def mount_nested_controller(nested_controller)
|
44
|
+
mount nested_controller if should_be_mounted? nested_controller
|
45
|
+
end
|
46
|
+
|
47
|
+
def should_be_mounted?(controller)
|
48
|
+
if controller.instance_of?(Module)
|
49
|
+
controller.const_defined?(:IndexController, false)
|
50
|
+
else
|
51
|
+
controller.actions.any? && !@reverse_routes.key?(controller.to_s)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'routes_refine/mounting'
|
4
|
+
|
5
|
+
module Flame
|
6
|
+
class Router
|
7
|
+
## Helper class for controller routing refine
|
8
|
+
class RoutesRefine
|
9
|
+
attr_reader :routes, :reverse_routes
|
10
|
+
|
11
|
+
class << self
|
12
|
+
include Memery
|
13
|
+
|
14
|
+
## Defaults REST routes (methods, pathes, controllers actions)
|
15
|
+
def rest_routes
|
16
|
+
[
|
17
|
+
{ method: :GET, path: '/', action: :index },
|
18
|
+
{ method: :POST, path: '/', action: :create },
|
19
|
+
{ method: :GET, path: '/', action: :show },
|
20
|
+
{ method: :PUT, path: '/', action: :update },
|
21
|
+
{ method: :DELETE, path: '/', action: :delete }
|
22
|
+
]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
using GorillaPatch::Namespace
|
27
|
+
|
28
|
+
def initialize(
|
29
|
+
namespace_name, controller_or_name, path, nested: true, &block
|
30
|
+
)
|
31
|
+
@controller =
|
32
|
+
ControllerFinder.new(namespace_name, controller_or_name).controller
|
33
|
+
@namespace_name = @controller.deconstantize
|
34
|
+
@path = Flame::Path.new(path || @controller.path)
|
35
|
+
@controller.path_arguments = @path.parts.select(&:arg?).map(&:to_sym)
|
36
|
+
@routes, @endpoint = @path.to_routes_with_endpoint
|
37
|
+
@reverse_routes = {}
|
38
|
+
@mount_nested = nested
|
39
|
+
execute(&block)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
HTTP_METHODS.each do |http_method|
|
45
|
+
## Define refine methods for all HTTP methods
|
46
|
+
## @overload post(path, action)
|
47
|
+
## Execute action on requested path and HTTP method
|
48
|
+
## @param path [String] path of method for the request
|
49
|
+
## @param action [Symbol] name of method for the request
|
50
|
+
## @example
|
51
|
+
## Set path to '/bye' and method to :POST for action `goodbye`
|
52
|
+
## post '/bye', :goodbye
|
53
|
+
## @overload post(action)
|
54
|
+
## Execute action on requested HTTP method
|
55
|
+
## @param action [Symbol] name of method for the request
|
56
|
+
## @example Set method to :POST for action `goodbye`
|
57
|
+
## post :goodbye
|
58
|
+
define_method(http_method.downcase) do |action_path, action = nil|
|
59
|
+
## Swap arguments if action in path variable
|
60
|
+
unless action
|
61
|
+
action = action_path.to_sym
|
62
|
+
action_path = nil
|
63
|
+
end
|
64
|
+
## Initialize new route
|
65
|
+
route = Route.new(@controller, action)
|
66
|
+
## Make path by controller method with parameners
|
67
|
+
action_path = Flame::Path.new(action_path).adapt(@controller, action)
|
68
|
+
## Validate action path
|
69
|
+
validate_action_path(action, action_path)
|
70
|
+
## Merge action path with controller path
|
71
|
+
path = Flame::Path.new(@path, action_path)
|
72
|
+
## Remove the same route if needed
|
73
|
+
remove_old_routes(action, route)
|
74
|
+
## Add new route
|
75
|
+
add_new_route(route, action, path, http_method)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
## Assign remaining methods of the controller
|
80
|
+
## to defaults pathes and HTTP methods
|
81
|
+
def defaults
|
82
|
+
rest
|
83
|
+
|
84
|
+
@controller.actions.each do |action|
|
85
|
+
get action unless find_reverse_route(action)
|
86
|
+
end
|
87
|
+
|
88
|
+
mount_nested_controllers if @mount_nested && (
|
89
|
+
@controller.demodulize == 'IndexController' &&
|
90
|
+
!@namespace_name.empty?
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
## Assign methods of the controller to REST architecture
|
95
|
+
def rest
|
96
|
+
self.class.rest_routes.each do |rest_route|
|
97
|
+
action = rest_route[:action]
|
98
|
+
next if !@controller.actions.include?(action) || find_reverse_route(action)
|
99
|
+
|
100
|
+
send(*rest_route.values.map(&:downcase))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
include Mounting
|
105
|
+
|
106
|
+
## Execute block of refinings end sorting routes
|
107
|
+
def execute(&block)
|
108
|
+
@controller.refined_http_methods
|
109
|
+
.each do |action, (http_method, action_path)|
|
110
|
+
send(http_method, action_path, action)
|
111
|
+
end
|
112
|
+
instance_exec(&block) if block
|
113
|
+
defaults
|
114
|
+
end
|
115
|
+
|
116
|
+
def find_reverse_route(action)
|
117
|
+
@reverse_routes.dig(@controller.to_s, action)
|
118
|
+
end
|
119
|
+
|
120
|
+
def validate_action_path(action, action_path)
|
121
|
+
Validators::RouteArgumentsValidator
|
122
|
+
.new(@controller, action_path, action)
|
123
|
+
.valid?
|
124
|
+
end
|
125
|
+
|
126
|
+
def remove_old_routes(action, new_route)
|
127
|
+
old_path = @reverse_routes[@controller.to_s]&.delete(action)
|
128
|
+
return unless old_path
|
129
|
+
|
130
|
+
@routes.dig(*old_path.parts)
|
131
|
+
.delete_if { |_method, old_route| old_route == new_route }
|
132
|
+
end
|
133
|
+
|
134
|
+
using GorillaPatch::DeepMerge
|
135
|
+
|
136
|
+
def add_new_route(route, action, path, http_method)
|
137
|
+
path_routes, endpoint = path.to_routes_with_endpoint
|
138
|
+
endpoint[http_method] = route
|
139
|
+
@routes.deep_merge!(path_routes)
|
140
|
+
(@reverse_routes[@controller.to_s] ||= {})[action] = path
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|