media_types-serialization 0.8.1 → 1.0.1
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/.github/workflows/ci.yml +10 -1
- data/.gitignore +12 -12
- data/.idea/.rakeTasks +5 -5
- data/.idea/inspectionProfiles/Project_Default.xml +5 -5
- data/.idea/runConfigurations/test.xml +19 -19
- data/CHANGELOG.md +18 -0
- data/CODE_OF_CONDUCT.md +74 -74
- data/Gemfile +4 -4
- data/Gemfile.lock +58 -61
- data/LICENSE.txt +21 -21
- data/README.md +640 -173
- data/Rakefile +10 -10
- data/bin/console +14 -14
- data/bin/setup +8 -8
- data/lib/media_types/problem.rb +64 -0
- data/lib/media_types/serialization.rb +431 -172
- data/lib/media_types/serialization/base.rb +111 -91
- data/lib/media_types/serialization/error.rb +178 -0
- data/lib/media_types/serialization/fake_validator.rb +52 -0
- data/lib/media_types/serialization/serialization_dsl.rb +117 -0
- data/lib/media_types/serialization/serialization_registration.rb +235 -0
- data/lib/media_types/serialization/serializers/api_viewer.rb +133 -0
- data/lib/media_types/serialization/serializers/common_css.rb +168 -0
- data/lib/media_types/serialization/serializers/endpoint_description_serializer.rb +80 -0
- data/lib/media_types/serialization/serializers/fallback_not_acceptable_serializer.rb +85 -0
- data/lib/media_types/serialization/serializers/fallback_unsupported_media_type_serializer.rb +58 -0
- data/lib/media_types/serialization/serializers/input_validation_error_serializer.rb +89 -0
- data/lib/media_types/serialization/serializers/problem_serializer.rb +87 -0
- data/lib/media_types/serialization/version.rb +1 -1
- data/media_types-serialization.gemspec +50 -50
- metadata +40 -43
- data/.travis.yml +0 -17
- data/lib/generators/media_types/serialization/api_viewer/api_viewer_generator.rb +0 -25
- data/lib/generators/media_types/serialization/api_viewer/templates/api_viewer.html.erb +0 -98
- data/lib/generators/media_types/serialization/api_viewer/templates/initializer.rb +0 -33
- data/lib/generators/media_types/serialization/api_viewer/templates/template_controller.rb +0 -23
- data/lib/media_types/serialization/media_type/register.rb +0 -4
- data/lib/media_types/serialization/migrations_command.rb +0 -38
- data/lib/media_types/serialization/migrations_support.rb +0 -50
- data/lib/media_types/serialization/mime_type_support.rb +0 -64
- data/lib/media_types/serialization/no_content_type_given.rb +0 -11
- data/lib/media_types/serialization/no_media_type_serializers.rb +0 -11
- data/lib/media_types/serialization/no_serializer_for_content_type.rb +0 -15
- data/lib/media_types/serialization/renderer.rb +0 -41
- data/lib/media_types/serialization/renderer/register.rb +0 -4
- data/lib/media_types/serialization/wrapper.rb +0 -13
- data/lib/media_types/serialization/wrapper/html_wrapper.rb +0 -45
- data/lib/media_types/serialization/wrapper/media_collection_wrapper.rb +0 -61
- data/lib/media_types/serialization/wrapper/media_index_wrapper.rb +0 -61
- data/lib/media_types/serialization/wrapper/media_object_wrapper.rb +0 -55
- data/lib/media_types/serialization/wrapper_support.rb +0 -38
@@ -0,0 +1,168 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
require 'base64'
|
5
|
+
require 'active_support'
|
6
|
+
|
7
|
+
module MediaTypes
|
8
|
+
module Serialization
|
9
|
+
module Serializers
|
10
|
+
module CommonCSS
|
11
|
+
|
12
|
+
mattr_accessor :logo_data, :logo_media_type, :logo_width, :background, :custom_css
|
13
|
+
|
14
|
+
self.background = 'linear-gradient(245deg, rgba(255,89,89,1) 0%, rgba(255,164,113,1) 100%)'
|
15
|
+
self.logo_media_type = 'image/svg+xml'
|
16
|
+
self.logo_width = 8
|
17
|
+
self.logo_data = <<-HERE
|
18
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 114 93">
|
19
|
+
<title>Delft Solutions</title>
|
20
|
+
|
21
|
+
<filter id="dropshadow">
|
22
|
+
<feGaussianBlur in="SourceAlpha" stdDeviation="1"></feGaussianBlur> <!-- stdDeviation is how much to blur -->
|
23
|
+
<feOffset dx="2" dy="1" result="offsetblur"></feOffset> <!-- how much to offset -->
|
24
|
+
<feComponentTransfer>
|
25
|
+
<feFuncA type="linear" slope="0.5"></feFuncA> <!-- slope is the opacity of the shadow -->
|
26
|
+
</feComponentTransfer>
|
27
|
+
<feMerge>
|
28
|
+
<feMergeNode></feMergeNode> <!-- this contains the offset blurred image -->
|
29
|
+
<feMergeNode in="SourceGraphic"></feMergeNode> <!-- this contains the element that the filter is applied to -->
|
30
|
+
</feMerge>
|
31
|
+
</filter>
|
32
|
+
|
33
|
+
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
34
|
+
<g fill="#FFFFFF" fill-rule="nonzero">
|
35
|
+
|
36
|
+
<path d="M81.5784638,1.07718279e-13 C82.7664738,1.07718279e-13 83.8032488,0.734079641 84.4157016,1.75205281 L109.531129,43.5095713 C110.813908,45.6417099 110.657922,48.2974919 109.15835,50.2831454 L80.6973102,87.9923196 C80.0557678,88.7870619 79.0855103,89.3973447 78.0602378,89.3973447 L42.8594985,89.3973447 L14.6289023,43.5796094 L38.1043811,13.5281311 L47.8307983,13.5281311 L25.7347121,43.6175319 L48.0361926,79.9158441 L75.0253918,79.9158441 L101.326814,46.2820182 L73.5454136,1.07718279e-13 L81.5784638,1.07718279e-13 Z M68.8174965,0.000338312914 L96.4191607,45.9808751 L73.2382461,75.6684695 L61.4283598,75.6684695 L84.975762,45.385564 L63.4142078,9.46643441 L36.1380842,9.46643441 L9.60299852,43.3032035 L35.9112712,85.3931029 L38.1241857,89.3191214 L29.1498474,89.3973434 C27.9592604,89.4075947 26.8506993,88.7919375 26.2302294,87.7757572 L0.893096605,46.2796422 C-0.418595034,44.1314075 -0.274907213,41.3978442 1.25477457,39.3989643 L30.388821,1.32865425 L30.4563519,1.24328222 C31.0981823,0.458113729 32.0600455,0.000338312914 33.0779839,0.000338312914 L68.8174965,0.000338312914 Z" id="logo-mark-colour"></path>
|
37
|
+
</g>
|
38
|
+
</g>
|
39
|
+
</svg>
|
40
|
+
HERE
|
41
|
+
|
42
|
+
def self.logo_url
|
43
|
+
"data:#{logo_media_type};base64,#{Base64.encode64(logo_data).tr("\n", '')}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.css
|
47
|
+
template = ERB.new <<-TEMPLATE
|
48
|
+
html {
|
49
|
+
min-height: 100%;
|
50
|
+
background: <%= background %>;
|
51
|
+
}
|
52
|
+
|
53
|
+
body {
|
54
|
+
color: #fff;
|
55
|
+
margin-left: 10em;
|
56
|
+
margin-right: 10em;
|
57
|
+
margin-top: 1em;
|
58
|
+
|
59
|
+
font-family: -apple-system, ".SFNSText-Regular", "San Francisco", "Roboto", "Segoe UI", "Helvetica Neue", "Lucida Grande", sans-serif;
|
60
|
+
font-size: 16px;
|
61
|
+
line-height: 1.6;
|
62
|
+
-webkit-font-feature-settings: "kern","liga","clig","calt";
|
63
|
+
font-feature-settings: "kern","liga","clig","calt";
|
64
|
+
-webkit-font-smoothing: antialiased;
|
65
|
+
-moz-osx-font-smoothing: grayscale
|
66
|
+
}
|
67
|
+
|
68
|
+
header {
|
69
|
+
display: inline-block;
|
70
|
+
}
|
71
|
+
|
72
|
+
a:link {
|
73
|
+
color: #293590;
|
74
|
+
}
|
75
|
+
a:visited {
|
76
|
+
color: #080F43;
|
77
|
+
}
|
78
|
+
a:hover {
|
79
|
+
color: #5E7EFF;
|
80
|
+
}
|
81
|
+
|
82
|
+
#logo {
|
83
|
+
width: <%= logo_width %>em;
|
84
|
+
height: 6em;
|
85
|
+
background-repeat: no-repeat;
|
86
|
+
background-position-x: right;
|
87
|
+
background-position-y: center;
|
88
|
+
background-image: url(<%= logo_url %>);
|
89
|
+
float: left;
|
90
|
+
margin-right: 0.75em;
|
91
|
+
}
|
92
|
+
|
93
|
+
header h1 {
|
94
|
+
clear: right;
|
95
|
+
text-overflow: ellipsis;
|
96
|
+
white-space: nowrap;
|
97
|
+
overflow: hidden;
|
98
|
+
}
|
99
|
+
|
100
|
+
#content {
|
101
|
+
margin-top: 1em;
|
102
|
+
padding-left: 3em;
|
103
|
+
padding-right: 3em;
|
104
|
+
padding-bottom: 3em;
|
105
|
+
color: #060B34;
|
106
|
+
background-color: #fff;
|
107
|
+
border-radius: 1em;
|
108
|
+
border: 1px solid #E0E1E4;
|
109
|
+
}
|
110
|
+
|
111
|
+
nav h2 {
|
112
|
+
font-size: 1em;
|
113
|
+
line-height: 1.25em;
|
114
|
+
margin-bottom: 0;
|
115
|
+
}
|
116
|
+
nav .label {
|
117
|
+
float: left;
|
118
|
+
}
|
119
|
+
|
120
|
+
nav ul {
|
121
|
+
clear: right;
|
122
|
+
display: inline-block;
|
123
|
+
margin: 0;
|
124
|
+
padding: 0;
|
125
|
+
}
|
126
|
+
nav li {
|
127
|
+
float: left;
|
128
|
+
list-style: none;
|
129
|
+
margin-right: 0.3em;
|
130
|
+
}
|
131
|
+
nav li a.active {
|
132
|
+
font-weight: bold;
|
133
|
+
}
|
134
|
+
nav li a.active:link, nav li a.active:visited {
|
135
|
+
color: #060B34;
|
136
|
+
}
|
137
|
+
nav li + li:before {
|
138
|
+
content: "|";
|
139
|
+
margin-right: 0.3em;
|
140
|
+
}
|
141
|
+
|
142
|
+
nav #representations {
|
143
|
+
margin-left: -2em;
|
144
|
+
margin-right: -2em;
|
145
|
+
margin-bottom: 2em;
|
146
|
+
}
|
147
|
+
nav hr {
|
148
|
+
border: none;
|
149
|
+
border-top: solid 1px #E0E1E4;
|
150
|
+
}
|
151
|
+
|
152
|
+
nav #links {
|
153
|
+
display: inline-block;
|
154
|
+
margin-bottom: 1em;
|
155
|
+
}
|
156
|
+
nav #links ul {
|
157
|
+
float: left;
|
158
|
+
}
|
159
|
+
|
160
|
+
TEMPLATE
|
161
|
+
template = ERB.new custom_css unless custom_css.nil?
|
162
|
+
|
163
|
+
template.result(binding())
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'media_types/serialization/base'
|
5
|
+
|
6
|
+
module MediaTypes
|
7
|
+
module Serialization
|
8
|
+
module Serializers
|
9
|
+
class EndpointDescriptionSerializer < MediaTypes::Serialization::Base
|
10
|
+
|
11
|
+
unvalidated 'application/vnd.delftsolutions.endpoint_description'
|
12
|
+
|
13
|
+
disable_wildcards
|
14
|
+
|
15
|
+
def self.to_input_identifiers(serializers)
|
16
|
+
serializers.flat_map do |s|
|
17
|
+
s[:serializer].inputs_for(views: [s[:view]]).registrations.keys
|
18
|
+
end
|
19
|
+
end
|
20
|
+
def self.to_output_identifiers(serializers)
|
21
|
+
serializers.flat_map do |s|
|
22
|
+
s[:serializer].outputs_for(views: [s[:view]]).registrations.keys
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
output version: 1 do |input, version, context|
|
27
|
+
request_path = context.request.original_fullpath.split('?')[0]
|
28
|
+
|
29
|
+
path_prefix = ENV.fetch('RAILS_RELATIVE_URL_ROOT') { '' }
|
30
|
+
request_path = request_path.sub(path_prefix, '')
|
31
|
+
|
32
|
+
my_controller = Rails.application.routes.recognize_path request_path
|
33
|
+
|
34
|
+
methods_available = {}
|
35
|
+
methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
|
36
|
+
methods.each do |m|
|
37
|
+
begin
|
38
|
+
found_controller = Rails.application.routes.recognize_path request_path, method: m
|
39
|
+
if found_controller[:controller] == my_controller[:controller]
|
40
|
+
methods_available[m] = found_controller[:action]
|
41
|
+
end
|
42
|
+
rescue ActionController::RoutingError
|
43
|
+
# not available
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
input_definitions = input[:actions][:input] || {}
|
48
|
+
output_definitions = input[:actions][:output] || {}
|
49
|
+
viewer_definitions = input[:api_viewer] || {}
|
50
|
+
|
51
|
+
result = {}
|
52
|
+
global_in = input_definitions['all_actions'] || []
|
53
|
+
global_out = output_definitions['all_actions'] || []
|
54
|
+
global_viewer = viewer_definitions['all_actions'] || false
|
55
|
+
|
56
|
+
viewer_uri = URI.parse(context.request.original_url)
|
57
|
+
query_parts = viewer_uri.query&.split('&') || []
|
58
|
+
query_parts = query_parts.select { |q| !q.start_with? 'api_viewer=' }
|
59
|
+
viewer_uri.query = (query_parts + ["api_viewer=last"]).join('&')
|
60
|
+
|
61
|
+
methods_available.each do |method, action|
|
62
|
+
has_viewer = viewer_definitions[action] || global_viewer
|
63
|
+
input_serializers = global_in + (input_definitions[action] || [])
|
64
|
+
output_serializers = global_out + (output_definitions[action] || [])
|
65
|
+
result[method] = {
|
66
|
+
input: to_input_identifiers(input_serializers),
|
67
|
+
output: to_output_identifiers(output_serializers),
|
68
|
+
}
|
69
|
+
|
70
|
+
result[method].delete(:input) if method == 'GET'
|
71
|
+
result[method][:api_viewer] = viewer_uri.to_s if has_viewer
|
72
|
+
end
|
73
|
+
|
74
|
+
result
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types/serialization/base'
|
4
|
+
|
5
|
+
module MediaTypes
|
6
|
+
module Serialization
|
7
|
+
module Serializers
|
8
|
+
# The serializer used when no serializer has been configured.
|
9
|
+
class FallbackNotAcceptableSerializer < MediaTypes::Serialization::Base
|
10
|
+
unvalidated 'text/html'
|
11
|
+
|
12
|
+
output_raw do |obj, version, context|
|
13
|
+
|
14
|
+
available_types = []
|
15
|
+
begin
|
16
|
+
original_uri = URI.parse(context.request.original_url)
|
17
|
+
stripped_original = original_uri.dup
|
18
|
+
query_parts = stripped_original.query&.split('&') || []
|
19
|
+
query_parts = query_parts.select { |q| !q.start_with? 'api_viewer=' }
|
20
|
+
|
21
|
+
available_types = obj[:registrations].registrations.keys.map do |identifier|
|
22
|
+
stripped_original.query = (query_parts + ["api_viewer=#{identifier}"]).join('&')
|
23
|
+
{
|
24
|
+
identifier: identifier,
|
25
|
+
url: stripped_original.to_s,
|
26
|
+
}
|
27
|
+
end
|
28
|
+
rescue URI::InvalidURIError
|
29
|
+
available_types = obj[:registrations].registrations.keys.map do |identifier|
|
30
|
+
{
|
31
|
+
identifier: identifier,
|
32
|
+
url: context.request.original_url,
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
input = OpenStruct.new(
|
38
|
+
media_types: available_types,
|
39
|
+
has_viewer: obj[:has_viewer],
|
40
|
+
css: CommonCSS.css,
|
41
|
+
acceptable_types: obj[:request].headers["Accept"] || "<none>",
|
42
|
+
)
|
43
|
+
|
44
|
+
template = ERB.new <<-TEMPLATE
|
45
|
+
<html lang="en">
|
46
|
+
<head>
|
47
|
+
<title>Unable to provide requested media types</title>
|
48
|
+
<style>
|
49
|
+
<%= css.split("\n").join("\n ") %>
|
50
|
+
</style>
|
51
|
+
</head>
|
52
|
+
<body>
|
53
|
+
<header>
|
54
|
+
<div id="logo"></div>
|
55
|
+
<h1>Not acceptable</h1>
|
56
|
+
</header>
|
57
|
+
<section id="content">
|
58
|
+
<nav>
|
59
|
+
<section id="representations">
|
60
|
+
<h2>Please choose one of the following types:</h2>
|
61
|
+
<p>This endpoint tried really hard to show you the information you requested. Unfortunately you specified in your <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept">Accept header</a> that you only wanted to see the following types: <code><%= CGI::escapeHTML(acceptable_types) %></code>.
|
62
|
+
<p>Please add one of the following types to your Accept header to see the content or error message:
|
63
|
+
<hr>
|
64
|
+
</section>
|
65
|
+
</nav>
|
66
|
+
<main>
|
67
|
+
<% media_types.each do |m| %>
|
68
|
+
<li>
|
69
|
+
<a href="<%= m[:url] %>">
|
70
|
+
<%= CGI::escapeHTML(m[:identifier]) %>
|
71
|
+
</a>
|
72
|
+
</li>
|
73
|
+
<% end %>
|
74
|
+
</main>
|
75
|
+
</section>
|
76
|
+
<!-- API viewer made with ❤ by: https://delftsolutions.com -->
|
77
|
+
</body>
|
78
|
+
</html>
|
79
|
+
TEMPLATE
|
80
|
+
template.result(input.instance_eval { binding })
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types/serialization/base'
|
4
|
+
|
5
|
+
module MediaTypes
|
6
|
+
module Serialization
|
7
|
+
module Serializers
|
8
|
+
# The serializer used when no serializer has been configured.
|
9
|
+
class FallbackUnsupportedMediaTypeSerializer < MediaTypes::Serialization::Base
|
10
|
+
unvalidated 'text/html'
|
11
|
+
|
12
|
+
output_raw do |obj, version, context|
|
13
|
+
|
14
|
+
available_types = obj[:registrations].registrations.keys
|
15
|
+
|
16
|
+
input = OpenStruct.new(
|
17
|
+
media_types: available_types,
|
18
|
+
css: CommonCSS.css
|
19
|
+
)
|
20
|
+
|
21
|
+
template = ERB.new <<-TEMPLATE
|
22
|
+
<html lang="en">
|
23
|
+
<head>
|
24
|
+
<title>Unsupported Media Type</title>
|
25
|
+
<style>
|
26
|
+
<%= css.split("\n").join("\n ") %>
|
27
|
+
</style>
|
28
|
+
</head>
|
29
|
+
<body>
|
30
|
+
<header>
|
31
|
+
<div id="logo"></div>
|
32
|
+
<h1>Unsupported Media Type</h1>
|
33
|
+
</header>
|
34
|
+
<section id="content">
|
35
|
+
<nav>
|
36
|
+
<section id="representations">
|
37
|
+
<h2>Please use one of the following <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type">Content-Types</a> when making your request:</h2>
|
38
|
+
<hr>
|
39
|
+
</section>
|
40
|
+
</nav>
|
41
|
+
<main>
|
42
|
+
<% media_types.each do |m| %>
|
43
|
+
<li>
|
44
|
+
<%= CGI::escapeHTML(m) %>
|
45
|
+
</li>
|
46
|
+
<% end %>
|
47
|
+
</main>
|
48
|
+
</section>
|
49
|
+
<!-- API viewer made with ❤ by: https://delftsolutions.com -->
|
50
|
+
</body>
|
51
|
+
</html>
|
52
|
+
TEMPLATE
|
53
|
+
template.result(input.instance_eval { binding })
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'media_types/serialization/base'
|
4
|
+
require 'erb'
|
5
|
+
require 'cgi'
|
6
|
+
|
7
|
+
module MediaTypes
|
8
|
+
module Serialization
|
9
|
+
module Serializers
|
10
|
+
class InputValidationErrorSerializer < MediaTypes::Serialization::Base
|
11
|
+
unvalidated 'text/html'
|
12
|
+
|
13
|
+
def self.escape_text(text)
|
14
|
+
text.split("\n").
|
15
|
+
map { |l| CGI::escapeHTML(l).gsub(/ (?= )/, ' ') }.
|
16
|
+
map { |l| (l.gsub(/\bhttps?:\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;{}]*[-A-Z0-9+@#\/%=}~_|](?![a-z]*;)/i) do |m|
|
17
|
+
converted = m
|
18
|
+
invalid = false
|
19
|
+
begin
|
20
|
+
converted = viewerify(m, context.request.host)
|
21
|
+
rescue URI::InvalidURIError
|
22
|
+
invalid = true
|
23
|
+
end
|
24
|
+
style = ''
|
25
|
+
style = ' style="color: red"' if invalid
|
26
|
+
"<a#{style} href=\"#{converted}\">#{m}</a>"
|
27
|
+
end) }.
|
28
|
+
join("<br>\n")
|
29
|
+
end
|
30
|
+
|
31
|
+
output_raw do |obj, version, context|
|
32
|
+
input_identifier = obj[:identifier]
|
33
|
+
original_input = obj[:input]
|
34
|
+
error = obj[:error]
|
35
|
+
|
36
|
+
escaped_error = escape_text(error.message)
|
37
|
+
escaped_input = escape_text(original_input)
|
38
|
+
|
39
|
+
input = OpenStruct.new(
|
40
|
+
original_identifier: input_identifier,
|
41
|
+
escaped_error: escaped_error,
|
42
|
+
escaped_input: escaped_input,
|
43
|
+
css: CommonCSS.css,
|
44
|
+
)
|
45
|
+
|
46
|
+
template = ERB.new <<-TEMPLATE
|
47
|
+
<html lang="en">
|
48
|
+
<head>
|
49
|
+
<title>Invalid input detected</title>
|
50
|
+
<style>
|
51
|
+
<%= css.split("\n").join("\n ") %>
|
52
|
+
</style>
|
53
|
+
</head>
|
54
|
+
<body>
|
55
|
+
<header>
|
56
|
+
<div id="logo"></div>
|
57
|
+
<h1>Invalid input detected</h1>
|
58
|
+
</header>
|
59
|
+
<section id="content">
|
60
|
+
<nav>
|
61
|
+
<section id="representations">
|
62
|
+
<h2>While trying to process the <%= CGI::escapeHTML(original_identifier) %> input you sent; I encountered the following error:</h2>
|
63
|
+
<hr>
|
64
|
+
</section>
|
65
|
+
</nav>
|
66
|
+
<main>
|
67
|
+
<section id="error">
|
68
|
+
<code id="error">
|
69
|
+
<%= escaped_error %>
|
70
|
+
</code>
|
71
|
+
</section>
|
72
|
+
<section id="input">
|
73
|
+
<h2>Original input:</h2>
|
74
|
+
<code id="input">
|
75
|
+
<%= escaped_input %>
|
76
|
+
</code>
|
77
|
+
</section>
|
78
|
+
</main>
|
79
|
+
</section>
|
80
|
+
<!-- API viewer made with ❤ by: https://delftsolutions.com -->
|
81
|
+
</body>
|
82
|
+
</html>
|
83
|
+
TEMPLATE
|
84
|
+
template.result(input.instance_eval { binding })
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|