racket-mvc 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/racket.rb +2 -0
- data/lib/racket/application.rb +43 -84
- data/lib/racket/controller.rb +22 -29
- data/lib/racket/current.rb +2 -2
- data/lib/racket/router.rb +47 -20
- data/lib/racket/settings/application.rb +74 -0
- data/lib/racket/settings/base.rb +83 -0
- data/lib/racket/settings/controller.rb +44 -0
- data/lib/racket/utils.rb +1 -1
- data/lib/racket/version.rb +2 -2
- data/lib/racket/view_manager.rb +53 -49
- data/spec/_custom.rb +27 -5
- data/spec/_default.rb +10 -10
- data/spec/test_custom_app/controllers/sub3/custom_sub_controller_3.rb +2 -2
- data/spec/test_custom_app/controllers/sub4/custom_sub_controller_4.rb +9 -9
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02938217c8513aa37d2e87dbc1232d85be25b94a
|
4
|
+
data.tar.gz: e2686cd3a87d07793b886e48428b277bd3ae1639
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a76232aecaf2a67c3062713204dca768b1cbc6307e70cb6eaa3fa90d56e0c814c1cace2f756071294b09f1f28375c46e81e93db5842151e9d0e44a5489b74d61
|
7
|
+
data.tar.gz: 509beaf22372ace6bd8cc85739c8524006cc03790bf9a49279991c89d4991514a7e176626904ce9367beba87c72f72ed51702026f99357d0e042b3747804d9ad
|
data/lib/racket.rb
CHANGED
@@ -26,6 +26,8 @@ require_relative 'racket/request.rb'
|
|
26
26
|
require_relative 'racket/response.rb'
|
27
27
|
require_relative 'racket/router.rb'
|
28
28
|
require_relative 'racket/session.rb'
|
29
|
+
require_relative 'racket/settings/application.rb'
|
30
|
+
require_relative 'racket/settings/controller.rb'
|
29
31
|
require_relative 'racket/view_manager.rb'
|
30
32
|
require_relative 'racket/utils.rb'
|
31
33
|
|
data/lib/racket/application.rb
CHANGED
@@ -16,28 +16,34 @@
|
|
16
16
|
# You should have received a copy of the GNU Affero General Public License
|
17
17
|
# along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
18
18
|
|
19
|
-
require 'logger'
|
20
|
-
|
21
19
|
module Racket
|
22
20
|
# Racket main application class.
|
23
21
|
class Application
|
24
|
-
@
|
22
|
+
@settings = nil
|
23
|
+
|
24
|
+
class << self
|
25
|
+
attr_reader :router, :settings
|
26
|
+
end
|
25
27
|
|
26
28
|
# Returns the internal application object. When called for the first time this method will use
|
27
|
-
# Rack::Builder to
|
29
|
+
# Rack::Builder to construct the application.
|
28
30
|
#
|
29
31
|
# @return [Rack::Builder]
|
30
32
|
def self.application
|
31
33
|
return @application if @application
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
load_middleware
|
35
|
+
build_application
|
36
|
+
end
|
37
|
+
|
38
|
+
# Builds an application from a Rack::Builder object.
|
39
|
+
#
|
40
|
+
# @return [Rack::Builder]
|
41
|
+
def self.build_application
|
36
42
|
instance = self
|
37
43
|
@application = Rack::Builder.new do
|
38
|
-
instance.
|
44
|
+
instance.settings.middleware.each do |middleware|
|
39
45
|
klass, opts = middleware
|
40
|
-
instance.inform_dev("Loading middleware #{klass} with
|
46
|
+
instance.inform_dev("Loading middleware #{klass} with settings #{opts.inspect}.")
|
41
47
|
use(*middleware)
|
42
48
|
end
|
43
49
|
run lambda { |env|
|
@@ -56,42 +62,11 @@ module Racket
|
|
56
62
|
application.call(env.dup)
|
57
63
|
end
|
58
64
|
|
59
|
-
# Returns a list of default options for Racket::Application.
|
60
|
-
#
|
61
|
-
# @return [Hash]
|
62
|
-
def self.default_options
|
63
|
-
root_dir = Utils.build_path(Dir.pwd)
|
64
|
-
{
|
65
|
-
controller_dir: Utils.build_path(root_dir, 'controllers'),
|
66
|
-
default_action: :index,
|
67
|
-
default_content_type: 'text/html',
|
68
|
-
default_controller_helpers: [:routing, :view],
|
69
|
-
default_layout: nil,
|
70
|
-
default_view: nil,
|
71
|
-
helper_dir: Utils.build_path(root_dir, 'helpers'),
|
72
|
-
layout_dir: Utils.build_path(root_dir, 'layouts'),
|
73
|
-
logger: Logger.new($stdout),
|
74
|
-
middleware: [],
|
75
|
-
mode: :live,
|
76
|
-
public_dir: Utils.build_path(root_dir, 'public'),
|
77
|
-
root_dir: root_dir,
|
78
|
-
session_handler: [
|
79
|
-
Rack::Session::Cookie,
|
80
|
-
{
|
81
|
-
key: 'racket.session',
|
82
|
-
old_secret: SecureRandom.hex(16),
|
83
|
-
secret: SecureRandom.hex(16)
|
84
|
-
}
|
85
|
-
],
|
86
|
-
view_dir: Utils.build_path(root_dir, 'views')
|
87
|
-
}
|
88
|
-
end
|
89
|
-
|
90
65
|
# Returns whether the application runs in dev mode.
|
91
66
|
#
|
92
67
|
# @return [true|false]
|
93
68
|
def self.dev_mode?
|
94
|
-
@
|
69
|
+
@settings.mode == :dev
|
95
70
|
end
|
96
71
|
|
97
72
|
# Returns a route to the specified controller/action/parameter combination.
|
@@ -104,7 +79,7 @@ module Racket
|
|
104
79
|
@router.get_route(controller, action, params)
|
105
80
|
end
|
106
81
|
|
107
|
-
# Initializes a new Racket::Application object with default
|
82
|
+
# Initializes a new Racket::Application object with default settings.
|
108
83
|
#
|
109
84
|
# @param [true|false] reboot
|
110
85
|
# @return [Class]
|
@@ -112,22 +87,13 @@ module Racket
|
|
112
87
|
init({}, reboot)
|
113
88
|
end
|
114
89
|
|
115
|
-
# Expands all paths defined in the application, but only if it is set to something usable.
|
116
|
-
#
|
117
|
-
# @return [nil]
|
118
|
-
def self.expand_paths
|
119
|
-
[:controller_dir, :helper_dir, :layout_dir, :public_dir, :view_dir].each do |dir|
|
120
|
-
@options[dir] = Utils.build_path(@options[dir]) if @options[dir]
|
121
|
-
end && nil
|
122
|
-
end
|
123
|
-
|
124
90
|
# Writes a message to the logger if there is one present.
|
125
91
|
#
|
126
92
|
# @param [String] message
|
127
93
|
# @param [Symbol] level
|
128
94
|
# @return nil
|
129
95
|
def self.inform(message, level)
|
130
|
-
(@
|
96
|
+
(@settings.logger.send(level, message) if @settings.logger) && nil
|
131
97
|
end
|
132
98
|
|
133
99
|
# Sends a message to the logger.
|
@@ -150,14 +116,13 @@ module Racket
|
|
150
116
|
|
151
117
|
# Initializes the Racket application.
|
152
118
|
#
|
153
|
-
# @param [Hash]
|
119
|
+
# @param [Hash] settings
|
154
120
|
# @param [true|false] reboot
|
155
121
|
# @return [Class]
|
156
|
-
def self.init(
|
122
|
+
def self.init(settings, reboot)
|
157
123
|
instance_variables.each { |ivar| instance_variable_set(ivar, nil) } if reboot
|
158
|
-
fail 'Application has already been initialized!' if @
|
159
|
-
@
|
160
|
-
expand_paths
|
124
|
+
fail 'Application has already been initialized!' if @settings
|
125
|
+
@settings = Settings::Application.new(settings)
|
161
126
|
setup_static_server
|
162
127
|
reload
|
163
128
|
self
|
@@ -168,32 +133,33 @@ module Racket
|
|
168
133
|
# @return [nil]
|
169
134
|
def self.load_controllers
|
170
135
|
inform_dev('Loading controllers.')
|
171
|
-
@
|
136
|
+
@settings.store(:last_added_controller, [])
|
172
137
|
@controller = nil
|
173
|
-
Dir.chdir(@
|
138
|
+
Dir.chdir(@settings.controller_dir) do
|
174
139
|
files = Pathname.glob(File.join('**', '*.rb')).map!(&:to_s)
|
175
140
|
# Sort by longest path so that the longer paths gets matched first
|
176
141
|
# HttpRouter claims to be doing this already, but this "hack" is needed in order
|
177
142
|
# for the router to work.
|
178
|
-
files.sort!
|
179
|
-
b.split('/').length <=> a.split('/').length
|
180
|
-
end
|
143
|
+
files.sort! { |a, b| b.split('/').length <=> a.split('/').length }
|
181
144
|
files.each do |file|
|
182
145
|
::Kernel.require File.expand_path(file)
|
183
146
|
path = "/#{File.dirname(file)}"
|
184
147
|
path = '' if path == '/.'
|
185
|
-
@router.map(path, @
|
148
|
+
@router.map(path, @settings.fetch(:last_added_controller).pop)
|
186
149
|
end
|
187
150
|
end
|
188
|
-
@
|
151
|
+
@settings.delete(:last_added_controller)
|
189
152
|
inform_dev('Done loading controllers.') && nil
|
190
153
|
end
|
191
154
|
|
192
|
-
#
|
155
|
+
# Loads some middleware (based on settings).
|
193
156
|
#
|
194
|
-
# @return
|
195
|
-
def self.
|
196
|
-
@
|
157
|
+
# @return nil
|
158
|
+
def self.load_middleware
|
159
|
+
@settings.middleware.unshift(@settings.session_handler) if @settings.session_handler
|
160
|
+
@settings.middleware.unshift([Rack::ContentType, @settings.default_content_type]) if
|
161
|
+
@settings.default_content_type
|
162
|
+
(@settings.middleware.unshift([Rack::ShowExceptions]) if dev_mode?) && nil
|
197
163
|
end
|
198
164
|
|
199
165
|
# Reloads the application, making any changes to the controller configuration visible
|
@@ -213,13 +179,6 @@ module Racket
|
|
213
179
|
(::Kernel.require Utils.build_path(*args)) && nil
|
214
180
|
end
|
215
181
|
|
216
|
-
# Returns the router associated with the currenntly running Racket::Application.
|
217
|
-
#
|
218
|
-
# @return [Racket::Router]
|
219
|
-
def self.router
|
220
|
-
@router
|
221
|
-
end
|
222
|
-
|
223
182
|
# Serves a static file (if Racket is configured to serve static files).
|
224
183
|
#
|
225
184
|
# @param [Hash] env Rack environment
|
@@ -242,28 +201,28 @@ module Racket
|
|
242
201
|
# @return [nil]
|
243
202
|
def self.setup_static_server
|
244
203
|
@static_server = nil
|
245
|
-
return nil unless (public_dir = @
|
204
|
+
return nil unless (public_dir = @settings.public_dir) && Utils.dir_readable?(public_dir)
|
246
205
|
inform_dev("Setting up static server to serve files from #{public_dir}.")
|
247
206
|
(@static_server = Rack::File.new(public_dir)) && nil
|
248
207
|
end
|
249
208
|
|
250
|
-
# Initializes a new Racket::Application object with
|
209
|
+
# Initializes a new Racket::Application object with settings specified by +settings+.
|
251
210
|
#
|
252
|
-
# @param [Hash]
|
211
|
+
# @param [Hash] settings
|
253
212
|
# @param [true|false] reboot
|
254
213
|
# @return [Class]
|
255
|
-
def self.using(
|
256
|
-
init(
|
214
|
+
def self.using(settings, reboot = false)
|
215
|
+
init(settings, reboot)
|
257
216
|
end
|
258
217
|
|
259
218
|
# Returns the view cache of the currently running application.
|
260
219
|
#
|
261
220
|
# @return [Racket::ViewManager]
|
262
221
|
def self.view_manager
|
263
|
-
@view_manager ||= ViewManager.new(@
|
222
|
+
@view_manager ||= ViewManager.new(@settings.layout_dir, @settings.view_dir)
|
264
223
|
end
|
265
224
|
|
266
|
-
private_class_method :application, :
|
267
|
-
:
|
225
|
+
private_class_method :application, :build_application, :inform, :init, :load_controllers,
|
226
|
+
:load_middleware, :setup_routes, :setup_static_server
|
268
227
|
end
|
269
228
|
end
|
data/lib/racket/controller.rb
CHANGED
@@ -20,7 +20,7 @@ module Racket
|
|
20
20
|
# Base controller class. Your controllers should inherit this class.
|
21
21
|
class Controller
|
22
22
|
def self.__load_helpers(helpers)
|
23
|
-
helper_dir = Application.
|
23
|
+
helper_dir = Application.settings.helper_dir
|
24
24
|
helper_modules = {}
|
25
25
|
helpers.each do |helper|
|
26
26
|
helper_module = helper.to_s.split('_').collect(&:capitalize).join.to_sym
|
@@ -51,9 +51,9 @@ module Racket
|
|
51
51
|
key = "#{type}_hooks".to_sym
|
52
52
|
meths = public_instance_methods(false)
|
53
53
|
meths &= methods.map(&:to_sym) unless methods.empty?
|
54
|
-
hooks =
|
54
|
+
hooks = settings.fetch(key, {})
|
55
55
|
meths.each { |meth| hooks[meth] = blk }
|
56
|
-
|
56
|
+
setting(key, hooks)
|
57
57
|
nil
|
58
58
|
end
|
59
59
|
|
@@ -78,62 +78,55 @@ module Racket
|
|
78
78
|
end
|
79
79
|
|
80
80
|
# Adds one or more helpers to the controller. All controllers get some default helpers
|
81
|
-
# (
|
81
|
+
# (:routing and :view by default), but if you have your own helpers you want to load this
|
82
82
|
# is the preferred method.
|
83
83
|
#
|
84
84
|
# By default Racket will look for your helpers in the helpers directory, but you can specify
|
85
|
-
# another location by
|
85
|
+
# another location by changing the helper_dir setting.
|
86
86
|
#
|
87
87
|
# @param [Array] helpers An array of symbols representing classes living in the Racket::Helpers
|
88
88
|
# namespace.
|
89
89
|
def self.helper(*helpers)
|
90
90
|
helper_modules = {}
|
91
|
-
existing_helpers =
|
91
|
+
existing_helpers = settings.fetch(:helpers)
|
92
92
|
if existing_helpers.nil?
|
93
93
|
# No helpers has been loaded yet. Load the default helpers.
|
94
|
-
existing_helpers = Application.
|
94
|
+
existing_helpers = Application.settings.default_controller_helpers
|
95
95
|
helper_modules.merge!(__load_helpers(existing_helpers))
|
96
96
|
end
|
97
97
|
# Load new helpers
|
98
98
|
helpers.map!(&:to_sym)
|
99
99
|
helpers.reject! { |helper| helper_modules.key?(helper) }
|
100
100
|
helper_modules.merge!(__load_helpers(helpers))
|
101
|
-
|
101
|
+
setting(:helpers, helper_modules)
|
102
102
|
end
|
103
103
|
|
104
104
|
# :nodoc:
|
105
105
|
def self.inherited(klass)
|
106
|
-
Application.
|
106
|
+
Application.settings.fetch(:last_added_controller).push(klass)
|
107
107
|
end
|
108
108
|
|
109
|
-
# Returns
|
110
|
-
# it is inheriting from.
|
109
|
+
# Returns the settings associated with the current controller class.
|
111
110
|
#
|
112
|
-
# @
|
113
|
-
|
114
|
-
|
115
|
-
@options ||= {}
|
116
|
-
return @options[key] if @options.key?(key)
|
117
|
-
# We are running out of controller options, do one final lookup in Application.options
|
118
|
-
return Application.options.fetch(key, nil) if superclass == Controller
|
119
|
-
superclass.get_option(key)
|
111
|
+
# @return [Racket::Settings::Controller]
|
112
|
+
def self.settings
|
113
|
+
@settings ||= Settings::Controller.new(self)
|
120
114
|
end
|
121
115
|
|
122
|
-
#
|
116
|
+
# Creates/updates a setting for the current controller class.
|
123
117
|
#
|
124
118
|
# @param [Symbol] key
|
125
119
|
# @param [Object] value
|
126
|
-
|
127
|
-
|
128
|
-
(
|
120
|
+
# @return [nil]
|
121
|
+
def self.setting(key, value)
|
122
|
+
settings.store(key, value)
|
129
123
|
end
|
130
124
|
|
131
|
-
# Returns
|
125
|
+
# Returns the settings for a controller instance.
|
132
126
|
#
|
133
|
-
# @
|
134
|
-
|
135
|
-
|
136
|
-
self.class.get_option(key)
|
127
|
+
# @return [Racket::Settings::Controller]
|
128
|
+
def settings
|
129
|
+
self.class.settings
|
137
130
|
end
|
138
131
|
|
139
132
|
# Redirects the client. After hooks are run.
|
@@ -196,7 +189,7 @@ module Racket
|
|
196
189
|
end
|
197
190
|
|
198
191
|
def __run_hook(type)
|
199
|
-
hooks =
|
192
|
+
hooks = settings.fetch("#{type}_hooks".to_sym, {})
|
200
193
|
blk = hooks.fetch(racket.action, nil)
|
201
194
|
(instance_eval(&blk) if blk) && nil
|
202
195
|
end
|
data/lib/racket/current.rb
CHANGED
@@ -34,10 +34,10 @@ module Racket
|
|
34
34
|
# @param [Array] params Parameters sent to the action
|
35
35
|
# @return [Module] A module encapsulating all state relating to the current request
|
36
36
|
def self.init(env, klass, action, params)
|
37
|
-
klass.helper if klass.
|
37
|
+
klass.helper if klass.settings.fetch(:helpers).nil? # Makes sure default helpers are loaded.
|
38
38
|
properties = init_properties(action, params, env)
|
39
39
|
Module.new do
|
40
|
-
klass.
|
40
|
+
klass.settings.fetch(:helpers).each_value { |helper| include helper }
|
41
41
|
properties.each_pair { |key, value| define_method(key) { value } }
|
42
42
|
end
|
43
43
|
end
|
data/lib/racket/router.rb
CHANGED
@@ -94,34 +94,61 @@ module Racket
|
|
94
94
|
# @return [Array] A Rack response triplet
|
95
95
|
def route(env)
|
96
96
|
catch :response do # Catches early exits from Controller.respond.
|
97
|
-
#
|
98
|
-
|
99
|
-
|
100
|
-
matching_routes = @router.recognize(env)
|
101
|
-
|
102
|
-
# Exit early if no controller is responsible for the route
|
103
|
-
return render_error(404) if matching_routes.first.nil?
|
104
|
-
|
105
|
-
# Some controller is claiming to be responsible for the route
|
106
|
-
target_klass = matching_routes.first.first.route.dest
|
107
|
-
params = matching_routes.first.first.param_values.first.reject(&:empty?)
|
108
|
-
action = params.empty? ? target_klass.get_option(:default_action) : params.shift.to_sym
|
109
|
-
|
110
|
-
# Check if action is available on target
|
111
|
-
return render_error(404) unless @action_cache[target_klass].include?(action)
|
97
|
+
# Ensure that that a controller will respond to the request. If not, send a 404.
|
98
|
+
return render_error(404) if (target_info = target_info(env)).nil?
|
99
|
+
target_klass, params, action = target_info
|
112
100
|
|
113
101
|
# Rewrite PATH_INFO to reflect that we split out the parameters
|
114
|
-
env
|
115
|
-
.split('/')[0...-params.count]
|
116
|
-
.join('/') unless params.empty?
|
102
|
+
update_path_info(env, params.length)
|
117
103
|
|
118
104
|
# Initialize and render target
|
119
105
|
target = target_klass.new
|
120
106
|
target.extend(Current.init(env, target_klass, action, params))
|
121
107
|
target.__run
|
122
108
|
end
|
123
|
-
|
124
|
-
|
109
|
+
rescue => err
|
110
|
+
render_error(500, err)
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
# Returns information about the target of the request. If no valid target can be found, +nil+
|
116
|
+
# is returned.
|
117
|
+
#
|
118
|
+
# @param [Hash] env
|
119
|
+
# @return [Array|nil]
|
120
|
+
def target_info(env)
|
121
|
+
matching_routes = @router.recognize(env)
|
122
|
+
# Exit early if no controller is responsible for the route
|
123
|
+
return nil if matching_routes.first.nil?
|
124
|
+
# Some controller is claiming to be responsible for the route
|
125
|
+
result = extract_target(matching_routes)
|
126
|
+
# Exit early if action is not available on target
|
127
|
+
return nil unless @action_cache[result.first].include?(result.last)
|
128
|
+
result
|
129
|
+
end
|
130
|
+
|
131
|
+
# Extracts the target class, target params and target action from a list of valid routes.
|
132
|
+
#
|
133
|
+
# @param [Array] routes
|
134
|
+
# @return [Array]
|
135
|
+
def extract_target(routes)
|
136
|
+
target_klass = routes.first.first.route.dest
|
137
|
+
params = routes.first.first.param_values.first.reject(&:empty?)
|
138
|
+
action = params.empty? ? target_klass.settings.fetch(:default_action) : params.shift.to_sym
|
139
|
+
[target_klass, params, action]
|
140
|
+
end
|
141
|
+
|
142
|
+
# Updates the PATH_INFO environment variable.
|
143
|
+
#
|
144
|
+
# @param [Hash] env
|
145
|
+
# @param [Fixnum] num_params
|
146
|
+
# @return [nil]
|
147
|
+
def update_path_info(env, num_params)
|
148
|
+
env['PATH_INFO'] = env['PATH_INFO']
|
149
|
+
.split('/')[0...-num_params]
|
150
|
+
.join('/') unless num_params.zero?
|
151
|
+
nil
|
125
152
|
end
|
126
153
|
end
|
127
154
|
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
|
+
require 'logger'
|
20
|
+
|
21
|
+
require_relative 'base.rb'
|
22
|
+
|
23
|
+
module Racket
|
24
|
+
module Settings
|
25
|
+
# Class for storing application settings.
|
26
|
+
class Application < Base
|
27
|
+
setting(:default_action, :index)
|
28
|
+
setting(:default_content_type, 'text/html')
|
29
|
+
setting(:default_controller_helpers, [:routing, :view])
|
30
|
+
setting(:default_layout, nil)
|
31
|
+
setting(:default_view, nil)
|
32
|
+
setting(:logger, Logger.new($stdout))
|
33
|
+
setting(:middleware, [])
|
34
|
+
setting(:mode, :live)
|
35
|
+
setting(
|
36
|
+
:session_handler,
|
37
|
+
[
|
38
|
+
Rack::Session::Cookie,
|
39
|
+
{
|
40
|
+
key: 'racket.session',
|
41
|
+
old_secret: SecureRandom.hex(16),
|
42
|
+
secret: SecureRandom.hex(16)
|
43
|
+
}
|
44
|
+
]
|
45
|
+
)
|
46
|
+
setting(:root_dir, nil) # Will be set automatically by constructor.
|
47
|
+
|
48
|
+
def initialize(defaults = {})
|
49
|
+
defaults[:root_dir] = Dir.pwd unless defaults.key?(:root_dir)
|
50
|
+
super(defaults)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Creates a directory setting with a default value.
|
54
|
+
#
|
55
|
+
# @param [Symbol] symbol
|
56
|
+
# @param [String] directory
|
57
|
+
# @return [nil]
|
58
|
+
def self.directory_setting(symbol, directory)
|
59
|
+
ivar = "@#{symbol}".to_sym
|
60
|
+
define_method symbol do
|
61
|
+
instance_variable_set(ivar, directory) unless instance_variables.include?(ivar)
|
62
|
+
Utils.build_path(instance_variable_get(ivar))
|
63
|
+
end
|
64
|
+
attr_writer(symbol) && nil
|
65
|
+
end
|
66
|
+
|
67
|
+
directory_setting(:controller_dir, 'controllers')
|
68
|
+
directory_setting(:helper_dir, 'helpers')
|
69
|
+
directory_setting(:layout_dir, 'layouts')
|
70
|
+
directory_setting(:public_dir, 'public')
|
71
|
+
directory_setting(:view_dir, 'views')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,83 @@
|
|
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 for handling Racket settings.
|
21
|
+
module Settings
|
22
|
+
# Base class for settings.
|
23
|
+
class Base
|
24
|
+
def initialize(defaults = {})
|
25
|
+
@custom = {}
|
26
|
+
defaults.each_pair do |key, value|
|
27
|
+
meth = "#{key}=".to_sym
|
28
|
+
if respond_to?(meth) then send(meth, value)
|
29
|
+
else @custom[key] = value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Deletes a custom setting associated with the application.
|
35
|
+
#
|
36
|
+
# @param [Symbol] key
|
37
|
+
# @return [nil]
|
38
|
+
def delete(key)
|
39
|
+
fail ArgumentErrpr,
|
40
|
+
"Cannot delete standard setting #{key}" if respond_to?(key.to_sym)
|
41
|
+
@custom.delete(key) && nil
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns a settings value associated with the application. Both standard and custom
|
45
|
+
# settings are searched. If the key cannot be found, a default value is returned.
|
46
|
+
#
|
47
|
+
# @param [Symbol] key
|
48
|
+
# @param [Object] default
|
49
|
+
# @return [Object]
|
50
|
+
def fetch(key, default = nil)
|
51
|
+
meth = key.to_sym
|
52
|
+
return send(meth) if respond_to?(meth)
|
53
|
+
@custom.fetch(key, default)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Sets/updates a custom setting in the application.
|
57
|
+
#
|
58
|
+
# @param [Symbol] key
|
59
|
+
# @param [Object] value
|
60
|
+
# @return [nil]
|
61
|
+
def store(key, value)
|
62
|
+
fail ArgumentError,
|
63
|
+
"Cannot overwrite standard setting #{key}" if respond_to?("#{key}=".to_sym)
|
64
|
+
(@custom[key] = value) && nil
|
65
|
+
end
|
66
|
+
|
67
|
+
# Creates a setting with a default value.
|
68
|
+
#
|
69
|
+
# @param [Symbol] symbol
|
70
|
+
# @param [Object] default
|
71
|
+
# @param [true|false] writable
|
72
|
+
# @return [nil]
|
73
|
+
def self.setting(symbol, default = nil, writable = true)
|
74
|
+
ivar = "@#{symbol}".to_sym
|
75
|
+
define_method symbol do
|
76
|
+
instance_variable_set(ivar, default) unless instance_variables.include?(ivar)
|
77
|
+
instance_variable_get(ivar)
|
78
|
+
end
|
79
|
+
(attr_writer(symbol) if writable) && nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,44 @@
|
|
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
|
+
require_relative 'base.rb'
|
20
|
+
|
21
|
+
module Racket
|
22
|
+
module Settings
|
23
|
+
# Class for storing controller settings.
|
24
|
+
# This settings class will lookup settings further up in the inheritance chain and will use
|
25
|
+
# the application settings as a final fallback.
|
26
|
+
class Controller < Base
|
27
|
+
def initialize(owner, defaults = {})
|
28
|
+
super(defaults)
|
29
|
+
@owner = owner
|
30
|
+
end
|
31
|
+
|
32
|
+
# Fetches settings from the current object. If the setting cannot be found in the Current
|
33
|
+
# object, the controller superklass will be queried. If all controller classes in the
|
34
|
+
# inheritance chain has been queried, the Application settings will be used as a final
|
35
|
+
# fallback.
|
36
|
+
def fetch(key, default = nil)
|
37
|
+
return @custom[key] if @custom.key?(key)
|
38
|
+
return ::Racket::Application.settings.fetch(key, default) if
|
39
|
+
@owner.superclass == ::Racket::Controller
|
40
|
+
@owner.superclass.settings.fetch(key, default)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/racket/utils.rb
CHANGED
@@ -63,7 +63,7 @@ module Racket
|
|
63
63
|
else
|
64
64
|
args.map!(&:to_s)
|
65
65
|
path = Pathname.new(args.shift)
|
66
|
-
path = Pathname.new(Application.
|
66
|
+
path = Pathname.new(Application.settings.root_dir).join(path) if path.relative?
|
67
67
|
args.each do |arg|
|
68
68
|
path_part = Pathname.new(arg)
|
69
69
|
next unless path_part.relative?
|
data/lib/racket/version.rb
CHANGED
data/lib/racket/view_manager.rb
CHANGED
@@ -38,26 +38,15 @@ module Racket
|
|
38
38
|
# @return [Hash]
|
39
39
|
def render(controller)
|
40
40
|
template_path = get_template_path(controller)
|
41
|
-
view =
|
42
|
-
|
43
|
-
|
44
|
-
layout = get_layout(template_path, controller)
|
45
|
-
output = Tilt.new(layout).render(controller) { output } if layout
|
46
|
-
else
|
47
|
-
output = controller.racket.action_result
|
48
|
-
end
|
41
|
+
view = get_template(template_path, controller, :view)
|
42
|
+
layout = view ? get_template(template_path, controller, :layout) : nil
|
43
|
+
output = view ? render_template(controller, view, layout) : controller.racket.action_result
|
49
44
|
controller.response.write(output)
|
50
45
|
controller.response.finish
|
51
46
|
end
|
52
47
|
|
53
48
|
private
|
54
49
|
|
55
|
-
def get_template_path(controller)
|
56
|
-
template_path = [Application.get_route(controller.class), controller.racket.action].join('/')
|
57
|
-
template_path = template_path[1..-1] if template_path.start_with?('//')
|
58
|
-
template_path
|
59
|
-
end
|
60
|
-
|
61
50
|
# Calls a template proc. Depending on how many parameters the template proc takes, different
|
62
51
|
# types of information will be passed to the proc.
|
63
52
|
# If the proc takes zero parameters, no information will be passed.
|
@@ -82,56 +71,40 @@ module Racket
|
|
82
71
|
# @param [String] path
|
83
72
|
# @param [Racket::Controller] controller
|
84
73
|
# @param [Symbol] type
|
74
|
+
# @return [String|Proc|nil]
|
85
75
|
def ensure_in_cache(path, controller, type)
|
86
76
|
store = instance_variable_get("@#{type}_cache".to_sym)
|
87
77
|
return store[path] if store.key?(path)
|
88
|
-
|
89
|
-
default_template = controller.controller_option("default_#{type}".to_sym)
|
90
|
-
template = lookup_template(base_dir, path)
|
91
|
-
template =
|
92
|
-
lookup_default_template(base_dir, File.dirname(path), default_template) unless template
|
93
|
-
Application.inform_dev(
|
94
|
-
"Using #{type} #{template.inspect} for #{controller.class}.#{controller.racket.action}."
|
95
|
-
)
|
96
|
-
store[path] = template
|
78
|
+
store_in_cache(store, path, controller, type)
|
97
79
|
end
|
98
80
|
|
99
|
-
# Tries to locate a
|
81
|
+
# Tries to locate a template matching +path+ in the file system and returns the path if a
|
100
82
|
# matching file is found. If no matching file is found, +nil+ is returned. The result is cached,
|
101
83
|
# meaning that the filesystem lookup for a specific path will only happen once.
|
102
84
|
#
|
103
85
|
# @param [String] path
|
104
86
|
# @param [Racket::Controller] controller
|
87
|
+
# @param [Symbol] type
|
105
88
|
# @return [String|nil]
|
106
|
-
def
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
layout
|
89
|
+
def get_template(path, controller, type)
|
90
|
+
template = ensure_in_cache(path, controller, type)
|
91
|
+
# If template is a Proc, call it
|
92
|
+
template =
|
93
|
+
lookup_template(
|
94
|
+
instance_variable_get("@#{type}_base_dir".to_sym),
|
95
|
+
[File.dirname(path), call_template_proc(template, controller)].join('/')
|
96
|
+
) if template.is_a?(Proc)
|
97
|
+
template
|
116
98
|
end
|
117
99
|
|
118
|
-
#
|
119
|
-
# matching file is found. If no matching file is found, +nil+ is returned. The result is cached,
|
120
|
-
# meaning that the filesystem lookup for a specific path will only happen once.
|
100
|
+
# Returns the "url path" that should be used when searching for templates.
|
121
101
|
#
|
122
|
-
# @param [String] path
|
123
102
|
# @param [Racket::Controller] controller
|
124
|
-
# @return [String
|
125
|
-
def
|
126
|
-
|
127
|
-
if
|
128
|
-
|
129
|
-
lookup_template(
|
130
|
-
@view_base_dir,
|
131
|
-
[File.dirname(path), call_template_proc(view, controller)].join('/')
|
132
|
-
)
|
133
|
-
end
|
134
|
-
view
|
103
|
+
# @return [String]
|
104
|
+
def get_template_path(controller)
|
105
|
+
template_path = [Application.get_route(controller.class), controller.racket.action].join('/')
|
106
|
+
template_path = template_path[1..-1] if template_path.start_with?('//')
|
107
|
+
template_path
|
135
108
|
end
|
136
109
|
|
137
110
|
def lookup_default_template(base_path, path, default)
|
@@ -159,5 +132,36 @@ module Racket
|
|
159
132
|
Utils.file_readable?(final_path) ? final_path : nil
|
160
133
|
end
|
161
134
|
end
|
135
|
+
|
136
|
+
# Renders a template/layout combo using Tilt and returns it as a string.
|
137
|
+
#
|
138
|
+
# @param [Racket::Controller] controller
|
139
|
+
# @param [String] view
|
140
|
+
# @param [String|nil] layout
|
141
|
+
# @return [String]
|
142
|
+
def render_template(controller, view, layout)
|
143
|
+
output = Tilt.new(view).render(controller)
|
144
|
+
output = Tilt.new(layout).render(controller) { output } if layout
|
145
|
+
output
|
146
|
+
end
|
147
|
+
|
148
|
+
# Stores the location of a template (not its contents) in the cache.
|
149
|
+
#
|
150
|
+
# @param [Object] store Where to store the location
|
151
|
+
# @param [String] path
|
152
|
+
# @param [Racket::Controller] controller
|
153
|
+
# @param [Symbol] type
|
154
|
+
# @return [String|Proc|nil]
|
155
|
+
def store_in_cache(store, path, controller, type)
|
156
|
+
base_dir = instance_variable_get("@#{type}_base_dir".to_sym)
|
157
|
+
default_template = controller.settings.fetch("default_#{type}".to_sym)
|
158
|
+
template = lookup_template(base_dir, path)
|
159
|
+
template =
|
160
|
+
lookup_default_template(base_dir, File.dirname(path), default_template) unless template
|
161
|
+
Application.inform_dev(
|
162
|
+
"Using #{type} #{template.inspect} for #{controller.class}.#{controller.racket.action}."
|
163
|
+
)
|
164
|
+
store[path] = template
|
165
|
+
end
|
162
166
|
end
|
163
167
|
end
|
data/spec/_custom.rb
CHANGED
@@ -2,17 +2,23 @@ describe 'A custom Racket test Application' do
|
|
2
2
|
extend Rack::Test::Methods
|
3
3
|
def app
|
4
4
|
@app ||= Racket::Application.using(
|
5
|
-
{
|
5
|
+
{
|
6
|
+
default_layout: 'zebra.*',
|
7
|
+
logger: nil,
|
8
|
+
my_custom_secret: 42,
|
9
|
+
mode: :dev,
|
10
|
+
view_dir: 'templates'
|
11
|
+
},
|
6
12
|
true
|
7
13
|
)
|
8
14
|
end
|
9
15
|
|
10
|
-
it 'should set requested
|
11
|
-
app.
|
12
|
-
app.
|
16
|
+
it 'should set requested settings' do
|
17
|
+
app.settings.default_layout.should.equal('zebra.*')
|
18
|
+
app.settings.view_dir.should.equal(Racket::Utils.build_path('templates'))
|
13
19
|
end
|
14
20
|
|
15
|
-
it 'should be able to get/set
|
21
|
+
it 'should be able to get/set settings on controller' do
|
16
22
|
get '/sub3/a_secret_place'
|
17
23
|
last_response.status.should.equal(302)
|
18
24
|
last_response.headers['Location'].should.equal('/sub3/a_secret_place/42')
|
@@ -118,4 +124,20 @@ describe 'A custom Racket test Application' do
|
|
118
124
|
last_response.status.should.equal(200)
|
119
125
|
last_response.body.should.equal("LAYOUT: default: BAZ\n\n")
|
120
126
|
end
|
127
|
+
|
128
|
+
it 'should handle changes to global settings' do
|
129
|
+
app.settings.fetch(:my_custom_secret).should.equal(42)
|
130
|
+
app.settings.store(:my_custom_secret, '9Lazy9')
|
131
|
+
app.settings.fetch(:my_custom_secret).should.equal('9Lazy9')
|
132
|
+
app.settings.delete(:my_custom_secret)
|
133
|
+
app.settings.fetch(:my_custom_secret).should.equal(nil)
|
134
|
+
app.settings.default_content_type.should.equal('text/html')
|
135
|
+
app.settings.fetch(:default_content_type).should.equal('text/html')
|
136
|
+
app.settings.default_content_type = 'text/plain'
|
137
|
+
app.settings.default_content_type.should.equal('text/plain')
|
138
|
+
app.settings.fetch(:default_content_type).should.equal('text/plain')
|
139
|
+
app.settings.default_content_type = 'text/html'
|
140
|
+
app.settings.default_content_type.should.equal('text/html')
|
141
|
+
app.settings.fetch(:default_content_type).should.equal('text/html')
|
142
|
+
end
|
121
143
|
end
|
data/spec/_default.rb
CHANGED
@@ -101,27 +101,27 @@ describe 'A default Racket test Application' do
|
|
101
101
|
end
|
102
102
|
|
103
103
|
it 'should be able to log messages to everybody' do
|
104
|
-
original_logger = app.
|
104
|
+
original_logger = app.settings.logger
|
105
105
|
sio = StringIO.new
|
106
|
-
app.
|
106
|
+
app.settings.logger = Logger.new(sio)
|
107
107
|
app.inform_all('Informational message')
|
108
108
|
sio.string.should.match(/Informational message/)
|
109
|
-
app.
|
109
|
+
app.settings.logger = original_logger
|
110
110
|
end
|
111
111
|
|
112
112
|
it 'should be able to log messages to developer' do
|
113
|
-
original_logger = app.
|
114
|
-
original_mode = app.
|
113
|
+
original_logger = app.settings.logger
|
114
|
+
original_mode = app.settings.mode
|
115
115
|
sio = StringIO.new
|
116
|
-
app.
|
117
|
-
app.
|
116
|
+
app.settings.logger = Logger.new(sio)
|
117
|
+
app.settings.mode = :live
|
118
118
|
app.inform_dev('Development message')
|
119
119
|
sio.string.should.be.empty
|
120
|
-
app.
|
120
|
+
app.settings.mode = :dev
|
121
121
|
app.inform_dev('Hey, listen up!')
|
122
122
|
sio.string.should.match(/Hey, listen up!/)
|
123
|
-
app.
|
124
|
-
app.
|
123
|
+
app.settings.mode = original_mode
|
124
|
+
app.settings.logger = original_logger
|
125
125
|
end
|
126
126
|
|
127
127
|
it 'should be able to set and clear session variables' do
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# Custom sub controller 3
|
2
2
|
class CustomSubController3 < Racket::Controller
|
3
|
-
|
3
|
+
setting(:top_secret, 42)
|
4
4
|
|
5
5
|
def index
|
6
6
|
"#{self.class}::#{__method__}"
|
7
7
|
end
|
8
8
|
|
9
9
|
def a_secret_place
|
10
|
-
redirect(rs(__method__,
|
10
|
+
redirect(rs(__method__, settings.fetch(:top_secret)))
|
11
11
|
end
|
12
12
|
|
13
13
|
def not_so_secret
|
@@ -1,15 +1,15 @@
|
|
1
1
|
# Custom sub controller 4
|
2
2
|
class CustomSubController4 < Racket::Controller
|
3
|
-
|
3
|
+
setting :default_layout, -> { 'layout.erb' }
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
setting :default_view,
|
6
|
+
lambda { |action|
|
7
|
+
case action
|
8
|
+
when :foo then 'myfoo.erb'
|
9
|
+
when :bar then 'mybar.erb'
|
10
|
+
else 'default.erb'
|
11
|
+
end
|
12
|
+
}
|
13
13
|
|
14
14
|
def foo
|
15
15
|
@data = 'FOO'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: racket-mvc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lars Olsson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http_router
|
@@ -156,6 +156,9 @@ files:
|
|
156
156
|
- lib/racket/response.rb
|
157
157
|
- lib/racket/router.rb
|
158
158
|
- lib/racket/session.rb
|
159
|
+
- lib/racket/settings/application.rb
|
160
|
+
- lib/racket/settings/base.rb
|
161
|
+
- lib/racket/settings/controller.rb
|
159
162
|
- lib/racket/utils.rb
|
160
163
|
- lib/racket/version.rb
|
161
164
|
- lib/racket/view_manager.rb
|