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.
Files changed (64) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +921 -0
  3. data/LICENSE.txt +19 -0
  4. data/README.md +135 -0
  5. data/lib/flame.rb +12 -5
  6. data/lib/flame/application.rb +47 -46
  7. data/lib/flame/config.rb +73 -0
  8. data/lib/flame/controller.rb +45 -78
  9. data/lib/flame/controller/actions.rb +122 -0
  10. data/lib/flame/{dispatcher → controller}/cookies.rb +8 -3
  11. data/lib/flame/controller/path_to.rb +34 -10
  12. data/lib/flame/dispatcher.rb +14 -17
  13. data/lib/flame/dispatcher/request.rb +25 -6
  14. data/lib/flame/dispatcher/routes.rb +22 -14
  15. data/lib/flame/dispatcher/static.rb +13 -9
  16. data/lib/flame/errors/argument_not_assigned_error.rb +3 -8
  17. data/lib/flame/errors/config_file_not_found_error.rb +17 -0
  18. data/lib/flame/errors/controller_not_found_error.rb +19 -0
  19. data/lib/flame/errors/route_arguments_order_error.rb +5 -10
  20. data/lib/flame/errors/route_extra_arguments_error.rb +10 -20
  21. data/lib/flame/errors/route_not_found_error.rb +3 -8
  22. data/lib/flame/errors/template_not_found_error.rb +2 -8
  23. data/lib/flame/path.rb +36 -18
  24. data/lib/flame/render.rb +13 -5
  25. data/lib/flame/router.rb +7 -157
  26. data/lib/flame/router/controller_finder.rb +56 -0
  27. data/lib/flame/router/route.rb +9 -0
  28. data/lib/flame/router/routes.rb +58 -8
  29. data/lib/flame/router/routes_refine.rb +144 -0
  30. data/lib/flame/router/routes_refine/mounting.rb +57 -0
  31. data/lib/flame/validators.rb +14 -10
  32. data/lib/flame/version.rb +1 -1
  33. metadata +91 -99
  34. data/bin/flame +0 -16
  35. data/lib/flame/application/config.rb +0 -49
  36. data/template/.editorconfig +0 -15
  37. data/template/.gitignore +0 -28
  38. data/template/.rubocop.yml +0 -14
  39. data/template/Gemfile +0 -55
  40. data/template/Rakefile +0 -824
  41. data/template/application.rb.erb +0 -10
  42. data/template/config.ru.erb +0 -72
  43. data/template/config/config.rb.erb +0 -56
  44. data/template/config/database.example.yml +0 -5
  45. data/template/config/deploy.example.yml +0 -2
  46. data/template/config/puma.rb +0 -56
  47. data/template/config/sequel.rb.erb +0 -22
  48. data/template/config/server.example.yml +0 -32
  49. data/template/config/session.example.yml +0 -7
  50. data/template/controllers/_controller.rb.erb +0 -14
  51. data/template/controllers/site/_controller.rb.erb +0 -18
  52. data/template/controllers/site/index_controller.rb.erb +0 -12
  53. data/template/db/.keep +0 -0
  54. data/template/filewatchers.yml +0 -12
  55. data/template/helpers/.keep +0 -0
  56. data/template/lib/.keep +0 -0
  57. data/template/locales/en.yml +0 -0
  58. data/template/models/.keep +0 -0
  59. data/template/public/.keep +0 -0
  60. data/template/server +0 -200
  61. data/template/services/.keep +0 -0
  62. data/template/views/.keep +0 -0
  63. data/template/views/site/index.html.erb.erb +0 -1
  64. data/template/views/site/layout.html.erb.erb +0 -10
@@ -9,14 +9,9 @@ module Flame
9
9
  ## @param argument [Flame::Path::Part, String, Symbol]
10
10
  ## not assigned argument
11
11
  def initialize(path, argument)
12
- @path = path
13
- @argument = argument
14
- end
15
-
16
- ## Calculated message of the error
17
- ## @return [String] message of the error
18
- def message
19
- "Argument '#{@argument}' for path '#{@path}' is not assigned"
12
+ super(
13
+ "Argument '#{argument}' for path '#{path}' is not assigned"
14
+ )
20
15
  end
21
16
  end
22
17
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flame
4
+ module Errors
5
+ ## Error for not found config file in Config
6
+ class ConfigFileNotFoundError < StandardError
7
+ ## Create a new instance of error
8
+ ## @param file_name [String]
9
+ ## file name mask by which file was not found
10
+ ## @param directory [String] directory in which file was not found
11
+ def initialize(file_name, directory)
12
+ directory = directory.sub(%r{^/+}, '').sub(%r{/+$}, '')
13
+ super "Config file '#{file_name}' not found in '#{directory}/'"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flame
4
+ module Errors
5
+ ## Error for not found controller by name in namespace
6
+ class ControllerNotFoundError < StandardError
7
+ ## Create a new instance of error
8
+ ## @param controller_name [Symbol, String]
9
+ ## name of controller which not found
10
+ ## @param namespace [Module]
11
+ ## namespace for which controller not found
12
+ def initialize(controller_name, namespace)
13
+ super(
14
+ "Controller '#{controller_name}' not found for '#{namespace}'"
15
+ )
16
+ end
17
+ end
18
+ end
19
+ end
@@ -9,16 +9,11 @@ module Flame
9
9
  ## @param wrong_ordered_arguments [Array<Symbol>]
10
10
  ## two wrong ordered arguments
11
11
  def initialize(path, wrong_ordered_arguments)
12
- @path = path
13
- @wrong_ordered_arguments = wrong_ordered_arguments
14
- end
15
-
16
- ## Calculated message of the error
17
- ## @return [String] message of the error
18
- def message
19
- "Path '#{@path}' should have" \
20
- " '#{@wrong_ordered_arguments.first}' argument before" \
21
- " '#{@wrong_ordered_arguments.last}'"
12
+ super(
13
+ "Path '#{path}' should have" \
14
+ " '#{wrong_ordered_arguments.first}' argument before" \
15
+ " '#{wrong_ordered_arguments.last}'"
16
+ )
22
17
  end
23
18
  end
24
19
  end
@@ -13,30 +13,20 @@ module Flame
13
13
  ## @option extra [Symbol] :place extra arguments in controller or path
14
14
  ## @option extra [Array<Symbol>] :args extra arguments
15
15
  def initialize(ctrl, action, path, extra)
16
- @ctrl = ctrl
17
- @action = action
18
- @path = path
19
- @extra = extra
20
- @extra[:type_name] = {
21
- req: 'required',
22
- opt: 'optional'
23
- }[@extra[:type]]
24
- end
16
+ extra[:type_name] = { req: 'required', opt: 'optional' }[extra[:type]]
25
17
 
26
- ## Calculated message of the error
27
- ## @return [String] message of the error
28
- def message
29
- case @extra[:place]
30
- when :ctrl
18
+ entity = {
31
19
  ## Error if path has no arguments, that controller's method has
32
20
  ## NOTE: It isn't using because `Flame::Path#adopt`
33
- "Path '#{@path}' has no #{@extra[:type_name]}" \
34
- " arguments #{@extra[:args].inspect}"
35
- when :path
21
+ ctrl: "Path '#{path}'",
36
22
  ## Error if path has more arguments, than controller's method
37
- "Action '#{@ctrl}##{@action}' has no #{@extra[:type_name]}" \
38
- " arguments #{@extra[:args].inspect}"
39
- end
23
+ path: "Action '#{ctrl}##{action}'"
24
+ }[extra[:place]]
25
+
26
+ super(
27
+ "#{entity} has no " \
28
+ "#{extra[:type_name]} arguments #{extra[:args].inspect}"
29
+ )
40
30
  end
41
31
  end
42
32
  end
@@ -9,15 +9,10 @@ module Flame
9
9
  ## controller with which route not found
10
10
  ## @param action [Symbol] action with which route not found
11
11
  def initialize(controller, action)
12
- @controller = controller
13
- @action = action
14
- end
15
-
16
- ## Calculated message of the error
17
- ## @return [String] message of the error
18
- def message
19
- "Route with controller '#{@controller}' and action '#{@action}'" \
12
+ super(
13
+ "Route with controller '#{controller}' and action '#{action}'" \
20
14
  ' not found in application routes'
15
+ )
21
16
  end
22
17
  end
23
18
  end
@@ -9,15 +9,9 @@ module Flame
9
9
  ## controller from which template not found
10
10
  ## @param path [String, Symbol] path of not founded template
11
11
  def initialize(controller, path)
12
- @controller = controller
13
- @controller = @controller.class unless @controller.is_a? Class
14
- @path = path
15
- end
12
+ controller = controller.class unless controller.is_a? Class
16
13
 
17
- ## Calculated message of the error
18
- ## @return [String] message of the error
19
- def message
20
- "Template '#{@path}' not found for '#{@controller}'"
14
+ super "Template '#{path}' not found for '#{controller}'"
21
15
  end
22
16
  end
23
17
  end
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
- @parts ||= @path.to_s.split('/').reject(&:empty?)
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
- parts_size = self_parts.size <=> other_parts.size
53
- return parts_size unless parts_size.zero?
54
- self_parts.zip(other_parts)
55
- .reduce(0) do |result, (self_part, other_part)|
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[part[2..-1].to_sym] if part.opt_arg?
125
+ return args.delete(part[2..-1].to_sym) if part.opt_arg?
126
+
124
127
  ## Required argument
125
- param = args[part[1..-1].to_sym]
128
+ param = args.delete(part[1..-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.clean.to_sym] = extract
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 :@part, :[], :hash, :empty?, :b
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 String without arguments characters
233
- ## @return [String] clean String
234
- def clean
235
- @part.delete ARG_CHAR + ARG_CHAR_OPT
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
- raise Flame::Errors::TemplateNotFoundError.new(controller, path)
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
- %w[_controller _ctrl]
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 }
data/lib/flame/router.rb CHANGED
@@ -3,16 +3,19 @@
3
3
  require 'gorilla_patch/deep_merge'
4
4
  require 'gorilla_patch/inflections'
5
5
  require 'gorilla_patch/namespace'
6
- require 'gorilla_patch/transform'
7
6
 
8
- require_relative 'router/routes'
9
- require_relative 'router/route'
7
+ require_relative 'router/controller_finder'
8
+ require_relative 'errors/controller_not_found_error'
10
9
 
11
10
  module Flame
12
11
  ## Router class for routing
13
12
  class Router
14
13
  HTTP_METHODS = %i[GET POST PUT PATCH DELETE].freeze
15
14
 
15
+ require_relative 'router/route'
16
+ require_relative 'router/routes'
17
+ require_relative 'router/routes_refine'
18
+
16
19
  extend Forwardable
17
20
  def_delegators :routes, :navigate
18
21
 
@@ -40,7 +43,7 @@ module Flame
40
43
  def find_nearest_route(path)
41
44
  path_parts = path.parts.dup
42
45
  loop do
43
- route = routes.navigate(*path_parts)&.values&.grep(Route)&.first
46
+ route = routes.navigate(*path_parts)&.first_route
44
47
  break route if route || path_parts.pop.nil?
45
48
  end
46
49
  end
@@ -60,158 +63,5 @@ module Flame
60
63
  end
61
64
  reverse_routes.dig(controller.to_s, action)
62
65
  end
63
-
64
- ## Helper class for controller routing refine
65
- class RoutesRefine
66
- attr_reader :routes, :reverse_routes
67
-
68
- ## Defaults REST routes (methods, pathes, controllers actions)
69
- def self.rest_routes
70
- @rest_routes ||= [
71
- { method: :GET, path: '/', action: :index },
72
- { method: :POST, path: '/', action: :create },
73
- { method: :GET, path: '/', action: :show },
74
- { method: :PUT, path: '/', action: :update },
75
- { method: :DELETE, path: '/', action: :delete }
76
- ]
77
- end
78
-
79
- def initialize(router, namespace_name, controller_name, path, &block)
80
- @router = router
81
- @controller = constantize_controller namespace_name, controller_name
82
- @path = Flame::Path.new(path || @controller.default_path)
83
- @routes, @endpoint = @path.to_routes_with_endpoint
84
- @reverse_routes = {}
85
- execute(&block)
86
- end
87
-
88
- private
89
-
90
- using GorillaPatch::Inflections
91
-
92
- def constantize_controller(namespace_name, controller_name)
93
- controller_name = controller_name.to_s.camelize
94
- namespace =
95
- namespace_name.empty? ? Object : Object.const_get(namespace_name)
96
- if namespace.const_defined?(controller_name)
97
- controller = namespace.const_get(controller_name)
98
- return controller if controller < Flame::Controller
99
- controller::IndexController
100
- else
101
- namespace.const_get("#{controller_name}Controller")
102
- end
103
- end
104
-
105
- HTTP_METHODS.each do |http_method|
106
- ## Define refine methods for all HTTP methods
107
- ## @overload post(path, action)
108
- ## Execute action on requested path and HTTP method
109
- ## @param path [String] path of method for the request
110
- ## @param action [Symbol] name of method for the request
111
- ## @example Set path to '/bye' and method to :POST for action `goodbye`
112
- ## post '/bye', :goodbye
113
- ## @overload post(action)
114
- ## Execute action on requested HTTP method
115
- ## @param action [Symbol] name of method for the request
116
- ## @example Set method to :POST for action `goodbye`
117
- ## post :goodbye
118
- define_method(http_method.downcase) do |action_path, action = nil|
119
- ## Swap arguments if action in path variable
120
- unless action
121
- action = action_path.to_sym
122
- action_path = nil
123
- end
124
- ## Initialize new route
125
- route = Route.new(@controller, action)
126
- ## Make path by controller method with parameners
127
- action_path = Flame::Path.new(action_path).adapt(@controller, action)
128
- ## Validate action path
129
- validate_action_path(action, action_path)
130
- ## Merge action path with controller path
131
- path = Flame::Path.new(@path, action_path)
132
- ## Remove the same route if needed
133
- remove_old_routes(action, route)
134
- ## Add new route
135
- add_new_route(route, action, path, http_method)
136
- end
137
- end
138
-
139
- ## Assign remaining methods of the controller
140
- ## to defaults pathes and HTTP methods
141
- def defaults
142
- rest
143
- @controller.actions.each do |action|
144
- next if find_reverse_route(action)
145
- send(:GET.downcase, action)
146
- end
147
- end
148
-
149
- ## Assign methods of the controller to REST architecture
150
- def rest
151
- self.class.rest_routes.each do |rest_route|
152
- action = rest_route[:action]
153
- next if !@controller.actions.include?(action) ||
154
- find_reverse_route(action)
155
- send(*rest_route.values.map(&:downcase))
156
- end
157
- end
158
-
159
- using GorillaPatch::Namespace
160
- using GorillaPatch::Transform
161
- using GorillaPatch::DeepMerge
162
-
163
- ## Mount controller inside other (parent) controller
164
- ## @param controller [Flame::Controller] class of mounting controller
165
- ## @param path [String, nil] root path for mounting controller
166
- ## @yield Block of code for routes refine
167
- def mount(controller_name, path = nil, &block)
168
- routes_refine = self.class.new(
169
- @router, @controller.deconstantize, controller_name, path, &block
170
- )
171
-
172
- @endpoint.deep_merge! routes_refine.routes
173
-
174
- @reverse_routes.merge!(
175
- routes_refine.reverse_routes.transform_values do |hash|
176
- hash.transform_values { |action_path| @path + action_path }
177
- end
178
- )
179
- end
180
-
181
- # private
182
-
183
- ## Execute block of refinings end sorting routes
184
- def execute(&block)
185
- @controller.refined_http_methods
186
- .each do |http_method, action_path, action|
187
- send(http_method, action_path, action)
188
- end
189
- instance_exec(&block) if block
190
- defaults
191
- end
192
-
193
- def find_reverse_route(action)
194
- @reverse_routes.dig(@controller.to_s, action)
195
- end
196
-
197
- def validate_action_path(action, action_path)
198
- Validators::RouteArgumentsValidator.new(
199
- @controller, action_path, action
200
- ).valid?
201
- end
202
-
203
- def remove_old_routes(action, new_route)
204
- return unless (old_path = @reverse_routes[@controller.to_s]&.delete(action))
205
- @routes.dig(*old_path.parts)
206
- .delete_if { |_method, old_route| old_route == new_route }
207
- end
208
-
209
- def add_new_route(route, action, path, http_method)
210
- path_routes, endpoint = path.to_routes_with_endpoint
211
- endpoint[http_method] = route
212
- @routes.deep_merge!(path_routes)
213
- (@reverse_routes[@controller.to_s] ||= {})[action] = path
214
- end
215
- end
216
66
  end
217
67
  end