racket-mvc 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/racket/utils/helpers.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
|
#
|
@@ -22,9 +22,25 @@ module Racket
|
|
22
22
|
module Helpers
|
23
23
|
# Cache for helpers, ensuring that helpers get loaded exactly once.
|
24
24
|
class HelperCache
|
25
|
-
|
25
|
+
# Returns a service proc that can be used by the registry.
|
26
|
+
#
|
27
|
+
# @param [Hash] _options (unused)
|
28
|
+
# @return [Proc]
|
29
|
+
def self.service(_options = {})
|
30
|
+
lambda do |reg|
|
31
|
+
new(
|
32
|
+
reg.application_settings.helper_dir,
|
33
|
+
reg.application_logger,
|
34
|
+
reg.utils
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(helper_dir, logger, utils)
|
26
40
|
@helper_dir = helper_dir
|
27
41
|
@helpers = {}
|
42
|
+
@logger = logger
|
43
|
+
@utils = utils
|
28
44
|
end
|
29
45
|
|
30
46
|
# Loads helper files and return the loadad modules as a hash. Any helper files that
|
@@ -51,20 +67,24 @@ module Racket
|
|
51
67
|
|
52
68
|
def load_helper_file(helper)
|
53
69
|
require_helper_file(helper)
|
54
|
-
|
70
|
+
load_helper_module(helper)
|
55
71
|
end
|
56
72
|
|
57
73
|
def require_helper_file(helper)
|
58
|
-
loaded =
|
59
|
-
|
74
|
+
loaded = @utils.safe_require("racket/helpers/#{helper}")
|
75
|
+
@utils.safe_require(@utils.build_path(@helper_dir, helper).to_s) if !loaded && @helper_dir
|
60
76
|
end
|
61
77
|
|
62
|
-
|
78
|
+
# Loads a helper module
|
79
|
+
#
|
80
|
+
# @param [Symbol] helper
|
81
|
+
# @return [Module]
|
82
|
+
def load_helper_module(helper)
|
63
83
|
helper_module = nil
|
64
|
-
|
84
|
+
@utils.run_block(NameError) do
|
65
85
|
helper_module =
|
66
86
|
Racket::Helpers.const_get(helper.to_s.split('_').collect(&:capitalize).join.to_sym)
|
67
|
-
|
87
|
+
@logger.inform_dev("Loaded helper module #{helper.inspect}.")
|
68
88
|
end
|
69
89
|
helper_module
|
70
90
|
end
|
@@ -73,23 +93,25 @@ module Racket
|
|
73
93
|
# Applies helpers to a controller class by including the modules in the class.
|
74
94
|
#
|
75
95
|
# @param [Class] klass
|
76
|
-
def
|
96
|
+
def apply_helpers(klass)
|
77
97
|
klass.helper unless klass.settings.fetch(:helpers) # Makes sure default helpers are loaded.
|
78
98
|
__apply_helpers(klass)
|
79
99
|
nil
|
80
100
|
end
|
81
101
|
|
82
|
-
|
102
|
+
# Applies helpers to a controller class by including the modules in the class.
|
103
|
+
#
|
104
|
+
# @param [Class] klass
|
105
|
+
# @return [Class]
|
106
|
+
def __apply_helpers(klass)
|
83
107
|
klass.settings.fetch(:helpers).reverse_each do |pair|
|
84
108
|
helper_key, helper = pair
|
85
|
-
|
109
|
+
klass.context.logger.inform_dev(
|
86
110
|
"Adding helper module #{helper_key.inspect} to #{klass}"
|
87
111
|
)
|
88
112
|
klass.send(:include, helper)
|
89
113
|
end
|
90
114
|
end
|
91
|
-
|
92
|
-
private_class_method :__apply_helpers
|
93
115
|
end
|
94
116
|
end
|
95
117
|
end
|
data/lib/racket/utils/routing.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
|
#
|
@@ -25,10 +25,19 @@ module Racket
|
|
25
25
|
|
26
26
|
# Class for caching actions
|
27
27
|
class ActionCache
|
28
|
+
# Returns a service proc that can be used by the registry.
|
29
|
+
#
|
30
|
+
# @param [Hash] _options (unused)
|
31
|
+
# @return [Proc]
|
32
|
+
def self.service(_options = {})
|
33
|
+
->(reg) { new(reg.application_logger) }
|
34
|
+
end
|
35
|
+
|
28
36
|
attr_reader :items
|
29
37
|
|
30
|
-
def initialize
|
38
|
+
def initialize(logger)
|
31
39
|
@items = {}
|
40
|
+
@logger = logger
|
32
41
|
end
|
33
42
|
|
34
43
|
# Returns whether +controller_class+ is in the cache and that it contains the action
|
@@ -50,7 +59,7 @@ module Racket
|
|
50
59
|
__add(controller_class)
|
51
60
|
actions = @items[controller_class].to_a
|
52
61
|
@items[controller_class] = actions
|
53
|
-
|
62
|
+
@logger.inform_dev(
|
54
63
|
"Registering actions #{actions} for #{controller_class}."
|
55
64
|
) && nil
|
56
65
|
end
|
@@ -69,55 +78,62 @@ module Racket
|
|
69
78
|
end
|
70
79
|
end
|
71
80
|
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
81
|
+
# Class responsible for dispatching requests to controllers.
|
82
|
+
class Dispatcher
|
83
|
+
# Extracts the target class, target params and target action from a list of valid routes.
|
84
|
+
#
|
85
|
+
# @param [HttpRouter::Response] response
|
86
|
+
# @return [Array]
|
87
|
+
def self.extract_target(response)
|
88
|
+
target_klass = response.route.dest
|
89
|
+
params = response.param_values.first.reject(&:empty?)
|
90
|
+
action =
|
91
|
+
if params.empty?
|
92
|
+
target_klass.settings.fetch(:default_action)
|
93
|
+
else
|
94
|
+
params.shift.to_sym
|
95
|
+
end
|
96
|
+
[target_klass, params, action]
|
97
|
+
end
|
82
98
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
99
|
+
# Constructor
|
100
|
+
#
|
101
|
+
# @param [Hash] env
|
102
|
+
# @param [Array] target_info
|
103
|
+
def initialize(env, target_info)
|
104
|
+
@env = env
|
105
|
+
@controller_class, @params, @action = target_info
|
106
|
+
end
|
88
107
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
controller_class, params, action = target_info
|
108
|
+
# Dispatches request to a controller. This is the default action whenever a matching
|
109
|
+
# route for a request is found.
|
110
|
+
#
|
111
|
+
# @return [Array] A racket response triplet
|
112
|
+
def dispatch
|
113
|
+
# Rewrite PATH_INFO to reflect that we split out the parameters
|
114
|
+
update_path_info(@params.length)
|
97
115
|
|
98
|
-
|
99
|
-
|
116
|
+
# Call controller
|
117
|
+
call_controller(Current.init(RouterParams.new(@action, @params, @env)))
|
118
|
+
end
|
100
119
|
|
101
|
-
|
102
|
-
call_controller(
|
103
|
-
controller_class,
|
104
|
-
Current.init(RouterParams.new(action, params, env))
|
105
|
-
)
|
106
|
-
end
|
120
|
+
private
|
107
121
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
# @param [Fixnum] num_params
|
112
|
-
# @return [nil]
|
113
|
-
def self.update_path_info(env, num_params)
|
114
|
-
env['PATH_INFO'] = env['PATH_INFO']
|
115
|
-
.split('/')[0...-num_params]
|
116
|
-
.join('/') unless num_params.zero?
|
117
|
-
nil
|
118
|
-
end
|
122
|
+
def call_controller(mod)
|
123
|
+
@controller_class.new.extend(mod).__run
|
124
|
+
end
|
119
125
|
|
120
|
-
|
126
|
+
# Updates the PATH_INFO environment variable.
|
127
|
+
#
|
128
|
+
# @param [Fixnum] num_params
|
129
|
+
# @return [nil]
|
130
|
+
def update_path_info(num_params)
|
131
|
+
@env['PATH_INFO'] = @env['PATH_INFO']
|
132
|
+
.split('/')[0...-num_params]
|
133
|
+
.join('/') unless num_params.zero?
|
134
|
+
nil
|
135
|
+
end
|
136
|
+
end
|
121
137
|
end
|
122
138
|
end
|
123
139
|
end
|
data/lib/racket/utils/views.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
|
#
|
@@ -18,197 +18,29 @@
|
|
18
18
|
|
19
19
|
require 'tilt'
|
20
20
|
|
21
|
+
require_relative 'views/renderer.rb'
|
22
|
+
require_relative 'views/template_cache.rb'
|
23
|
+
require_relative 'views/template_locator.rb'
|
24
|
+
require_relative 'views/template_resolver.rb'
|
25
|
+
|
21
26
|
module Racket
|
22
27
|
module Utils
|
23
|
-
#
|
28
|
+
# Namespace for view utilities
|
24
29
|
module Views
|
25
|
-
#
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def ensure_in_cache(path, template_params)
|
38
|
-
return @cache[path] if @cache.key?(path)
|
39
|
-
@cache[path] = TemplateLocator.calculate_path(path, template_params)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Class used for locating templates.
|
44
|
-
class TemplateLocator
|
45
|
-
def initialize(layout_base_dir, view_base_dir)
|
46
|
-
@layout_base_dir = layout_base_dir
|
47
|
-
@view_base_dir = view_base_dir
|
48
|
-
@layout_cache = TemplateCache.new
|
49
|
-
@view_cache = TemplateCache.new
|
50
|
-
end
|
51
|
-
|
52
|
-
# Returns the layout associated with the current request. On the first request to any action
|
53
|
-
# the result is cached, meaning that the layout only needs to be looked up once.
|
54
|
-
#
|
55
|
-
# @param [Racket::Controller] controller
|
56
|
-
# @return [String|nil]
|
57
|
-
def get_layout(controller)
|
58
|
-
get_template(TemplateParams.new(:layout, controller, @layout_base_dir, @layout_cache))
|
59
|
-
end
|
60
|
-
|
61
|
-
# Returns the view associated with the current request. On the first request to any action
|
62
|
-
# the result is cached, meaning that the view only needs to be looked up once.
|
63
|
-
#
|
64
|
-
# @param [Racket::Controller] controller
|
65
|
-
# @return [String|nil]
|
66
|
-
def get_view(controller)
|
67
|
-
get_template(TemplateParams.new(:view, controller, @view_base_dir, @view_cache))
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
# Tries to locate a template matching +path+ in the file system and returns the path if a
|
73
|
-
# matching file is found. If no matching file is found, +nil+ is returned. The result is
|
74
|
-
# cached, meaning that the filesystem lookup for a specific path will only happen once.
|
75
|
-
#
|
76
|
-
# @param [TemplateParams] template_params
|
77
|
-
# @return [String|nil]
|
78
|
-
def get_template(template_params)
|
79
|
-
klass = self.class
|
80
|
-
path = klass.get_template_path(template_params.controller)
|
81
|
-
template = template_params.cache.ensure_in_cache(path, template_params)
|
82
|
-
klass.resolve_template(path, template, template_params)
|
83
|
-
end
|
84
|
-
|
85
|
-
def self.calculate_path(path, template_params)
|
86
|
-
type, controller, base_dir = template_params.to_a
|
87
|
-
default_template = controller.settings.fetch("default_#{type}".to_sym)
|
88
|
-
template =
|
89
|
-
TemplateLocator.lookup_template_with_default(
|
90
|
-
Utils.fs_path(base_dir, path), default_template
|
91
|
-
)
|
92
|
-
::Racket::Application.inform_dev(
|
93
|
-
"Using #{type} #{template.inspect} for #{controller.class}.#{controller.racket.action}."
|
94
|
-
)
|
95
|
-
template
|
96
|
-
end
|
97
|
-
|
98
|
-
def self.resolve_template(path, template, template_params)
|
99
|
-
return template unless template.is_a?(Proc)
|
100
|
-
_, controller, base_dir = template_params.to_a
|
101
|
-
lookup_template(
|
102
|
-
Utils.fs_path(
|
103
|
-
Utils.fs_path(base_dir, path).dirname,
|
104
|
-
call_template_proc(template, controller)
|
105
|
-
)
|
106
|
-
)
|
107
|
-
end
|
108
|
-
|
109
|
-
# Calls a template proc. Depending on how many parameters the template proc takes, different
|
110
|
-
# types of information will be passed to the proc.
|
111
|
-
# If the proc takes zero parameters, no information will be passed.
|
112
|
-
# If the proc takes one parameter, it will contain the current action.
|
113
|
-
# If the proc takes two parameters, they will contain the current action and the current
|
114
|
-
# params.
|
115
|
-
# If the proc takes three parameters, they will contain the current action, the current
|
116
|
-
# params and the current request.
|
117
|
-
#
|
118
|
-
# @param [Proc] proc
|
119
|
-
# @param [Racket::Controller] controller
|
120
|
-
# @return [String]
|
121
|
-
def self.call_template_proc(proc, controller)
|
122
|
-
racket = controller.racket
|
123
|
-
proc_args = [racket.action, racket.params, controller.request].slice(0...proc.arity)
|
124
|
-
proc.call(*proc_args).to_s
|
125
|
-
end
|
126
|
-
|
127
|
-
# Returns the "url path" that should be used when searching for templates.
|
128
|
-
#
|
129
|
-
# @param [Racket::Controller] controller
|
130
|
-
# @return [String]
|
131
|
-
def self.get_template_path(controller)
|
132
|
-
template_path =
|
133
|
-
[::Racket::Application.get_route(controller.class), controller.racket.action].join('/')
|
134
|
-
template_path = template_path[1..-1] if template_path.start_with?('//')
|
135
|
-
template_path
|
136
|
-
end
|
137
|
-
|
138
|
-
# Locates a file in the filesystem matching an URL path. If there exists a matching file,
|
139
|
-
# the path to it is returned. If there is no matching file, +nil+ is returned.
|
140
|
-
# @param [Pathname] path
|
141
|
-
# @return [Pathname|nil]
|
142
|
-
def self.lookup_template(path)
|
143
|
-
Utils.first_matching_path(*Utils.extract_dir_and_glob(path))
|
144
|
-
end
|
145
|
-
|
146
|
-
# Locates a file in the filesystem matching an URL path. If there exists a matching file,
|
147
|
-
# the path to it is returned. If there is no matching file and +default_template+ is a
|
148
|
-
# String or a Symbol, another lookup will be performed using +default_template+. If
|
149
|
-
# +default_template+ is a Proc or nil, +default_template+ will be used as is instead.
|
150
|
-
#
|
151
|
-
# @param [Pathname] path
|
152
|
-
# @param [String|Symbol|Proc|nil] default_template
|
153
|
-
# @return [String|Proc|nil]
|
154
|
-
def self.lookup_template_with_default(path, default_template)
|
155
|
-
template = lookup_template(path)
|
156
|
-
unless template
|
157
|
-
if default_template.is_a?(String) || default_template.is_a?(Symbol)
|
158
|
-
# Strings and symbols can be lookup up in the file system...
|
159
|
-
template = lookup_template(Utils.fs_path(path.dirname, default_template))
|
160
|
-
else
|
161
|
-
# ...but not nils/procs!
|
162
|
-
template = default_template
|
163
|
-
end
|
164
|
-
end
|
165
|
-
template
|
30
|
+
# Extracts what template settings to use based on context and incoming parameters.
|
31
|
+
#
|
32
|
+
# @param [Object] context
|
33
|
+
# @param [Hash] template_settings
|
34
|
+
# @return [Hash]
|
35
|
+
def self.extract_template_settings(context, template_settings)
|
36
|
+
if context.respond_to?(:view_settings) && !template_settings
|
37
|
+
context.view_settings
|
38
|
+
elsif template_settings
|
39
|
+
template_settings
|
40
|
+
else
|
41
|
+
{}
|
166
42
|
end
|
167
43
|
end
|
168
|
-
|
169
|
-
# Class responsible for rendering a controller/view/layout combination.
|
170
|
-
class ViewRenderer
|
171
|
-
# Renders a page using the provided controller/view and layout combination and returns an
|
172
|
-
# response array that can be sent to the client.
|
173
|
-
#
|
174
|
-
# @param [Racket::Controller] controller
|
175
|
-
# @param [String] view
|
176
|
-
# @param [String] layout
|
177
|
-
# @return [Array]
|
178
|
-
def self.render(controller, view, layout)
|
179
|
-
send_response(
|
180
|
-
controller.response,
|
181
|
-
view ? render_template(controller, view, layout) : controller.racket.action_result
|
182
|
-
)
|
183
|
-
end
|
184
|
-
|
185
|
-
# Renders a template/layout combo using Tilt and returns it as a string.
|
186
|
-
#
|
187
|
-
# @param [Racket::Controller] controller
|
188
|
-
# @param [String] view
|
189
|
-
# @param [String|nil] layout
|
190
|
-
# @return [String]
|
191
|
-
def self.render_template(controller, view, layout)
|
192
|
-
output = Tilt.new(view).render(controller)
|
193
|
-
output = Tilt.new(layout).render(controller) { output } if layout
|
194
|
-
output
|
195
|
-
end
|
196
|
-
|
197
|
-
# Sends response to client.
|
198
|
-
#
|
199
|
-
# @param [Racket::Response] response
|
200
|
-
# @param [String] output
|
201
|
-
# @return nil
|
202
|
-
def self.send_response(response, output)
|
203
|
-
response.write(output)
|
204
|
-
response.finish
|
205
|
-
end
|
206
|
-
|
207
|
-
private_class_method :render_template, :send_response
|
208
|
-
end
|
209
|
-
|
210
|
-
# Struct for holding template data.
|
211
|
-
TemplateParams = Struct.new(:type, :controller, :base_dir, :cache)
|
212
44
|
end
|
213
45
|
end
|
214
46
|
end
|