flame 1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|