utopia 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|