racket-mvc 0.3.1 → 0.3.2
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 +1 -1
- data/lib/racket/application.rb +25 -53
- data/lib/racket/controller.rb +15 -24
- data/lib/racket/current.rb +9 -3
- data/lib/racket/helpers/view.rb +1 -0
- data/lib/racket/router.rb +22 -36
- data/lib/racket/settings/base.rb +9 -0
- data/lib/racket/settings/controller.rb +2 -1
- data/lib/racket/utils.rb +4 -0
- data/lib/racket/utils/application.rb +83 -0
- data/lib/racket/utils/exceptions.rb +1 -1
- data/lib/racket/utils/file_system.rb +86 -25
- data/lib/racket/utils/helpers.rb +74 -0
- data/lib/racket/utils/routing.rb +70 -0
- data/lib/racket/utils/views.rb +164 -81
- data/lib/racket/version.rb +1 -1
- data/lib/racket/view_manager.rb +10 -62
- data/spec/_custom.rb +5 -8
- data/spec/_default.rb +19 -15
- data/spec/racket.rb +16 -2
- metadata +4 -2
@@ -42,7 +42,7 @@ module Racket
|
|
42
42
|
# Returns an anonymous module that can be used to rescue exceptions dynamically.
|
43
43
|
def self.boolean_module(errors)
|
44
44
|
Module.new do
|
45
|
-
|
45
|
+
singleton_class.instance_eval do
|
46
46
|
define_method(:===) do |error|
|
47
47
|
errors.any? { |err| error.class <= err }
|
48
48
|
end
|
@@ -21,6 +21,25 @@ module Racket
|
|
21
21
|
module Utils
|
22
22
|
# Utility functions for filesystem.
|
23
23
|
module FileSystem
|
24
|
+
# Class used for comparing length of paths.
|
25
|
+
class SizedPath
|
26
|
+
attr_reader :path, :size
|
27
|
+
|
28
|
+
def initialize(path)
|
29
|
+
@path = path
|
30
|
+
@size = 0
|
31
|
+
@path.ascend { @size += 1 }
|
32
|
+
end
|
33
|
+
|
34
|
+
# Allow us to compare the current object against other objects of the same type.
|
35
|
+
#
|
36
|
+
# @param [SizedPath] other
|
37
|
+
# @return [Fixnum]
|
38
|
+
def <=>(other)
|
39
|
+
other.size <=> @size
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
24
43
|
# Build path in the filesystem.
|
25
44
|
class PathBuilder
|
26
45
|
# Creates a new instance of PathBuilder using +args+ and then returning the final path as
|
@@ -32,15 +51,6 @@ module Racket
|
|
32
51
|
new(args).path
|
33
52
|
end
|
34
53
|
|
35
|
-
# Creates a new instance of PathBuilder using +args+ and then returning the final path as
|
36
|
-
# a string.
|
37
|
-
#
|
38
|
-
# @param [Array] args
|
39
|
-
# @return [String]
|
40
|
-
def self.to_s(*args)
|
41
|
-
new(args).path.to_s
|
42
|
-
end
|
43
|
-
|
44
54
|
attr_reader :path
|
45
55
|
|
46
56
|
private
|
@@ -69,7 +79,7 @@ module Racket
|
|
69
79
|
def build_path
|
70
80
|
@args.each do |arg|
|
71
81
|
path_part = Pathname.new(arg)
|
72
|
-
|
82
|
+
fail ArgumentError, arg unless path_part.relative?
|
73
83
|
@path = @path.join(path_part)
|
74
84
|
end
|
75
85
|
remove_instance_variable :@args
|
@@ -81,9 +91,9 @@ module Racket
|
|
81
91
|
# relative, otherwise they will be removed from the final path.
|
82
92
|
#
|
83
93
|
# @param [Array] args
|
84
|
-
# @return [
|
94
|
+
# @return [Pathname]
|
85
95
|
def self.build_path(*args)
|
86
|
-
PathBuilder.
|
96
|
+
PathBuilder.to_pathname(*args)
|
87
97
|
end
|
88
98
|
|
89
99
|
# Returns whether a directory is readable or not. In order to be readable, the directory must
|
@@ -91,11 +101,33 @@ module Racket
|
|
91
101
|
# b) be a directory
|
92
102
|
# c) be readable by the current user
|
93
103
|
#
|
94
|
-
# @param [
|
104
|
+
# @param [Pathname] path
|
95
105
|
# @return [true|false]
|
96
106
|
def self.dir_readable?(path)
|
97
|
-
|
98
|
-
|
107
|
+
path.exist? && path.directory? && path.readable?
|
108
|
+
end
|
109
|
+
|
110
|
+
# Extracts the correct directory and glob for a given base path/path combination.
|
111
|
+
#
|
112
|
+
# @param [Pathname] path
|
113
|
+
# @return [Array]
|
114
|
+
def self.extract_dir_and_glob(path)
|
115
|
+
basename = path.basename
|
116
|
+
[
|
117
|
+
path.dirname,
|
118
|
+
path.extname.empty? ? Pathname.new("#{basename}.*") : basename
|
119
|
+
]
|
120
|
+
end
|
121
|
+
|
122
|
+
# Given a base pathname and a url path string, returns a pathname.
|
123
|
+
#
|
124
|
+
# @param [Pathname] base_pathname
|
125
|
+
# @param [String] url_path
|
126
|
+
# @return [Pathname]
|
127
|
+
def self.fs_path(base_pathname, url_path)
|
128
|
+
parts = url_path.split('/').reject(&:empty?)
|
129
|
+
parts.each { |part| base_pathname = base_pathname.join(part) }
|
130
|
+
base_pathname
|
99
131
|
end
|
100
132
|
|
101
133
|
# Returns whether a file is readable or not. In order to be readable, the file must
|
@@ -103,9 +135,34 @@ module Racket
|
|
103
135
|
# b) be a file
|
104
136
|
# c) be readable by the current user
|
105
137
|
#
|
138
|
+
# @param [Pathname|String] path
|
139
|
+
# @return [true|false]
|
140
|
+
# @todo Remove temporary workaround for handling string, we want to use Pathname everywhere
|
141
|
+
# possible.
|
106
142
|
def self.file_readable?(path)
|
107
|
-
|
108
|
-
|
143
|
+
# path = Pathname.new(path) unless path.is_a?(Pathname)
|
144
|
+
path.exist? && path.file? && path.readable?
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns all paths under +base_path+ that matches +glob+.
|
148
|
+
#
|
149
|
+
# @param [Pathname] base_path
|
150
|
+
# @param [Pathname] glob
|
151
|
+
# @return [Array]
|
152
|
+
def self.matching_paths(base_path, glob)
|
153
|
+
return [] unless Utils.dir_readable?(base_path)
|
154
|
+
Dir.chdir(base_path) { Pathname.glob(glob) }.map { |path| base_path.join(path) }
|
155
|
+
end
|
156
|
+
|
157
|
+
# Returns the first matching path under +base_path+ matching +glob+. If no matching path can
|
158
|
+
# be found, +nil+ is returned.
|
159
|
+
#
|
160
|
+
# @param [Pathname] base_path
|
161
|
+
# @param [Pathname] glob
|
162
|
+
# @return [Pathname|nil]
|
163
|
+
def self.first_matching_path(base_path, glob)
|
164
|
+
paths = matching_paths(base_path, glob)
|
165
|
+
paths.empty? ? nil : paths.first
|
109
166
|
end
|
110
167
|
|
111
168
|
# Returns a list of relative file paths, sorted by path (longest first).
|
@@ -113,14 +170,18 @@ module Racket
|
|
113
170
|
# @param [String] base_dir
|
114
171
|
# @param [String] glob
|
115
172
|
# return [Array]
|
116
|
-
def self.
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
173
|
+
def self.paths_by_longest_path(base_dir, glob)
|
174
|
+
paths = matching_paths(base_dir, glob).map { |path| SizedPath.new(path) }.sort
|
175
|
+
paths.map(&:path)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Safely requires a file. This method will catch load errors and return true (if the file
|
179
|
+
# was loaded) or false (if the file was not loaded).
|
180
|
+
#
|
181
|
+
# @param [String] resource
|
182
|
+
# @return [true|false]
|
183
|
+
def self.safe_require(resource)
|
184
|
+
Utils.run_block(LoadError) { require resource }
|
124
185
|
end
|
125
186
|
end
|
126
187
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Racket - The noisy Rack MVC framework
|
2
|
+
# Copyright (C) 2015 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
|
+
# Utility functions for routing.
|
22
|
+
module Helpers
|
23
|
+
# Cache for helpers, ensuring that helpers get loaded exactly once.
|
24
|
+
class HelperCache
|
25
|
+
def initialize(helper_dir)
|
26
|
+
@helper_dir = helper_dir
|
27
|
+
@helpers = {}
|
28
|
+
end
|
29
|
+
|
30
|
+
# Loads helper files and return the loadad modules as a hash. Any helper files that
|
31
|
+
# cannot be loaded are excluded from the result.
|
32
|
+
#
|
33
|
+
# @param [Array] helpers An array of symbols
|
34
|
+
# @return [Hash]
|
35
|
+
def load_helpers(helpers)
|
36
|
+
helper_modules = {}
|
37
|
+
helpers.each do |helper|
|
38
|
+
helper_module = load_helper(helper)
|
39
|
+
helper_modules[helper] = helper_module if helper_module
|
40
|
+
end
|
41
|
+
helper_modules
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def load_helper(helper)
|
47
|
+
return @helpers[helper] if @helpers.key?(helper)
|
48
|
+
helper_module = load_helper_file(helper)
|
49
|
+
@helpers[helper] = helper_module if helper_module
|
50
|
+
end
|
51
|
+
|
52
|
+
def load_helper_file(helper)
|
53
|
+
require_helper_file(helper)
|
54
|
+
self.class.load_helper_module(helper)
|
55
|
+
end
|
56
|
+
|
57
|
+
def require_helper_file(helper)
|
58
|
+
loaded = Utils.safe_require("racket/helpers/#{helper}")
|
59
|
+
Utils.safe_require(Utils.build_path(@helper_dir, helper).to_s) if !loaded && @helper_dir
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.load_helper_module(helper)
|
63
|
+
helper_module = nil
|
64
|
+
Utils.run_block(NameError) do
|
65
|
+
helper_module =
|
66
|
+
Racket::Helpers.const_get(helper.to_s.split('_').collect(&:capitalize).join.to_sym)
|
67
|
+
::Racket::Application.inform_dev("Loaded helper module #{helper.inspect}.")
|
68
|
+
end
|
69
|
+
helper_module
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/racket/utils/routing.rb
CHANGED
@@ -20,6 +20,52 @@ module Racket
|
|
20
20
|
module Utils
|
21
21
|
# Utility functions for routing.
|
22
22
|
module Routing
|
23
|
+
# Class for caching actions
|
24
|
+
class ActionCache
|
25
|
+
attr_reader :items
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@items = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns whether +controller_class+ is in the cache and that it contains the action
|
32
|
+
# +action+.
|
33
|
+
#
|
34
|
+
# @param [Class] controller_class
|
35
|
+
# @param [Symbol] action
|
36
|
+
# @return [true|false]
|
37
|
+
def present?(controller_class, action)
|
38
|
+
@items.fetch(controller_class, []).include?(action)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Caches all actions for a controller class. This is used on every request to quickly decide
|
42
|
+
# whether an action is valid or not.
|
43
|
+
#
|
44
|
+
# @param [Class] controller_class
|
45
|
+
# @return [nil]
|
46
|
+
def add(controller_class)
|
47
|
+
__add(controller_class)
|
48
|
+
actions = @items[controller_class].to_a
|
49
|
+
@items[controller_class] = actions
|
50
|
+
::Racket::Application.inform_dev(
|
51
|
+
"Registering actions #{actions} for #{controller_class}."
|
52
|
+
) && nil
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Internal handler for adding actions to the cache.
|
58
|
+
#
|
59
|
+
# @param [Class] controller_class
|
60
|
+
# @return [nil]
|
61
|
+
def __add(controller_class)
|
62
|
+
return if controller_class == Controller
|
63
|
+
actions = @items.fetch(controller_class, SortedSet.new)
|
64
|
+
@items[controller_class] = actions.merge(controller_class.public_instance_methods(false))
|
65
|
+
__add(controller_class.superclass) && nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
23
69
|
# Extracts the target class, target params and target action from a list of valid routes.
|
24
70
|
#
|
25
71
|
# @param [HttpRouter::Response] response
|
@@ -31,6 +77,28 @@ module Racket
|
|
31
77
|
[target_klass, params, action]
|
32
78
|
end
|
33
79
|
|
80
|
+
def self.call_controller(target_klass, mod)
|
81
|
+
target = target_klass.new
|
82
|
+
target.extend(mod)
|
83
|
+
target.__run
|
84
|
+
end
|
85
|
+
|
86
|
+
# Renders a controller. This is the default action whenever a matching route for a request
|
87
|
+
# is found.
|
88
|
+
#
|
89
|
+
# @param [Hash] env
|
90
|
+
# @param [Array] target_info
|
91
|
+
# @return [Array] A racket response triplet
|
92
|
+
def self.render_controller(env, target_info)
|
93
|
+
controller_class, params, action = target_info
|
94
|
+
|
95
|
+
# Rewrite PATH_INFO to reflect that we split out the parameters
|
96
|
+
update_path_info(env, params.length)
|
97
|
+
|
98
|
+
# Initialize and render target
|
99
|
+
call_controller(controller_class, Current.init(env, controller_class, action, params))
|
100
|
+
end
|
101
|
+
|
34
102
|
# Updates the PATH_INFO environment variable.
|
35
103
|
#
|
36
104
|
# @param [Hash] env
|
@@ -42,6 +110,8 @@ module Racket
|
|
42
110
|
.join('/') unless num_params.zero?
|
43
111
|
nil
|
44
112
|
end
|
113
|
+
|
114
|
+
private_class_method :call_controller, :update_path_info
|
45
115
|
end
|
46
116
|
end
|
47
117
|
end
|
data/lib/racket/utils/views.rb
CHANGED
@@ -22,95 +22,178 @@ module Racket
|
|
22
22
|
module Utils
|
23
23
|
# Utility functions for views.
|
24
24
|
module Views
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
# @param [Racket::Controller] controller
|
36
|
-
# @return [String]
|
37
|
-
def self.call_template_proc(proc, controller)
|
38
|
-
possible_proc_args =
|
39
|
-
[controller.racket.action, controller.racket.params, controller.request]
|
40
|
-
proc_args = []
|
41
|
-
1.upto(proc.arity) { proc_args.push(possible_proc_args.shift) }
|
42
|
-
proc.call(*proc_args).to_s
|
43
|
-
end
|
25
|
+
# Class used for locating templates.
|
26
|
+
class TemplateLocator
|
27
|
+
# Struct for holding template data.
|
28
|
+
TemplateParams = Struct.new(:type, :controller, :base_dir, :cache)
|
29
|
+
def initialize(layout_base_dir, view_base_dir)
|
30
|
+
@layout_base_dir = layout_base_dir
|
31
|
+
@view_base_dir = view_base_dir
|
32
|
+
@layout_cache = {}
|
33
|
+
@view_cache = {}
|
34
|
+
end
|
44
35
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
template_path
|
54
|
-
end
|
36
|
+
# Returns the layout associated with the current request. On the first request to any action
|
37
|
+
# the result is cached, meaning that the layout only needs to be looked up once.
|
38
|
+
#
|
39
|
+
# @param [Racket::Controller] controller
|
40
|
+
# @return [String|nil]
|
41
|
+
def get_layout(controller)
|
42
|
+
get_template(TemplateParams.new(:layout, controller, @layout_base_dir, @layout_cache))
|
43
|
+
end
|
55
44
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
file_path = File.join(base_path, path)
|
64
|
-
action = File.basename(file_path)
|
65
|
-
file_path = File.dirname(file_path)
|
66
|
-
return nil unless Utils.dir_readable?(file_path)
|
67
|
-
matcher = File.extname(action).empty? ? "#{action}.*" : action
|
68
|
-
Dir.chdir(file_path) do
|
69
|
-
files = Pathname.glob(matcher)
|
70
|
-
return nil if files.empty?
|
71
|
-
final_path = File.join(file_path, files.first.to_s)
|
72
|
-
Utils.file_readable?(final_path) ? final_path : nil
|
45
|
+
# Returns the view associated with the current request. On the first request to any action
|
46
|
+
# the result is cached, meaning that the view only needs to be looked up once.
|
47
|
+
#
|
48
|
+
# @param [Racket::Controller] controller
|
49
|
+
# @return [String|nil]
|
50
|
+
def get_view(controller)
|
51
|
+
get_template(TemplateParams.new(:view, controller, @view_base_dir, @view_cache))
|
73
52
|
end
|
74
|
-
end
|
75
53
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
54
|
+
private
|
55
|
+
|
56
|
+
def calculate_path(template_params, path)
|
57
|
+
type, controller, base_dir = template_params.to_a
|
58
|
+
default_template = controller.settings.fetch("default_#{type}".to_sym)
|
59
|
+
template =
|
60
|
+
self.class.lookup_template_with_default(Utils.fs_path(base_dir, path), default_template)
|
61
|
+
::Racket::Application.inform_dev(
|
62
|
+
"Using #{type} #{template.inspect} for #{controller.class}.#{controller.racket.action}."
|
63
|
+
)
|
64
|
+
template
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a cached template. If the template has not been cached yet, this method will run a
|
68
|
+
# lookup against the provided parameters.
|
69
|
+
#
|
70
|
+
# @param [TemplateParams] template_params
|
71
|
+
# @return [String|Proc|nil]
|
72
|
+
def ensure_in_cache(template_params, path)
|
73
|
+
cache = template_params.cache
|
74
|
+
return cache[path] if cache.key?(path)
|
75
|
+
cache[path] = calculate_path(template_params, path)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Tries to locate a template matching +path+ in the file system and returns the path if a
|
79
|
+
# matching file is found. If no matching file is found, +nil+ is returned. The result is
|
80
|
+
# cached, meaning that the filesystem lookup for a specific path will only happen once.
|
81
|
+
#
|
82
|
+
# @param [TemplateParams] template_params
|
83
|
+
# @return [String|nil]
|
84
|
+
def get_template(template_params)
|
85
|
+
klass = self.class
|
86
|
+
path = klass.get_template_path(template_params.controller)
|
87
|
+
template = ensure_in_cache(template_params, path)
|
88
|
+
klass.resolve_template(template_params, path, template)
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.resolve_template(template_params, path, template)
|
92
|
+
return template unless template.is_a?(Proc)
|
93
|
+
_, controller, base_dir = template_params.to_a
|
94
|
+
lookup_template(
|
95
|
+
Utils.fs_path(
|
96
|
+
Utils.fs_path(base_dir, path).dirname,
|
97
|
+
call_template_proc(template, controller)
|
98
|
+
)
|
99
|
+
)
|
90
100
|
end
|
91
|
-
template || default_template
|
92
|
-
end
|
93
101
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
102
|
+
# Calls a template proc. Depending on how many parameters the template proc takes, different
|
103
|
+
# types of information will be passed to the proc.
|
104
|
+
# If the proc takes zero parameters, no information will be passed.
|
105
|
+
# If the proc takes one parameter, it will contain the current action.
|
106
|
+
# If the proc takes two parameters, they will contain the current action and the current
|
107
|
+
# params.
|
108
|
+
# If the proc takes three parameters, they will contain the current action, the current
|
109
|
+
# params and the current request.
|
110
|
+
#
|
111
|
+
# @param [Proc] proc
|
112
|
+
# @param [Racket::Controller] controller
|
113
|
+
# @return [String]
|
114
|
+
def self.call_template_proc(proc, controller)
|
115
|
+
possible_proc_args =
|
116
|
+
[controller.racket.action, controller.racket.params, controller.request]
|
117
|
+
proc_args = []
|
118
|
+
1.upto(proc.arity) { proc_args.push(possible_proc_args.shift) }
|
119
|
+
proc.call(*proc_args).to_s
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns the "url path" that should be used when searching for templates.
|
123
|
+
#
|
124
|
+
# @param [Racket::Controller] controller
|
125
|
+
# @return [String]
|
126
|
+
def self.get_template_path(controller)
|
127
|
+
template_path =
|
128
|
+
[::Racket::Application.get_route(controller.class), controller.racket.action].join('/')
|
129
|
+
template_path = template_path[1..-1] if template_path.start_with?('//')
|
130
|
+
template_path
|
131
|
+
end
|
132
|
+
|
133
|
+
# Locates a file in the filesystem matching an URL path. If there exists a matching file,
|
134
|
+
# the path to it is returned. If there is no matching file, +nil+ is returned.
|
135
|
+
# @param [Pathname] path
|
136
|
+
# @return [Pathname|nil]
|
137
|
+
def self.lookup_template(path)
|
138
|
+
Utils.first_matching_path(*Utils.extract_dir_and_glob(path))
|
139
|
+
end
|
140
|
+
|
141
|
+
# Locates a file in the filesystem matching an URL path. If there exists a matching file,
|
142
|
+
# the path to it is returned. If there is no matching file and +default_template+ is a
|
143
|
+
# String or a Symbol, another lookup will be performed using +default_template+. If
|
144
|
+
# +default_template+ is a Proc or nil, +default_template+ will be used as is instead.
|
145
|
+
#
|
146
|
+
# @param [Pathname] path
|
147
|
+
# @param [String|Symbol|Proc|nil] default_template
|
148
|
+
# @return [String|Proc|nil]
|
149
|
+
def self.lookup_template_with_default(path, default_template)
|
150
|
+
template = lookup_template(path)
|
151
|
+
if !template && (default_template.is_a?(String) || default_template.is_a?(Symbol))
|
152
|
+
template = lookup_template(Utils.fs_path(path.dirname, default_template))
|
153
|
+
end
|
154
|
+
template || default_template
|
155
|
+
end
|
104
156
|
end
|
105
157
|
|
106
|
-
#
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
158
|
+
# Class responsible for rendering a controller/view/layout combination.
|
159
|
+
class ViewRenderer
|
160
|
+
# Renders a page using the provided controller/view and layout combination and returns an
|
161
|
+
# response array that can be sent to the client.
|
162
|
+
#
|
163
|
+
# @param [Racket::Controller] controller
|
164
|
+
# @param [String] view
|
165
|
+
# @param [String] layout
|
166
|
+
# @return [Array]
|
167
|
+
def self.render(controller, view, layout)
|
168
|
+
send_response(
|
169
|
+
controller.response,
|
170
|
+
view ? render_template(controller, view, layout) : controller.racket.action_result
|
171
|
+
)
|
172
|
+
end
|
173
|
+
|
174
|
+
# Renders a template/layout combo using Tilt and returns it as a string.
|
175
|
+
#
|
176
|
+
# @param [Racket::Controller] controller
|
177
|
+
# @param [String] view
|
178
|
+
# @param [String|nil] layout
|
179
|
+
# @return [String]
|
180
|
+
def self.render_template(controller, view, layout)
|
181
|
+
output = Tilt.new(view).render(controller)
|
182
|
+
output = Tilt.new(layout).render(controller) { output } if layout
|
183
|
+
output
|
184
|
+
end
|
185
|
+
|
186
|
+
# Sends response to client.
|
187
|
+
#
|
188
|
+
# @param [Racket::Response] response
|
189
|
+
# @param [String] output
|
190
|
+
# @return nil
|
191
|
+
def self.send_response(response, output)
|
192
|
+
response.write(output)
|
193
|
+
response.finish
|
194
|
+
end
|
195
|
+
|
196
|
+
private_class_method :render_template, :send_response
|
114
197
|
end
|
115
198
|
end
|
116
199
|
end
|