govuk_tech_docs 1.5.0 → 1.6.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/.rubocop.yml +3 -0
- data/CHANGELOG.md +90 -0
- data/docs/configuration.md +15 -0
- data/docs/frontmatter.md +2 -14
- data/docs/page-expiry.md +69 -0
- data/example/Gemfile +1 -0
- data/example/config/tech-docs.yml +6 -0
- data/example/source/api-path.html.md +7 -0
- data/example/source/api-reference.html.md +5 -0
- data/example/source/pets.yml +106 -0
- data/govuk_tech_docs.gemspec +2 -0
- data/lib/assets/javascripts/_analytics.js +12 -0
- data/lib/assets/javascripts/_modules/collapsible-navigation.js +5 -3
- data/lib/assets/javascripts/_modules/search.js +175 -6
- data/lib/assets/stylesheets/modules/_collapsible.scss +12 -5
- data/lib/assets/stylesheets/modules/_technical-documentation.scss +16 -11
- data/lib/assets/stylesheets/modules/_toc.scss +1 -1
- data/lib/govuk_tech_docs.rb +13 -2
- data/lib/govuk_tech_docs/api_reference/api_reference_extension.rb +100 -0
- data/lib/govuk_tech_docs/api_reference/api_reference_renderer.rb +279 -0
- data/lib/govuk_tech_docs/api_reference/templates/api_reference_full.html.erb +9 -0
- data/lib/govuk_tech_docs/api_reference/templates/operation.html.erb +11 -0
- data/lib/govuk_tech_docs/api_reference/templates/parameters.html.erb +28 -0
- data/lib/govuk_tech_docs/api_reference/templates/path.html.erb +4 -0
- data/lib/govuk_tech_docs/api_reference/templates/responses.html.erb +33 -0
- data/lib/govuk_tech_docs/api_reference/templates/schema.html.erb +29 -0
- data/lib/govuk_tech_docs/page_review.rb +15 -3
- data/lib/govuk_tech_docs/pages.rb +3 -2
- data/lib/govuk_tech_docs/tech_docs_html_renderer.rb +10 -0
- data/lib/govuk_tech_docs/version.rb +1 -1
- data/lib/source/layouts/_header.erb +2 -4
- metadata +42 -4
- data/lib/source/images/arrow-down.svg +0 -9
- data/lib/source/images/arrow-up.svg +0 -9
@@ -11,6 +11,10 @@
|
|
11
11
|
display: block
|
12
12
|
}
|
13
13
|
}
|
14
|
+
.collapsible__heading,
|
15
|
+
.toc__list > ul > li > a:link.collapsible__heading {
|
16
|
+
margin-right: 30px;
|
17
|
+
}
|
14
18
|
.collapsible__toggle {
|
15
19
|
position: absolute;
|
16
20
|
top: 0;
|
@@ -34,12 +38,15 @@
|
|
34
38
|
&::after {
|
35
39
|
content: '';
|
36
40
|
display: block;
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
+
border: 2px solid $black;
|
42
|
+
border-width: 2px 2px 0 0;
|
43
|
+
transform: rotate(135deg);
|
44
|
+
width: 10px;
|
45
|
+
height: 10px;
|
46
|
+
margin-top: 10px;
|
41
47
|
}
|
42
48
|
.collapsible.is-open &::after {
|
43
|
-
|
49
|
+
transform: rotate(315deg);
|
50
|
+
margin-top: 18px;
|
44
51
|
}
|
45
52
|
}
|
@@ -3,11 +3,11 @@
|
|
3
3
|
@mixin heading-offset($tabletTopMargin) {
|
4
4
|
// Scale margins with font size on mobile (16/19ths)
|
5
5
|
$mobileTopMargin: ceil($tabletTopMargin * (16 / 19));
|
6
|
-
|
6
|
+
|
7
7
|
// Offset headings down on mobile so that linking to anchors they appear after
|
8
8
|
// the sticky 'table of contents' element
|
9
9
|
$stickyTocOffset: 20px + $gutter-half + 10px + 1px;
|
10
|
-
|
10
|
+
|
11
11
|
// Pad the heading so that when linking to an anchor there is at most a
|
12
12
|
// $gutter-half (mobile) or $gutter (tablet and above) sized gap between the
|
13
13
|
// top of the viewport and the heading.
|
@@ -25,9 +25,9 @@
|
|
25
25
|
display: block;
|
26
26
|
margin: 0 $gutter-half 10px;
|
27
27
|
max-width: 40em;
|
28
|
-
|
28
|
+
|
29
29
|
line-height: 1.4;
|
30
|
-
|
30
|
+
|
31
31
|
color: $text-colour;
|
32
32
|
|
33
33
|
@include media(tablet) {
|
@@ -45,14 +45,14 @@
|
|
45
45
|
@include bold-48;
|
46
46
|
@include heading-offset($gutter * 2);
|
47
47
|
border-top: 5px solid $text-colour;
|
48
|
-
|
48
|
+
|
49
49
|
&:first-of-type {
|
50
50
|
@include heading-offset($gutter);
|
51
51
|
border-top: none;
|
52
52
|
}
|
53
53
|
}
|
54
54
|
|
55
|
-
h2 {
|
55
|
+
h2 {
|
56
56
|
@include bold-36;
|
57
57
|
@include heading-offset($gutter * 1.5);
|
58
58
|
}
|
@@ -125,7 +125,7 @@
|
|
125
125
|
|
126
126
|
ol + p, ul + p, .table-container + p {
|
127
127
|
margin-top: ceil($gutter * (16 / 19));
|
128
|
-
|
128
|
+
|
129
129
|
@include media(tablet) {
|
130
130
|
margin-top: $gutter;
|
131
131
|
}
|
@@ -144,6 +144,11 @@
|
|
144
144
|
overflow: auto;
|
145
145
|
position: relative;
|
146
146
|
border: 1px solid $code-02;
|
147
|
+
// Restrict the width of pre tags, as they have a tendency grow larger than
|
148
|
+
// the viewport when placed within table cells.
|
149
|
+
// @todo: Use table-layout: fixed, and remove the max-width definition from
|
150
|
+
// .technical-documentation so tables can fill the viewport.
|
151
|
+
max-width: 40em;
|
147
152
|
}
|
148
153
|
|
149
154
|
pre code {
|
@@ -156,11 +161,11 @@
|
|
156
161
|
background: $code-01;
|
157
162
|
padding: 3px 5px;
|
158
163
|
border-radius: 1px;
|
159
|
-
|
164
|
+
|
160
165
|
font-family: monaco, Consolas, "Lucida Console", monospace;
|
161
166
|
font-size: 15px;
|
162
167
|
color: $code-0E;
|
163
|
-
|
168
|
+
|
164
169
|
@include media(tablet) {
|
165
170
|
font-size: 16px;
|
166
171
|
}
|
@@ -191,11 +196,11 @@
|
|
191
196
|
display: block;
|
192
197
|
max-width: 100%;
|
193
198
|
overflow-x: auto;
|
194
|
-
|
199
|
+
|
195
200
|
margin-top: $gutter-half;
|
196
201
|
}
|
197
202
|
|
198
|
-
table {
|
203
|
+
table {
|
199
204
|
width: 100%;
|
200
205
|
|
201
206
|
border-collapse: collapse;
|
data/lib/govuk_tech_docs.rb
CHANGED
@@ -20,6 +20,7 @@ require 'govuk_tech_docs/pages'
|
|
20
20
|
require 'govuk_tech_docs/tech_docs_html_renderer'
|
21
21
|
require 'govuk_tech_docs/unique_identifier_extension'
|
22
22
|
require 'govuk_tech_docs/unique_identifier_generator'
|
23
|
+
require 'govuk_tech_docs/api_reference/api_reference_extension'
|
23
24
|
|
24
25
|
module GovukTechDocs
|
25
26
|
# Configure the tech docs template
|
@@ -37,7 +38,9 @@ module GovukTechDocs
|
|
37
38
|
context.set :markdown_engine, :redcarpet
|
38
39
|
context.set :markdown,
|
39
40
|
renderer: TechDocsHTMLRenderer.new(
|
40
|
-
with_toc_data: true
|
41
|
+
with_toc_data: true,
|
42
|
+
api: true,
|
43
|
+
context: context
|
41
44
|
),
|
42
45
|
fenced_code_blocks: true,
|
43
46
|
tables: true,
|
@@ -56,6 +59,8 @@ module GovukTechDocs
|
|
56
59
|
context.config[:tech_docs] = YAML.load_file('config/tech-docs.yml').with_indifferent_access
|
57
60
|
context.activate :unique_identifier
|
58
61
|
|
62
|
+
context.activate :api_reference
|
63
|
+
|
59
64
|
context.helpers do
|
60
65
|
include GovukTechDocs::TableOfContents::Helpers
|
61
66
|
include GovukTechDocs::ContributionBanner
|
@@ -65,7 +70,7 @@ module GovukTechDocs
|
|
65
70
|
end
|
66
71
|
|
67
72
|
def current_page_review
|
68
|
-
@current_page_review ||= GovukTechDocs::PageReview.new(current_page)
|
73
|
+
@current_page_review ||= GovukTechDocs::PageReview.new(current_page, config)
|
69
74
|
end
|
70
75
|
|
71
76
|
def format_date(date)
|
@@ -102,6 +107,12 @@ module GovukTechDocs
|
|
102
107
|
content: { boost: 50, store: true },
|
103
108
|
url: { index: false, store: true },
|
104
109
|
}
|
110
|
+
|
111
|
+
search.pipeline_remove = [
|
112
|
+
'stopWordFilter'
|
113
|
+
]
|
114
|
+
|
115
|
+
search.tokenizer_separator = '/[\s\-/]+/'
|
105
116
|
end
|
106
117
|
end
|
107
118
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'openapi3_parser'
|
3
|
+
require 'uri'
|
4
|
+
require 'pry'
|
5
|
+
require 'govuk_tech_docs/api_reference/api_reference_renderer'
|
6
|
+
|
7
|
+
module GovukTechDocs
|
8
|
+
module ApiReference
|
9
|
+
class Extension < Middleman::Extension
|
10
|
+
expose_to_application api: :api
|
11
|
+
|
12
|
+
def initialize(app, options_hash = {}, &block)
|
13
|
+
super
|
14
|
+
|
15
|
+
@app = app
|
16
|
+
@config = @app.config[:tech_docs]
|
17
|
+
|
18
|
+
# If no api path then just return.
|
19
|
+
if @config['api_path'].to_s.empty?
|
20
|
+
raise 'No api path defined in tech-docs.yml'
|
21
|
+
end
|
22
|
+
|
23
|
+
# Is the api_path a url or path?
|
24
|
+
if uri?(@config['api_path'])
|
25
|
+
@api_parser = true
|
26
|
+
@document = Openapi3Parser.load_url(@config['api_path'])
|
27
|
+
elsif File.exist?(@config['api_path'])
|
28
|
+
# Load api file and set existence flag.
|
29
|
+
@api_parser = true
|
30
|
+
@document = Openapi3Parser.load_file(@config['api_path'])
|
31
|
+
else
|
32
|
+
@api_parser = false
|
33
|
+
raise 'Unable to load api path from tech-docs.yml'
|
34
|
+
end
|
35
|
+
@render = Renderer.new(@app, @document)
|
36
|
+
end
|
37
|
+
|
38
|
+
def uri?(string)
|
39
|
+
uri = URI.parse(string)
|
40
|
+
%w(http https).include?(uri.scheme)
|
41
|
+
rescue URI::BadURIError
|
42
|
+
false
|
43
|
+
rescue URI::InvalidURIError
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
47
|
+
def api(text)
|
48
|
+
if @api_parser == true
|
49
|
+
|
50
|
+
keywords = {
|
51
|
+
'api>' => 'default',
|
52
|
+
'api_schema>' => 'schema'
|
53
|
+
}
|
54
|
+
|
55
|
+
regexp = keywords.map { |k, _| Regexp.escape(k) }.join('|')
|
56
|
+
|
57
|
+
md = text.match(/^<p>(#{regexp})/)
|
58
|
+
if md
|
59
|
+
key = md.captures[0]
|
60
|
+
type = keywords[key]
|
61
|
+
|
62
|
+
text.gsub!(/#{ Regexp.escape(key) }\s+?/, '')
|
63
|
+
|
64
|
+
# Strip paragraph tags from text
|
65
|
+
text = text.gsub(/<\/?[^>]*>/, '')
|
66
|
+
text = text.strip
|
67
|
+
|
68
|
+
if text == 'api>'
|
69
|
+
@render.api_full(api_info, api_server)
|
70
|
+
elsif type == 'default'
|
71
|
+
output = @render.path(text)
|
72
|
+
# Render any schemas referenced in the above path
|
73
|
+
output += @render.schemas_from_path(text)
|
74
|
+
output
|
75
|
+
else
|
76
|
+
@render.schema(text)
|
77
|
+
end
|
78
|
+
|
79
|
+
else
|
80
|
+
return text
|
81
|
+
end
|
82
|
+
else
|
83
|
+
text
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def api_info
|
90
|
+
@document.info
|
91
|
+
end
|
92
|
+
|
93
|
+
def api_server
|
94
|
+
@document.servers[0]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
::Middleman::Extensions.register(:api_reference, GovukTechDocs::ApiReference::Extension)
|
@@ -0,0 +1,279 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module GovukTechDocs
|
5
|
+
module ApiReference
|
6
|
+
class Renderer
|
7
|
+
def initialize(app, document)
|
8
|
+
@app = app
|
9
|
+
@document = document
|
10
|
+
|
11
|
+
# Load template files
|
12
|
+
@template_api_full = get_renderer('api_reference_full.html.erb')
|
13
|
+
@template_path = get_renderer('path.html.erb')
|
14
|
+
@template_schema = get_renderer('schema.html.erb')
|
15
|
+
@template_operation = get_renderer('operation.html.erb')
|
16
|
+
@template_parameters = get_renderer('parameters.html.erb')
|
17
|
+
@template_responses = get_renderer('responses.html.erb')
|
18
|
+
end
|
19
|
+
|
20
|
+
def api_full(info, server)
|
21
|
+
paths = ''
|
22
|
+
paths_data = @document.paths
|
23
|
+
paths_data.each do |path_data|
|
24
|
+
# For some reason paths.each returns an array of arrays [title, object]
|
25
|
+
# instead of an array of objects
|
26
|
+
text = path_data[0]
|
27
|
+
paths += path(text)
|
28
|
+
end
|
29
|
+
schemas = ''
|
30
|
+
schemas_data = @document.components.schemas
|
31
|
+
schemas_data.each do |schema_data|
|
32
|
+
text = schema_data[0]
|
33
|
+
schemas += schema(text)
|
34
|
+
end
|
35
|
+
@template_api_full.result(binding)
|
36
|
+
end
|
37
|
+
|
38
|
+
def path(text)
|
39
|
+
path = @document.paths[text]
|
40
|
+
id = text.parameterize
|
41
|
+
operations = operations(path, id)
|
42
|
+
@template_path.result(binding)
|
43
|
+
end
|
44
|
+
|
45
|
+
def schema(text)
|
46
|
+
schemas = ''
|
47
|
+
schemas_data = @document.components.schemas
|
48
|
+
schemas_data.each do |schema_data|
|
49
|
+
all_of = schema_data[1]["allOf"]
|
50
|
+
properties = []
|
51
|
+
if !all_of.blank?
|
52
|
+
all_of.each do |schema_nested|
|
53
|
+
schema_nested.properties.each do |property|
|
54
|
+
properties.push property
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
schema_data[1].properties.each do |property|
|
60
|
+
properties.push property
|
61
|
+
end
|
62
|
+
|
63
|
+
if schema_data[0] == text
|
64
|
+
title = schema_data[0]
|
65
|
+
schema = schema_data[1]
|
66
|
+
return @template_schema.result(binding)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def schemas_from_path(text)
|
72
|
+
path = @document.paths[text]
|
73
|
+
operations = get_operations(path)
|
74
|
+
# Get all referenced schemas
|
75
|
+
schemas = []
|
76
|
+
operations.compact.each_value do |operation|
|
77
|
+
responses = operation.responses
|
78
|
+
responses.each do |_rkey, response|
|
79
|
+
if response.content['application/json']
|
80
|
+
schema = response.content['application/json'].schema
|
81
|
+
schema_name = get_schema_name(schema.node_context.source_location.to_s)
|
82
|
+
if !schema_name.nil?
|
83
|
+
schemas.push schema_name
|
84
|
+
end
|
85
|
+
schemas.concat(schemas_from_schema(schema))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
# Render all referenced schemas
|
90
|
+
output = ''
|
91
|
+
schemas.uniq.each do |schema_name|
|
92
|
+
output += schema(schema_name)
|
93
|
+
end
|
94
|
+
if !output.empty?
|
95
|
+
output.prepend('<h2 id="schemas">Schemas</h2>')
|
96
|
+
end
|
97
|
+
output
|
98
|
+
end
|
99
|
+
|
100
|
+
def schemas_from_schema(schema)
|
101
|
+
schemas = []
|
102
|
+
properties = []
|
103
|
+
schema.properties.each do |property|
|
104
|
+
properties.push property[1]
|
105
|
+
end
|
106
|
+
if schema.type == 'array'
|
107
|
+
properties.push schema.items
|
108
|
+
end
|
109
|
+
all_of = schema["allOf"]
|
110
|
+
if !all_of.blank?
|
111
|
+
all_of.each do |schema_nested|
|
112
|
+
schema_nested.properties.each do |property|
|
113
|
+
properties.push property[1]
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
properties.each do |property|
|
118
|
+
# Must be a schema be referenced by another schema
|
119
|
+
# And not a property of a schema
|
120
|
+
if property.node_context.referenced_by.to_s.include?('#/components/schemas') &&
|
121
|
+
!property.node_context.source_location.to_s.include?('/properties/')
|
122
|
+
schema_name = get_schema_name(property.node_context.source_location.to_s)
|
123
|
+
end
|
124
|
+
if !schema_name.nil?
|
125
|
+
schemas.push schema_name
|
126
|
+
end
|
127
|
+
# Check sub-properties for references
|
128
|
+
schemas.concat(schemas_from_schema(property))
|
129
|
+
end
|
130
|
+
schemas
|
131
|
+
end
|
132
|
+
|
133
|
+
def operations(path, path_id)
|
134
|
+
output = ''
|
135
|
+
operations = get_operations(path)
|
136
|
+
operations.compact.each do |key, operation|
|
137
|
+
id = "#{path_id}-#{key.parameterize}"
|
138
|
+
parameters = parameters(operation, id)
|
139
|
+
responses = responses(operation, id)
|
140
|
+
output += @template_operation.result(binding)
|
141
|
+
end
|
142
|
+
output
|
143
|
+
end
|
144
|
+
|
145
|
+
def parameters(operation, operation_id)
|
146
|
+
parameters = operation.parameters
|
147
|
+
id = "#{operation_id}-parameters"
|
148
|
+
output = @template_parameters.result(binding)
|
149
|
+
output
|
150
|
+
end
|
151
|
+
|
152
|
+
def responses(operation, operation_id)
|
153
|
+
responses = operation.responses
|
154
|
+
id = "#{operation_id}-responses"
|
155
|
+
output = @template_responses.result(binding)
|
156
|
+
output
|
157
|
+
end
|
158
|
+
|
159
|
+
def markdown(text)
|
160
|
+
if text
|
161
|
+
Tilt['markdown'].new(context: @app) { text }.render
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def json_output(schema)
|
166
|
+
properties = schema_properties(schema)
|
167
|
+
JSON.pretty_generate(properties)
|
168
|
+
end
|
169
|
+
|
170
|
+
def json_prettyprint(data)
|
171
|
+
JSON.pretty_generate(data)
|
172
|
+
end
|
173
|
+
|
174
|
+
def schema_properties(schema_data)
|
175
|
+
properties = Hash.new
|
176
|
+
if defined? schema_data.properties
|
177
|
+
schema_data.properties.each do |key, property|
|
178
|
+
properties[key] = property
|
179
|
+
end
|
180
|
+
end
|
181
|
+
properties.merge! get_all_of_hash(schema_data)
|
182
|
+
properties_hash = Hash.new
|
183
|
+
properties.each do |pkey, property|
|
184
|
+
if property.type == 'object'
|
185
|
+
properties_hash[pkey] = Hash.new
|
186
|
+
items = property.items
|
187
|
+
if !items.blank?
|
188
|
+
properties_hash[pkey] = schema_properties(items)
|
189
|
+
end
|
190
|
+
if !property.properties.blank?
|
191
|
+
properties_hash[pkey] = schema_properties(property)
|
192
|
+
end
|
193
|
+
elsif property.type == 'array'
|
194
|
+
properties_hash[pkey] = Array.new
|
195
|
+
items = property.items
|
196
|
+
if !items.blank?
|
197
|
+
properties_hash[pkey].push schema_properties(items)
|
198
|
+
end
|
199
|
+
else
|
200
|
+
properties_hash[pkey] = !property.example.nil? ? property.example : property.type
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
properties_hash
|
205
|
+
end
|
206
|
+
|
207
|
+
private
|
208
|
+
|
209
|
+
def get_all_of_array(schema)
|
210
|
+
properties = Array.new
|
211
|
+
if schema.is_a?(Array)
|
212
|
+
schema = schema[1]
|
213
|
+
end
|
214
|
+
if schema["allOf"]
|
215
|
+
all_of = schema["allOf"]
|
216
|
+
end
|
217
|
+
if !all_of.blank?
|
218
|
+
all_of.each do |schema_nested|
|
219
|
+
schema_nested.properties.each do |property|
|
220
|
+
if property.is_a?(Array)
|
221
|
+
property = property[1]
|
222
|
+
end
|
223
|
+
properties.push property
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
properties
|
228
|
+
end
|
229
|
+
|
230
|
+
def get_all_of_hash(schema)
|
231
|
+
properties = Hash.new
|
232
|
+
if schema["allOf"]
|
233
|
+
all_of = schema["allOf"]
|
234
|
+
end
|
235
|
+
if !all_of.blank?
|
236
|
+
all_of.each do |schema_nested|
|
237
|
+
schema_nested.properties.each do |key, property|
|
238
|
+
properties[key] = property
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
properties
|
243
|
+
end
|
244
|
+
|
245
|
+
def get_renderer(file)
|
246
|
+
template_path = File.join(File.dirname(__FILE__), 'templates/' + file)
|
247
|
+
template = File.open(template_path, 'r').read
|
248
|
+
ERB.new(template)
|
249
|
+
end
|
250
|
+
|
251
|
+
def get_operations(path)
|
252
|
+
operations = {}
|
253
|
+
operations['get'] = path.get if defined? path.get
|
254
|
+
operations['put'] = path.put if defined? path.put
|
255
|
+
operations['post'] = path.post if defined? path.post
|
256
|
+
operations['delete'] = path.delete if defined? path.delete
|
257
|
+
operations['patch'] = path.patch if defined? path.patch
|
258
|
+
operations
|
259
|
+
end
|
260
|
+
|
261
|
+
def get_schema_name(text)
|
262
|
+
unless text.is_a?(String)
|
263
|
+
return nil
|
264
|
+
end
|
265
|
+
# Schema dictates that it's always components['schemas']
|
266
|
+
text.gsub(/#\/components\/schemas\//, '')
|
267
|
+
end
|
268
|
+
|
269
|
+
def get_schema_link(schema)
|
270
|
+
schema_name = get_schema_name schema.node_context.source_location.to_s
|
271
|
+
if !schema_name.nil?
|
272
|
+
id = "schema-#{schema_name.parameterize}"
|
273
|
+
output = "<a href='\##{id}'>#{schema_name}</a>"
|
274
|
+
output
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|