racket-mvc 0.0.3
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 +7 -0
- data/COPYING.AGPL +661 -0
- data/README.md +40 -0
- data/Rakefile +13 -0
- data/lib/racket.rb +53 -0
- data/lib/racket/application.rb +253 -0
- data/lib/racket/controller.rb +148 -0
- data/lib/racket/current.rb +49 -0
- data/lib/racket/request.rb +32 -0
- data/lib/racket/response.rb +25 -0
- data/lib/racket/router.rb +114 -0
- data/lib/racket/session.rb +37 -0
- data/lib/racket/utils.rb +25 -0
- data/lib/racket/version.rb +33 -0
- data/lib/racket/view_cache.rb +110 -0
- data/spec/_custom.rb +48 -0
- data/spec/_default.rb +175 -0
- data/spec/racket.rb +24 -0
- data/spec/test_custom_app/controllers/sub1/custom_sub_controller_1.rb +15 -0
- data/spec/test_custom_app/controllers/sub2/custom_sub_controller_2.rb +32 -0
- data/spec/test_custom_app/controllers/sub3/custom_sub_controller_3.rb +13 -0
- data/spec/test_custom_app/controllers/sub3/inherited/custom_inherited_controller.rb +9 -0
- data/spec/test_custom_app/extra/blob.rb +1 -0
- data/spec/test_custom_app/extra/blob/inner_blob.rb +1 -0
- data/spec/test_custom_app/layouts/sub2/zebra.erb +7 -0
- data/spec/test_custom_app/templates/sub2/template.erb +1 -0
- data/spec/test_default_app/controllers/default_root_controller.rb +32 -0
- data/spec/test_default_app/controllers/sub1/default_sub_controller_1.rb +15 -0
- data/spec/test_default_app/controllers/sub2/default_sub_controller_2.rb +15 -0
- data/spec/test_default_app/controllers/sub3/default_sub_controller_3.rb +7 -0
- data/spec/test_default_app/controllers/sub3/inherited/default_inherited_controller.rb +9 -0
- metadata +200 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Racket - The noisy Rack MVC framework
|
|
3
|
+
Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
|
|
4
|
+
|
|
5
|
+
This file is part of Racket.
|
|
6
|
+
|
|
7
|
+
Racket is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
Racket is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU Affero General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU Affero General Public License
|
|
18
|
+
along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
|
19
|
+
=end
|
|
20
|
+
|
|
21
|
+
module Racket
|
|
22
|
+
# Represents the current state of Racket while processing a request. The state gets mixed into
|
|
23
|
+
# the controller instance at the start of the request, making it easy to keep track on everything
|
|
24
|
+
# from within the controller instance.
|
|
25
|
+
class Current
|
|
26
|
+
# Holds Racket internal state, available to the controller instance but mostly used for keeping
|
|
27
|
+
# track of things that don't belong to the actual request.
|
|
28
|
+
State = Struct.new(:action, :action_result, :params, :redirected)
|
|
29
|
+
|
|
30
|
+
# Called whenever a new request needs to be processed.
|
|
31
|
+
#
|
|
32
|
+
# @param [Hash] env Rack environment
|
|
33
|
+
# @param [Symbol] action Keeps track of which action was called on the controller
|
|
34
|
+
# @param [Array] params Parameters sent to the action
|
|
35
|
+
# @return [Module] A module encapsulating all state relating to the current request
|
|
36
|
+
def self.init(env, action, params)
|
|
37
|
+
racket = State.new(action, nil, params, false)
|
|
38
|
+
request = Request.new(env)
|
|
39
|
+
response = Response.new
|
|
40
|
+
session = Session.new(env['rack.session']) if env.key?('rack.session')
|
|
41
|
+
Module.new do
|
|
42
|
+
define_method(:racket) { racket }
|
|
43
|
+
define_method(:request) { request }
|
|
44
|
+
define_method(:response) { response }
|
|
45
|
+
define_method(:session) { session } if env.key?('rack.session')
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Racket - The noisy Rack MVC framework
|
|
3
|
+
Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
|
|
4
|
+
|
|
5
|
+
This file is part of Racket.
|
|
6
|
+
|
|
7
|
+
Racket is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
Racket is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU Affero General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU Affero General Public License
|
|
18
|
+
along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
|
19
|
+
=end
|
|
20
|
+
|
|
21
|
+
module Racket
|
|
22
|
+
# Represents an incoming request. Mostly matches Rack::Request but removes some methods that
|
|
23
|
+
# don't fit with racket.
|
|
24
|
+
class Request < Rack::Request
|
|
25
|
+
# Force explicit use of request.GET and request.POST
|
|
26
|
+
# For racket params, use racket.params
|
|
27
|
+
undef_method :params
|
|
28
|
+
|
|
29
|
+
# Unless sessions are loaded explicitly, session methods should not be available
|
|
30
|
+
undef_method :session, :session_options
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Racket - The noisy Rack MVC framework
|
|
3
|
+
Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
|
|
4
|
+
|
|
5
|
+
This file is part of Racket.
|
|
6
|
+
|
|
7
|
+
Racket is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
Racket is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU Affero General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU Affero General Public License
|
|
18
|
+
along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
|
19
|
+
=end
|
|
20
|
+
|
|
21
|
+
module Racket
|
|
22
|
+
# Represents a response from the application
|
|
23
|
+
class Response < Rack::Response
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Racket - The noisy Rack MVC framework
|
|
3
|
+
Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
|
|
4
|
+
|
|
5
|
+
This file is part of Racket.
|
|
6
|
+
|
|
7
|
+
Racket is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
Racket is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU Affero General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU Affero General Public License
|
|
18
|
+
along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
|
19
|
+
=end
|
|
20
|
+
|
|
21
|
+
require 'http_router'
|
|
22
|
+
|
|
23
|
+
module Racket
|
|
24
|
+
# Handles routing in Racket applications.
|
|
25
|
+
class Router
|
|
26
|
+
def initialize
|
|
27
|
+
@router = HttpRouter.new
|
|
28
|
+
@routes_by_controller = {}
|
|
29
|
+
@actions_by_controller = {}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Caches available actions for each controller class. This also works for controller classes
|
|
33
|
+
# that inherit from other controller classes.
|
|
34
|
+
#
|
|
35
|
+
# @param [Class] controller
|
|
36
|
+
# @return [nil]
|
|
37
|
+
def cache_actions(controller)
|
|
38
|
+
actions = Set.new
|
|
39
|
+
current = controller
|
|
40
|
+
while current < Controller
|
|
41
|
+
actions.merge(current.instance_methods(false))
|
|
42
|
+
current = current.superclass
|
|
43
|
+
end
|
|
44
|
+
@actions_by_controller[controller] = actions.to_a
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Returns a route to the specified controller/action/parameter combination.
|
|
49
|
+
#
|
|
50
|
+
# @param [Class] controller
|
|
51
|
+
# @param [Symbol] action
|
|
52
|
+
# @param [Array] params
|
|
53
|
+
# @return [String]
|
|
54
|
+
def get_route(controller, action, params)
|
|
55
|
+
route = ''
|
|
56
|
+
route << @routes_by_controller[controller] if @routes_by_controller.key?(controller)
|
|
57
|
+
action = action.to_s
|
|
58
|
+
route << "/#{action}" unless action.empty?
|
|
59
|
+
route << "/#{params.join('/')}" unless params.empty?
|
|
60
|
+
route = route[1..-1] if route.start_with?('//') # Special case for root path
|
|
61
|
+
route
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Maps a controller to the specified path.
|
|
65
|
+
#
|
|
66
|
+
# @param [String] path
|
|
67
|
+
# @param [Class] controller
|
|
68
|
+
# @return [nil]
|
|
69
|
+
def map(path, controller)
|
|
70
|
+
controller_base_path = path.empty? ? '/' : path
|
|
71
|
+
Application.inform_dev("Mapping #{controller} to #{controller_base_path}.")
|
|
72
|
+
@router.add("#{path}(/*params)").to(controller)
|
|
73
|
+
@routes_by_controller[controller] = controller_base_path
|
|
74
|
+
cache_actions(controller)
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @todo: Allow the user to set custom handlers for different errors
|
|
79
|
+
def render_404(message = '404 Not found')
|
|
80
|
+
[404, { 'Content-Type' => 'text/plain' }, message]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Routes a request and renders it.
|
|
84
|
+
#
|
|
85
|
+
# @param [Hash] env Rack environment
|
|
86
|
+
# @return [Array] A Rack response triplet
|
|
87
|
+
def route(env)
|
|
88
|
+
# Find controller in map
|
|
89
|
+
# If controller exists, call it
|
|
90
|
+
# Otherwise, send a 404
|
|
91
|
+
matching_routes = @router.recognize(env)
|
|
92
|
+
unless matching_routes.first.nil?
|
|
93
|
+
target_klass = matching_routes.first.first.route.dest
|
|
94
|
+
params = matching_routes.first.first.param_values.first.reject { |e| e.empty? }
|
|
95
|
+
action = params.empty? ? target_klass.get_option(:default_action) : params.shift.to_sym
|
|
96
|
+
|
|
97
|
+
# Check if action is available on target
|
|
98
|
+
return render_404 unless @actions_by_controller[target_klass].include?(action)
|
|
99
|
+
|
|
100
|
+
# Initialize target
|
|
101
|
+
target = target_klass.new
|
|
102
|
+
# @fixme: File.dirname should not be used on urls!
|
|
103
|
+
1.upto(params.count) do
|
|
104
|
+
env['PATH_INFO'] = File.dirname(env['PATH_INFO'])
|
|
105
|
+
end
|
|
106
|
+
target.extend(Current.init(env, action, params))
|
|
107
|
+
target.render(action)
|
|
108
|
+
else
|
|
109
|
+
render_404
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Racket - The noisy Rack MVC framework
|
|
3
|
+
Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
|
|
4
|
+
|
|
5
|
+
This file is part of Racket.
|
|
6
|
+
|
|
7
|
+
Racket is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
Racket is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU Affero General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU Affero General Public License
|
|
18
|
+
along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
|
19
|
+
=end
|
|
20
|
+
|
|
21
|
+
module Racket
|
|
22
|
+
# Racket::Session is just a thin wrapper around whetever object that is implementing the session
|
|
23
|
+
# storage. By default this is an instance of Rack::Session::Abstract::SessionHash, but
|
|
24
|
+
# Racket::Session will happily wrap anything found in the rack environment.
|
|
25
|
+
#
|
|
26
|
+
# To provide your own session handler and have it wrapped by Racket::Session, just add your
|
|
27
|
+
# session handler as a middleware and make sure it writes the current session to the key
|
|
28
|
+
# rack.session in the rack environment.
|
|
29
|
+
class Session < SimpleDelegator
|
|
30
|
+
# Look the same regardless of what the underlying implementation is.
|
|
31
|
+
def inspect
|
|
32
|
+
"#<#{self.class}:#{object_id}>"
|
|
33
|
+
end
|
|
34
|
+
alias :to_s inspect
|
|
35
|
+
alias :to_str inspect
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/racket/utils.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Racket
|
|
2
|
+
# Collects utilities needed by different objects in Racket.
|
|
3
|
+
class Utils
|
|
4
|
+
# Builds and returns a path in the file system from the provided arguments. The first element
|
|
5
|
+
# in the argument list can be either absolute or relative, all other arguments must be relative,
|
|
6
|
+
# otherwise they will be removed from the final path.
|
|
7
|
+
#
|
|
8
|
+
# @param [Array] args
|
|
9
|
+
# @return [String]
|
|
10
|
+
def self.build_path(*args)
|
|
11
|
+
if (args.empty?)
|
|
12
|
+
path = Pathname.pwd
|
|
13
|
+
else
|
|
14
|
+
path = Pathname.new(args.shift)
|
|
15
|
+
path = Pathname.new(Application.options[:root_dir]).join(path) if path.relative?
|
|
16
|
+
args.each do |arg|
|
|
17
|
+
path_part = Pathname.new(arg)
|
|
18
|
+
next unless path_part.relative?
|
|
19
|
+
path = path.join(path_part)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
path.cleanpath.expand_path.to_s
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Racket - The noisy Rack MVC framework
|
|
3
|
+
Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
|
|
4
|
+
|
|
5
|
+
This file is part of Racket.
|
|
6
|
+
|
|
7
|
+
Racket is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
Racket is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU Affero General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU Affero General Public License
|
|
18
|
+
along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
|
19
|
+
=end
|
|
20
|
+
|
|
21
|
+
module Racket
|
|
22
|
+
module Version
|
|
23
|
+
MAJOR = 0
|
|
24
|
+
MINOR = 0
|
|
25
|
+
TEENY = 3
|
|
26
|
+
|
|
27
|
+
def current
|
|
28
|
+
[MAJOR, MINOR, TEENY].join('.')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module_function :current
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
=begin
|
|
2
|
+
Racket - The noisy Rack MVC framework
|
|
3
|
+
Copyright (C) 2015 Lars Olsson <lasso@lassoweb.se>
|
|
4
|
+
|
|
5
|
+
This file is part of Racket.
|
|
6
|
+
|
|
7
|
+
Racket is free software: you can redistribute it and/or modify
|
|
8
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
9
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
10
|
+
(at your option) any later version.
|
|
11
|
+
|
|
12
|
+
Racket is distributed in the hope that it will be useful,
|
|
13
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15
|
+
GNU Affero General Public License for more details.
|
|
16
|
+
|
|
17
|
+
You should have received a copy of the GNU Affero General Public License
|
|
18
|
+
along with Racket. If not, see <http://www.gnu.org/licenses/>.
|
|
19
|
+
=end
|
|
20
|
+
|
|
21
|
+
require 'tilt'
|
|
22
|
+
|
|
23
|
+
module Racket
|
|
24
|
+
# Handles rendering in Racket applications.
|
|
25
|
+
class ViewCache
|
|
26
|
+
|
|
27
|
+
def initialize(layout_base_dir, template_base_dir)
|
|
28
|
+
@layout_base_dir = layout_base_dir
|
|
29
|
+
@template_base_dir = template_base_dir
|
|
30
|
+
@layouts_by_path = {}
|
|
31
|
+
@templates_by_path = {}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Renders a controller based on the request path and the variables set in the
|
|
35
|
+
# controller instance.
|
|
36
|
+
#
|
|
37
|
+
# @param [Controller] controller
|
|
38
|
+
# @return [Hash]
|
|
39
|
+
def render(controller)
|
|
40
|
+
unless controller.racket.redirected
|
|
41
|
+
template =
|
|
42
|
+
find_template(controller.request.path, controller.controller_option(:default_view))
|
|
43
|
+
if template
|
|
44
|
+
output = Tilt.new(template).render(controller)
|
|
45
|
+
layout =
|
|
46
|
+
find_layout(controller.request.path, controller.controller_option(:default_layout))
|
|
47
|
+
output = Tilt.new(layout).render(controller) { output } if layout
|
|
48
|
+
else
|
|
49
|
+
output = controller.racket.action_result
|
|
50
|
+
end
|
|
51
|
+
controller.response.write(output)
|
|
52
|
+
end
|
|
53
|
+
controller.response.finish
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
# Tries to locate a layout matching +url_path+ in the file system and returns the path if a
|
|
59
|
+
# matching file is found. If no matching file is found, +nil+ is returned. The result is
|
|
60
|
+
# cached, meaning that the filesystem lookup for a specific url_path will only happen once.
|
|
61
|
+
#
|
|
62
|
+
# @param [String] url_path
|
|
63
|
+
# @param [String|nil] default_layout
|
|
64
|
+
# @return [String|nil]
|
|
65
|
+
def find_layout(url_path, default_layout)
|
|
66
|
+
return @layouts_by_path[url_path] if @layouts_by_path.key?(url_path)
|
|
67
|
+
@layouts_by_path[url_path] = find_matching_file(@layout_base_dir, url_path, default_layout)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Tries to locate a template matching +url_path+ in the file system and returns the path if a
|
|
71
|
+
# matching file is found. If no matching file is found, +nil+ is returned. The result is
|
|
72
|
+
# cached, meaning that the filesystem lookup for a specific url_path will only happen once.
|
|
73
|
+
#
|
|
74
|
+
# @param [String] url_path
|
|
75
|
+
# @param [String|nil] default_view
|
|
76
|
+
# @return [String|nil]
|
|
77
|
+
def find_template(url_path, default_view)
|
|
78
|
+
return @templates_by_path[url_path] if @templates_by_path.key?(url_path)
|
|
79
|
+
@templates_by_path[url_path] = find_matching_file(@template_base_dir, url_path, default_view)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Locates a file in the filesystem matching an URL path. If there exists a matching file, the
|
|
83
|
+
# path to it is returned. If there is no matching file, +nil+ is returned.
|
|
84
|
+
#
|
|
85
|
+
# @param [String] base_file_path
|
|
86
|
+
# @param [String] url_path
|
|
87
|
+
# @param [String|nil] default_file
|
|
88
|
+
# @return [String|nil]
|
|
89
|
+
def find_matching_file(base_file_path, url_path, default_file)
|
|
90
|
+
file_path = File.join(base_file_path, url_path)
|
|
91
|
+
action = File.basename(file_path)
|
|
92
|
+
file_path = File.dirname(file_path)
|
|
93
|
+
return nil unless File.exists?(file_path) && File.directory?(file_path)
|
|
94
|
+
Dir.chdir(file_path) do
|
|
95
|
+
files = Pathname.glob("#{action}.*")
|
|
96
|
+
if files.empty?
|
|
97
|
+
if default_file
|
|
98
|
+
files = Pathname.glob(default_file)
|
|
99
|
+
return nil if files.empty? # No default file found
|
|
100
|
+
return File.join(file_path, files.first.to_s)
|
|
101
|
+
end
|
|
102
|
+
return nil # Neither default file or specified file found
|
|
103
|
+
end
|
|
104
|
+
File.join(file_path, files.first.to_s)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
end
|
data/spec/_custom.rb
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
describe 'The custom Racket test Application' do
|
|
2
|
+
extend Rack::Test::Methods
|
|
3
|
+
def app
|
|
4
|
+
@app ||= Racket::Application.using(default_layout: 'zebra.*', view_dir: 'templates')
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
it 'should set requested options' do
|
|
8
|
+
app.options[:default_layout].should.equal('zebra.*')
|
|
9
|
+
app.options[:view_dir].should.equal('templates')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'should be able to get/set options on controller' do
|
|
13
|
+
get '/sub3/a_secret_place'
|
|
14
|
+
last_response.status.should.equal(302)
|
|
15
|
+
last_response.headers['Location'].should.equal('/sub3/a_secret_place/42')
|
|
16
|
+
last_response.body.length.should.equal(0)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'should return a 404 on a nonexisting url' do
|
|
20
|
+
get '/nosuchurl'
|
|
21
|
+
last_response.status.should.equal(404)
|
|
22
|
+
last_response.body.should.equal('404 Not found')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'should be able to render a template and a layout' do
|
|
26
|
+
get '/sub2/template'
|
|
27
|
+
last_response.status.should.equal(200)
|
|
28
|
+
last_response.body.should.match(/Message from template/)
|
|
29
|
+
last_response.body.should.match(/A groovy layout/)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'should be able to require custom files' do
|
|
33
|
+
Module.constants.should.not.include(:Blob)
|
|
34
|
+
Racket.require 'extra/blob'
|
|
35
|
+
Module.constants.should.include(:Blob)
|
|
36
|
+
Module.constants.should.not.include(:InnerBlob)
|
|
37
|
+
Racket.require 'extra', 'blob', 'inner_blob'
|
|
38
|
+
Module.constants.should.include(:InnerBlob)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it 'should be able to use before/after hooks' do
|
|
42
|
+
get '/sub2/hook_action'
|
|
43
|
+
last_response.headers.key?('X-Hook-Action').should.equal(true)
|
|
44
|
+
last_response.headers['X-Hook-Action'].should.equal('run')
|
|
45
|
+
response = JSON.parse(last_response.body)
|
|
46
|
+
response.should.equal(["Data added in before block", "Data added in action"])
|
|
47
|
+
end
|
|
48
|
+
end
|