racket-mvc 0.4.0 → 0.5.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 +4 -4
- data/README.md +7 -5
- data/lib/racket.rb +25 -7
- data/lib/racket/application.rb +70 -136
- data/lib/racket/controller.rb +95 -38
- data/lib/racket/current.rb +1 -1
- data/lib/racket/helpers/file.rb +7 -5
- data/lib/racket/helpers/routing.rb +5 -5
- data/lib/racket/helpers/sass.rb +11 -11
- data/lib/racket/helpers/view.rb +8 -5
- data/lib/racket/plugins/base.rb +1 -1
- data/lib/racket/plugins/sass.rb +1 -1
- data/lib/racket/request.rb +3 -3
- data/lib/racket/response.rb +1 -1
- data/lib/racket/router.rb +31 -12
- data/lib/racket/session.rb +3 -3
- data/lib/racket/settings/application.rb +27 -33
- data/lib/racket/settings/base.rb +19 -8
- data/lib/racket/settings/controller.rb +9 -7
- data/lib/racket/settings/defaults.rb +81 -0
- data/lib/racket/utils.rb +19 -15
- data/lib/racket/utils/application.rb +4 -114
- data/lib/racket/utils/application/handler_stack.rb +163 -0
- data/lib/racket/utils/application/logger.rb +72 -0
- data/lib/racket/utils/application/registry_builder.rb +88 -0
- data/lib/racket/utils/application/stateless_services.rb +73 -0
- data/lib/racket/utils/exceptions.rb +3 -3
- data/lib/racket/utils/file_system.rb +75 -47
- data/lib/racket/utils/helpers.rb +35 -13
- data/lib/racket/utils/routing.rb +62 -46
- data/lib/racket/utils/views.rb +19 -187
- data/lib/racket/utils/views/renderer.rb +75 -0
- data/lib/racket/utils/views/template_cache.rb +126 -0
- data/lib/racket/utils/views/template_locator.rb +83 -0
- data/lib/racket/utils/views/template_resolver.rb +112 -0
- data/lib/racket/version.rb +2 -2
- data/lib/racket/view_manager.rb +12 -4
- data/rake/utils.rb +5 -5
- data/spec/_custom.rb +69 -19
- data/spec/_default.rb +60 -44
- data/spec/_plugin.rb +12 -14
- data/spec/_template_cache.rb +176 -0
- data/spec/racket.rb +10 -13
- data/spec/test_custom_app/controllers/sub1/custom_sub_controller_1.rb +1 -1
- data/spec/test_custom_app/controllers/sub3/custom_sub_controller_3.rb +19 -1
- data/spec/test_custom_app/controllers/sub5/custom_sub_controller_5.rb +8 -0
- data/spec/test_custom_app/files/stuff.rb +3 -0
- data/spec/test_custom_app/files/triplet.erb +5 -0
- data/spec/test_custom_app/templates/sub5/text.erb +3 -0
- data/spec/test_default_app/controllers/default_root_controller.rb +3 -0
- data/spec/test_default_app/controllers/sub1/default_sub_controller_1.rb +1 -1
- metadata +52 -11
@@ -0,0 +1,75 @@
|
|
1
|
+
# Racket - The noisy Rack MVC framework
|
2
|
+
# Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
|
3
|
+
#
|
4
|
+
# This file is part of Racket.
|
5
|
+
#
|
6
|
+
# Racket is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Racket is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
module Racket
|
20
|
+
module Utils
|
21
|
+
module Views
|
22
|
+
# Class responsible for rendering a controller/view/layout combination.
|
23
|
+
class Renderer
|
24
|
+
# Returns a service proc that can be used by the registry.
|
25
|
+
#
|
26
|
+
# @param [Hash] _options (unused)
|
27
|
+
# @return [Proc]
|
28
|
+
def self.service(_options = {})
|
29
|
+
-> { self }
|
30
|
+
end
|
31
|
+
|
32
|
+
# Renders a page using the provided controller/view and layout combination and returns an
|
33
|
+
# response array that can be sent to the client.
|
34
|
+
#
|
35
|
+
# @param [Racket::Controller] controller
|
36
|
+
# @param [String] view
|
37
|
+
# @param [String] layout
|
38
|
+
# @return [Array]
|
39
|
+
def self.render(controller, view, layout)
|
40
|
+
send_response(
|
41
|
+
controller.response,
|
42
|
+
view ? render_template(controller, view, layout) : controller.racket.action_result
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Renders a template/layout combo using Tilt and returns it as a string.
|
47
|
+
#
|
48
|
+
# @param [Racket::Controller] controller
|
49
|
+
# @param [String] view
|
50
|
+
# @param [String|nil] layout
|
51
|
+
# @return [String]
|
52
|
+
def self.render_template(controller, view, layout)
|
53
|
+
output = Tilt.new(view, nil, controller.view_settings).render(controller)
|
54
|
+
output =
|
55
|
+
Tilt.new(
|
56
|
+
layout, nil, controller.layout_settings
|
57
|
+
).render(controller) { output } if layout
|
58
|
+
output
|
59
|
+
end
|
60
|
+
|
61
|
+
# Sends response to client.
|
62
|
+
#
|
63
|
+
# @param [Racket::Response] response
|
64
|
+
# @param [String] output
|
65
|
+
# @return nil
|
66
|
+
def self.send_response(response, output)
|
67
|
+
response.write(output)
|
68
|
+
response.finish
|
69
|
+
end
|
70
|
+
|
71
|
+
private_class_method :render_template, :send_response
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# Racket - The noisy Rack MVC framework
|
2
|
+
# Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
|
3
|
+
#
|
4
|
+
# This file is part of Racket.
|
5
|
+
#
|
6
|
+
# Racket is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Racket is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
module Racket
|
20
|
+
module Utils
|
21
|
+
module Views
|
22
|
+
# Class for caching templates.
|
23
|
+
# This class adheres to the Moneta API
|
24
|
+
# (https://github.com/minad/moneta#user-content-moneta-api), even though it is not using the
|
25
|
+
# Moneta framework.
|
26
|
+
class TemplateCache
|
27
|
+
# Default options for template cache
|
28
|
+
DEFAULT_OPTIONS = { expires: 0 }.freeze
|
29
|
+
|
30
|
+
# Returns a service proc that can be used by the registry.
|
31
|
+
#
|
32
|
+
# @param [Hash] _options (unused)
|
33
|
+
# @return [Proc]
|
34
|
+
def self.service(_options = {})
|
35
|
+
-> { new({}) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(options)
|
39
|
+
@expirations = {}
|
40
|
+
@items = {}
|
41
|
+
@options = DEFAULT_OPTIONS.merge(options)
|
42
|
+
end
|
43
|
+
|
44
|
+
def [](key)
|
45
|
+
load(key)
|
46
|
+
end
|
47
|
+
|
48
|
+
def []=(key, value)
|
49
|
+
store(key, value)
|
50
|
+
end
|
51
|
+
|
52
|
+
def clear(_options = {})
|
53
|
+
@expirations.clear
|
54
|
+
@items.clear
|
55
|
+
end
|
56
|
+
|
57
|
+
def close
|
58
|
+
clear
|
59
|
+
end
|
60
|
+
|
61
|
+
def create(_key, _value, _options = {})
|
62
|
+
raise NotImplementedError
|
63
|
+
end
|
64
|
+
|
65
|
+
def decrement(_key, _amount = 1, _options = {})
|
66
|
+
raise NotImplementedError
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete(key, _options = {})
|
70
|
+
@expirations.delete(key)
|
71
|
+
@items.delete(key)
|
72
|
+
end
|
73
|
+
|
74
|
+
def features
|
75
|
+
[]
|
76
|
+
end
|
77
|
+
|
78
|
+
# This method handles both forms of fetch.
|
79
|
+
# With a default block - fetch(key, options = {}, &block)
|
80
|
+
# With a default value - fetch(key, value, options = {})
|
81
|
+
def fetch(*args)
|
82
|
+
key = args.shift
|
83
|
+
return load(key) if key?(key)
|
84
|
+
block_given? ? yield : args.first
|
85
|
+
end
|
86
|
+
|
87
|
+
def increment(_key, _amount = 1, _options = {})
|
88
|
+
raise NotImplementedError
|
89
|
+
end
|
90
|
+
|
91
|
+
def key?(key)
|
92
|
+
@items.key?(key)
|
93
|
+
end
|
94
|
+
|
95
|
+
def load(key, _options = {})
|
96
|
+
return @items[key] unless @expirations.key?(key)
|
97
|
+
if Time.now > @expirations[key]
|
98
|
+
@expirations.delete(key)
|
99
|
+
@items.delete(key)
|
100
|
+
end
|
101
|
+
@items[key]
|
102
|
+
end
|
103
|
+
|
104
|
+
def store(key, value, options = {})
|
105
|
+
set_expiration(key, options.fetch(:expires, @options[:expires]))
|
106
|
+
@items[key] = value
|
107
|
+
end
|
108
|
+
|
109
|
+
def supports?(feature)
|
110
|
+
features.include?(feature)
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def set_expiration(key, expires)
|
116
|
+
expire_at = expires > 0 ? Time.now + expires : nil
|
117
|
+
if expire_at
|
118
|
+
@expirations[key] = expire_at
|
119
|
+
else
|
120
|
+
@expirations.delete(key)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# Racket - The noisy Rack MVC framework
|
2
|
+
# Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
|
3
|
+
#
|
4
|
+
# This file is part of Racket.
|
5
|
+
#
|
6
|
+
# Racket is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Racket is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
module Racket
|
20
|
+
module Utils
|
21
|
+
module Views
|
22
|
+
# Class used for locating templates. This class uses the TemplateResolver class internally
|
23
|
+
# for getting the template for the first time and caches the result so that subsequent Calls
|
24
|
+
# will not need to resolve the template again.
|
25
|
+
class TemplateLocator
|
26
|
+
# Returns a service proc that can be used by the registry.
|
27
|
+
#
|
28
|
+
# @param [Hash] _options (unused)
|
29
|
+
# @return [Proc]
|
30
|
+
def self.service(_options = {})
|
31
|
+
lambda do |reg|
|
32
|
+
new(
|
33
|
+
layout_cache: reg.layout_cache,
|
34
|
+
layout_resolver: reg.layout_resolver,
|
35
|
+
view_cache: reg.view_cache,
|
36
|
+
view_resolver: reg.view_resolver
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def initialize(options)
|
42
|
+
options.each_pair { |key, value| instance_variable_set("@#{key}".to_sym, value) }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the layout associated with the current request. On the first request to any action
|
46
|
+
# the result is cached, meaning that the layout only needs to be looked up once.
|
47
|
+
#
|
48
|
+
# @param [Racket::Controller] controller
|
49
|
+
# @return [String|nil]
|
50
|
+
def get_layout(controller)
|
51
|
+
self.class.get_template(@layout_cache, @layout_resolver, controller)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the view associated with the current request. On the first request to any action
|
55
|
+
# the result is cached, meaning that the view only needs to be looked up once.
|
56
|
+
#
|
57
|
+
# @param [Racket::Controller] controller
|
58
|
+
# @return [String|nil]
|
59
|
+
def get_view(controller)
|
60
|
+
self.class.get_template(@view_cache, @view_resolver, controller)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Tries to locate a template matching the controllers action in the file system and returns
|
64
|
+
# the path if a matching file is found. If no matching file is found, +nil+ is returned.
|
65
|
+
# The result is cached, meaning that the filesystem lookup for a specific path will only
|
66
|
+
# happen once.
|
67
|
+
#
|
68
|
+
# @param [TemplateCache] cache
|
69
|
+
# @param [TemplateResolver] resolver
|
70
|
+
# @param [Racket::Controller] controller
|
71
|
+
# @return [String|nil]
|
72
|
+
def self.get_template(cache, resolver, controller)
|
73
|
+
path = TemplateResolver.get_template_path(controller)
|
74
|
+
unless cache.key?(path)
|
75
|
+
template = resolver.get_template_object(path, controller)
|
76
|
+
cache.store(path, template)
|
77
|
+
end
|
78
|
+
resolver.resolve_template(path, cache.load(path), controller)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# Racket - The noisy Rack MVC framework
|
2
|
+
# Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
|
3
|
+
#
|
4
|
+
# This file is part of Racket.
|
5
|
+
#
|
6
|
+
# Racket is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Racket is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
module Racket
|
20
|
+
module Utils
|
21
|
+
module Views
|
22
|
+
# Class used for resolving template paths.
|
23
|
+
class TemplateResolver
|
24
|
+
# Alias for Racket::Utils::FileSystem module.
|
25
|
+
FSM = Racket::Utils::FileSystem
|
26
|
+
|
27
|
+
# Returns a service proc that can be used by the registry.
|
28
|
+
#
|
29
|
+
# @param [Hash] options
|
30
|
+
# @return [Proc]
|
31
|
+
def self.service(options = {})
|
32
|
+
type = options[:type]
|
33
|
+
lambda do |reg|
|
34
|
+
new(
|
35
|
+
base_dir: reg.application_settings.send("#{type}_dir"),
|
36
|
+
logger: reg.application_logger,
|
37
|
+
type: type,
|
38
|
+
utils: reg.utils
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(options)
|
44
|
+
options.each_pair { |key, value| instance_variable_set("@#{key}".to_sym, value) }
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the template object representing the specified path/controller combination.
|
48
|
+
#
|
49
|
+
# @param [String] path
|
50
|
+
# @param [Racket::Controller] controller
|
51
|
+
# @return [Pathname|Proc|nil]
|
52
|
+
def get_template_object(path, controller)
|
53
|
+
default_template = controller.settings.fetch("default_#{@type}".to_sym)
|
54
|
+
template =
|
55
|
+
FSM.resolve_path_with_default(
|
56
|
+
FSM.fs_path(@base_dir, path), default_template
|
57
|
+
)
|
58
|
+
@logger.inform_dev(
|
59
|
+
"Using #{@type} #{template.inspect} for #{controller.class}." \
|
60
|
+
"#{controller.racket.action}."
|
61
|
+
)
|
62
|
+
template
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the "resolved" path for the given parameters. This is either a pathname or nil.
|
66
|
+
#
|
67
|
+
# @param [String] path
|
68
|
+
# @param [Proc|String|nil] template
|
69
|
+
# @param [Racket::Controller] controller
|
70
|
+
# @return [pathname|nil]
|
71
|
+
def resolve_template(path, template, controller)
|
72
|
+
return template unless template.is_a?(Proc)
|
73
|
+
FSM.resolve_path(
|
74
|
+
FSM.fs_path(
|
75
|
+
FSM.fs_path(@base_dir, path).dirname,
|
76
|
+
self.class.call_template_proc(template, controller)
|
77
|
+
)
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Calls a template proc. Depending on how many parameters the template proc takes, different
|
82
|
+
# types of information will be passed to the proc.
|
83
|
+
# If the proc takes zero parameters, no information will be passed.
|
84
|
+
# If the proc takes one parameter, it will contain the current action.
|
85
|
+
# If the proc takes two parameters, they will contain the current action and the current
|
86
|
+
# params.
|
87
|
+
# If the proc takes three parameters, they will contain the current action, the current
|
88
|
+
# params and the current request.
|
89
|
+
#
|
90
|
+
# @param [Proc] proc
|
91
|
+
# @param [Racket::Controller] controller
|
92
|
+
# @return [String]
|
93
|
+
def self.call_template_proc(proc, controller)
|
94
|
+
racket = controller.racket
|
95
|
+
proc_args = [racket.action, racket.params, controller.request].slice(0...proc.arity)
|
96
|
+
proc.call(*proc_args).to_s
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns the "url path" that should be used when searching for templates.
|
100
|
+
#
|
101
|
+
# @param [Racket::Controller] controller
|
102
|
+
# @return [String]
|
103
|
+
def self.get_template_path(controller)
|
104
|
+
template_path =
|
105
|
+
[controller.class.get_route, controller.racket.action].join('/')
|
106
|
+
template_path = template_path[1..-1] if template_path.start_with?('//')
|
107
|
+
template_path
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
data/lib/racket/version.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Racket - The noisy Rack MVC framework
|
2
|
-
# Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
|
2
|
+
# Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
|
3
3
|
#
|
4
4
|
# This file is part of Racket.
|
5
5
|
#
|
@@ -23,7 +23,7 @@ module Racket
|
|
23
23
|
# Major version
|
24
24
|
MAJOR = 0
|
25
25
|
# Minor version
|
26
|
-
MINOR =
|
26
|
+
MINOR = 5
|
27
27
|
# Teeny version
|
28
28
|
TEENY = 0
|
29
29
|
# Is it a prerelease?
|
data/lib/racket/view_manager.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Racket - The noisy Rack MVC framework
|
2
|
-
# Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
|
2
|
+
# Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
|
3
3
|
#
|
4
4
|
# This file is part of Racket.
|
5
5
|
#
|
@@ -19,9 +19,17 @@
|
|
19
19
|
module Racket
|
20
20
|
# Handles rendering in Racket applications.
|
21
21
|
class ViewManager
|
22
|
-
|
23
|
-
|
24
|
-
|
22
|
+
# Returns a service proc that can be used by the registry.
|
23
|
+
#
|
24
|
+
# @param [Hash] _options (unused)
|
25
|
+
# @return [Proc]
|
26
|
+
def self.service(_options = {})
|
27
|
+
->(reg) { new(reg.template_locator, reg.template_renderer) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(locator, renderer)
|
31
|
+
@locator = locator
|
32
|
+
@renderer = renderer
|
25
33
|
end
|
26
34
|
|
27
35
|
# Renders a controller based on the request path and the variables set in the
|
data/rake/utils.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Racket - The noisy Rack MVC framework
|
2
|
-
# Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
|
2
|
+
# Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
|
3
3
|
#
|
4
4
|
# This file is part of Racket.
|
5
5
|
#
|
@@ -28,9 +28,9 @@ end
|
|
28
28
|
|
29
29
|
def racket_files
|
30
30
|
Dir.chdir(File.dirname(File.dirname(__FILE__))) do
|
31
|
-
files =
|
32
|
-
files.concat(
|
33
|
-
files.concat(
|
34
|
-
files.concat(
|
31
|
+
files = Dir.glob('lib/**/*.rb')
|
32
|
+
files.concat(Dir.glob('rake/**/*'))
|
33
|
+
files.concat(Dir.glob('spec/**/*'))
|
34
|
+
files.concat(['COPYING.AGPL', 'Rakefile', 'README.md'])
|
35
35
|
end
|
36
36
|
end
|