racket-mvc 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|