racket-mvc 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +7 -5
- data/lib/racket.rb +25 -7
- data/lib/racket/application.rb +70 -136
- data/lib/racket/controller.rb +95 -38
- data/lib/racket/current.rb +1 -1
- data/lib/racket/helpers/file.rb +7 -5
- data/lib/racket/helpers/routing.rb +5 -5
- data/lib/racket/helpers/sass.rb +11 -11
- data/lib/racket/helpers/view.rb +8 -5
- data/lib/racket/plugins/base.rb +1 -1
- data/lib/racket/plugins/sass.rb +1 -1
- data/lib/racket/request.rb +3 -3
- data/lib/racket/response.rb +1 -1
- data/lib/racket/router.rb +31 -12
- data/lib/racket/session.rb +3 -3
- data/lib/racket/settings/application.rb +27 -33
- data/lib/racket/settings/base.rb +19 -8
- data/lib/racket/settings/controller.rb +9 -7
- data/lib/racket/settings/defaults.rb +81 -0
- data/lib/racket/utils.rb +19 -15
- data/lib/racket/utils/application.rb +4 -114
- data/lib/racket/utils/application/handler_stack.rb +163 -0
- data/lib/racket/utils/application/logger.rb +72 -0
- data/lib/racket/utils/application/registry_builder.rb +88 -0
- data/lib/racket/utils/application/stateless_services.rb +73 -0
- data/lib/racket/utils/exceptions.rb +3 -3
- data/lib/racket/utils/file_system.rb +75 -47
- data/lib/racket/utils/helpers.rb +35 -13
- data/lib/racket/utils/routing.rb +62 -46
- data/lib/racket/utils/views.rb +19 -187
- data/lib/racket/utils/views/renderer.rb +75 -0
- data/lib/racket/utils/views/template_cache.rb +126 -0
- data/lib/racket/utils/views/template_locator.rb +83 -0
- data/lib/racket/utils/views/template_resolver.rb +112 -0
- data/lib/racket/version.rb +2 -2
- data/lib/racket/view_manager.rb +12 -4
- data/rake/utils.rb +5 -5
- data/spec/_custom.rb +69 -19
- data/spec/_default.rb +60 -44
- data/spec/_plugin.rb +12 -14
- data/spec/_template_cache.rb +176 -0
- data/spec/racket.rb +10 -13
- data/spec/test_custom_app/controllers/sub1/custom_sub_controller_1.rb +1 -1
- data/spec/test_custom_app/controllers/sub3/custom_sub_controller_3.rb +19 -1
- data/spec/test_custom_app/controllers/sub5/custom_sub_controller_5.rb +8 -0
- data/spec/test_custom_app/files/stuff.rb +3 -0
- data/spec/test_custom_app/files/triplet.erb +5 -0
- data/spec/test_custom_app/templates/sub5/text.erb +3 -0
- data/spec/test_default_app/controllers/default_root_controller.rb +3 -0
- data/spec/test_default_app/controllers/sub1/default_sub_controller_1.rb +1 -1
- metadata +52 -11
@@ -0,0 +1,72 @@
|
|
1
|
+
# Racket - The noisy Rack MVC framework
|
2
|
+
# Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
|
3
|
+
#
|
4
|
+
# This file is part of Racket.
|
5
|
+
#
|
6
|
+
# Racket is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Racket is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
module Racket
|
20
|
+
module Utils
|
21
|
+
# Namespace for application utilities
|
22
|
+
module Application
|
23
|
+
# Class for logging messages in the application.
|
24
|
+
class Logger
|
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
|
+
settings = reg.application_settings
|
32
|
+
new(settings.logger, settings.mode)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(logger, mode)
|
37
|
+
@logger = logger
|
38
|
+
@in_dev_mode = (mode == :dev)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sends a message to the logger.
|
42
|
+
#
|
43
|
+
# @param [String] message
|
44
|
+
# @param [Symbol] level
|
45
|
+
# @return nil
|
46
|
+
def inform_all(message, level = :info)
|
47
|
+
inform(message, level)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Sends a message to the logger, but only if we are running in dev mode.
|
51
|
+
#
|
52
|
+
# @param [String] message
|
53
|
+
# @param [Symbol] level
|
54
|
+
# @return nil
|
55
|
+
def inform_dev(message, level = :debug)
|
56
|
+
(inform(message, level) if @in_dev_mode) && nil
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# Writes a message to the logger if there is one present.
|
62
|
+
#
|
63
|
+
# @param [String] message
|
64
|
+
# @param [Symbol] level
|
65
|
+
# @return nil
|
66
|
+
def inform(message, level)
|
67
|
+
(@logger.send(level, message) if @logger) && nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# Racket - The noisy Rack MVC framework
|
2
|
+
# Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
|
3
|
+
#
|
4
|
+
# This file is part of Racket.
|
5
|
+
#
|
6
|
+
# Racket is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Racket is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
require 'racket/registry'
|
20
|
+
|
21
|
+
require_relative 'stateless_services.rb'
|
22
|
+
|
23
|
+
module Racket
|
24
|
+
module Utils
|
25
|
+
module Application
|
26
|
+
# Class for easily building a Racket::Registry.
|
27
|
+
class RegistryBuilder
|
28
|
+
attr_reader :registry
|
29
|
+
|
30
|
+
def initialize(settings = {})
|
31
|
+
@settings = settings
|
32
|
+
@registry = Racket::Registry.singleton_map(service_map)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Sets up a static server with the specified directory and logger.
|
36
|
+
#
|
37
|
+
# @param [Pathname|nil] static_dir
|
38
|
+
# @param [Logger] logger
|
39
|
+
# @return [Proc|nil]
|
40
|
+
def self.static_server(static_dir, logger)
|
41
|
+
unless static_dir
|
42
|
+
logger.inform_dev('Static server disabled.')
|
43
|
+
return nil
|
44
|
+
end
|
45
|
+
logger.inform_dev("Setting up static server to serve files from #{static_dir}.")
|
46
|
+
handler = Rack::File.new(static_dir)
|
47
|
+
->(env) { handler.call(env) }
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def controller_context
|
53
|
+
lambda do |reg|
|
54
|
+
Module.new do
|
55
|
+
define_singleton_method(:application_settings) { reg.application_settings }
|
56
|
+
define_singleton_method(:helper_cache) { reg.helper_cache }
|
57
|
+
define_singleton_method(:logger) { reg.application_logger }
|
58
|
+
define_singleton_method(:get_route) do |klass, action, params|
|
59
|
+
reg.router.get_route(klass, action, params)
|
60
|
+
end
|
61
|
+
define_singleton_method(:utils) { reg.utils }
|
62
|
+
define_singleton_method(:view_manager) { reg.view_manager }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def service_map
|
68
|
+
{
|
69
|
+
application_settings: Racket::Settings::Application.service(@settings),
|
70
|
+
controller_context: controller_context,
|
71
|
+
static_server: static_server,
|
72
|
+
utils: Racket::Utils::ToolBelt.service(root_dir: @settings.fetch(:root_dir, Dir.pwd))
|
73
|
+
}.merge!(StatelessServices.services)
|
74
|
+
end
|
75
|
+
|
76
|
+
def static_server
|
77
|
+
klass = self.class
|
78
|
+
lambda do |reg|
|
79
|
+
klass.static_server(
|
80
|
+
Racket::Utils::FileSystem.dir_or_nil(reg.application_settings.public_dir),
|
81
|
+
reg.application_logger
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Racket - The noisy Rack MVC framework
|
2
|
+
# Copyright (C) 2015-2016 Lars Olsson <lasso@lassoweb.se>
|
3
|
+
#
|
4
|
+
# This file is part of Racket.
|
5
|
+
#
|
6
|
+
# Racket is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU Affero General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Racket is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU Affero General Public License
|
17
|
+
# along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
18
|
+
|
19
|
+
module Racket
|
20
|
+
module Utils
|
21
|
+
module Application
|
22
|
+
# Collects stateless services across the Racket
|
23
|
+
module StatelessServices
|
24
|
+
# Returns a list of stateless services (in the form of procs)
|
25
|
+
# that Racket uses. This is merely as conveniance method for getting
|
26
|
+
# all services from one place, the actual services are spread out over
|
27
|
+
# a large number of classes.
|
28
|
+
#
|
29
|
+
# @return [Hash]
|
30
|
+
def self.services
|
31
|
+
basic_services
|
32
|
+
.merge!(layout_services)
|
33
|
+
.merge!(template_services)
|
34
|
+
.merge!(view_services)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.basic_services
|
38
|
+
{
|
39
|
+
action_cache: Racket::Utils::Routing::ActionCache.service,
|
40
|
+
application_logger: Racket::Utils::Application::Logger.service,
|
41
|
+
handler_stack: Racket::Utils::Application::HandlerStack.service,
|
42
|
+
helper_cache: Racket::Utils::Helpers::HelperCache.service,
|
43
|
+
router: Racket::Router.service
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.layout_services
|
48
|
+
{
|
49
|
+
layout_cache: Racket::Utils::Views::TemplateCache.service,
|
50
|
+
layout_resolver: Racket::Utils::Views::TemplateResolver.service(type: :layout)
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.template_services
|
55
|
+
{
|
56
|
+
template_locator: Racket::Utils::Views::TemplateLocator.service,
|
57
|
+
template_renderer: Racket::Utils::Views::Renderer.service
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.view_services
|
62
|
+
{
|
63
|
+
view_cache: Racket::Utils::Views::TemplateCache.service,
|
64
|
+
view_manager: Racket::ViewManager.service,
|
65
|
+
view_resolver: Racket::Utils::Views::TemplateResolver.service(type: :view)
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
private_class_method :basic_services, :layout_services, :template_services, :view_services
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -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
|
#
|
@@ -31,7 +31,7 @@ module Racket
|
|
31
31
|
# @param [Array] errors
|
32
32
|
# @return [true|flase]
|
33
33
|
def self.run_block(errors)
|
34
|
-
|
34
|
+
raise 'Need a block' unless block_given?
|
35
35
|
begin
|
36
36
|
true.tap { yield }
|
37
37
|
rescue boolean_module(errors)
|
@@ -60,7 +60,7 @@ module Racket
|
|
60
60
|
#
|
61
61
|
# @param [Array] errors
|
62
62
|
# @return [true|flase]
|
63
|
-
def
|
63
|
+
def run_block(*errors, &block)
|
64
64
|
ExceptionHandler.run_block(errors, &block)
|
65
65
|
end
|
66
66
|
end
|
@@ -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
|
#
|
@@ -47,15 +47,16 @@ module Racket
|
|
47
47
|
#
|
48
48
|
# @param [Array] args
|
49
49
|
# @return [Pathname]
|
50
|
-
def self.to_pathname(*args)
|
51
|
-
new(args).path
|
50
|
+
def self.to_pathname(root_dir, *args)
|
51
|
+
new(root_dir, args).path
|
52
52
|
end
|
53
53
|
|
54
54
|
attr_reader :path
|
55
55
|
|
56
56
|
private
|
57
57
|
|
58
|
-
def initialize(args)
|
58
|
+
def initialize(root_dir, args)
|
59
|
+
@root_dir = root_dir
|
59
60
|
extract_base_path(args.dup)
|
60
61
|
build_path
|
61
62
|
clean_path
|
@@ -72,30 +73,19 @@ module Racket
|
|
72
73
|
end
|
73
74
|
@args.map!(&:to_s)
|
74
75
|
@path = Pathname.new(@args.shift)
|
75
|
-
@path = Pathname.new(
|
76
|
-
@path.relative?
|
76
|
+
@path = Pathname.new(@root_dir).join(@path) if @path.relative?
|
77
77
|
end
|
78
78
|
|
79
79
|
def build_path
|
80
80
|
@args.each do |arg|
|
81
81
|
path_part = Pathname.new(arg)
|
82
|
-
|
82
|
+
raise ArgumentError, arg unless path_part.relative?
|
83
83
|
@path = @path.join(path_part)
|
84
84
|
end
|
85
85
|
remove_instance_variable :@args
|
86
86
|
end
|
87
87
|
end
|
88
88
|
|
89
|
-
# Builds and returns a path in the file system from the provided arguments. The first element
|
90
|
-
# in the argument list can be either absolute or relative, all other arguments must be
|
91
|
-
# relative, otherwise they will be removed from the final path.
|
92
|
-
#
|
93
|
-
# @param [Array] args
|
94
|
-
# @return [Pathname]
|
95
|
-
def self.build_path(*args)
|
96
|
-
PathBuilder.to_pathname(*args)
|
97
|
-
end
|
98
|
-
|
99
89
|
# Returns whether a directory is readable or not. In order to be readable, the directory must
|
100
90
|
# a) exist
|
101
91
|
# b) be a directory
|
@@ -107,6 +97,16 @@ module Racket
|
|
107
97
|
path.exist? && path.directory? && path.readable?
|
108
98
|
end
|
109
99
|
|
100
|
+
# Return a Pathname for a directory if the directory is readable, otherwise returns nil.
|
101
|
+
#
|
102
|
+
# @param [String] path
|
103
|
+
# @return [Pathname|nil]
|
104
|
+
def self.dir_or_nil(path)
|
105
|
+
return nil unless path
|
106
|
+
path = Pathname.new(path)
|
107
|
+
dir_readable?(path) ? path : nil
|
108
|
+
end
|
109
|
+
|
110
110
|
# Extracts the correct directory and glob for a given base path/path combination.
|
111
111
|
#
|
112
112
|
# @param [Pathname] path
|
@@ -119,17 +119,6 @@ module Racket
|
|
119
119
|
]
|
120
120
|
end
|
121
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
|
131
|
-
end
|
132
|
-
|
133
122
|
# Returns whether a file is readable or not. In order to be readable, the file must
|
134
123
|
# a) exist
|
135
124
|
# b) be a file
|
@@ -144,16 +133,6 @@ module Racket
|
|
144
133
|
path.exist? && path.file? && path.readable?
|
145
134
|
end
|
146
135
|
|
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
136
|
# Returns the first matching path under +base_path+ matching +glob+. If no matching path can
|
158
137
|
# be found, +nil+ is returned.
|
159
138
|
#
|
@@ -165,14 +144,63 @@ module Racket
|
|
165
144
|
paths.empty? ? nil : paths.first
|
166
145
|
end
|
167
146
|
|
168
|
-
#
|
147
|
+
# Given a base pathname and a url path string, returns a pathname.
|
169
148
|
#
|
170
|
-
# @param [
|
171
|
-
# @param [String]
|
172
|
-
# return [
|
173
|
-
def self.
|
174
|
-
|
175
|
-
|
149
|
+
# @param [Pathname] base_pathname
|
150
|
+
# @param [String] url_path
|
151
|
+
# @return [Pathname]
|
152
|
+
def self.fs_path(base_pathname, url_path)
|
153
|
+
parts = url_path.split('/').reject(&:empty?)
|
154
|
+
parts.each { |part| base_pathname = base_pathname.join(part) }
|
155
|
+
base_pathname
|
156
|
+
end
|
157
|
+
|
158
|
+
# Locates a file in the filesystem matching an URL path. If there exists a matching file,
|
159
|
+
# the path to it is returned. If there is no matching file, +nil+ is returned.
|
160
|
+
# @param [Pathname] path
|
161
|
+
# @return [Pathname|nil]
|
162
|
+
def self.resolve_path(path)
|
163
|
+
first_matching_path(*extract_dir_and_glob(path))
|
164
|
+
end
|
165
|
+
|
166
|
+
# Locates a file in the filesystem matching an URL path. If there exists a matching file,
|
167
|
+
# the path to it is returned. If there is no matching file and +default+ is a
|
168
|
+
# String or a Symbol, another lookup will be performed using +default+. If
|
169
|
+
# +default+ is a Proc or nil, +default+ will be used as is instead.
|
170
|
+
#
|
171
|
+
# @param [Pathname] path
|
172
|
+
# @param [String|Symbol|Proc|nil] default
|
173
|
+
# @return [String|Proc|nil]
|
174
|
+
def self.resolve_path_with_default(path, default)
|
175
|
+
# Return template if it can be found in the file system
|
176
|
+
template = resolve_path(path)
|
177
|
+
return template if template
|
178
|
+
# No template found for path. Try the default template instead.
|
179
|
+
# If default template is a string or a symbol, look it up in the file system
|
180
|
+
return resolve_path(fs_path(path.dirname, default)) if
|
181
|
+
default.is_a?(String) || default.is_a?(Symbol)
|
182
|
+
# If default template is a proc or nil, just return it
|
183
|
+
default
|
184
|
+
end
|
185
|
+
|
186
|
+
# Returns all paths under +base_path+ that matches +glob+.
|
187
|
+
#
|
188
|
+
# @param [Pathname] base_path
|
189
|
+
# @param [Pathname] glob
|
190
|
+
# @return [Array]
|
191
|
+
def self.matching_paths(base_path, glob)
|
192
|
+
return [] unless dir_readable?(base_path)
|
193
|
+
Dir.chdir(base_path) { Pathname.glob(glob) }.map { |path| base_path.join(path) }
|
194
|
+
end
|
195
|
+
|
196
|
+
# Builds and returns a path in the file system from the provided arguments. The first element
|
197
|
+
# in the argument list can be either absolute or relative, all other arguments must be
|
198
|
+
# relative, otherwise they will be removed from the final path.
|
199
|
+
#
|
200
|
+
# @param [Array] args
|
201
|
+
# @return [Pathname]
|
202
|
+
def build_path(*args)
|
203
|
+
PathBuilder.to_pathname(@root_dir, *args)
|
176
204
|
end
|
177
205
|
|
178
206
|
# Safely requires a file. This method will catch load errors and return true (if the file
|
@@ -180,8 +208,8 @@ module Racket
|
|
180
208
|
#
|
181
209
|
# @param [String] resource
|
182
210
|
# @return [true|false]
|
183
|
-
def
|
184
|
-
|
211
|
+
def safe_require(resource)
|
212
|
+
run_block(LoadError) { require resource }
|
185
213
|
end
|
186
214
|
end
|
187
215
|
end
|