flame 1.0
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 +7 -0
- data/lib/flame.rb +4 -0
- data/lib/flame/application.rb +43 -0
- data/lib/flame/controller.rb +26 -0
- data/lib/flame/dispatcher.rb +102 -0
- data/lib/flame/errors.rb +80 -0
- data/lib/flame/render.rb +49 -0
- data/lib/flame/route.rb +114 -0
- data/lib/flame/router.rb +126 -0
- data/lib/flame/validators.rb +89 -0
- data/public/favicon.ico +0 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c69eaa9cc9a97a1ad4962f3e54011450dc1e1f11
|
4
|
+
data.tar.gz: 91d5016a225a54dbd43b36b8ef12b07f9b174051
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 669fed7b35fc590a1c895292f704fe2d20a112189c34e14ee87fbf95ae70441f425a45648d070d1cf20809077234cbfc716809394290141d74f062dc978772b5
|
7
|
+
data.tar.gz: 7ec7a41b6c8ba4b58c83ad613df1f54280504d3cbbb0f22bc27cb7f4827f16e1fb8bdb269b821fe75abb6be69c9c1e0998bd5323bae54d74b1fd8be62e0bb920
|
data/lib/flame.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative './router'
|
2
|
+
require_relative './dispatcher'
|
3
|
+
|
4
|
+
module Flame
|
5
|
+
## Core class, like Framework::Application
|
6
|
+
class Application
|
7
|
+
## Framework configuration
|
8
|
+
def self.config
|
9
|
+
@config ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def config
|
13
|
+
self.class.config
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.inherited(app)
|
17
|
+
root_dir = File.dirname(caller[0].split(':')[0])
|
18
|
+
app.config.merge!(
|
19
|
+
root_dir: root_dir,
|
20
|
+
public_dir: File.join(root_dir, 'public'),
|
21
|
+
views_dir: File.join(root_dir, 'views')
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
## Init function
|
26
|
+
def call(env)
|
27
|
+
Dispatcher.new(self, env).run!
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.mount(ctrl, path = nil, &block)
|
31
|
+
router.add_controller(ctrl, path, block)
|
32
|
+
end
|
33
|
+
|
34
|
+
## Router for routing
|
35
|
+
def self.router
|
36
|
+
@router ||= Flame::Router.new
|
37
|
+
end
|
38
|
+
|
39
|
+
def router
|
40
|
+
self.class.router
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative './render'
|
2
|
+
|
3
|
+
module Flame
|
4
|
+
## Class for controllers helpers, like Framework::Controller
|
5
|
+
class Controller
|
6
|
+
include Flame::Render
|
7
|
+
|
8
|
+
def initialize(dispatcher)
|
9
|
+
@dispatcher = dispatcher
|
10
|
+
end
|
11
|
+
|
12
|
+
def config
|
13
|
+
@dispatcher.config
|
14
|
+
end
|
15
|
+
|
16
|
+
def params
|
17
|
+
@dispatcher.params
|
18
|
+
end
|
19
|
+
|
20
|
+
def path_to(*params)
|
21
|
+
@dispatcher.path_to(*params)
|
22
|
+
end
|
23
|
+
|
24
|
+
## TODO: Add more helpers
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'rack'
|
2
|
+
|
3
|
+
module Flame
|
4
|
+
## Class initialize when Application.call(env) invoked
|
5
|
+
## For new request and response
|
6
|
+
class Dispatcher
|
7
|
+
def initialize(app, env)
|
8
|
+
@app = app
|
9
|
+
@env = env
|
10
|
+
end
|
11
|
+
|
12
|
+
def run!
|
13
|
+
body = catch :halt do
|
14
|
+
try_route ||
|
15
|
+
try_static ||
|
16
|
+
try_static(File.join(__dir__, '..', '..', 'public')) ||
|
17
|
+
halt(404)
|
18
|
+
end
|
19
|
+
response.write body
|
20
|
+
response.finish
|
21
|
+
end
|
22
|
+
|
23
|
+
## Helpers
|
24
|
+
def config
|
25
|
+
@app.config
|
26
|
+
end
|
27
|
+
|
28
|
+
def request
|
29
|
+
@request ||= Rack::Request.new(@env)
|
30
|
+
end
|
31
|
+
|
32
|
+
def params
|
33
|
+
request.params
|
34
|
+
end
|
35
|
+
|
36
|
+
def response
|
37
|
+
@response ||= Rack::Response.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def status(value = nil)
|
41
|
+
response.status ||= 200
|
42
|
+
value ? response.status = value : response.status
|
43
|
+
end
|
44
|
+
|
45
|
+
def halt(new_status, body = '', new_headers = {})
|
46
|
+
status new_status
|
47
|
+
response.headers.merge!(new_headers)
|
48
|
+
# p response.body
|
49
|
+
if body.empty? &&
|
50
|
+
!Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status)
|
51
|
+
body = Rack::Utils::HTTP_STATUS_CODES[status]
|
52
|
+
end
|
53
|
+
throw :halt, body
|
54
|
+
end
|
55
|
+
|
56
|
+
def path_to(ctrl, action, args = {})
|
57
|
+
route = @app.router.find_route(controller: ctrl, action: action)
|
58
|
+
fail RouteNotFoundError.new(ctrl, action) unless route
|
59
|
+
route.assign_arguments(args)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def try_route
|
65
|
+
method = params['_method'] || request.request_method
|
66
|
+
path = request.path_info
|
67
|
+
route = @app.router.find_route(
|
68
|
+
method: method,
|
69
|
+
path: path
|
70
|
+
)
|
71
|
+
# p route
|
72
|
+
return nil unless route
|
73
|
+
status 200
|
74
|
+
route.execute(self)
|
75
|
+
end
|
76
|
+
|
77
|
+
def try_static(dir = config[:public_dir])
|
78
|
+
static_file = File.join(dir, request.path_info)
|
79
|
+
# p static_file
|
80
|
+
return nil unless File.exist?(static_file) && File.file?(static_file)
|
81
|
+
return_static(static_file)
|
82
|
+
end
|
83
|
+
|
84
|
+
def return_static(file)
|
85
|
+
since = @env['HTTP_IF_MODIFIED_SINCE']
|
86
|
+
file_time = File.mtime(file)
|
87
|
+
halt 304 if since && Time.httpdate(since).to_i >= file_time.to_i
|
88
|
+
response.headers.merge!(
|
89
|
+
'Content-Type' => file_mime_type(file),
|
90
|
+
'Last-Modified' => file_time.httpdate
|
91
|
+
# 'Content-Disposition' => 'attachment;' \
|
92
|
+
# "filename=\"#{File.basename(static_file)}\"",
|
93
|
+
# 'Content-Length' => File.size?(static_file).to_s
|
94
|
+
)
|
95
|
+
halt 200, File.read(file)
|
96
|
+
end
|
97
|
+
|
98
|
+
def file_mime_type(file)
|
99
|
+
Rack::Mime.mime_type(File.extname(file))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/flame/errors.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
module Flame
|
2
|
+
module RouterError
|
3
|
+
## Error for Flame::Router.compare_actions
|
4
|
+
class ActionsError < StandardError
|
5
|
+
def initialize(ctrl, extra_actions)
|
6
|
+
@ctrl = ctrl
|
7
|
+
@extra_actions = extra_actions
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
## Error if routes have more actions, than controller
|
12
|
+
class ExtraRoutesActionsError < ActionsError
|
13
|
+
def message
|
14
|
+
"Controller '#{@ctrl}' has no methods" \
|
15
|
+
" '#{@extra_actions.join(', ')}' from routes"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
## Error if controller has not assigned in routes actions
|
20
|
+
class ExtraControllerActionsError < ActionsError
|
21
|
+
def message
|
22
|
+
"Routes for '#{@ctrl}' has no methods" \
|
23
|
+
" '#{@extra_actions.join(', ')}'"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
## Error for Flame::Router::RouteRefine.arguments_valid?
|
28
|
+
class ArgumentsError < StandardError
|
29
|
+
def initialize(ctrl, action, path, extra_args)
|
30
|
+
@ctrl = ctrl
|
31
|
+
@action = action
|
32
|
+
@path = path
|
33
|
+
@extra_args = extra_args
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
## Error if path has more arguments, than controller's method
|
38
|
+
class ExtraPathArgumentsError < ArgumentsError
|
39
|
+
def message
|
40
|
+
"Method '#{@action}' from controller '#{@ctrl}'" \
|
41
|
+
" does not know arguments '#{@extra_args.join(', ')}'" \
|
42
|
+
" from path '#{@path}'"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
## Error if path has no arguments, that controller's method required
|
47
|
+
class ExtraActionArgumentsError < ArgumentsError
|
48
|
+
def message
|
49
|
+
"Path '#{@path}' does not contain required arguments" \
|
50
|
+
" '#{@extra_args.join(', ')}' of method '#{@action}'" \
|
51
|
+
" from controller '#{@ctrl}'"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
## Error for Flame::Router.find_path
|
57
|
+
class RouteNotFoundError < StandardError
|
58
|
+
def initialize(ctrl, method)
|
59
|
+
@ctrl = ctrl
|
60
|
+
@method = method
|
61
|
+
end
|
62
|
+
|
63
|
+
def message
|
64
|
+
"Route with controller '#{@ctrl}' and method '#{@method}'" \
|
65
|
+
' not found in application routes'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
## Error for Flame::Controller.path_to
|
70
|
+
class ArgumentNotAssigned < StandardError
|
71
|
+
def initialize(path, path_part)
|
72
|
+
@path = path
|
73
|
+
@path_part = path_part
|
74
|
+
end
|
75
|
+
|
76
|
+
def message
|
77
|
+
"Argument '#{@path_part}' for '#{@path}' is not assigned"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/flame/render.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
|
3
|
+
module Flame
|
4
|
+
## Helper for render functionality
|
5
|
+
module Render
|
6
|
+
def view(path, options = {})
|
7
|
+
## Take options for rendering
|
8
|
+
scope = options.delete(:scope) || self
|
9
|
+
layout = options.delete(:layout) || 'layout.*'
|
10
|
+
## And get the rest variables to locals
|
11
|
+
locals = options.merge(options.delete(:locals) || {})
|
12
|
+
## Find filename
|
13
|
+
filename = find_file(path)
|
14
|
+
## Compile Tilt to instance hash
|
15
|
+
@tilts ||= {}
|
16
|
+
@tilts[filename] ||= Tilt.new(filename)
|
17
|
+
## Render Tilt from instance hash with new options
|
18
|
+
layout_render layout, @tilts[filename].render(scope, locals)
|
19
|
+
end
|
20
|
+
|
21
|
+
alias_method :render, :view
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
## TODO: Add `views_dir` for Application and Controller
|
26
|
+
## TODO: Add `layout` method for Controller
|
27
|
+
def find_file(path)
|
28
|
+
## Build controller_dir
|
29
|
+
controller_dir = (
|
30
|
+
self.class.name.split(/(?=[A-Z])/) - ['Controller']
|
31
|
+
).join('_').downcase
|
32
|
+
## Get full filename
|
33
|
+
Dir[File.join(
|
34
|
+
config[:views_dir],
|
35
|
+
"{#{controller_dir},}",
|
36
|
+
"#{path}.*"
|
37
|
+
)].find do |file|
|
38
|
+
Tilt[file]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def layout_render(layout, result)
|
43
|
+
layout_filename = find_file(layout)
|
44
|
+
## Compile layout to hash
|
45
|
+
@tilts[layout_filename] ||= Tilt.new(layout_filename)
|
46
|
+
@tilts[layout_filename] ? @tilts[layout_filename].render { result } : result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/flame/route.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
module Flame
|
2
|
+
## Class for Route in Router.routes
|
3
|
+
class Route
|
4
|
+
attr_reader :attributes
|
5
|
+
|
6
|
+
def initialize(attrs = {})
|
7
|
+
@attributes = attrs
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](attribute)
|
11
|
+
@attributes[attribute]
|
12
|
+
end
|
13
|
+
|
14
|
+
## Arguments in order as parameters of method of controller
|
15
|
+
def arrange_arguments(args = {})
|
16
|
+
self[:controller].instance_method(self[:action]).parameters
|
17
|
+
.each_with_object([]) do |par, arr|
|
18
|
+
arr << args[par[1]] if par[0] == :req || args[par[1]]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
## Compare attributes for `Router.find_route`
|
23
|
+
def compare_attributes(attrs)
|
24
|
+
attrs.each do |name, value|
|
25
|
+
next true if compare_attribute(name, value)
|
26
|
+
break false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
## Assign arguments to path for `Controller.path_to`
|
31
|
+
def assign_arguments(args = {})
|
32
|
+
extract_path_parts
|
33
|
+
.map { |path_part| assign_argument(path_part, args) }
|
34
|
+
.unshift('').join('/').gsub(%r{\/{2,}}, '/')
|
35
|
+
end
|
36
|
+
|
37
|
+
## Execute by Application.call
|
38
|
+
def execute(app)
|
39
|
+
ctrl = self[:controller].new(app)
|
40
|
+
args = extract_arguments(app.request.path_info)
|
41
|
+
app.params.merge!(args)
|
42
|
+
ctrl.send(self[:action], *arrange_arguments(args))
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
## Helpers for attributes accessors
|
48
|
+
def extract_path_parts(other_path = self[:path])
|
49
|
+
other_path.to_s.split('/').reject(&:empty?)
|
50
|
+
end
|
51
|
+
|
52
|
+
## Extract arguments from request_path for `execute`
|
53
|
+
def extract_arguments(request_path)
|
54
|
+
args = {}
|
55
|
+
request_parts = extract_path_parts(request_path)
|
56
|
+
extract_path_parts.each_with_index do |path_part, i|
|
57
|
+
next unless path_part[0] == ':'
|
58
|
+
args[
|
59
|
+
path_part[(path_part[1] == '?' ? 2 : 1)..-1].to_sym
|
60
|
+
] = URI.decode(request_parts[i])
|
61
|
+
end
|
62
|
+
args
|
63
|
+
end
|
64
|
+
|
65
|
+
## Helpers for `compare_attributes`
|
66
|
+
def compare_attribute(name, value)
|
67
|
+
case name
|
68
|
+
when :method
|
69
|
+
compare_method(value)
|
70
|
+
when :path
|
71
|
+
compare_path(value)
|
72
|
+
else
|
73
|
+
self[name] == value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def compare_method(request_method)
|
78
|
+
self[:method].upcase.to_sym == request_method.upcase.to_sym
|
79
|
+
end
|
80
|
+
|
81
|
+
def compare_path(request_path)
|
82
|
+
path_parts = extract_path_parts
|
83
|
+
request_parts = extract_path_parts(request_path)
|
84
|
+
# p route_path
|
85
|
+
req_path_parts = path_parts.select { |part| part[1] != '?' }
|
86
|
+
return false if request_parts.count < req_path_parts.count
|
87
|
+
compare_parts(request_parts, path_parts)
|
88
|
+
end
|
89
|
+
|
90
|
+
def compare_parts(request_parts, path_parts)
|
91
|
+
request_parts.each_with_index do |request_part, i|
|
92
|
+
path_part = path_parts[i]
|
93
|
+
# p request_part, path_part
|
94
|
+
break false unless path_part
|
95
|
+
next if path_part[0] == ':'
|
96
|
+
break false unless request_part == path_part
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
## Helpers for `assign_arguments`
|
101
|
+
def assign_argument(path_part, args = {})
|
102
|
+
## Not argument
|
103
|
+
return path_part unless path_part[0] == ':'
|
104
|
+
## Not required argument
|
105
|
+
return args[path_part[2..-1].to_sym] if path_part[1] == '?'
|
106
|
+
## Required argument
|
107
|
+
param = args[path_part[1..-1].to_sym]
|
108
|
+
## Required argument is nil
|
109
|
+
fail ArgumentNotAssigned.new(self[:path], path_part) if param.nil?
|
110
|
+
## All is ok
|
111
|
+
param
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/flame/router.rb
ADDED
@@ -0,0 +1,126 @@
|
|
1
|
+
require_relative './route.rb'
|
2
|
+
require_relative './validators.rb'
|
3
|
+
|
4
|
+
module Flame
|
5
|
+
## Router class for routing
|
6
|
+
class Router
|
7
|
+
attr_accessor :routes
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@routes = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_controller(ctrl, path, block = nil)
|
14
|
+
## TODO: Add Regexp paths
|
15
|
+
## TODO: Add `before` and `after` methods
|
16
|
+
|
17
|
+
## Add routes from controller to glob array
|
18
|
+
ctrl_routes = RouteRefine.new(ctrl, path, block).routes
|
19
|
+
ActionsValidator.new(ctrl_routes, ctrl).valid?
|
20
|
+
routes.concat(ctrl_routes)
|
21
|
+
end
|
22
|
+
|
23
|
+
## Find route by any attributes
|
24
|
+
def find_route(attrs)
|
25
|
+
routes.find { |route| route.compare_attributes(attrs) }
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
## Helper module for routing refine
|
31
|
+
class RouteRefine
|
32
|
+
attr_accessor :rest_routes
|
33
|
+
attr_reader :routes
|
34
|
+
|
35
|
+
def self.http_methods
|
36
|
+
[:GET, :POST, :PUT, :DELETE]
|
37
|
+
end
|
38
|
+
|
39
|
+
def rest_routes
|
40
|
+
@rest_routes ||= [
|
41
|
+
{ method: :GET, path: '/', action: :index },
|
42
|
+
{ method: :POST, path: '/', action: :create },
|
43
|
+
{ method: :GET, path: '/', action: :show },
|
44
|
+
{ method: :PUT, path: '/', action: :update },
|
45
|
+
{ method: :DELETE, path: '/', action: :delete }
|
46
|
+
]
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize(ctrl, path, block)
|
50
|
+
@ctrl = ctrl
|
51
|
+
@path = path || default_controller_path
|
52
|
+
@routes = []
|
53
|
+
block.nil? ? defaults : instance_exec(&block)
|
54
|
+
# p @routes
|
55
|
+
@routes.sort! { |a, b| b[:path] <=> a[:path] }
|
56
|
+
end
|
57
|
+
|
58
|
+
http_methods.each do |request_method|
|
59
|
+
define_method(request_method.downcase) do |path, action|
|
60
|
+
ArgumentsValidator.new(@ctrl, path, action).valid?
|
61
|
+
add_route(request_method, path, action)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def defaults
|
66
|
+
@ctrl.public_instance_methods(false).each do |action|
|
67
|
+
next if find_route_index(action: action)
|
68
|
+
add_route(:GET, nil, action)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def rest
|
73
|
+
rest_routes.each do |rest_route|
|
74
|
+
action = rest_route[:action]
|
75
|
+
if @ctrl.public_instance_methods.include?(action) &&
|
76
|
+
find_route_index(action: action).nil?
|
77
|
+
add_route(*rest_route.values, true)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
using GorillaPatch::StringExt
|
85
|
+
|
86
|
+
def default_controller_path
|
87
|
+
@ctrl.name.underscore
|
88
|
+
.split('_')
|
89
|
+
.take_while { |part| part != 'controller' }
|
90
|
+
.unshift(nil)
|
91
|
+
.join('/')
|
92
|
+
end
|
93
|
+
|
94
|
+
def make_path(path, action = nil, force_params = false)
|
95
|
+
## TODO: Add :arg:type support (:id:num, :name:str, etc.)
|
96
|
+
unshifted = force_params ? path : action_path(action)
|
97
|
+
if path.nil? || force_params
|
98
|
+
path = @ctrl.instance_method(action).parameters
|
99
|
+
.map { |par| ":#{par[0] == :req ? '' : '?'}#{par[1]}" }
|
100
|
+
.unshift(unshifted)
|
101
|
+
.join('/')
|
102
|
+
end
|
103
|
+
"#{@path}/#{path}".gsub(%r{\/{2,}}, '/')
|
104
|
+
end
|
105
|
+
|
106
|
+
def action_path(action)
|
107
|
+
action == :index ? '/' : action
|
108
|
+
end
|
109
|
+
|
110
|
+
def add_route(method, path, action, force_params = false)
|
111
|
+
route = Route.new(
|
112
|
+
method: method,
|
113
|
+
path: make_path(path, action, force_params),
|
114
|
+
controller: @ctrl,
|
115
|
+
action: action
|
116
|
+
)
|
117
|
+
index = find_route_index(action: action)
|
118
|
+
index ? @routes[index] = route : @routes.push(route)
|
119
|
+
end
|
120
|
+
|
121
|
+
def find_route_index(attrs)
|
122
|
+
@routes.find_index { |route| route.compare_attributes(attrs) }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require_relative './errors.rb'
|
2
|
+
|
3
|
+
module Flame
|
4
|
+
## Compare arguments from path and from controller's action
|
5
|
+
class ArgumentsValidator
|
6
|
+
def initialize(ctrl, path, action)
|
7
|
+
@ctrl = ctrl
|
8
|
+
@path = path
|
9
|
+
@action = action
|
10
|
+
end
|
11
|
+
|
12
|
+
def valid?
|
13
|
+
## Break path for ':arg' arguments
|
14
|
+
@path_args = path_arguments(@path)
|
15
|
+
## Take all and required arguments from Controller#action
|
16
|
+
@action_args = action_arguments(@action)
|
17
|
+
## Compare arguments from path and arguments from method
|
18
|
+
no_extra_path_arguments? && no_extra_action_arguments?
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
## Split path to args array
|
24
|
+
def path_arguments(path)
|
25
|
+
args = path.split('/').select { |part| part[0] == ':' }
|
26
|
+
args.map { |arg| arg[1..-1].to_sym }
|
27
|
+
end
|
28
|
+
|
29
|
+
## Take args from controller's action
|
30
|
+
def action_arguments(action)
|
31
|
+
parameters = @ctrl.instance_method(action).parameters
|
32
|
+
req_parameters = parameters.select { |par| par[0] == :req }
|
33
|
+
{
|
34
|
+
all: parameters.map { |par| par[1] },
|
35
|
+
req: req_parameters.map { |par| par[1] }
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def no_extra_path_arguments?
|
40
|
+
## Subtraction action args from path args
|
41
|
+
extra_path_args = @path_args - @action_args[:all]
|
42
|
+
return true if extra_path_args.empty?
|
43
|
+
fail RouterError::ExtraPathArgumentsError.new(
|
44
|
+
@ctrl, @action, @path, extra_path_args
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
def no_extra_action_arguments?
|
49
|
+
## Subtraction path args from action required args
|
50
|
+
extra_action_args = @action_args[:req] - @path_args
|
51
|
+
return true if extra_action_args.empty?
|
52
|
+
fail RouterError::ExtraActionArgumentsError.new(
|
53
|
+
@ctrl, @action, @path, extra_action_args
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
## Compare actions from routes and from controller
|
59
|
+
class ActionsValidator
|
60
|
+
def initialize(routes, ctrl)
|
61
|
+
@routes = routes
|
62
|
+
@ctrl = ctrl
|
63
|
+
end
|
64
|
+
|
65
|
+
def valid?
|
66
|
+
@routes_actions = @routes.map { |route| route[:action] }
|
67
|
+
@ctrl_actions = @ctrl.public_instance_methods(false)
|
68
|
+
no_extra_routes_actions? && no_extra_controller_actions?
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def no_extra_routes_actions?
|
74
|
+
extra_routes_actions = @routes_actions - @ctrl_actions
|
75
|
+
return true if extra_routes_actions.empty?
|
76
|
+
fail RouterError::ExtraRoutesActionsError.new(
|
77
|
+
@ctrl, extra_routes_actions
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def no_extra_controller_actions?
|
82
|
+
extra_ctrl_actions = @ctrl_actions - @routes_actions
|
83
|
+
return true if extra_ctrl_actions.empty?
|
84
|
+
fail RouterError::ExtraControllerActionsError.new(
|
85
|
+
@ctrl, extra_ctrl_actions
|
86
|
+
)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/public/favicon.ico
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flame
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexander Popov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-10-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rack
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.6'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.6.4
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.6'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.6.4
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: gorilla-patch
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.0'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.0.5.3
|
43
|
+
type: :runtime
|
44
|
+
prerelease: false
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '0.0'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 0.0.5.3
|
53
|
+
description: Use controller's classes with instance methods as routing actions, mounting
|
54
|
+
its in application class.
|
55
|
+
email:
|
56
|
+
- alex.wayfer@gmail.com
|
57
|
+
executables: []
|
58
|
+
extensions: []
|
59
|
+
extra_rdoc_files: []
|
60
|
+
files:
|
61
|
+
- lib/flame.rb
|
62
|
+
- lib/flame/application.rb
|
63
|
+
- lib/flame/controller.rb
|
64
|
+
- lib/flame/dispatcher.rb
|
65
|
+
- lib/flame/errors.rb
|
66
|
+
- lib/flame/render.rb
|
67
|
+
- lib/flame/route.rb
|
68
|
+
- lib/flame/router.rb
|
69
|
+
- lib/flame/validators.rb
|
70
|
+
- public/favicon.ico
|
71
|
+
homepage: https://gitlab.com/AlexWayfer/flame
|
72
|
+
licenses:
|
73
|
+
- MIT
|
74
|
+
metadata: {}
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
requirements: []
|
90
|
+
rubyforge_project:
|
91
|
+
rubygems_version: 2.4.5.1
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Web-framework, based on MVC-pattern
|
95
|
+
test_files: []
|