akephalos-nerian 0.2.4-java
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.
- data/MIT_LICENSE +20 -0
- data/README.md +56 -0
- data/bin/akephalos +88 -0
- data/lib/akephalos.rb +19 -0
- data/lib/akephalos/capybara.rb +316 -0
- data/lib/akephalos/client.rb +112 -0
- data/lib/akephalos/client/cookies.rb +73 -0
- data/lib/akephalos/client/filter.rb +120 -0
- data/lib/akephalos/configuration.rb +49 -0
- data/lib/akephalos/console.rb +32 -0
- data/lib/akephalos/cucumber.rb +6 -0
- data/lib/akephalos/htmlunit.rb +38 -0
- data/lib/akephalos/htmlunit/ext/http_method.rb +30 -0
- data/lib/akephalos/node.rb +172 -0
- data/lib/akephalos/page.rb +113 -0
- data/lib/akephalos/remote_client.rb +84 -0
- data/lib/akephalos/server.rb +56 -0
- data/lib/akephalos/version.rb +3 -0
- data/src/htmlunit/apache-mime4j-0.6.jar +0 -0
- data/src/htmlunit/commons-codec-1.4.jar +0 -0
- data/src/htmlunit/commons-collections-3.2.1.jar +0 -0
- data/src/htmlunit/commons-io-1.4.jar +0 -0
- data/src/htmlunit/commons-lang-2.4.jar +0 -0
- data/src/htmlunit/commons-logging-1.1.1.jar +0 -0
- data/src/htmlunit/cssparser-0.9.5.jar +0 -0
- data/src/htmlunit/htmlunit-2.8.jar +0 -0
- data/src/htmlunit/htmlunit-core-js-2.8.jar +0 -0
- data/src/htmlunit/httpclient-4.0.1.jar +0 -0
- data/src/htmlunit/httpcore-4.0.1.jar +0 -0
- data/src/htmlunit/httpmime-4.0.1.jar +0 -0
- data/src/htmlunit/nekohtml-1.9.14.jar +0 -0
- data/src/htmlunit/sac-1.3.jar +0 -0
- data/src/htmlunit/serializer-2.7.1.jar +0 -0
- data/src/htmlunit/xalan-2.7.1.jar +0 -0
- data/src/htmlunit/xercesImpl-2.9.1.jar +0 -0
- data/src/htmlunit/xml-apis-1.3.04.jar +0 -0
- metadata +150 -0
data/MIT_LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Bernerd Schaefer
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Akephalos
|
2
|
+
Akephalos is a full-stack headless browser for integration testing with
|
3
|
+
Capybara. It is built on top of [HtmlUnit](http://htmlunit.sourceforge.net),
|
4
|
+
a GUI-less browser for the Java platform, but can be run on both JRuby and
|
5
|
+
MRI with no need for JRuby to be installed on the system.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
gem install akephalos
|
10
|
+
|
11
|
+
## Setup
|
12
|
+
|
13
|
+
Configuring akephalos is as simple as requiring it and setting Capybara's
|
14
|
+
javascript driver:
|
15
|
+
|
16
|
+
require 'akephalos'
|
17
|
+
Capybara.javascript_driver = :akephalos
|
18
|
+
|
19
|
+
## Basic Usage
|
20
|
+
|
21
|
+
Akephalos provides a driver for Capybara, so using Akephalos is no
|
22
|
+
different than using Selenium or Rack::Test. For a full usage guide, check
|
23
|
+
out Capybara's DSL [documentation](http://github.com/jnicklas/capybara). It
|
24
|
+
makes no assumptions about the testing framework being used, and works with
|
25
|
+
RSpec, Cucumber, and Test::Unit.
|
26
|
+
|
27
|
+
Here's some sample RSpec code:
|
28
|
+
|
29
|
+
describe "Home Page" do
|
30
|
+
before { visit "/" }
|
31
|
+
context "searching" do
|
32
|
+
before do
|
33
|
+
fill_in "Search", :with => "akephalos"
|
34
|
+
click_button "Go"
|
35
|
+
end
|
36
|
+
it "returns results" { page.should have_css("#results") }
|
37
|
+
it "includes the search term" { page.should have_content("akephalos") }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
## More
|
42
|
+
|
43
|
+
* [bin/akephalos](http://bernerdschaefer.github.com/akephalos/akephalos-bin.html)
|
44
|
+
allows you to start an interactive shell or DRb server, as well as perform
|
45
|
+
other maintenance features.
|
46
|
+
|
47
|
+
* [Filters](http://bernerdschaefer.github.com/akephalos/filters.html) allows
|
48
|
+
you to declare mock responses for external resources and services requested
|
49
|
+
by the browser.
|
50
|
+
|
51
|
+
## Resources
|
52
|
+
|
53
|
+
* [API Documentation](http://bernerdschaefer.github.com/akephalos/api)
|
54
|
+
* [Source code](http://github.com/bernerdschaefer/akephalos) and
|
55
|
+
[issues](http://github.com/bernerdschaefer/akephalos/issues) are hosted on
|
56
|
+
github.
|
data/bin/akephalos
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim:set filetype=ruby:
|
3
|
+
|
4
|
+
require "pathname"
|
5
|
+
require "optparse"
|
6
|
+
|
7
|
+
options = { :interactive => false }
|
8
|
+
|
9
|
+
parser = OptionParser.new do |opts|
|
10
|
+
opts.banner = "Usage: akephalos [--interactive, --use-htmlunit-snapshot] | [--server] <port>"
|
11
|
+
opts.on("-s", "--server", "Run in server mode (default)")
|
12
|
+
opts.on("-i", "--interactive", "Run in interactive mode") { options[:interactive] = true }
|
13
|
+
opts.on("--use-htmlunit-snapshot", "Use the snapshot of htmlunit") { options[:use_htmlunit_snapshot] = true }
|
14
|
+
|
15
|
+
opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
|
16
|
+
end
|
17
|
+
parser.parse!
|
18
|
+
|
19
|
+
root = Pathname(__FILE__).expand_path.dirname.parent
|
20
|
+
lib = root + 'lib'
|
21
|
+
src = root + 'src'
|
22
|
+
|
23
|
+
case
|
24
|
+
when options[:use_htmlunit_snapshot]
|
25
|
+
require "fileutils"
|
26
|
+
|
27
|
+
FileUtils.mkdir_p("vendor/htmlunit")
|
28
|
+
Dir["vendor/htmlunit/*.jar"].each { |jar| File.unlink(jar) }
|
29
|
+
|
30
|
+
Dir.chdir("vendor") do
|
31
|
+
$stdout.print "Downloading latest snapshot... "
|
32
|
+
$stdout.flush
|
33
|
+
%x[curl -O http://build.canoo.com/htmlunit/artifacts/htmlunit-2.9-SNAPSHOT-with-dependencies.zip &> /dev/null]
|
34
|
+
puts "done"
|
35
|
+
|
36
|
+
$stdout.print "Extracting dependencies... "
|
37
|
+
$stdout.flush
|
38
|
+
%x[unzip -j -d htmlunit htmlunit-2.9-SNAPSHOT-with-dependencies.zip htmlunit-2.9-SNAPSHOT/lib htmlunit-2.9-SNAPSHOT/lib/* &> /dev/null]
|
39
|
+
puts "done"
|
40
|
+
|
41
|
+
File.unlink "htmlunit-2.9-SNAPSHOT-with-dependencies.zip"
|
42
|
+
end
|
43
|
+
|
44
|
+
$stdout.puts "="*40
|
45
|
+
$stdout.puts "The latest HtmlUnit snapshot has been extracted to vendor/htmlunit!"
|
46
|
+
when options[:interactive]
|
47
|
+
$:.unshift('vendor', lib, src)
|
48
|
+
require 'rubygems'
|
49
|
+
require 'akephalos'
|
50
|
+
require 'akephalos/console'
|
51
|
+
Akephalos::Console.start
|
52
|
+
else
|
53
|
+
unless port = ARGV[0]
|
54
|
+
puts parser.help
|
55
|
+
exit
|
56
|
+
end
|
57
|
+
|
58
|
+
if RUBY_PLATFORM == "java"
|
59
|
+
$:.unshift("vendor", lib, src)
|
60
|
+
require 'akephalos/server'
|
61
|
+
Akephalos::Server.start!(port)
|
62
|
+
else
|
63
|
+
require 'rubygems'
|
64
|
+
require 'jruby-jars'
|
65
|
+
|
66
|
+
jruby = JRubyJars.core_jar_path
|
67
|
+
jruby_stdlib = JRubyJars.stdlib_jar_path
|
68
|
+
|
69
|
+
java_args = [
|
70
|
+
"-Xmx128M",
|
71
|
+
"-cp", [JRubyJars.core_jar_path, JRubyJars.stdlib_jar_path].join(":"),
|
72
|
+
"org.jruby.Main"
|
73
|
+
]
|
74
|
+
ruby_args = [
|
75
|
+
"-Ku",
|
76
|
+
"-I", "vendor:#{lib}:#{src}",
|
77
|
+
"-r", "akephalos/server",
|
78
|
+
"-e", "Akephalos::Server.start!(#{port.inspect})"
|
79
|
+
]
|
80
|
+
|
81
|
+
# Bundler sets ENV["RUBYOPT"] to automatically load bundler/setup.rb, but
|
82
|
+
# since the akephalos server doesn't have any gem dependencies and is
|
83
|
+
# always executed with the same context, we clear RUBYOPT before running
|
84
|
+
# exec.
|
85
|
+
ENV["RUBYOPT"] = ""
|
86
|
+
exec("java", *(java_args + ruby_args))
|
87
|
+
end
|
88
|
+
end
|
data/lib/akephalos.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# **Akephalos** is a cross-platform Ruby interface for *HtmlUnit*, a headless
|
2
|
+
# browser for the Java platform.
|
3
|
+
#
|
4
|
+
# The only requirement is that a Java runtime is available.
|
5
|
+
#
|
6
|
+
require 'java' if RUBY_PLATFORM == 'java'
|
7
|
+
require 'pathname'
|
8
|
+
|
9
|
+
module Akephalos
|
10
|
+
BIN_DIR = Pathname(__FILE__).expand_path.dirname.parent + 'bin'
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'akephalos/client'
|
14
|
+
require 'capybara'
|
15
|
+
require 'akephalos/capybara'
|
16
|
+
|
17
|
+
Capybara.register_driver :akephalos do |app|
|
18
|
+
Capybara::Driver::Akephalos.new(app)
|
19
|
+
end
|
@@ -0,0 +1,316 @@
|
|
1
|
+
# Driver class exposed to Capybara. It implements Capybara's full driver API,
|
2
|
+
# and is the entry point for interaction between the test suites and HtmlUnit.
|
3
|
+
#
|
4
|
+
# This class and +Capybara::Driver::Akephalos::Node+ are written to run on both
|
5
|
+
# MRI and JRuby, and is agnostic whether the Akephalos::Client instance is used
|
6
|
+
# directly or over DRb.
|
7
|
+
class Capybara::Driver::Akephalos < Capybara::Driver::Base
|
8
|
+
|
9
|
+
# Akephalos-specific implementation for Capybara's Driver::Node class.
|
10
|
+
class Node < Capybara::Driver::Node
|
11
|
+
|
12
|
+
# @api capybara
|
13
|
+
# @return [String] the inner text of the node
|
14
|
+
def text
|
15
|
+
native.text
|
16
|
+
end
|
17
|
+
|
18
|
+
# @api capybara
|
19
|
+
# @param [String] name attribute name
|
20
|
+
# @return [String] the attribute value
|
21
|
+
def [](name)
|
22
|
+
name = name.to_s
|
23
|
+
case name
|
24
|
+
when 'checked'
|
25
|
+
native.checked?
|
26
|
+
else
|
27
|
+
native[name.to_s]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# @api capybara
|
32
|
+
# @return [String, Array<String>] the form element's value
|
33
|
+
def value
|
34
|
+
native.value
|
35
|
+
end
|
36
|
+
|
37
|
+
# Set the form element's value.
|
38
|
+
#
|
39
|
+
# @api capybara
|
40
|
+
# @param [String] value the form element's new value
|
41
|
+
def set(value)
|
42
|
+
if tag_name == 'textarea'
|
43
|
+
native.value = value.to_s
|
44
|
+
elsif tag_name == 'input' and type == 'radio'
|
45
|
+
click
|
46
|
+
elsif tag_name == 'input' and type == 'checkbox'
|
47
|
+
if value != self['checked']
|
48
|
+
click
|
49
|
+
end
|
50
|
+
elsif tag_name == 'input'
|
51
|
+
native.value = value.to_s
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @api capybara
|
56
|
+
def select_option
|
57
|
+
native.click
|
58
|
+
end
|
59
|
+
|
60
|
+
# Unselect an option from a select box.
|
61
|
+
#
|
62
|
+
# @api capybara
|
63
|
+
def unselect_option
|
64
|
+
unless select_node.multiple_select?
|
65
|
+
raise Capybara::UnselectNotAllowed, "Cannot unselect option from single select box."
|
66
|
+
end
|
67
|
+
|
68
|
+
native.unselect
|
69
|
+
end
|
70
|
+
|
71
|
+
# Click the element.
|
72
|
+
def click
|
73
|
+
native.click
|
74
|
+
end
|
75
|
+
|
76
|
+
# Drag the element on top of the target element.
|
77
|
+
#
|
78
|
+
# @api capybara
|
79
|
+
# @param [Node] element the target element
|
80
|
+
def drag_to(element)
|
81
|
+
trigger('mousedown')
|
82
|
+
element.trigger('mousemove')
|
83
|
+
element.trigger('mouseup')
|
84
|
+
end
|
85
|
+
|
86
|
+
# @api capybara
|
87
|
+
# @return [String] the element's tag name
|
88
|
+
def tag_name
|
89
|
+
native.tag_name
|
90
|
+
end
|
91
|
+
|
92
|
+
# @api capybara
|
93
|
+
# @return [true, false] the element's visiblity
|
94
|
+
def visible?
|
95
|
+
native.visible?
|
96
|
+
end
|
97
|
+
|
98
|
+
# @api capybara
|
99
|
+
# @return [true, false] the element's visiblity
|
100
|
+
def checked?
|
101
|
+
native.checked?
|
102
|
+
end
|
103
|
+
|
104
|
+
# @api capybara
|
105
|
+
# @return [true, false] the element's visiblity
|
106
|
+
def selected?
|
107
|
+
native.selected?
|
108
|
+
end
|
109
|
+
|
110
|
+
# @api capybara
|
111
|
+
# @return [String] the XPath to locate the node
|
112
|
+
def path
|
113
|
+
native.xpath
|
114
|
+
end
|
115
|
+
|
116
|
+
# Trigger an event on the element.
|
117
|
+
#
|
118
|
+
# @api capybara
|
119
|
+
# @param [String] event the event to trigger
|
120
|
+
def trigger(event)
|
121
|
+
native.fire_event(event.to_s)
|
122
|
+
end
|
123
|
+
|
124
|
+
# @api capybara
|
125
|
+
# @param [String] selector XPath query
|
126
|
+
# @return [Array<Node>] the matched nodes
|
127
|
+
def find(selector)
|
128
|
+
nodes = []
|
129
|
+
native.find(selector).each { |node| nodes << self.class.new(self, node) }
|
130
|
+
nodes
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
|
135
|
+
# @return [true, false] whether the node allows multiple-option selection (if the node is a select).
|
136
|
+
def multiple_select?
|
137
|
+
tag_name == "select" && native.multiple_select?
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
# Return all child nodes which match the selector criteria.
|
143
|
+
#
|
144
|
+
# @api capybara
|
145
|
+
# @return [Array<Node>] the matched nodes
|
146
|
+
def all_unfiltered(selector)
|
147
|
+
nodes = []
|
148
|
+
native.find(selector).each { |node| nodes << self.class.new(driver, node) }
|
149
|
+
nodes
|
150
|
+
end
|
151
|
+
|
152
|
+
# @return [String] the node's type attribute
|
153
|
+
def type
|
154
|
+
native[:type]
|
155
|
+
end
|
156
|
+
|
157
|
+
# @return [Node] the select node, if this is an option node
|
158
|
+
def select_node
|
159
|
+
find('./ancestor::select').first
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
attr_reader :app, :rack_server
|
164
|
+
|
165
|
+
# @return [Client] an instance of Akephalos::Client
|
166
|
+
def self.driver
|
167
|
+
@driver ||= Akephalos::Client.new
|
168
|
+
end
|
169
|
+
|
170
|
+
def initialize(app)
|
171
|
+
@app = app
|
172
|
+
@rack_server = Capybara::Server.new(@app)
|
173
|
+
@rack_server.boot if Capybara.run_server
|
174
|
+
end
|
175
|
+
|
176
|
+
# Visit the given path in the browser.
|
177
|
+
#
|
178
|
+
# @param [String] path relative path to visit
|
179
|
+
def visit(path)
|
180
|
+
browser.visit(url(path))
|
181
|
+
end
|
182
|
+
|
183
|
+
# @return [String] the page's original source
|
184
|
+
def source
|
185
|
+
page.source
|
186
|
+
end
|
187
|
+
|
188
|
+
# @return [String] the page's modified source
|
189
|
+
# page.modified_source will return a string with
|
190
|
+
# html entities converted into the unicode equivalent
|
191
|
+
# but the string will be marked as ASCII-8BIT
|
192
|
+
# which causes conversion issues so we force the encoding
|
193
|
+
# to UTF-8 (ruby 1.9 only)
|
194
|
+
def body
|
195
|
+
body_source = page.modified_source
|
196
|
+
|
197
|
+
if body_source.respond_to?(:force_encoding)
|
198
|
+
body_source.force_encoding("UTF-8")
|
199
|
+
else
|
200
|
+
body_source
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# @return [Hash{String => String}] the page's response headers
|
205
|
+
def response_headers
|
206
|
+
page.response_headers
|
207
|
+
end
|
208
|
+
|
209
|
+
# @return [Integer] the response's status code
|
210
|
+
def status_code
|
211
|
+
page.status_code
|
212
|
+
end
|
213
|
+
|
214
|
+
# Execute the given block within the context of a specified frame.
|
215
|
+
#
|
216
|
+
# @param [String] frame_id the frame's id
|
217
|
+
# @raise [Capybara::ElementNotFound] if the frame is not found
|
218
|
+
def within_frame(frame_id, &block)
|
219
|
+
unless page.within_frame(frame_id, &block)
|
220
|
+
raise Capybara::ElementNotFound, "Unable to find frame with id '#{frame_id}'"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# Clear all cookie session data.
|
225
|
+
# @deprecated This method is deprecated in Capybara's master branch. Use
|
226
|
+
# {#reset!} instead.
|
227
|
+
def cleanup!
|
228
|
+
reset!
|
229
|
+
end
|
230
|
+
|
231
|
+
# Clear all cookie session data.
|
232
|
+
def reset!
|
233
|
+
cookies.clear
|
234
|
+
end
|
235
|
+
|
236
|
+
# @return [String] the page's current URL
|
237
|
+
def current_url
|
238
|
+
page.current_url
|
239
|
+
end
|
240
|
+
|
241
|
+
# Search for nodes which match the given XPath selector.
|
242
|
+
#
|
243
|
+
# @param [String] selector XPath query
|
244
|
+
# @return [Array<Node>] the matched nodes
|
245
|
+
def find(selector)
|
246
|
+
nodes = []
|
247
|
+
page.find(selector).each { |node| nodes << Node.new(self, node) }
|
248
|
+
nodes
|
249
|
+
end
|
250
|
+
|
251
|
+
# Execute JavaScript against the current page, discarding any return value.
|
252
|
+
#
|
253
|
+
# @param [String] script the JavaScript to be executed
|
254
|
+
# @return [nil]
|
255
|
+
def execute_script(script)
|
256
|
+
page.execute_script script
|
257
|
+
end
|
258
|
+
|
259
|
+
# Execute JavaScript against the current page and return the results.
|
260
|
+
#
|
261
|
+
# @param [String] script the JavaScript to be executed
|
262
|
+
# @return the result of the JavaScript
|
263
|
+
def evaluate_script(script)
|
264
|
+
page.evaluate_script script
|
265
|
+
end
|
266
|
+
|
267
|
+
# @return the current page
|
268
|
+
def page
|
269
|
+
browser.page
|
270
|
+
end
|
271
|
+
|
272
|
+
# @return the browser
|
273
|
+
def browser
|
274
|
+
self.class.driver
|
275
|
+
end
|
276
|
+
|
277
|
+
# @return the session cookies
|
278
|
+
def cookies
|
279
|
+
browser.cookies
|
280
|
+
end
|
281
|
+
|
282
|
+
# @return [String] the current user agent string
|
283
|
+
def user_agent
|
284
|
+
browser.user_agent
|
285
|
+
end
|
286
|
+
|
287
|
+
# Set the User-Agent header for this session. If :default is given, the
|
288
|
+
# User-Agent header will be reset to the default browser's user agent.
|
289
|
+
#
|
290
|
+
# @param [:default] user_agent the default user agent
|
291
|
+
# @param [String] user_agent the user agent string to use
|
292
|
+
def user_agent=(user_agent)
|
293
|
+
browser.user_agent = user_agent
|
294
|
+
end
|
295
|
+
|
296
|
+
# Disable waiting in Capybara, since waiting is handled directly by
|
297
|
+
# Akephalos.
|
298
|
+
#
|
299
|
+
# @return [false]
|
300
|
+
def wait
|
301
|
+
false
|
302
|
+
end
|
303
|
+
|
304
|
+
private
|
305
|
+
|
306
|
+
# @param [String] path
|
307
|
+
# @return [String] the absolute URL for the given path
|
308
|
+
def url(path)
|
309
|
+
rack_server.url(path)
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
313
|
+
|
314
|
+
Capybara.register_driver :akephalos do |app|
|
315
|
+
Capybara::Driver::Akephalos.new(app)
|
316
|
+
end
|