mumukit 0.3.0 → 2.4.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/bin/limit +10 -0
- data/bin/mulang +0 -0
- data/lib/locales/en.yml +4 -0
- data/lib/locales/es.yml +4 -0
- data/lib/mumukit/defaults/default_expectations_hook.rb +9 -0
- data/lib/mumukit/defaults/default_feedback_hook.rb +5 -0
- data/lib/mumukit/defaults/default_metadata_hook.rb +5 -0
- data/lib/mumukit/defaults/default_query_hook.rb +9 -0
- data/lib/mumukit/defaults/default_test_hook.rb +9 -0
- data/lib/mumukit/defaults/default_validation_hook.rb +4 -0
- data/lib/mumukit/defaults.rb +11 -0
- data/lib/mumukit/env.rb +11 -0
- data/lib/mumukit/hook.rb +30 -0
- data/lib/mumukit/isolated_environment.rb +51 -0
- data/lib/mumukit/metatest/checker.rb +34 -0
- data/lib/mumukit/metatest/errors.rb +10 -0
- data/lib/mumukit/metatest/framework.rb +20 -0
- data/lib/mumukit/metatest/identity_runner.rb +7 -0
- data/lib/mumukit/metatest.rb +7 -0
- data/lib/mumukit/request_validation_error.rb +4 -0
- data/lib/mumukit/runner.rb +33 -0
- data/lib/mumukit/runtime/info.rb +30 -0
- data/lib/mumukit/runtime/runtime.rb +35 -0
- data/lib/mumukit/runtime/shortcuts.rb +12 -0
- data/lib/mumukit/runtime/symbol.rb +17 -0
- data/lib/mumukit/runtime.rb +4 -0
- data/lib/mumukit/server/app.rb +71 -0
- data/lib/mumukit/server/response_builder.rb +62 -0
- data/lib/mumukit/server/test_server.rb +118 -0
- data/lib/mumukit/server.rb +7 -0
- data/lib/mumukit/templates/file_hook.rb +49 -0
- data/lib/mumukit/templates/mulang_expectations_hook.rb +57 -0
- data/lib/mumukit/templates/with_code_smells.rb +7 -0
- data/lib/mumukit/templates/with_embedded_environment.rb +12 -0
- data/lib/mumukit/templates/with_isolated_environment.rb +11 -0
- data/lib/mumukit/templates/with_mashup_file_content.rb +11 -0
- data/lib/mumukit/templates/with_structured_results.rb +15 -0
- data/lib/mumukit/templates.rb +12 -0
- data/lib/mumukit/version.rb +1 -1
- data/lib/mumukit/with_command_line.rb +33 -1
- data/lib/mumukit/with_content_type.rb +5 -0
- data/lib/mumukit/with_tempfile.rb +13 -2
- data/lib/mumukit.rb +62 -8
- metadata +180 -21
- data/lib/mumukit/file_test_compiler.rb +0 -14
- data/lib/mumukit/file_test_runner.rb +0 -20
- data/lib/mumukit/stub.rb +0 -5
- data/lib/mumukit/test_server.rb +0 -42
- data/lib/mumukit/test_server_app.rb +0 -28
- data/lib/stubs/expectations_runner.rb +0 -5
- data/lib/stubs/test_runner.rb +0 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f8afb7358f144c2e25c356c70f0061144ff2f04
|
4
|
+
data.tar.gz: d41e834b6c92fea4897c87d46f5d3303983a8209
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4830f051f2f4ede2c1a76f05306974ea4c8d05947b2a0b8e4d2f7c3b3ad5dbb32207045accb30c10815bd90dd2e6ccc8a9e982a947e48c0fd17df2582b67e63
|
7
|
+
data.tar.gz: d1c908995f05aeab6cd7bc6a47c4614a20ec242cb3e49fee4497060c03ebc87a3636578652a3fb3716016543861405db1c7a4cd94335772303ed5cc30a06d30a
|
data/bin/limit
ADDED
data/bin/mulang
ADDED
Binary file
|
data/lib/locales/en.yml
ADDED
data/lib/locales/es.yml
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
module Mumukit
|
2
|
+
module Defaults
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
require_relative './defaults/default_expectations_hook'
|
7
|
+
require_relative './defaults/default_query_hook'
|
8
|
+
require_relative './defaults/default_feedback_hook'
|
9
|
+
require_relative './defaults/default_validation_hook'
|
10
|
+
require_relative './defaults/default_test_hook'
|
11
|
+
require_relative './defaults/default_metadata_hook'
|
data/lib/mumukit/env.rb
ADDED
data/lib/mumukit/hook.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
class Mumukit::Hook
|
2
|
+
attr_reader :config
|
3
|
+
|
4
|
+
include Mumukit::WithContentType
|
5
|
+
|
6
|
+
def initialize(config=nil)
|
7
|
+
@config = (config||{}).with_indifferent_access
|
8
|
+
end
|
9
|
+
|
10
|
+
def t(*args)
|
11
|
+
I18n.t(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(name, *args, &block)
|
15
|
+
super unless should_forward_to_config?(args, name, &block)
|
16
|
+
@config[name]
|
17
|
+
end
|
18
|
+
|
19
|
+
def env
|
20
|
+
Mumukit::Env.env
|
21
|
+
end
|
22
|
+
|
23
|
+
def logger
|
24
|
+
env['rack.logger']
|
25
|
+
end
|
26
|
+
|
27
|
+
def should_forward_to_config?(args, name)
|
28
|
+
args.length == 0 && !block_given? && @config[name]
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'docker'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Mumukit
|
5
|
+
class IsolatedEnvironment
|
6
|
+
|
7
|
+
attr_accessor :container
|
8
|
+
|
9
|
+
def configure!(*files)
|
10
|
+
|
11
|
+
filenames = files.map { |it| File.absolute_path(it.path) }
|
12
|
+
dirnames = filenames.map { |it| Pathname.new(it).dirname }
|
13
|
+
|
14
|
+
binds = dirnames.map { |it| "#{it}:#{it}" }
|
15
|
+
volumes = Hash[[dirnames.map { |it| [it, {}] }]]
|
16
|
+
|
17
|
+
command = yield(*filenames).split
|
18
|
+
|
19
|
+
self.container = Docker::Container.create(
|
20
|
+
'Image' => Mumukit.config.docker_image,
|
21
|
+
'Cmd' => command,
|
22
|
+
'NetworkDisabled' => true,
|
23
|
+
'HostConfig' => {
|
24
|
+
'Binds' => binds},
|
25
|
+
'Volumes' => volumes)
|
26
|
+
end
|
27
|
+
|
28
|
+
def run!
|
29
|
+
container.start
|
30
|
+
container.wait(Mumukit.config.command_time_limit)
|
31
|
+
|
32
|
+
exit = container.json['State']['ExitCode']
|
33
|
+
out = container.streaming_logs(stdout: true, stderr: true)
|
34
|
+
|
35
|
+
if exit == 0
|
36
|
+
[out, :passed]
|
37
|
+
else
|
38
|
+
[out, :failed]
|
39
|
+
end
|
40
|
+
rescue Docker::Error::TimeoutError => e
|
41
|
+
[I18n.t('mumukit.time_exceeded', limit: Mumukit.config.command_time_limit), :aborted]
|
42
|
+
end
|
43
|
+
|
44
|
+
def destroy!
|
45
|
+
if container
|
46
|
+
container.stop
|
47
|
+
container.delete
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Mumukit::Metatest
|
2
|
+
class Checker
|
3
|
+
def check(value, example)
|
4
|
+
example[:postconditions].each { |key, arg| check_assertion key, value, arg, example }
|
5
|
+
[example[:name], :passed, render_success_output(value)]
|
6
|
+
rescue => e
|
7
|
+
[example[:name], :failed, render_error_output(value, e.message)]
|
8
|
+
end
|
9
|
+
|
10
|
+
def render_success_output(value)
|
11
|
+
nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def render_error_output(value, error)
|
15
|
+
error
|
16
|
+
end
|
17
|
+
|
18
|
+
def check_assertion(key, value, arg, example)
|
19
|
+
send "check_#{key}", value, arg
|
20
|
+
end
|
21
|
+
|
22
|
+
def fail(message)
|
23
|
+
raise Mumukit::Metatest::Failed, message
|
24
|
+
end
|
25
|
+
|
26
|
+
def abort(message)
|
27
|
+
raise Mumukit::Metatest::Aborted, message
|
28
|
+
end
|
29
|
+
|
30
|
+
def error(message)
|
31
|
+
raise Mumukit::Metatest::Errored, message
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Mumukit::Metatest
|
2
|
+
class Framework
|
3
|
+
def initialize(options={})
|
4
|
+
@runner = options[:runner]
|
5
|
+
@checker = options[:checker]
|
6
|
+
end
|
7
|
+
|
8
|
+
def test(compilation, examples)
|
9
|
+
[examples.map { |it| example(compilation, it) }]
|
10
|
+
rescue Aborted => e
|
11
|
+
[e.message, :aborted]
|
12
|
+
rescue Errored => e
|
13
|
+
[e.message, :errored]
|
14
|
+
end
|
15
|
+
|
16
|
+
def example(compilation, example)
|
17
|
+
@checker.check(@runner.run(compilation, example), example)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class Mumukit::Runner
|
2
|
+
attr_reader :name, :runtime
|
3
|
+
|
4
|
+
def initialize(name)
|
5
|
+
@name = name
|
6
|
+
end
|
7
|
+
|
8
|
+
def configure
|
9
|
+
@config ||= self.class.default_config.clone
|
10
|
+
yield @config
|
11
|
+
end
|
12
|
+
|
13
|
+
def configure_runtime(config)
|
14
|
+
@runtime = Mumukit::Runtime.new(config)
|
15
|
+
end
|
16
|
+
|
17
|
+
def config
|
18
|
+
@config or raise 'This runner has not being configured yet'
|
19
|
+
end
|
20
|
+
|
21
|
+
def prefix
|
22
|
+
name.camelize
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.default_config
|
26
|
+
@default_config
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.configure_defaults
|
30
|
+
@default_config ||= OpenStruct.new
|
31
|
+
yield @default_config
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Mumukit::RuntimeInfo
|
2
|
+
def info
|
3
|
+
{
|
4
|
+
name: Mumukit.runner_name,
|
5
|
+
version: (File.read('version') rescue 'master'),
|
6
|
+
escualo_base_version: ENV['ESCUALO_BASE_VERSION'],
|
7
|
+
escualo_service_version: ENV['ESCUALO_SERVICE_VERSION'],
|
8
|
+
mumukit_version: Mumukit::VERSION,
|
9
|
+
output_content_type: Mumukit.config.content_type,
|
10
|
+
comment_type: Mumukit.config.comment_type,
|
11
|
+
features: {
|
12
|
+
query: query_hook?,
|
13
|
+
expectations: expectations_hook?,
|
14
|
+
feedback: feedback_hook?,
|
15
|
+
secure: validation_hook?,
|
16
|
+
stateful: Mumukit.config.stateful,
|
17
|
+
preprocessor: Mumukit.config.preprocessor_enabled,
|
18
|
+
|
19
|
+
sandboxed: any_hook_include?([:test, :query], Mumukit::Templates::WithIsolatedEnvironment),
|
20
|
+
structured: any_hook_include?([:test], Mumukit::Templates::WithStructuredResults) || Mumukit.config.structured
|
21
|
+
}
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def any_hook_include?(hooks, mixin)
|
28
|
+
hooks.any? { |it| hook_includes?(it, mixin) }
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Mumukit::Runtime
|
2
|
+
include Mumukit::RuntimeShortcuts
|
3
|
+
include Mumukit::RuntimeInfo
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@config = config
|
7
|
+
@hook_classes = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def hook_defined?(hook_name)
|
11
|
+
hook_name.to_default_mumukit_hook_class rescue raise "Wrong hook #{hook_name}"
|
12
|
+
|
13
|
+
Kernel.const_defined? hook_name.to_mumukit_hook_class_name
|
14
|
+
end
|
15
|
+
|
16
|
+
def hook_includes?(hook_name, mixin)
|
17
|
+
hook_class(hook_name).included_modules.include?(mixin)
|
18
|
+
end
|
19
|
+
|
20
|
+
def new_hook(hook_name)
|
21
|
+
hook_class(hook_name).new(@config)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def hook_class(hook_name)
|
27
|
+
@hook_classes[hook_name] ||=
|
28
|
+
if hook_defined? hook_name
|
29
|
+
hook_name.to_mumukit_hook_class
|
30
|
+
else
|
31
|
+
hook_name.to_default_mumukit_hook_class
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Symbol
|
2
|
+
def to_mumukit_hook_class_name
|
3
|
+
"#{Mumukit.prefix}#{to_simple_mumukit_hook_class_name}"
|
4
|
+
end
|
5
|
+
|
6
|
+
def to_simple_mumukit_hook_class_name
|
7
|
+
"#{to_s.camelize.to_sym}Hook"
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_mumukit_hook_class
|
11
|
+
Kernel.const_get to_mumukit_hook_class_name
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_default_mumukit_hook_class
|
15
|
+
Kernel.const_get "Mumukit::Defaults::#{to_simple_mumukit_hook_class_name}"
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'yaml'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
|
6
|
+
class Mumukit::Server::App < Sinatra::Base
|
7
|
+
configure do
|
8
|
+
set :mumuki_url, 'http://mumuki.io'
|
9
|
+
set :show_exceptions, :after_handler
|
10
|
+
enable :logging
|
11
|
+
end
|
12
|
+
|
13
|
+
configure :development do
|
14
|
+
set :config_filename, 'config/development.yml'
|
15
|
+
end
|
16
|
+
|
17
|
+
configure :production do
|
18
|
+
set :config_filename, 'config/production.yml'
|
19
|
+
end
|
20
|
+
|
21
|
+
runtime_config = YAML.load_file(settings.config_filename) rescue nil
|
22
|
+
|
23
|
+
Mumukit.configure_runtime(runtime_config)
|
24
|
+
|
25
|
+
set :server, Mumukit::Server::TestServer.new
|
26
|
+
|
27
|
+
before do
|
28
|
+
content_type 'application/json'
|
29
|
+
end
|
30
|
+
|
31
|
+
before do
|
32
|
+
Mumukit::Env.env = env
|
33
|
+
server.start_request!(parse_request)
|
34
|
+
end
|
35
|
+
|
36
|
+
helpers do
|
37
|
+
def server
|
38
|
+
settings.server
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse_request
|
42
|
+
@parsed_request ||= server.parse_request(request)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
get '/info' do
|
47
|
+
JSON.generate(server.info(request.url))
|
48
|
+
end
|
49
|
+
|
50
|
+
post '/test' do
|
51
|
+
JSON.generate(server.test!(parse_request))
|
52
|
+
end
|
53
|
+
|
54
|
+
post '/query' do
|
55
|
+
JSON.generate(server.query!(parse_request))
|
56
|
+
end
|
57
|
+
|
58
|
+
get '/*' do
|
59
|
+
redirect settings.mumuki_url
|
60
|
+
end
|
61
|
+
|
62
|
+
error StandardError do
|
63
|
+
content_type :json
|
64
|
+
status 200
|
65
|
+
|
66
|
+
message = Mumukit::ContentType::Plain.format_exception env['sinatra.error']
|
67
|
+
logger.error "Unhandled error #{message}"
|
68
|
+
|
69
|
+
{status: :errored, exit: message}.to_json
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class Mumukit::Server::ResponseBuilder
|
2
|
+
|
3
|
+
def add_test_results(r)
|
4
|
+
@response = base_response(r)
|
5
|
+
end
|
6
|
+
|
7
|
+
def add_query_results(r)
|
8
|
+
@response = unstructured_base_response(r)
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_expectation_results(r)
|
12
|
+
@response.merge!(expectationResults: r) if r.present?
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def add_feedback(f)
|
17
|
+
@response.merge!(feedback: f) if f.present?
|
18
|
+
end
|
19
|
+
|
20
|
+
def build
|
21
|
+
@response
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.build(&block)
|
25
|
+
builder = new
|
26
|
+
builder.instance_eval(&block)
|
27
|
+
builder.build
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def base_response(test_results)
|
33
|
+
if structured_test_result?(test_results)
|
34
|
+
structured_base_response(test_results)
|
35
|
+
elsif unstructured_test_result?(test_results)
|
36
|
+
unstructured_base_response(test_results)
|
37
|
+
else
|
38
|
+
raise "Invalid test results format: #{test_results}. You must either return [results_array] or [results_string, status]"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def structured_test_result?(test_results)
|
43
|
+
test_results.size == 1 && test_results[0].is_a?(Array)
|
44
|
+
end
|
45
|
+
|
46
|
+
def unstructured_test_result?(test_results)
|
47
|
+
test_results.size == 2 && test_results[0].is_a?(String)
|
48
|
+
end
|
49
|
+
|
50
|
+
def structured_base_response(test_results)
|
51
|
+
{testResults: test_results[0].map { |title, status, result|
|
52
|
+
{title: title,
|
53
|
+
status: status,
|
54
|
+
result: result} }}
|
55
|
+
end
|
56
|
+
|
57
|
+
def unstructured_base_response(test_results)
|
58
|
+
{exit: test_results[1],
|
59
|
+
out: test_results[0]}
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
class Mumukit::Server::TestServer
|
5
|
+
|
6
|
+
include Mumukit::WithContentType
|
7
|
+
|
8
|
+
def runtime
|
9
|
+
Mumukit.runtime
|
10
|
+
end
|
11
|
+
|
12
|
+
def info(url)
|
13
|
+
runtime.info.merge(runtime.metadata_hook.metadata).merge(url: url)
|
14
|
+
end
|
15
|
+
|
16
|
+
def start_request!(_request)
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_request(sinatra_request)
|
20
|
+
OpenStruct.new parse_request_headers(sinatra_request).merge(parse_request_body(sinatra_request))
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_request_headers(sinatra_request)
|
24
|
+
{}
|
25
|
+
end
|
26
|
+
|
27
|
+
def parse_request_body(sinatra_request)
|
28
|
+
JSON.parse(sinatra_request.body.read).tap do |it|
|
29
|
+
I18n.locale = it['locale'] || :en
|
30
|
+
end rescue {}
|
31
|
+
end
|
32
|
+
|
33
|
+
def test!(request)
|
34
|
+
respond_to(request) do |r|
|
35
|
+
test_results = run_tests! r
|
36
|
+
expectation_results = run_expectations! r
|
37
|
+
|
38
|
+
results = OpenStruct.new(test_results: test_results,
|
39
|
+
expectation_results: expectation_results)
|
40
|
+
|
41
|
+
feedback = run_feedback! r, results
|
42
|
+
|
43
|
+
Mumukit::Server::ResponseBuilder.build do
|
44
|
+
add_test_results(test_results)
|
45
|
+
add_expectation_results(expectation_results)
|
46
|
+
add_feedback(feedback)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def query!(request)
|
52
|
+
respond_to(request) do |r|
|
53
|
+
results = run_query!(r)
|
54
|
+
Mumukit::Server::ResponseBuilder.build do
|
55
|
+
add_query_results(results)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def run_query!(request)
|
61
|
+
compile_and_run runtime.query_hook, request
|
62
|
+
end
|
63
|
+
|
64
|
+
def run_tests!(request)
|
65
|
+
return ['', :passed] if request.test.blank?
|
66
|
+
|
67
|
+
compile_and_run runtime.test_hook, request
|
68
|
+
end
|
69
|
+
|
70
|
+
def run_expectations!(request)
|
71
|
+
if request.expectations
|
72
|
+
compile_and_run runtime.expectations_hook, request
|
73
|
+
else
|
74
|
+
[]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def run_feedback!(request, results)
|
79
|
+
runtime.feedback_hook.run!(request, results)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def compile_and_run(hook, request)
|
85
|
+
compilation = hook.compile(preprocess request)
|
86
|
+
hook.run!(compilation)
|
87
|
+
end
|
88
|
+
|
89
|
+
def preprocess(request)
|
90
|
+
if Mumukit.config.preprocessor_enabled
|
91
|
+
directives_pipeline.transform(request)
|
92
|
+
else
|
93
|
+
request
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def directives_pipeline
|
98
|
+
@pipeline ||= Mumukit::Directives::Pipeline.new(
|
99
|
+
[Mumukit::Directives::Sections.new,
|
100
|
+
Mumukit::Directives::Interpolations.new('test'),
|
101
|
+
Mumukit::Directives::Interpolations.new('extra'),
|
102
|
+
Mumukit::Directives::Flags.new],
|
103
|
+
Mumukit.config.comment_type)
|
104
|
+
end
|
105
|
+
|
106
|
+
def validate_request!(request)
|
107
|
+
runtime.validation_hook.validate! request
|
108
|
+
end
|
109
|
+
|
110
|
+
def respond_to(request)
|
111
|
+
yield request.tap { |r| validate_request! r }
|
112
|
+
rescue Mumukit::RequestValidationError => e
|
113
|
+
{exit: :aborted, out: e.message}
|
114
|
+
rescue Exception => e
|
115
|
+
{exit: :errored, out: content_type.format_exception(e)}
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|