utopia 1.4.0 → 1.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/.gitignore +1 -0
- data/.travis.yml +5 -1
- data/Gemfile +3 -0
- data/README.md +11 -0
- data/benchmarks/hash_vs_openstruct.rb +52 -0
- data/benchmarks/struct_vs_class.rb +89 -0
- data/bin/utopia +4 -5
- data/lib/utopia.rb +1 -0
- data/lib/utopia/content.rb +24 -15
- data/lib/utopia/content/node.rb +69 -3
- data/lib/utopia/content/processor.rb +32 -5
- data/lib/utopia/content/transaction.rb +138 -147
- data/lib/utopia/content_length.rb +50 -0
- data/lib/utopia/controller.rb +4 -0
- data/lib/utopia/controller/variables.rb +1 -1
- data/lib/utopia/http.rb +2 -0
- data/lib/utopia/localization.rb +4 -8
- data/lib/utopia/path.rb +13 -13
- data/lib/utopia/static.rb +25 -14
- data/lib/utopia/tags/environment.rb +1 -1
- data/lib/utopia/tags/override.rb +1 -1
- data/lib/utopia/version.rb +1 -1
- data/setup/server/git/hooks/post-receive +32 -24
- data/setup/site/Gemfile +8 -0
- data/setup/site/Rakefile +29 -6
- data/setup/site/config.ru +8 -8
- data/setup/site/pages/_heading.xnode +1 -1
- data/setup/site/pages/_page.xnode +2 -2
- data/spec/utopia/content_spec.rb +3 -3
- data/spec/utopia/controller/sequence_spec.rb +1 -1
- data/spec/utopia/controller/variables_spec.rb +1 -1
- data/spec/utopia/pages/node/index.xnode +1 -1
- data/spec/utopia/performance_spec.rb +90 -0
- data/spec/utopia/performance_spec/cache/head/readme.txt +1 -0
- data/spec/utopia/performance_spec/cache/meta/readme.txt +1 -0
- data/spec/utopia/performance_spec/config.ru +39 -0
- data/spec/utopia/performance_spec/lib/readme.txt +1 -0
- data/spec/utopia/performance_spec/pages/_heading.xnode +2 -0
- data/spec/utopia/performance_spec/pages/_page.xnode +26 -0
- data/spec/utopia/performance_spec/pages/api/controller.rb +7 -0
- data/spec/utopia/performance_spec/pages/errors/exception.xnode +5 -0
- data/spec/utopia/performance_spec/pages/errors/file-not-found.xnode +5 -0
- data/spec/utopia/performance_spec/pages/links.yaml +2 -0
- data/spec/utopia/performance_spec/pages/welcome/index.xnode +17 -0
- data/spec/utopia/rack_helper.rb +5 -2
- data/spec/utopia/setup_spec.rb +93 -0
- data/utopia.gemspec +1 -1
- metadata +34 -5
- data/lib/utopia/mail_exceptions.rb +0 -136
data/lib/utopia/localization.rb
CHANGED
@@ -24,13 +24,9 @@ module Utopia
|
|
24
24
|
# If you request a URL which has localized content, a localized redirect would be returned based on the content requested.
|
25
25
|
class Localization
|
26
26
|
# A wrapper to provide easy access to locale related data in the request.
|
27
|
-
class
|
28
|
-
def initialize(
|
29
|
-
|
30
|
-
@env = request.env
|
31
|
-
else
|
32
|
-
@env = request
|
33
|
-
end
|
27
|
+
class Wrapper
|
28
|
+
def initialize(env)
|
29
|
+
@env = env
|
34
30
|
end
|
35
31
|
|
36
32
|
def localization
|
@@ -54,7 +50,7 @@ module Utopia
|
|
54
50
|
end
|
55
51
|
|
56
52
|
def self.[] request
|
57
|
-
|
53
|
+
Wrapper.new(request.env)
|
58
54
|
end
|
59
55
|
|
60
56
|
RESOURCE_NOT_FOUND = [400, {}, []].freeze
|
data/lib/utopia/path.rb
CHANGED
@@ -203,14 +203,16 @@ module Utopia
|
|
203
203
|
end
|
204
204
|
|
205
205
|
def +(other)
|
206
|
-
if other.kind_of?
|
207
|
-
return join(other)
|
208
|
-
elsif other.kind_of? Path
|
206
|
+
if other.kind_of? Path
|
209
207
|
if other.absolute?
|
210
208
|
return other
|
211
209
|
else
|
212
210
|
return join(other.components)
|
213
211
|
end
|
212
|
+
elsif other.kind_of? Array
|
213
|
+
return join(other)
|
214
|
+
elsif other.kind_of? String
|
215
|
+
return join(other.split(SEPARATOR, -1))
|
214
216
|
else
|
215
217
|
return join([other.to_s])
|
216
218
|
end
|
@@ -268,27 +270,25 @@ module Utopia
|
|
268
270
|
def descend(&block)
|
269
271
|
return to_enum(:descend) unless block_given?
|
270
272
|
|
271
|
-
|
273
|
+
components = []
|
272
274
|
|
273
275
|
@components.each do |component|
|
274
|
-
|
276
|
+
components << component
|
275
277
|
|
276
|
-
yield self.class.new(
|
278
|
+
yield self.class.new(components.dup)
|
277
279
|
end
|
278
280
|
end
|
279
281
|
|
280
282
|
def ascend(&block)
|
281
283
|
return to_enum(:ascend) unless block_given?
|
282
284
|
|
283
|
-
|
285
|
+
components = self.components.dup
|
284
286
|
|
285
|
-
|
286
|
-
|
287
|
+
while components.any?
|
288
|
+
yield self.class.new(components.dup)
|
287
289
|
|
288
|
-
|
289
|
-
|
290
|
-
next_parent = parent.dirname
|
291
|
-
end until next_parent.eql?(parent)
|
290
|
+
components.pop
|
291
|
+
end
|
292
292
|
end
|
293
293
|
|
294
294
|
def split(at)
|
data/lib/utopia/static.rb
CHANGED
@@ -144,10 +144,12 @@ module Utopia
|
|
144
144
|
File.mtime(full_path).httpdate
|
145
145
|
end
|
146
146
|
|
147
|
-
def
|
147
|
+
def bytesize
|
148
148
|
File.size(full_path)
|
149
149
|
end
|
150
150
|
|
151
|
+
alias size bytesize
|
152
|
+
|
151
153
|
def each
|
152
154
|
File.open(full_path, "rb") do |file|
|
153
155
|
file.seek(@range.begin)
|
@@ -176,6 +178,9 @@ module Utopia
|
|
176
178
|
return true
|
177
179
|
end
|
178
180
|
|
181
|
+
CONTENT_LENGTH = Rack::CONTENT_LENGTH
|
182
|
+
CONTENT_RANGE = 'Content-Range'.freeze
|
183
|
+
|
179
184
|
def serve(env, response_headers)
|
180
185
|
ranges = Rack::Utils.byte_ranges(env, size)
|
181
186
|
response = [200, response_headers, self]
|
@@ -186,7 +191,7 @@ module Utopia
|
|
186
191
|
# No ranges, or multiple ranges (which we don't support).
|
187
192
|
# TODO: Support multiple byte-ranges, for now just send entire file:
|
188
193
|
response[0] = 200
|
189
|
-
response[1][
|
194
|
+
response[1][CONTENT_LENGTH] = size.to_s
|
190
195
|
@range = 0...size
|
191
196
|
else
|
192
197
|
# Partial content:
|
@@ -194,25 +199,25 @@ module Utopia
|
|
194
199
|
partial_size = @range.count
|
195
200
|
|
196
201
|
response[0] = 206
|
197
|
-
response[1][
|
198
|
-
response[1][
|
202
|
+
response[1][CONTENT_LENGTH] = partial_size.to_s
|
203
|
+
response[1][CONTENT_RANGE] = "bytes #{@range.min}-#{@range.max}/#{size}"
|
199
204
|
end
|
200
|
-
|
201
|
-
# puts "Serving file #{full_path.inspect}, range #{@range.inspect}"
|
202
|
-
|
205
|
+
|
203
206
|
return response
|
204
207
|
end
|
205
208
|
end
|
206
209
|
|
207
210
|
public
|
208
211
|
|
212
|
+
DEFAULT_CACHE_CONTROL = 'public, max-age=3600'.freeze
|
213
|
+
|
209
214
|
def initialize(app, **options)
|
210
215
|
@app = app
|
211
216
|
@root = (options[:root] || Utopia::default_root).freeze
|
212
217
|
|
213
218
|
@extensions = MimeTypeLoader.extensions_for(options[:types] || MIME_TYPES[:default])
|
214
219
|
|
215
|
-
@cache_control = (options[:cache_control] ||
|
220
|
+
@cache_control = (options[:cache_control] || DEFAULT_CACHE_CONTROL)
|
216
221
|
|
217
222
|
self.freeze
|
218
223
|
end
|
@@ -237,7 +242,13 @@ module Utopia
|
|
237
242
|
end
|
238
243
|
|
239
244
|
attr :extensions
|
240
|
-
|
245
|
+
|
246
|
+
LAST_MODIFIED = 'Last-Modified'.freeze
|
247
|
+
CONTENT_TYPE = HTTP::CONTENT_TYPE
|
248
|
+
CACHE_CONTROL = HTTP::CACHE_CONTROL
|
249
|
+
ETAG = 'ETag'.freeze
|
250
|
+
ACCEPT_RANGES = 'Accept-Ranges'.freeze
|
251
|
+
|
241
252
|
def call(env)
|
242
253
|
path_info = env[Rack::PATH_INFO]
|
243
254
|
extension = File.extname(path_info)
|
@@ -251,11 +262,11 @@ module Utopia
|
|
251
262
|
|
252
263
|
if file = fetch_file(path)
|
253
264
|
response_headers = {
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
265
|
+
LAST_MODIFIED => file.mtime_date,
|
266
|
+
CONTENT_TYPE => @extensions[extension],
|
267
|
+
CACHE_CONTROL => @cache_control,
|
268
|
+
ETAG => file.etag,
|
269
|
+
ACCEPT_RANGES => "bytes"
|
259
270
|
}
|
260
271
|
|
261
272
|
if file.modified?(env)
|
data/lib/utopia/tags/override.rb
CHANGED
data/lib/utopia/version.rb
CHANGED
@@ -26,43 +26,51 @@ require 'etc'
|
|
26
26
|
# Users in group wheel can execute all commands as user http with no password.
|
27
27
|
# %wheel ALL=(http) NOPASSWD: ALL
|
28
28
|
|
29
|
-
|
30
|
-
puts args.join(' ')
|
31
|
-
system(*args)
|
32
|
-
end
|
29
|
+
GIT_WORK_TREE = `git config core.worktree`.chomp
|
33
30
|
|
34
|
-
|
31
|
+
# We convert GIT_DIR to an absolute path:
|
32
|
+
ENV['GIT_DIR'] = File.join(Dir.pwd, ENV['GIT_DIR'])
|
35
33
|
|
36
|
-
|
34
|
+
# We deploy the site as the user and group of the directory for the working tree:
|
35
|
+
File.stat(GIT_WORK_TREE).tap do |stat|
|
36
|
+
ENV['DEPLOY_USER'] = DEPLOY_USER = Etc.getpwuid(stat.uid).name
|
37
|
+
ENV['DEPLOY_GROUP'] = DEPLOY_GROUP = Etc.getgrgid(stat.gid).name
|
38
|
+
end
|
37
39
|
|
38
|
-
|
39
|
-
working_tree_stat = File.stat(working_tree)
|
40
|
-
deploy_user = Etc.getpwuid(working_tree_stat.uid).name
|
41
|
-
deploy_group = Etc.getgrgid(working_tree_stat.gid).name
|
40
|
+
WHOAMI = `whoami`.chomp!
|
42
41
|
|
43
|
-
|
42
|
+
# We should find out if we need to use sudo or not:
|
43
|
+
SUDO = if WHOAMI != DEPLOY_USER
|
44
|
+
["sudo", "-u", "DEPLOY_USER"]
|
45
|
+
end
|
44
46
|
|
45
|
-
|
46
|
-
|
47
|
-
|
47
|
+
def sh(command)
|
48
|
+
puts command.join(' ')
|
49
|
+
system(*command) or abort("Deployment failed!")
|
50
|
+
end
|
48
51
|
|
49
|
-
|
52
|
+
def sudo(command)
|
53
|
+
sh([*SUDO, *command])
|
54
|
+
end
|
55
|
+
|
56
|
+
puts "Deploying to #{GIT_WORK_TREE} as #{DEPLOY_USER}:#{DEPLOY_GROUP}..."
|
57
|
+
Dir.chdir(GIT_WORK_TREE) do
|
58
|
+
# Pass on our rights to the files we just uploaded to the deployment user/group:
|
59
|
+
sh %W{chmod -Rf ug+rwX .}
|
60
|
+
sh %W{chown -Rf #{DEPLOY_USER}:#{DEPLOY_GROUP} .}
|
50
61
|
|
51
|
-
|
52
|
-
|
62
|
+
sudo %W{git checkout -f}
|
63
|
+
sudo %W{git submodule update -i}
|
53
64
|
|
54
65
|
if File.exist? 'Gemfile'
|
55
|
-
|
66
|
+
sudo %W{bundle install --deployment --clean --jobs=4 --retry=2 --quiet}
|
56
67
|
end
|
57
68
|
|
58
|
-
ENV['DEPLOY_USER'] = deploy_user
|
59
|
-
ENV['DEPLOY_GROUP'] = deploy_group
|
60
|
-
|
61
69
|
if File.exist? 'Rakefile'
|
62
|
-
|
70
|
+
sudo %W{bundle exec rake deploy}
|
63
71
|
end
|
64
72
|
|
65
73
|
puts "Restarting server..."
|
66
|
-
|
67
|
-
|
74
|
+
sudo %W{mkdir -p tmp} unless File.exist?('tmp')
|
75
|
+
sudo %W{touch tmp/restart.txt}
|
68
76
|
end
|
data/setup/site/Gemfile
CHANGED
@@ -5,6 +5,14 @@ gem "utopia", "~> $UTOPIA_VERSION"
|
|
5
5
|
# gem "utopia-tags-gallery"
|
6
6
|
# gem "utopia-tags-google-analytics"
|
7
7
|
|
8
|
+
gem "rake"
|
9
|
+
gem "bundler"
|
10
|
+
|
8
11
|
group :development do
|
12
|
+
# For `rake server`:
|
9
13
|
gem "puma"
|
14
|
+
|
15
|
+
# For `rake console`:
|
16
|
+
gem "pry"
|
17
|
+
gem "rack-test"
|
10
18
|
end
|
data/setup/site/Rakefile
CHANGED
@@ -1,9 +1,32 @@
|
|
1
1
|
|
2
|
-
|
3
|
-
system('puma')
|
4
|
-
end
|
5
|
-
|
2
|
+
desc 'Run by git post-update hook when deployed to a web server'
|
6
3
|
task :deploy do
|
7
4
|
# This task is typiclly run after the site is updated but before the server is restarted.
|
8
|
-
|
9
|
-
|
5
|
+
end
|
6
|
+
|
7
|
+
desc 'Set up the environment for running your web application'
|
8
|
+
task :environment do
|
9
|
+
RACK_ENV = (ENV['RACK_ENV'] ||= 'development').to_sym
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Run a server for testing your web application'
|
13
|
+
task :server => :environment do
|
14
|
+
port = ENV.fetch('SERVER_PORT', 9292)
|
15
|
+
system('puma', '-p', port)
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Start an interactive console for your web application'
|
19
|
+
task :console => :environment do
|
20
|
+
require 'pry'
|
21
|
+
require 'rack/test'
|
22
|
+
|
23
|
+
include Rack::Test::Methods
|
24
|
+
|
25
|
+
def app
|
26
|
+
@app ||= Rack::Builder.parse_file(File.expand_path("config.ru", __dir__)).first
|
27
|
+
end
|
28
|
+
|
29
|
+
Pry.start
|
30
|
+
end
|
31
|
+
|
32
|
+
task :default => :server
|
data/setup/site/config.ru
CHANGED
@@ -14,23 +14,28 @@ require 'utopia'
|
|
14
14
|
require 'rack/cache'
|
15
15
|
|
16
16
|
if RACK_ENV == :production
|
17
|
+
# Handle exceptions in production with a error page and send an email notification:
|
17
18
|
use Utopia::Exceptions::Handler
|
18
19
|
use Utopia::Exceptions::Mailer
|
19
|
-
|
20
|
-
|
20
|
+
else
|
21
|
+
# We want to propate exceptions up when running tests:
|
22
|
+
use Rack::ShowExceptions unless RACK_ENV == :test
|
23
|
+
|
24
|
+
# Serve the public directory in a similar way to the web server:
|
21
25
|
use Utopia::Static, root: 'public'
|
22
26
|
end
|
23
27
|
|
24
28
|
use Rack::Sendfile
|
25
29
|
|
26
30
|
if RACK_ENV == :production
|
31
|
+
# Cache dynamically generated content where possible:
|
27
32
|
use Rack::Cache,
|
28
33
|
metastore: "file:#{Utopia::default_root("cache/meta")}",
|
29
34
|
entitystore: "file:#{Utopia::default_root("cache/body")}",
|
30
35
|
verbose: RACK_ENV == :development
|
31
36
|
end
|
32
37
|
|
33
|
-
use
|
38
|
+
use Utopia::ContentLength
|
34
39
|
|
35
40
|
use Utopia::Redirection::Rewrite,
|
36
41
|
'/' => '/welcome/index'
|
@@ -50,11 +55,6 @@ use Utopia::Controller,
|
|
50
55
|
|
51
56
|
use Utopia::Static
|
52
57
|
|
53
|
-
if RACK_ENV != :production
|
54
|
-
# Serve static files from public/ when not running in a production environment:
|
55
|
-
use Utopia::Static, root: 'public'
|
56
|
-
end
|
57
|
-
|
58
58
|
# Serve dynamic content
|
59
59
|
use Utopia::Content,
|
60
60
|
cache_templates: (RACK_ENV == :production),
|
@@ -1,2 +1,2 @@
|
|
1
|
-
<?r
|
1
|
+
<?r transaction.attributes[:title] ||= content ?>
|
2
2
|
<h1><content/></h1>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<?r response.content_type = "text/html; charset=utf-8" ?>
|
5
5
|
<?r response.cache! ?>
|
6
6
|
|
7
|
-
<?r if title =
|
7
|
+
<?r if title = self[:title] ?>
|
8
8
|
<title>#{title.gsub(/<.*?>/, "")} - Utopia</title>
|
9
9
|
<?r else ?>
|
10
10
|
<title>Utopia</title>
|
@@ -14,7 +14,7 @@
|
|
14
14
|
<link rel="stylesheet" href="/_static/site.css" type="text/css" media="screen" />
|
15
15
|
</head>
|
16
16
|
|
17
|
-
<body class="#{attributes[
|
17
|
+
<body class="#{attributes[:class]}">
|
18
18
|
<div id="header">
|
19
19
|
<img src="/_static/utopia.svg" />
|
20
20
|
</div>
|
data/spec/utopia/content_spec.rb
CHANGED
@@ -79,15 +79,15 @@ module Utopia::ContentSpec
|
|
79
79
|
expect(output.string).to be == '<h1>Hello World</h1>'
|
80
80
|
end
|
81
81
|
|
82
|
-
it "should fetch
|
82
|
+
it "should fetch template and use cache" do
|
83
83
|
node_path = File.expand_path('../pages/index.xnode', __FILE__)
|
84
84
|
|
85
|
-
template = content.
|
85
|
+
template = content.fetch_template(node_path)
|
86
86
|
|
87
87
|
expect(template).to be_kind_of Trenni::Template
|
88
88
|
|
89
89
|
# Check that the same object is returned:
|
90
|
-
expect(template).to be content.
|
90
|
+
expect(template).to be content.fetch_template(node_path)
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
@@ -93,7 +93,7 @@ module Utopia::Controller::SequenceSpec
|
|
93
93
|
|
94
94
|
result = controller.process!(request, Utopia::Path["/variable"])
|
95
95
|
expect(result).to be == nil
|
96
|
-
expect(variables.to_hash).to be == {
|
96
|
+
expect(variables.to_hash).to be == {:variable => :value}
|
97
97
|
end
|
98
98
|
|
99
99
|
it "should call direct controller methods" do
|
@@ -1 +1 @@
|
|
1
|
-
#{local_path}
|
1
|
+
#{current.node.local_path}
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# Copyright, 2016, 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 'rack_helper'
|
22
|
+
|
23
|
+
require 'benchmark/ips' if ENV['BENCHMARK']
|
24
|
+
require 'ruby-prof' if ENV['PROFILE']
|
25
|
+
require 'flamegraph' if ENV['FLAMEGRAPH']
|
26
|
+
|
27
|
+
RSpec.describe "Utopia Performance" do
|
28
|
+
include_context "rack app", "performance_spec/config.ru"
|
29
|
+
|
30
|
+
if defined? Benchmark
|
31
|
+
def benchmark(name = nil)
|
32
|
+
Benchmark.ips do |benchmark|
|
33
|
+
benchmark.report(name) do |i|
|
34
|
+
yield i
|
35
|
+
end
|
36
|
+
|
37
|
+
benchmark.compare!
|
38
|
+
end
|
39
|
+
end
|
40
|
+
elsif defined? RubyProf
|
41
|
+
def benchmark(name)
|
42
|
+
result = RubyProf.profile do
|
43
|
+
yield 1000
|
44
|
+
end
|
45
|
+
|
46
|
+
result.eliminate_methods!([/^((?!Utopia).)*$/])
|
47
|
+
printer = RubyProf::FlatPrinter.new(result)
|
48
|
+
printer.print($stderr, min_percent: 1.0)
|
49
|
+
|
50
|
+
printer = RubyProf::GraphHtmlPrinter.new(result)
|
51
|
+
filename = name.gsub('/', '_') + '.html'
|
52
|
+
File.open(filename, "w") do |file|
|
53
|
+
printer.print(file)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
elsif defined? Flamegraph
|
57
|
+
def benchmark(name)
|
58
|
+
filename = name.gsub('/', '_') + '.html'
|
59
|
+
Flamegraph.generate(filename) do
|
60
|
+
yield 1
|
61
|
+
end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
def benchmark(name)
|
65
|
+
yield 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should be fast to access basic page" do
|
70
|
+
env = Rack::MockRequest.env_for("/welcome/index")
|
71
|
+
status, headers, response = app.call(env)
|
72
|
+
|
73
|
+
expect(status).to be == 200
|
74
|
+
|
75
|
+
benchmark("/welcome/index") do |i|
|
76
|
+
i.times { app.call(env) }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should be fast to invoke a controller" do
|
81
|
+
env = Rack::MockRequest.env_for("/api/fetch")
|
82
|
+
status, headers, response = app.call(env)
|
83
|
+
|
84
|
+
expect(status).to be == 200
|
85
|
+
|
86
|
+
benchmark("/api/fetch") do |i|
|
87
|
+
i.times { app.call(env) }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|