utopia 1.7.1 → 1.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -3
- data/README.md +142 -11
- data/benchmarks/string_vs_symbol.rb +12 -0
- data/lib/utopia/command.rb +16 -13
- data/lib/utopia/content.rb +1 -5
- data/lib/utopia/content/node.rb +9 -4
- data/lib/utopia/{extensions/rack.rb → content/response.rb} +33 -30
- data/lib/utopia/content/tag.rb +14 -17
- data/lib/utopia/content/transaction.rb +19 -17
- data/lib/utopia/controller.rb +29 -8
- data/lib/utopia/controller/actions.rb +148 -0
- data/lib/utopia/controller/base.rb +9 -49
- data/lib/utopia/controller/respond.rb +1 -1
- data/lib/utopia/controller/rewrite.rb +9 -1
- data/lib/utopia/controller/variables.rb +1 -0
- data/lib/utopia/localization.rb +4 -1
- data/lib/utopia/middleware.rb +0 -2
- data/lib/utopia/path.rb +9 -0
- data/lib/utopia/path/matcher.rb +0 -1
- data/lib/utopia/redirection.rb +3 -2
- data/lib/utopia/session.rb +119 -2
- data/lib/utopia/session/lazy_hash.rb +1 -3
- data/lib/utopia/setup.rb +73 -0
- data/lib/utopia/static.rb +9 -2
- data/lib/utopia/version.rb +1 -1
- data/setup/examples/wiki/controller.rb +41 -0
- data/setup/examples/wiki/edit.xnode +15 -0
- data/setup/examples/wiki/index.xnode +10 -0
- data/setup/examples/wiki/welcome/content.md +3 -0
- data/setup/server/config/environment.yaml +1 -0
- data/setup/server/git/hooks/post-receive +4 -5
- data/setup/site/Gemfile +5 -0
- data/setup/site/config.ru +2 -1
- data/setup/site/config/environment.rb +5 -17
- data/setup/site/pages/_page.xnode +4 -2
- data/setup/site/pages/links.yaml +1 -1
- data/setup/site/pages/welcome/index.xnode +33 -15
- data/setup/site/public/_static/site.css +72 -4
- data/setup/site/tasks/utopia.rake +8 -0
- data/spec/utopia/{rack_spec.rb → content/response_spec.rb} +12 -19
- data/spec/utopia/content_spec.rb +2 -3
- data/spec/utopia/controller/{action_spec.rb → actions_spec.rb} +18 -32
- data/spec/utopia/controller/middleware_spec.rb +10 -10
- data/spec/utopia/controller/middleware_spec/controller/controller.rb +3 -3
- data/spec/utopia/controller/middleware_spec/controller/nested/controller.rb +1 -1
- data/spec/utopia/controller/middleware_spec/redirect/controller.rb +1 -1
- data/spec/utopia/controller/respond_spec.rb +3 -2
- data/spec/utopia/controller/respond_spec/api/controller.rb +2 -2
- data/spec/utopia/controller/respond_spec/errors/controller.rb +1 -1
- data/spec/utopia/controller/rewrite_spec.rb +1 -1
- data/spec/utopia/controller/sequence_spec.rb +12 -16
- data/spec/utopia/exceptions/handler_spec/controller.rb +2 -2
- data/spec/utopia/performance_spec/config.ru +1 -0
- data/spec/utopia/session_spec.rb +34 -1
- data/spec/utopia/session_spec.ru +3 -3
- data/spec/utopia/setup_spec.rb +2 -2
- data/utopia.gemspec +2 -2
- metadata +18 -12
- data/lib/utopia/controller/action.rb +0 -116
- data/lib/utopia/session/encrypted_cookie.rb +0 -118
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b9d8f6593ca274e5489f7d07153aa9756ac83d6
|
4
|
+
data.tar.gz: a26c9e0b6c439311684563a66af2b0c8cd36e0df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5369e9923cbd895aeda163bb87234a3ef596afe999283e44c8520197f2043921d2cfd1450bd6fe3237ba6d92867932182079e20cd7efdb921bd2a9bc651cd0d7
|
7
|
+
data.tar.gz: 01b933e7c4c1d83e400ee809d474b4962739737d88908eaf67c388a7d1dc05ba6f062a5d3f40fc4076968ea30b9fee419539e63f90cc422fee79dba55aa44db8
|
data/.travis.yml
CHANGED
@@ -5,13 +5,12 @@ before_install:
|
|
5
5
|
- git config --global user.email "samuel@oriontransfer.net"
|
6
6
|
- git config --global user.name "Samuel Williams"
|
7
7
|
rvm:
|
8
|
-
- 2.1.8
|
9
8
|
- 2.2.4
|
10
9
|
- 2.3.0
|
11
10
|
- ruby-head
|
12
|
-
- rbx
|
11
|
+
- rbx
|
13
12
|
env: COVERAGE=true BENCHMARK=true
|
14
13
|
matrix:
|
15
14
|
allow_failures:
|
16
|
-
- rvm: rbx
|
15
|
+
- rvm: rbx
|
17
16
|
- rvm: ruby-head
|
data/README.md
CHANGED
@@ -1,14 +1,21 @@
|
|
1
1
|
# ![Utopia](materials/utopia.png?raw=true)
|
2
2
|
|
3
|
-
Utopia is a website generation framework which provides a robust set of tools
|
4
|
-
to build highly complex dynamic websites. It uses the filesystem heavily for
|
5
|
-
content and provides functions for interacting with files and directories as
|
6
|
-
structure representing the website.
|
3
|
+
Utopia is a website generation framework which provides a robust set of tools to build highly complex dynamic websites. It uses the filesystem heavily for content and provides functions for interacting with files and directories as structure representing the website.
|
7
4
|
|
8
5
|
[![Build Status](https://secure.travis-ci.org/ioquatix/utopia.svg)](http://travis-ci.org/ioquatix/utopia)
|
9
6
|
[![Code Climate](https://codeclimate.com/github/ioquatix/utopia.svg)](https://codeclimate.com/github/ioquatix/utopia)
|
10
7
|
[![Coverage Status](https://coveralls.io/repos/ioquatix/utopia/badge.svg)](https://coveralls.io/r/ioquatix/utopia)
|
11
8
|
|
9
|
+
## Motivation
|
10
|
+
|
11
|
+
The [original Utopia project](https://github.com/ioquatix/utopia-php) was written in PHP in the early 2000s. It consisted of an XML parser, a database layer and some code to assist with business logic. It was initially designed to reduce the amount of HTML required to build both content-centric websites and business apps. At the time, CSS was very poorly supported and thus a lot of the time, you'd be using quite complex `<table>`s with embedded `<img>`s to generate simple things like boxes with drop shadows, etc. Utopia provided a core concept - a node - which was essentially a small snippet of HTML, which could be composed into other nodes simply by using a named tag (similar to ColdFusion). Attributes and content were passed in, and thus you could easily build complex pages with simple semantic markup.
|
12
|
+
|
13
|
+
At the time, the available frameworks were pretty basic. Utopia was a working, albeit poor, implementation of MVC and supported several commercial websites I developed at the time. I made it, partly just because I could, but also because it served a commercial purpose.
|
14
|
+
|
15
|
+
Eventually one day I started using Ruby on Rails. There are aspects of the Rails framework which I like. However, at the time I was using it (starting with version 0.8), I found that it's flat organisation of controllers and views very limiting. Nested controllers and views make it easier to manage complexity in a web application. Utopia embraces this principle, and applies it to both the controller and view layers. I also developed a [model layer with similar principles](https://github.com/ioquatix/relaxo-model).
|
16
|
+
|
17
|
+
So, Utopia exists because it suits my way of thinking about web applications, and it's conceptual core has been refined for over a decade. It provides a considered amount of both flexibility, and opinionated behavior.
|
18
|
+
|
12
19
|
## Installation
|
13
20
|
|
14
21
|
### Local Setup
|
@@ -19,9 +26,10 @@ Install utopia:
|
|
19
26
|
|
20
27
|
Create a new site:
|
21
28
|
|
22
|
-
$
|
29
|
+
$ mkdir www.example.com
|
23
30
|
$ cd www.example.com
|
24
|
-
$
|
31
|
+
$ utopia site create
|
32
|
+
$ rake
|
25
33
|
|
26
34
|
#### Bower Integration
|
27
35
|
|
@@ -44,13 +52,17 @@ Firstly log into your remote site using `ssh` and install utopia:
|
|
44
52
|
|
45
53
|
Then use the utopia command to generate a new remote site:
|
46
54
|
|
47
|
-
$
|
55
|
+
$ mkdir /srv/http/www.example.com
|
56
|
+
$ cd /srv/http/www.example.com
|
57
|
+
$ sudo -u http utopia server create
|
48
58
|
|
49
59
|
On the local site, you can set up a git remote:
|
50
60
|
|
51
61
|
$ git remote add production ssh://remote/srv/http/www.example.com
|
52
62
|
$ git push --set-upstream production master
|
53
63
|
|
64
|
+
When you push to the remote site, `rake deploy` will be run after the code is updated, finally, `rake restart` will be run which ideally should restart the application server.
|
65
|
+
|
54
66
|
### Passenger+Nginx Setup
|
55
67
|
|
56
68
|
Utopia works well with Passenger+Nginx. Installing Passenger+Nginx is easy:
|
@@ -101,31 +113,150 @@ Utopia builds on top of Rack with the following middleware:
|
|
101
113
|
- `Utopia::Localization`: Non-intrusive localization of resources.
|
102
114
|
- `Utopia::Controller`: Dynamic behaviour with recursive execution.
|
103
115
|
- `Utopia::Content`: XML-style template engine with powerful tag behaviours.
|
104
|
-
- `Utopia::Session
|
116
|
+
- `Utopia::Session`: Session storage using an encrypted cookie.
|
117
|
+
|
118
|
+
The implementation of Utopia is considered thread-safe and reentrant. However, this does not guarantee that the code YOU write will be so.
|
105
119
|
|
106
120
|
### Static
|
107
121
|
|
108
|
-
This middleware serves static files using the `mime-types` library. By default, it works with `Rack::Sendfile` and
|
122
|
+
This middleware serves static files using the `mime-types` library. By default, it works with `Rack::Sendfile` and supports `ETag` based caching. Normally, you'd prefer to put static files into `public/_static` but it's also acceptable to put static content into `pages/` if it makes sense.
|
123
|
+
|
124
|
+
use Utopia::Static,
|
125
|
+
# The root path to serve files from:
|
126
|
+
root: "path/to/root",
|
127
|
+
# The mime-types to recognize/serve:
|
128
|
+
types: [:default, :xiph],
|
129
|
+
# Cache-Control header for files:
|
130
|
+
cache_control: 'public, max-age=7200'
|
109
131
|
|
110
132
|
### Redirection
|
111
133
|
|
112
134
|
A set of flexible URI rewriting middleware which includes support for string mappings, regular expressions and status codes (e.g. 404 errors).
|
113
135
|
|
136
|
+
# String (fast hash lookup) rewriting:
|
137
|
+
use Utopia::Redirection::Rewrite,
|
138
|
+
'/' => '/welcome/index'
|
139
|
+
|
140
|
+
# Redirect directories (e.g. /) to an index file (e.g. /index):
|
141
|
+
use Utopia::Redirection::DirectoryIndex,
|
142
|
+
index: 'index.html'
|
143
|
+
|
144
|
+
# Redirect (error) status codes to actual pages:
|
145
|
+
use Utopia::Redirection::Errors,
|
146
|
+
404 => '/errors/file-not-found'
|
147
|
+
|
114
148
|
### Localization
|
115
149
|
|
116
150
|
The localization middleware uses the `Accept-Language` header to guess the preferred locale out of the given options. If a request path maps to a resource, that resource is returned. Otherwise, a localized request is made.
|
117
151
|
|
152
|
+
use Utopia::Localization,
|
153
|
+
:default_locale => 'en',
|
154
|
+
:locales => ['en', 'de', 'ja', 'zh'],
|
155
|
+
:nonlocalized => ['/_static/', '/_cache/']
|
156
|
+
|
157
|
+
Somewhere further down the chain, you can localize a resource:
|
158
|
+
|
159
|
+
localization = Utopia::Localization[request]
|
160
|
+
show_welcome(localization.current_locale)
|
161
|
+
|
118
162
|
### Controller
|
119
163
|
|
120
|
-
A simple recursive controller layer which works in isolation from the view rendering middleware.
|
164
|
+
A simple recursive controller layer which works in isolation from the view rendering middleware.
|
165
|
+
|
166
|
+
use Utopia::Controller,
|
167
|
+
# The root directory where `controller.rb` files can be found.
|
168
|
+
root: 'path/to/root',
|
169
|
+
# The base class to use for all controllers:
|
170
|
+
base: Utopia::Controller::Base
|
171
|
+
# Whether or not to cache controller classes:
|
172
|
+
cache_controllers: (RACK_ENV == :production),
|
173
|
+
|
174
|
+
A controller is a file within the root directory (or subdirectory) with the name `controller.rb`. This code is dynamically loaded into an anonymous class and executed. The default controller has only a single function:
|
175
|
+
|
176
|
+
def passthrough(request, path)
|
177
|
+
# Call one of:
|
178
|
+
|
179
|
+
# This will cause the middleware to generate a response.
|
180
|
+
# def respond!(response)
|
181
|
+
|
182
|
+
# This will cause the controller to skip the request.
|
183
|
+
# def ignore!
|
184
|
+
|
185
|
+
# Request relative redirect. Respond with a redirect to the given target.
|
186
|
+
# def redirect! (target, status = 302)
|
187
|
+
|
188
|
+
# Controller relative redirect.
|
189
|
+
# def goto!(target, status = 302)
|
190
|
+
|
191
|
+
# Respond with an error which indiciates some kind of failure.
|
192
|
+
# def fail!(error = 400, message = nil)
|
193
|
+
|
194
|
+
# Succeed the request and immediately respond.
|
195
|
+
# def succeed!(status: 200, headers: {}, **options)
|
196
|
+
# options may include content: string or body: Enumerable (as per Rack specifications
|
197
|
+
end
|
198
|
+
|
199
|
+
The controller layer can do more complex operations by prepending modules into it.
|
200
|
+
|
201
|
+
prepend Rewrite, Actions
|
202
|
+
|
203
|
+
# Extracts an Integer
|
204
|
+
rewrite.extract_prefix id: Integer do
|
205
|
+
@user = User.find_by_id(@id)
|
206
|
+
end
|
207
|
+
|
208
|
+
before do |request, path|
|
209
|
+
# Always executed, before any controller specific actions are performed.
|
210
|
+
end
|
211
|
+
|
212
|
+
on 'edit' do |request, path|
|
213
|
+
if request.post?
|
214
|
+
@user.update_attributes(request[:user])
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
otherwise do |request, path|
|
219
|
+
# Executed if no specific named actions were executed.
|
220
|
+
end
|
221
|
+
|
222
|
+
after do |request, path|
|
223
|
+
# Always executed, after all other controller actions are performed.
|
224
|
+
end
|
121
225
|
|
122
226
|
### Content
|
123
227
|
|
124
228
|
A tag based content generation system which integrates nicely with HTML5. Supports structures which separate generic page templates from dynamically generated content in an easy and consistent way.
|
125
229
|
|
230
|
+
use Utopia::Content,
|
231
|
+
cache_templates: (RACK_ENV == :production),
|
232
|
+
tags: {
|
233
|
+
'deferred' => Utopia::Tags::Deferred,
|
234
|
+
'override' => Utopia::Tags::Override,
|
235
|
+
'node' => Utopia::Tags::Node,
|
236
|
+
'environment' => Utopia::Tags::Environment.for(RACK_ENV)
|
237
|
+
}
|
238
|
+
|
239
|
+
A basic template `create.xnode` looks something like:
|
240
|
+
|
241
|
+
<page>
|
242
|
+
<heading>Create User</heading>
|
243
|
+
<form action="#">
|
244
|
+
<input name="name" />
|
245
|
+
<input type="submit" />
|
246
|
+
</form>
|
247
|
+
</page>
|
248
|
+
|
249
|
+
This template would typically be designed with supporting `_page.xnode` and `_heading.xnode` in the same directory or, more typically, somewhere further up the directory hierarchy.
|
250
|
+
|
126
251
|
### Session
|
127
252
|
|
128
|
-
The
|
253
|
+
The session management uses symmetric private key encryption to store data on the client and avoid tampering.
|
254
|
+
|
255
|
+
use Utopia::Session,
|
256
|
+
:expire_after => 3600,
|
257
|
+
:secret => '40 or more random characters for your secret key'
|
258
|
+
|
259
|
+
All session data is stored on the client, but it's encrypted with a salt and the secret key. It would be hard for the client to decrypt the data without the secret.
|
129
260
|
|
130
261
|
## Contributing
|
131
262
|
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'benchmark/ips'
|
2
|
+
|
3
|
+
STRING_HASH = { "foo" => "bar" }
|
4
|
+
SYMBOL_HASH = { :foo => "bar" }
|
5
|
+
|
6
|
+
Benchmark.ips do |x|
|
7
|
+
x.report("string") { STRING_HASH["foo"] }
|
8
|
+
x.report("symbol") { SYMBOL_HASH[:foo] }
|
9
|
+
x.report("symbol-from-string") { SYMBOL_HASH["foo".to_sym] }
|
10
|
+
|
11
|
+
x.compare!
|
12
|
+
end
|
data/lib/utopia/command.rb
CHANGED
@@ -34,11 +34,10 @@ module Utopia
|
|
34
34
|
module Site
|
35
35
|
CONFIGURATION_FILES = ['config.ru', 'config/environment.rb', 'Gemfile', 'Rakefile', 'tasks/utopia.rake']
|
36
36
|
|
37
|
-
DIRECTORIES = ["config", "lib", "pages", "public", "tasks"
|
38
|
-
SYMLINKS = {"public/_static" => "../pages/_static"}
|
37
|
+
DIRECTORIES = ["config", "lib", "pages", "public", "tasks"]
|
39
38
|
|
40
39
|
# Removed during upgrade process
|
41
|
-
OLD_DIRECTORIES = ["access_log", "cache"]
|
40
|
+
OLD_DIRECTORIES = ["access_log", "cache", "tmp"]
|
42
41
|
|
43
42
|
ROOT = File.join(BASE, 'site')
|
44
43
|
end
|
@@ -56,7 +55,6 @@ module Utopia
|
|
56
55
|
destination_root = parent.root
|
57
56
|
|
58
57
|
FileUtils.mkdir_p File.join(destination_root, "public")
|
59
|
-
FileUtils.mkdir_p File.join(destination_root, "tmp")
|
60
58
|
|
61
59
|
Dir.chdir(destination_root) do
|
62
60
|
# Shared allows multiple users to access the site with the same group:
|
@@ -65,6 +63,7 @@ module Utopia
|
|
65
63
|
system("git", "config", "core.worktree", destination_root)
|
66
64
|
|
67
65
|
system("cp", "-r", File.join(Setup::Server::ROOT, 'git', 'hooks'), File.join(destination_root, '.git'))
|
66
|
+
system("cp", "-r", File.join(Setup::Server::ROOT, 'config'), File.join(destination_root))
|
68
67
|
end
|
69
68
|
|
70
69
|
hostname = `hostname`.chomp
|
@@ -110,10 +109,6 @@ module Utopia
|
|
110
109
|
end
|
111
110
|
end
|
112
111
|
|
113
|
-
Setup::Site::SYMLINKS.each do |path, target|
|
114
|
-
FileUtils.ln_s(target, File.join(destination_root, path), force: true)
|
115
|
-
end
|
116
|
-
|
117
112
|
Setup::Site::CONFIGURATION_FILES.each do |configuration_file|
|
118
113
|
destination_path = File.join(destination_root, configuration_file)
|
119
114
|
|
@@ -160,6 +155,16 @@ module Utopia
|
|
160
155
|
class Update < Samovar::Command
|
161
156
|
self.description = "Upgrade an existing site to use the latest configuration files from the template."
|
162
157
|
|
158
|
+
def move_static!
|
159
|
+
if File.lstat("public/_static").symlink?
|
160
|
+
FileUtils.rm_f "public/_static"
|
161
|
+
end
|
162
|
+
|
163
|
+
if File.directory?("pages/_static") and !File.exist?("public/_static")
|
164
|
+
system("git", "mv", "pages/_static", "public/_static")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
163
168
|
def invoke(parent)
|
164
169
|
destination_root = parent.root
|
165
170
|
branch_name = "utopia-upgrade-#{Utopia::VERSION}"
|
@@ -180,10 +185,6 @@ module Utopia
|
|
180
185
|
FileUtils.rm_rf(path)
|
181
186
|
end
|
182
187
|
|
183
|
-
Setup::Site::SYMLINKS.each do |path, target|
|
184
|
-
FileUtils.ln_s(target, File.join(destination_root, path), force: true)
|
185
|
-
end
|
186
|
-
|
187
188
|
Setup::Site::CONFIGURATION_FILES.each do |configuration_file|
|
188
189
|
source_path = File.join(Setup::Site::ROOT, configuration_file)
|
189
190
|
destination_path = File.join(destination_root, configuration_file)
|
@@ -201,7 +202,9 @@ module Utopia
|
|
201
202
|
system("git", "add", "-u")
|
202
203
|
|
203
204
|
# Stage any new files that we have explicitly added:
|
204
|
-
system("git", "add", *Setup::Site::CONFIGURATION_FILES
|
205
|
+
system("git", "add", *Setup::Site::CONFIGURATION_FILES)
|
206
|
+
|
207
|
+
move_static!
|
205
208
|
|
206
209
|
# Commit all changes:
|
207
210
|
system("git", "commit", "-m", "Upgrade to utopia #{Utopia::VERSION}.")
|
data/lib/utopia/content.rb
CHANGED
@@ -145,13 +145,9 @@ module Utopia
|
|
145
145
|
locale = env[Localization::CURRENT_LOCALE_KEY]
|
146
146
|
if link = Links.for(@root, path, locale)
|
147
147
|
if link.path and node = lookup_node(link.path)
|
148
|
-
response = Rack::Response.new
|
149
|
-
|
150
148
|
attributes = request.env.fetch(VARIABLES_KEY, {}).to_hash
|
151
149
|
|
152
|
-
node.process!(request,
|
153
|
-
|
154
|
-
return response.finish
|
150
|
+
return node.process!(request, attributes)
|
155
151
|
elsif redirect_uri = link[:uri]
|
156
152
|
return [307, {HTTP::LOCATION => redirect_uri}, []]
|
157
153
|
end
|
data/lib/utopia/content/node.rb
CHANGED
@@ -107,18 +107,23 @@ module Utopia
|
|
107
107
|
end
|
108
108
|
|
109
109
|
def call(transaction, state)
|
110
|
+
# Load the template:
|
110
111
|
template = @controller.fetch_template(@file_path)
|
111
112
|
|
113
|
+
# Evaluate the template/code:
|
112
114
|
context = Context.new(transaction, state)
|
113
115
|
markup = template.to_buffer(context)
|
114
116
|
|
117
|
+
# Render the resulting markup into the transaction:
|
115
118
|
transaction.parse_markup(markup)
|
116
119
|
end
|
117
120
|
|
118
|
-
def process!(request,
|
119
|
-
transaction = Transaction.new(request
|
121
|
+
def process!(request, attributes = {})
|
122
|
+
transaction = Transaction.new(request)
|
123
|
+
|
120
124
|
output = transaction.render_node(self, attributes)
|
121
|
-
|
125
|
+
|
126
|
+
return [200, transaction.headers, [output]]
|
122
127
|
end
|
123
128
|
end
|
124
129
|
|
@@ -149,7 +154,7 @@ module Utopia
|
|
149
154
|
end
|
150
155
|
|
151
156
|
def response
|
152
|
-
transaction
|
157
|
+
transaction
|
153
158
|
end
|
154
159
|
|
155
160
|
def attributes
|
@@ -18,39 +18,42 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
21
|
+
module Utopia
|
22
|
+
class Content
|
23
|
+
# Compatibility with older versions of rack:
|
24
|
+
EXPIRES = 'Expires'.freeze
|
25
|
+
CACHE_CONTROL = 'Cache-Control'.freeze
|
26
|
+
CONTENT_TYPE = 'Content-Type'.freeze
|
27
|
+
|
28
|
+
class Response
|
29
|
+
def initialize
|
30
|
+
@status = 200
|
31
|
+
@headers = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
attr :status
|
35
|
+
attr :headers
|
36
|
+
|
37
|
+
# Specifies that the content shouldn't be cached. Overrides `cache!` if already called.
|
38
|
+
def do_not_cache!
|
39
|
+
@headers[CACHE_CONTROL] = "no-cache, must-revalidate"
|
40
|
+
@headers[EXPIRES] = Time.now.httpdate
|
41
|
+
end
|
34
42
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
43
|
+
# Specify that the content should be cached.
|
44
|
+
def cache!(duration = 3600, access: "public")
|
45
|
+
unless @headers[CACHE_CONTROL] =~ /no-cache/
|
46
|
+
@headers[CACHE_CONTROL] = "#{access}, max-age=#{duration}"
|
47
|
+
@headers[EXPIRES] = (Time.now + duration).httpdate
|
48
|
+
end
|
40
49
|
end
|
41
|
-
end
|
42
50
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
headers[CONTENT_TYPE] = value
|
50
|
-
end
|
51
|
-
|
52
|
-
def content_type
|
53
|
-
headers[CONTENT_TYPE]
|
51
|
+
# Specify the content type of the response data.
|
52
|
+
def content_type= value
|
53
|
+
@headers[CONTENT_TYPE] = value
|
54
|
+
end
|
55
|
+
|
56
|
+
alias content_type! content_type=
|
54
57
|
end
|
55
58
|
end
|
56
59
|
end
|