utopia 1.9.11 → 2.0.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/.codeclimate.yml +3 -2
- data/.gitignore +4 -1
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/.yardopts +2 -0
- data/Gemfile +8 -1
- data/README.md +2 -2
- data/Rakefile +10 -10
- data/benchmarks/call_vs_check.rb +36 -0
- data/benchmarks/const_vs_hash.rb +33 -0
- data/documentation/Gemfile +5 -0
- data/documentation/Guardfile +20 -0
- data/documentation/config.ru +6 -13
- data/documentation/config/puma.rb +20 -0
- data/documentation/pages/_editor.xnode +64 -0
- data/documentation/pages/_heading.xnode +2 -2
- data/documentation/pages/_page.xnode +1 -2
- data/documentation/pages/errors/exception.xnode +3 -3
- data/documentation/pages/errors/file-not-found.xnode +3 -3
- data/documentation/pages/wiki/bower-integration/content.md +1 -1
- data/documentation/pages/wiki/content.md +6 -8
- data/documentation/pages/wiki/controller.rb +3 -3
- data/documentation/pages/wiki/edit.xnode +7 -19
- data/documentation/pages/wiki/middleware/content/content.md +4 -10
- data/documentation/pages/wiki/{controller → middleware/controller}/actions/content.md +0 -0
- data/documentation/pages/wiki/{controller → middleware/controller}/links.yaml +0 -0
- data/documentation/pages/wiki/{controller → middleware/controller}/rewrite/content.md +3 -3
- data/documentation/pages/wiki/show.xnode +4 -6
- data/documentation/pages/wiki/updating-utopia/content.md +55 -0
- data/documentation/pages/wiki/your-first-page/content.md +5 -3
- data/documentation/public/materials +1 -0
- data/lib/utopia.rb +3 -4
- data/lib/utopia/command.rb +4 -284
- data/lib/utopia/command/server.rb +115 -0
- data/lib/utopia/command/setup.rb +78 -0
- data/lib/utopia/command/site.rb +183 -0
- data/lib/utopia/content.rb +83 -59
- data/lib/utopia/content/{transaction.rb → document.rb} +116 -110
- data/lib/utopia/content/link.rb +7 -2
- data/lib/utopia/content/links.rb +2 -1
- data/lib/utopia/content/markup.rb +7 -2
- data/lib/utopia/{tags/deferred.rb → content/namespace.rb} +25 -6
- data/lib/utopia/content/node.rb +74 -76
- data/lib/utopia/content/response.rb +22 -3
- data/lib/utopia/content/tags.rb +66 -0
- data/lib/utopia/controller.rb +10 -18
- data/lib/utopia/controller/actions.rb +10 -0
- data/lib/utopia/controller/base.rb +2 -1
- data/lib/utopia/controller/respond.rb +1 -1
- data/lib/utopia/controller/rewrite.rb +8 -4
- data/lib/utopia/exceptions.rb +1 -0
- data/lib/utopia/exceptions/handler.rb +7 -2
- data/lib/utopia/exceptions/mailer.rb +33 -12
- data/lib/utopia/{tags/node.rb → extensions/array_split.rb} +11 -9
- data/lib/utopia/{tags/environment.rb → extensions/date_comparisons.rb} +24 -14
- data/lib/utopia/http.rb +2 -0
- data/lib/utopia/locale.rb +1 -0
- data/lib/utopia/localization.rb +37 -28
- data/lib/utopia/logger.rb +1 -0
- data/lib/utopia/logger/compact_formatter.rb +1 -0
- data/lib/utopia/middleware.rb +11 -1
- data/lib/utopia/path.rb +1 -0
- data/lib/utopia/path/matcher.rb +14 -2
- data/lib/utopia/redirection.rb +13 -16
- data/lib/utopia/session.rb +14 -6
- data/lib/utopia/setup.rb +3 -1
- data/lib/utopia/static.rb +11 -12
- data/lib/utopia/version.rb +1 -1
- data/setup/server/git/hooks/post-receive +0 -4
- data/setup/site/.gitignore +9 -0
- data/setup/site/.rspec +1 -0
- data/setup/site/Gemfile +4 -0
- data/setup/site/Guardfile +17 -0
- data/setup/site/Rakefile +2 -2
- data/setup/site/config.ru +5 -12
- data/setup/site/pages/_heading.xnode +2 -2
- data/setup/site/pages/_page.xnode +1 -1
- data/setup/site/pages/errors/exception.xnode +3 -3
- data/setup/site/pages/errors/file-not-found.xnode +3 -3
- data/setup/site/pages/welcome/index.xnode +3 -3
- data/setup/site/public/_static/site.css +4 -0
- data/setup/site/spec/spec_helper.rb +29 -0
- data/setup/site/tasks/deploy.rake +13 -0
- data/setup/site/tasks/development.rake +34 -0
- data/setup/site/tasks/environment.rake +17 -0
- data/spec/mock_node.rb +15 -0
- data/spec/spec_helper.rb +29 -0
- data/{lib/utopia/extensions/date.rb → spec/utopia/content/document_spec.rb} +31 -21
- data/spec/utopia/content/markup_spec.rb +2 -2
- data/spec/utopia/content/{tag_spec.rb → namespace_spec.rb} +17 -10
- data/spec/utopia/content/tags_spec.rb +80 -0
- data/spec/utopia/content_spec.rb +1 -1
- data/spec/utopia/content_spec.ru +1 -6
- data/spec/utopia/content_spec/_heading.xnode +1 -1
- data/spec/utopia/content_spec/content/test-partial.xnode +1 -1
- data/spec/utopia/content_spec/index.xnode +1 -1
- data/spec/utopia/controller/middleware_spec.ru +1 -3
- data/spec/utopia/controller/respond_spec.rb +2 -22
- data/spec/utopia/controller/respond_spec.ru +1 -5
- data/spec/utopia/controller/respond_spec/errors/file-not-found.xnode +7 -6
- data/spec/utopia/exceptions/handler_spec.ru +1 -2
- data/spec/utopia/exceptions/mailer_spec.ru +1 -2
- data/spec/utopia/extensions_spec.rb +2 -2
- data/spec/utopia/localization_spec.ru +1 -2
- data/spec/utopia/performance_spec.rb +2 -6
- data/spec/utopia/performance_spec/config.ru +5 -12
- data/spec/utopia/performance_spec/pages/_heading.xnode +2 -2
- data/spec/utopia/performance_spec/pages/_page.xnode +1 -1
- data/spec/utopia/performance_spec/pages/errors/exception.xnode +3 -3
- data/spec/utopia/performance_spec/pages/errors/file-not-found.xnode +3 -3
- data/spec/utopia/performance_spec/pages/welcome/index.xnode +3 -3
- data/spec/utopia/setup_spec.rb +79 -15
- data/utopia.gemspec +3 -3
- metadata +41 -27
- data/.simplecov +0 -9
- data/documentation/pages/welcome/index.xnode +0 -41
- data/lib/utopia/content/tag.rb +0 -90
- data/lib/utopia/extensions/array.rb +0 -29
- data/lib/utopia/tags/override.rb +0 -33
- data/setup/site/.simplecov +0 -9
- data/setup/site/tasks/test.rake +0 -10
- data/setup/site/tasks/utopia.rake +0 -41
- data/spec/utopia/controller/respond_spec/rewrite/controller.rb +0 -12
data/lib/utopia/content/node.rb
CHANGED
|
@@ -20,12 +20,14 @@
|
|
|
20
20
|
|
|
21
21
|
require_relative 'markup'
|
|
22
22
|
require_relative 'links'
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
require_relative 'document'
|
|
24
25
|
|
|
25
26
|
require 'pathname'
|
|
26
27
|
|
|
27
28
|
module Utopia
|
|
28
29
|
class Content
|
|
30
|
+
# Represents an immutable node within the content hierarchy.
|
|
29
31
|
class Node
|
|
30
32
|
def initialize(controller, uri_path, request_path, file_path)
|
|
31
33
|
@controller = controller
|
|
@@ -39,12 +41,16 @@ module Utopia
|
|
|
39
41
|
attr :uri_path
|
|
40
42
|
attr :file_path
|
|
41
43
|
|
|
44
|
+
def name
|
|
45
|
+
@uri_path.basename
|
|
46
|
+
end
|
|
47
|
+
|
|
42
48
|
def link
|
|
43
49
|
return Link.new(:file, uri_path)
|
|
44
50
|
end
|
|
45
51
|
|
|
46
52
|
def lookup_node(path)
|
|
47
|
-
@controller.lookup_node(path)
|
|
53
|
+
@controller.lookup_node(@uri_path + Path[path])
|
|
48
54
|
end
|
|
49
55
|
|
|
50
56
|
def local_path(path = '.', base = nil)
|
|
@@ -60,17 +66,6 @@ module Utopia
|
|
|
60
66
|
end
|
|
61
67
|
end
|
|
62
68
|
|
|
63
|
-
def lookup(tag)
|
|
64
|
-
from_path = parent_path
|
|
65
|
-
|
|
66
|
-
# If the current node is called 'foo', we can't lookup 'foo' in the current directory or we will have infinite recursion.
|
|
67
|
-
if tag.name == @uri_path.basename
|
|
68
|
-
from_path = from_path.dirname
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
return @controller.lookup_tag(tag.name, from_path)
|
|
72
|
-
end
|
|
73
|
-
|
|
74
69
|
def parent_path
|
|
75
70
|
uri_path.dirname
|
|
76
71
|
end
|
|
@@ -105,83 +100,86 @@ module Utopia
|
|
|
105
100
|
def sibling_links(**options)
|
|
106
101
|
return Links.index(@controller.root, siblings_path, options)
|
|
107
102
|
end
|
|
108
|
-
|
|
109
|
-
|
|
103
|
+
|
|
104
|
+
# Lookup the given tag which is being rendered within the given node. Invoked by {Document}.
|
|
105
|
+
# @return [Node] The node which will be used to render the tag.
|
|
106
|
+
def lookup_tag(tag)
|
|
107
|
+
return @controller.lookup_tag(tag.name, self)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Invoked when the node is being rendered by {Document}.
|
|
111
|
+
def call(document, state)
|
|
110
112
|
# Load the template:
|
|
111
113
|
template = @controller.fetch_template(@file_path)
|
|
112
114
|
|
|
113
115
|
# Evaluate the template/code:
|
|
114
|
-
context = Context.new(
|
|
116
|
+
context = Context.new(document, state)
|
|
115
117
|
markup = template.to_buffer(context)
|
|
116
118
|
|
|
117
|
-
# Render the resulting markup into the
|
|
118
|
-
|
|
119
|
+
# Render the resulting markup into the document:
|
|
120
|
+
document.parse_markup(markup)
|
|
119
121
|
end
|
|
120
|
-
|
|
122
|
+
|
|
121
123
|
def process!(request, attributes = {})
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
output = transaction.render_node(self, attributes)
|
|
125
|
-
|
|
126
|
-
return [200, transaction.headers, [output]]
|
|
124
|
+
Document.render(self, request, attributes).to_a
|
|
127
125
|
end
|
|
128
|
-
end
|
|
129
126
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
127
|
+
# This is a special context in which a limited set of well defined methods are exposed in the content view.
|
|
128
|
+
Context = Struct.new(:document, :state) do
|
|
129
|
+
def partial(*args, &block)
|
|
130
|
+
if block_given?
|
|
131
|
+
state.defer(&block)
|
|
132
|
+
else
|
|
133
|
+
state.defer do |document|
|
|
134
|
+
document.tag(*args)
|
|
135
|
+
end
|
|
138
136
|
end
|
|
139
137
|
end
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
end
|
|
138
|
+
|
|
139
|
+
alias deferred_tag partial
|
|
140
|
+
|
|
141
|
+
def controller
|
|
142
|
+
document.controller
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def localization
|
|
146
|
+
document.localization
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def request
|
|
150
|
+
document.request
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def response
|
|
154
|
+
document
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def attributes
|
|
158
|
+
state.attributes
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def [] key
|
|
162
|
+
state.attributes.fetch(key) {document.attributes[key]}
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
alias current state
|
|
166
|
+
|
|
167
|
+
def content
|
|
168
|
+
document.content
|
|
169
|
+
end
|
|
173
170
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
171
|
+
def parent
|
|
172
|
+
document.parent
|
|
173
|
+
end
|
|
177
174
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
175
|
+
def first
|
|
176
|
+
document.first
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def links(*arguments, &block)
|
|
180
|
+
state.node.links(*arguments, &block)
|
|
181
|
+
end
|
|
184
182
|
end
|
|
185
183
|
end
|
|
186
184
|
end
|
|
187
|
-
end
|
|
185
|
+
end
|
|
@@ -24,25 +24,44 @@ module Utopia
|
|
|
24
24
|
EXPIRES = 'Expires'.freeze
|
|
25
25
|
CACHE_CONTROL = 'Cache-Control'.freeze
|
|
26
26
|
CONTENT_TYPE = 'Content-Type'.freeze
|
|
27
|
+
NO_CACHE = 'no-cache'.freeze
|
|
27
28
|
|
|
29
|
+
# A basic content response, including useful defaults for typical HTML5 content.
|
|
28
30
|
class Response
|
|
29
31
|
def initialize
|
|
30
32
|
@status = 200
|
|
31
33
|
@headers = {}
|
|
34
|
+
@body = []
|
|
35
|
+
|
|
36
|
+
# The default content type:
|
|
37
|
+
self.content_type = "text/html; charset=utf-8"
|
|
32
38
|
end
|
|
33
39
|
|
|
34
40
|
attr :status
|
|
35
41
|
attr :headers
|
|
42
|
+
attr :body
|
|
43
|
+
|
|
44
|
+
def content
|
|
45
|
+
@body.join
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def lookup(tag)
|
|
49
|
+
return nil
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def to_a
|
|
53
|
+
[@status, @headers, @body]
|
|
54
|
+
end
|
|
36
55
|
|
|
37
56
|
# Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
|
|
38
57
|
def do_not_cache!
|
|
39
58
|
@headers[CACHE_CONTROL] = "no-cache, must-revalidate"
|
|
40
59
|
@headers[EXPIRES] = Time.now.httpdate
|
|
41
60
|
end
|
|
42
|
-
|
|
43
|
-
# Specify that the content
|
|
61
|
+
|
|
62
|
+
# Specify that the content could be cached.
|
|
44
63
|
def cache!(duration = 3600, access: "public")
|
|
45
|
-
unless @headers[CACHE_CONTROL]
|
|
64
|
+
unless cache_control = @headers[CACHE_CONTROL] and cache_control.include?(NO_CACHE)
|
|
46
65
|
@headers[CACHE_CONTROL] = "#{access}, max-age=#{duration}"
|
|
47
66
|
@headers[EXPIRES] = (Time.now + duration).httpdate
|
|
48
67
|
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
|
11
|
+
# all copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
# THE SOFTWARE.
|
|
20
|
+
|
|
21
|
+
require_relative 'namespace'
|
|
22
|
+
|
|
23
|
+
module Utopia
|
|
24
|
+
class Content
|
|
25
|
+
# Tags which provide intrinsic behaviour within the content middleware.
|
|
26
|
+
module Tags
|
|
27
|
+
extend Namespace
|
|
28
|
+
|
|
29
|
+
# Invokes a node and renders a single node to the output stream.
|
|
30
|
+
# @param path [String] The path of the node to invoke.
|
|
31
|
+
tag('node') do |document, state|
|
|
32
|
+
path = Path[state[:path]]
|
|
33
|
+
|
|
34
|
+
node = document.lookup_node(path)
|
|
35
|
+
|
|
36
|
+
document.render_node(node)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Invokes a deferred tag from the current state. Works together with {Document::State#defer}.
|
|
40
|
+
# @param id [String] The id of the deferred to invoke.
|
|
41
|
+
tag('deferred') do |document, state|
|
|
42
|
+
id = state[:id].to_i
|
|
43
|
+
|
|
44
|
+
deferred = document.parent.deferred[id]
|
|
45
|
+
|
|
46
|
+
deferred.call(document, state)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Renders the content of the parent node into the output of the document.
|
|
50
|
+
tag('content') do |document, state|
|
|
51
|
+
# We are invoking this node within a parent who has content, and we want to generate output equal to that.
|
|
52
|
+
document.write(document.parent.content)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Render the contents only if in the correct environment.
|
|
56
|
+
# @param only [String] A comma separated list of environments to check.
|
|
57
|
+
tag('environment') do |document, state|
|
|
58
|
+
environment = document.attributes.fetch(:environment){RACK_ENV}.to_s
|
|
59
|
+
|
|
60
|
+
if state[:only].split(',').include?(environment)
|
|
61
|
+
document.parse_markup(state.content)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/utopia/controller.rb
CHANGED
|
@@ -54,39 +54,31 @@ module Utopia
|
|
|
54
54
|
request.env[VARIABLES_KEY]
|
|
55
55
|
end
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
# @param root [String] The content root where controllers will be loaded from.
|
|
58
|
+
# @param base [Class] The base class for controllers.
|
|
59
|
+
def initialize(app, root: Utopia::default_root, base: Controller::Base)
|
|
58
60
|
@app = app
|
|
59
|
-
@root = root
|
|
61
|
+
@root = root
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
@controller_cache = Concurrent::Map.new
|
|
63
|
-
else
|
|
64
|
-
@controller_cache = nil
|
|
65
|
-
end
|
|
63
|
+
@controller_cache = Concurrent::Map.new
|
|
66
64
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
@base = base || Controller::Base.dup.prepend(Controller::Actions)
|
|
65
|
+
@base = base
|
|
70
66
|
end
|
|
71
67
|
|
|
72
68
|
attr :app
|
|
73
69
|
|
|
74
70
|
def freeze
|
|
75
|
-
|
|
71
|
+
return self if frozen?
|
|
76
72
|
|
|
77
|
-
|
|
78
|
-
|
|
73
|
+
@root.freeze
|
|
74
|
+
@base.freeze
|
|
79
75
|
|
|
80
76
|
super
|
|
81
77
|
end
|
|
82
78
|
|
|
83
79
|
# Fetch the controller for the given relative path. May be cached.
|
|
84
80
|
def lookup_controller(path)
|
|
85
|
-
|
|
86
|
-
@controller_cache.fetch_or_store(path.to_s) do
|
|
87
|
-
load_controller_file(path)
|
|
88
|
-
end
|
|
89
|
-
else
|
|
81
|
+
@controller_cache.fetch_or_store(path.to_s) do
|
|
90
82
|
load_controller_file(path)
|
|
91
83
|
end
|
|
92
84
|
end
|
|
@@ -22,11 +22,17 @@ require_relative '../http'
|
|
|
22
22
|
|
|
23
23
|
module Utopia
|
|
24
24
|
class Controller
|
|
25
|
+
# A controller layer which invokes functinality based on the request path.
|
|
26
|
+
# @example
|
|
27
|
+
# on '*' do |request, path|
|
|
28
|
+
# succeed! content: 'Hello World'
|
|
29
|
+
# end
|
|
25
30
|
module Actions
|
|
26
31
|
def self.prepended(base)
|
|
27
32
|
base.extend(ClassMethods)
|
|
28
33
|
end
|
|
29
34
|
|
|
35
|
+
# A nested action lookup hash table.
|
|
30
36
|
class Action < Hash
|
|
31
37
|
def initialize(options = {}, &block)
|
|
32
38
|
@options = options
|
|
@@ -53,7 +59,10 @@ module Utopia
|
|
|
53
59
|
super and @callback == other.callback and @options == other.options
|
|
54
60
|
end
|
|
55
61
|
|
|
62
|
+
# Matches 0 or more path components.
|
|
56
63
|
WILDCARD_GREEDY = '**'.freeze
|
|
64
|
+
|
|
65
|
+
# Matches any 1 path component.
|
|
57
66
|
WILDCARD = '*'.freeze
|
|
58
67
|
|
|
59
68
|
# Given a path, iterate over all actions that match. Actions match from most specific to most general.
|
|
@@ -112,6 +121,7 @@ module Utopia
|
|
|
112
121
|
end
|
|
113
122
|
end
|
|
114
123
|
|
|
124
|
+
# Exposed to the controller class.
|
|
115
125
|
module ClassMethods
|
|
116
126
|
def self.extended(klass)
|
|
117
127
|
klass.instance_eval do
|
|
@@ -22,6 +22,7 @@ require_relative '../http'
|
|
|
22
22
|
|
|
23
23
|
module Utopia
|
|
24
24
|
class Controller
|
|
25
|
+
# The base implementation of a controller class.
|
|
25
26
|
class Base
|
|
26
27
|
# A string which is the full path to the directory which contains the controller.
|
|
27
28
|
def self.base_path
|
|
@@ -87,7 +88,7 @@ module Utopia
|
|
|
87
88
|
end
|
|
88
89
|
|
|
89
90
|
# Request relative redirect. Respond with a redirect to the given target.
|
|
90
|
-
def redirect!
|
|
91
|
+
def redirect!(target, status = 302)
|
|
91
92
|
status = HTTP::Status.new(status, 300...400)
|
|
92
93
|
location = target.to_s
|
|
93
94
|
|
|
@@ -23,7 +23,7 @@ require_relative '../path/matcher'
|
|
|
23
23
|
|
|
24
24
|
module Utopia
|
|
25
25
|
class Controller
|
|
26
|
-
#
|
|
26
|
+
# A controller layer which provides a convenient way to respond to different requested content types. The order in which you add converters matters, as it determines how the incoming Accept: header is mapped, e.g. the first converter is also defined as matching the media range */*.
|
|
27
27
|
module Respond
|
|
28
28
|
def self.prepended(base)
|
|
29
29
|
base.extend(ClassMethods)
|
|
@@ -25,15 +25,16 @@ module Utopia
|
|
|
25
25
|
class Controller
|
|
26
26
|
# This controller layer rewrites the path before executing controller actions. When the rule matches, the supplied block is executed.
|
|
27
27
|
# @example
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
#
|
|
31
|
-
#
|
|
28
|
+
# prepend Rewrite
|
|
29
|
+
# rewrite.extract_prefix id: Integer do
|
|
30
|
+
# @user = User.find(@id)
|
|
31
|
+
# end
|
|
32
32
|
module Rewrite
|
|
33
33
|
def self.prepended(base)
|
|
34
34
|
base.extend(ClassMethods)
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
# A abstract rule which can match against a request path.
|
|
37
38
|
class Rule
|
|
38
39
|
def apply_match_to_context(match_data, context)
|
|
39
40
|
match_data.names.each do |name|
|
|
@@ -42,6 +43,7 @@ module Utopia
|
|
|
42
43
|
end
|
|
43
44
|
end
|
|
44
45
|
|
|
46
|
+
# A rule which extracts a prefix pattern from the request path.
|
|
45
47
|
class ExtractPrefixRule < Rule
|
|
46
48
|
def initialize(patterns, block)
|
|
47
49
|
@matcher = Path::Matcher.new(patterns)
|
|
@@ -70,6 +72,7 @@ module Utopia
|
|
|
70
72
|
end
|
|
71
73
|
end
|
|
72
74
|
|
|
75
|
+
# Rewrite a request path based on a set of defined rules.
|
|
73
76
|
class Rewriter
|
|
74
77
|
def initialize
|
|
75
78
|
@rules = []
|
|
@@ -94,6 +97,7 @@ module Utopia
|
|
|
94
97
|
end
|
|
95
98
|
end
|
|
96
99
|
|
|
100
|
+
# Exposed to the controller class.
|
|
97
101
|
module ClassMethods
|
|
98
102
|
def rewrite
|
|
99
103
|
@rewriter ||= Rewriter.new
|