nexmo_markdown_renderer 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +54 -0
  4. data/config/code_languages.yml +138 -0
  5. data/config/dynamic_content.yml +1 -0
  6. data/config/locales/en.yml +176 -0
  7. data/lib/nexmo_markdown_renderer.rb +28 -0
  8. data/lib/nexmo_markdown_renderer/core_ext/string.rb +14 -0
  9. data/lib/nexmo_markdown_renderer/filters/anchor_filter.rb +14 -0
  10. data/lib/nexmo_markdown_renderer/filters/audio_filter.rb +18 -0
  11. data/lib/nexmo_markdown_renderer/filters/block_escape_filter.rb +21 -0
  12. data/lib/nexmo_markdown_renderer/filters/break_filter.rb +10 -0
  13. data/lib/nexmo_markdown_renderer/filters/code_filter.rb +62 -0
  14. data/lib/nexmo_markdown_renderer/filters/code_snippet_filter.rb +187 -0
  15. data/lib/nexmo_markdown_renderer/filters/code_snippet_list_filter.rb +26 -0
  16. data/lib/nexmo_markdown_renderer/filters/code_snippets_filter.rb +170 -0
  17. data/lib/nexmo_markdown_renderer/filters/collapsible_filter.rb +27 -0
  18. data/lib/nexmo_markdown_renderer/filters/columns_filter.rb +47 -0
  19. data/lib/nexmo_markdown_renderer/filters/concept_list_filter.rb +30 -0
  20. data/lib/nexmo_markdown_renderer/filters/dynamic_content_filter.rb +28 -0
  21. data/lib/nexmo_markdown_renderer/filters/external_link_filter.rb +29 -0
  22. data/lib/nexmo_markdown_renderer/filters/frontmatter_filter.rb +11 -0
  23. data/lib/nexmo_markdown_renderer/filters/heading_filter.rb +57 -0
  24. data/lib/nexmo_markdown_renderer/filters/i18n/frontmatter_filter.rb +16 -0
  25. data/lib/nexmo_markdown_renderer/filters/i18n/smartling_converter_filter.rb +22 -0
  26. data/lib/nexmo_markdown_renderer/filters/icon_filter.rb +19 -0
  27. data/lib/nexmo_markdown_renderer/filters/indent_filter.rb +17 -0
  28. data/lib/nexmo_markdown_renderer/filters/inline_escape_filter.rb +14 -0
  29. data/lib/nexmo_markdown_renderer/filters/js_sequence_diagram_filter.rb +18 -0
  30. data/lib/nexmo_markdown_renderer/filters/label_filter.rb +29 -0
  31. data/lib/nexmo_markdown_renderer/filters/language_filter.rb +12 -0
  32. data/lib/nexmo_markdown_renderer/filters/markdown_filter.rb +81 -0
  33. data/lib/nexmo_markdown_renderer/filters/mermaid_filter.rb +29 -0
  34. data/lib/nexmo_markdown_renderer/filters/modal_filter.rb +37 -0
  35. data/lib/nexmo_markdown_renderer/filters/partial_filter.rb +29 -0
  36. data/lib/nexmo_markdown_renderer/filters/php_inliner_filter.rb +11 -0
  37. data/lib/nexmo_markdown_renderer/filters/screenshot_filter.rb +22 -0
  38. data/lib/nexmo_markdown_renderer/filters/tab_filter.rb +298 -0
  39. data/lib/nexmo_markdown_renderer/filters/techio_filter.rb +20 -0
  40. data/lib/nexmo_markdown_renderer/filters/tooltip_filter.rb +18 -0
  41. data/lib/nexmo_markdown_renderer/filters/unfreeze_filter.rb +16 -0
  42. data/lib/nexmo_markdown_renderer/filters/use_case_list_filter.rb +20 -0
  43. data/lib/nexmo_markdown_renderer/initializers/doc_finder.rb +5 -0
  44. data/lib/nexmo_markdown_renderer/initializers/i18n.rb +4 -0
  45. data/lib/nexmo_markdown_renderer/initializers/redcarpet.rb +7 -0
  46. data/lib/nexmo_markdown_renderer/markdown_renderer.rb +47 -0
  47. data/lib/nexmo_markdown_renderer/models/code_language.rb +79 -0
  48. data/lib/nexmo_markdown_renderer/models/code_snippet.rb +72 -0
  49. data/lib/nexmo_markdown_renderer/models/concept.rb +83 -0
  50. data/lib/nexmo_markdown_renderer/models/tutorial.rb +148 -0
  51. data/lib/nexmo_markdown_renderer/models/use_case.rb +81 -0
  52. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/android.rb +25 -0
  53. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/base.rb +12 -0
  54. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/curl.rb +29 -0
  55. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/dotnet.rb +23 -0
  56. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/java.rb +32 -0
  57. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/javascript.rb +23 -0
  58. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/kotlin.rb +25 -0
  59. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/objective_c.rb +25 -0
  60. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/php.rb +23 -0
  61. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/python.rb +23 -0
  62. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/ruby.rb +23 -0
  63. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/swift.rb +25 -0
  64. data/lib/nexmo_markdown_renderer/services/doc_finder.rb +119 -0
  65. data/lib/nexmo_markdown_renderer/views/code_snippets/_application_messages_dispatch.html.erb +9 -0
  66. data/lib/nexmo_markdown_renderer/views/code_snippets/_application_rtc.html.erb +28 -0
  67. data/lib/nexmo_markdown_renderer/views/code_snippets/_application_voice.html.erb +24 -0
  68. data/lib/nexmo_markdown_renderer/views/code_snippets/_code_only.html.erb +6 -0
  69. data/lib/nexmo_markdown_renderer/views/code_snippets/_configure_client.html.erb +20 -0
  70. data/lib/nexmo_markdown_renderer/views/code_snippets/_dependencies.html.erb +11 -0
  71. data/lib/nexmo_markdown_renderer/views/code_snippets/_write_code.html.erb +13 -0
  72. data/lib/nexmo_markdown_renderer/views/code_snippets/list/plain.html.erb +10 -0
  73. data/lib/nexmo_markdown_renderer/views/concepts/list/plain.html.erb +5 -0
  74. data/lib/nexmo_markdown_renderer/views/use_case/_index.html.erb +41 -0
  75. data/lib/nexmo_markdown_renderer/views/use_case/index.html.erb +48 -0
  76. data/lib/nexmo_markdown_renderer/views/use_case/list/plain.html.erb +5 -0
  77. data/lib/nexmo_markdown_renderer/views/use_case/show.html.erb +8 -0
  78. data/lib/version.rb +7 -0
  79. metadata +320 -0
@@ -0,0 +1,14 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class AnchorFilter < Banzai::Filter
4
+ def call(input)
5
+ input.gsub(/^[\u{2693}](.+?)\n/) do
6
+ <<~HEREDOC
7
+ <a name="#{$1.parameterize}"></a>
8
+ HEREDOC
9
+ end
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class AudioFilter < Banzai::Filter
4
+ def call(input)
5
+ input.gsub(/[u{🔈}]\[(.+?)\]/) do
6
+ audio = <<~HEREDOC
7
+ <audio controls>
8
+ <source src="#{$1}" type="audio/mpeg">
9
+ </audio>
10
+ HEREDOC
11
+
12
+ "FREEZESTART#{Base64.urlsafe_encode64(audio)}FREEZEEND"
13
+ end
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class BlockEscapeFilter < Banzai::Filter
4
+ def call(input)
5
+ # Freeze to prevent Markdown formatting
6
+ input.gsub(/````\n(.+?)````/m) do |_s|
7
+ lexer = Rouge::Lexer.find('text')
8
+ formatter = Rouge::Formatters::HTML.new
9
+ highlighted_source = formatter.format(lexer.lex($1))
10
+
11
+ output = <<~HEREDOC
12
+ <pre class="highlight #{lexer.tag}"><code>#{highlighted_source}</code></pre>
13
+ HEREDOC
14
+
15
+ "FREEZESTART#{Base64.urlsafe_encode64(output)}FREEZEEND"
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class BreakFilter < Banzai::Filter
4
+ def call(input)
5
+ input.gsub('§', '<br>')
6
+ end
7
+ end
8
+
9
+ end
10
+ end
@@ -0,0 +1,62 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class CodeFilter < Banzai::Filter
4
+ def call(input)
5
+ input.gsub(/(?!.*snippet)```code(.+?)```/m) do |_s|
6
+ config = YAML.safe_load($1)
7
+
8
+ if config['config']
9
+ configs = YAML.load_file("#{ENV['DOCS_BASE_PATH']}/config/code_examples.yml")
10
+ config = config['config'].split('.').inject(configs) { |h, k| h[k] }
11
+ end
12
+
13
+ code = File.read("#{ENV['DOCS_BASE_PATH']}/#{config['source']}")
14
+ language = File.extname("#{ENV['DOCS_BASE_PATH']}/#{config['source']}")[1..-1]
15
+ lexer = language_to_lexer(language)
16
+
17
+ total_lines = code.lines.count
18
+
19
+ # Minus one since lines are not zero-indexed
20
+ from_line = (config['from_line'] || 1) - 1
21
+ to_line = (config['to_line'] || total_lines) - 1
22
+
23
+ code = code.lines[from_line..to_line].join
24
+ code.unindent! if config['unindent']
25
+
26
+ highlighted_source = highlight(code, lexer)
27
+
28
+ <<~HEREDOC
29
+ <pre class="highlight #{lexer.tag}"><code>#{highlighted_source}</code></pre>
30
+ HEREDOC
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def highlight(source, lexer)
37
+ formatter = Rouge::Formatters::HTML.new
38
+ formatter.format(lexer.lex(source))
39
+ end
40
+
41
+ def language_to_lexer_name(language)
42
+ if language_configuration[language]
43
+ language_configuration[language]['lexer']
44
+ else
45
+ language
46
+ end
47
+ end
48
+
49
+ def language_to_lexer(language)
50
+ language = language_to_lexer_name(language)
51
+ return Rouge::Lexers::PHP.new({ start_inline: true }) if language == 'php'
52
+
53
+ Rouge::Lexer.find(language.downcase) || Rouge::Lexer.find('text')
54
+ end
55
+
56
+ def language_configuration
57
+ @language_configuration ||= YAML.load_file("#{GEM_ROOT}/config/code_languages.yml")
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,187 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class CodeSnippetFilter < Banzai::Filter
4
+ include OcticonsHelper
5
+
6
+ def call(input)
7
+ input.gsub(/```single_code_snippet(.+?)```/m) do |_s|
8
+ config = YAML.safe_load($1)
9
+
10
+ @renderer = get_renderer(config['language'])
11
+
12
+ lexer = Nexmo::Markdown::CodeLanguage.find(config['language']).lexer
13
+ lang = config['title'].delete('.')
14
+
15
+ application_html = generate_application_block(config['application'])
16
+
17
+ # Read the client
18
+ if config['client']
19
+ highlighted_client_source = generate_code_block(config['language'], config['client'], config['unindent'])
20
+ end
21
+
22
+ # Read the code
23
+ highlighted_code_source = generate_code_block(config['language'], config['code'], config['unindent'])
24
+
25
+ dependency_html = ''
26
+ if config['dependencies']
27
+ dependency_html = generate_dependencies(lexer.tag, config['dependencies'])
28
+ end
29
+
30
+ source_url = generate_source_url(config['code'])
31
+
32
+ client_html = ''
33
+ if highlighted_client_source
34
+ client_url = generate_source_url(config['client'])
35
+ id = SecureRandom.hex
36
+ create_instructions = @renderer.create_instructions(config['file_name']).render_markdown
37
+ erb = File.read("#{GEM_ROOT}/lib/nexmo_markdown_renderer/views/code_snippets/_configure_client.html.erb")
38
+ client_html = ERB.new(erb).result(binding)
39
+ end
40
+
41
+ add_instructions = @renderer.add_instructions(config['file_name']).render_markdown
42
+ if config['code_only']
43
+ erb = File.read("#{GEM_ROOT}/lib/nexmo_markdown_renderer/views/code_snippets/_code_only.html.erb")
44
+ else
45
+ erb = File.read("#{GEM_ROOT}/lib/nexmo_markdown_renderer/views/code_snippets/_write_code.html.erb")
46
+ end
47
+
48
+ code_html = ERB.new(erb).result(binding)
49
+
50
+ return code_html if config['code_only']
51
+
52
+ config['run_command'] = config['run_command'].gsub('{filename}', config['file_name']) if config['run_command']
53
+ run_html = @renderer.run_command(config['run_command'], config['file_name'], config['code']['source']).to_s
54
+
55
+ prereqs = (application_html + dependency_html + client_html).strip
56
+ prereqs = "<h2>#{::I18n.t('.filters.prerequisites')}</h2>#{prereqs}" unless prereqs.empty?
57
+ prereqs + code_html + run_html
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def highlight(source, lexer)
64
+ formatter = Rouge::Formatters::HTML.new
65
+ formatter.format(lexer.lex(source))
66
+ end
67
+
68
+ def generate_code_block(language, input, unindent)
69
+ filename = "#{ENV['DOCS_BASE_PATH']}/#{input['source']}"
70
+ return '' unless input
71
+ raise "CodeSnippetFilter - Could not load #{filename} for language #{language}" unless File.exist?(filename)
72
+
73
+ code = File.read(filename)
74
+ lexer = Nexmo::Markdown::CodeLanguage.find(language).lexer
75
+
76
+ total_lines = code.lines.count
77
+
78
+ # Minus one since lines are not zero-indexed
79
+ from_line = (input['from_line'] || 1) - 1
80
+ to_line = (input['to_line'] || total_lines) - 1
81
+
82
+ code = code.lines[from_line..to_line].join
83
+ code.unindent! if unindent
84
+
85
+ highlight(code, lexer)
86
+ end
87
+
88
+ def get_renderer(language)
89
+ language = 'dotnet' if language == 'csharp'
90
+ case language
91
+ when 'curl'
92
+ Nexmo::Markdown::CodeSnippetRenderer::Curl
93
+ when 'node'
94
+ Nexmo::Markdown::CodeSnippetRenderer::Javascript
95
+ when 'javascript'
96
+ Nexmo::Markdown::CodeSnippetRenderer::Javascript
97
+ when 'java'
98
+ Nexmo::Markdown::CodeSnippetRenderer::Java
99
+ when 'dotnet'
100
+ Nexmo::Markdown::CodeSnippetRenderer::Dotnet
101
+ when 'python'
102
+ Nexmo::Markdown::CodeSnippetRenderer::Python
103
+ when 'ruby'
104
+ Nexmo::Markdown::CodeSnippetRenderer::Ruby
105
+ when 'php'
106
+ Nexmo::Markdown::CodeSnippetRenderer::Php
107
+ when 'android'
108
+ Nexmo::Markdown::CodeSnippetRenderer::Android
109
+ when 'kotlin'
110
+ Nexmo::Markdown::CodeSnippetRenderer::Kotlin
111
+ when 'objective_c'
112
+ Nexmo::Markdown::CodeSnippetRenderer::ObjectiveC
113
+ when 'swift'
114
+ Nexmo::Markdown::CodeSnippetRenderer::Swift
115
+ else
116
+ raise "Unknown language: #{language}"
117
+ end
118
+ end
119
+
120
+ def generate_dependencies(language, dependencies)
121
+ # The only valid dependency for curl examples is `JWT`
122
+ if dependencies.map(&:upcase).include?('JWT')
123
+ title = ::I18n.t('filters.generate_your_jwt')
124
+ else
125
+ title = ::I18n.t('filters.install_dependencies')
126
+ end
127
+ deps = @renderer.dependencies(dependencies)
128
+ id = SecureRandom.hex
129
+ erb = File.read("#{GEM_ROOT}/lib/nexmo_markdown_renderer/views/code_snippets/_dependencies.html.erb")
130
+ ERB.new(erb).result(binding)
131
+ end
132
+
133
+ def generate_application_block(app)
134
+ return '' unless app
135
+
136
+ base_url = 'http://demo.ngrok.io'
137
+ base_url = 'https://example.com' if app['disable_ngrok']
138
+
139
+ app['name'] = 'ExampleProject' unless app['name']
140
+
141
+ # We should remove this default once we're sure that all Code Snippets
142
+ # have a type set e.g audit
143
+ app['type'] ||= 'voice'
144
+
145
+ if ['voice', 'rtc'].include? app['type']
146
+ app['event_url'] = "#{base_url}/webhooks/events" unless app['event_url']
147
+ app['answer_url'] = "#{base_url}/webhooks/answer" unless app['answer_url']
148
+ erb = File.read("#{GEM_ROOT}/lib/nexmo_markdown_renderer/views/code_snippets/_application_#{app['type']}.html.erb")
149
+ elsif ['messages', 'dispatch'].include? app['type']
150
+ erb = File.read("#{GEM_ROOT}/lib/nexmo_markdown_renderer/views/code_snippets/_application_messages_dispatch.html.erb")
151
+ else
152
+ raise "Invalid application type when creating code snippet: '#{app['type']}'"
153
+ end
154
+
155
+ id = SecureRandom.hex
156
+
157
+ ERB.new(erb).result(binding)
158
+ end
159
+
160
+ def generate_source_url(code)
161
+ # Source example: .repos/nexmo/nexmo-java-code-snippets/ExampleClass.java
162
+ # Direct link on GitHub is in form https://github.com/nexmo/nexmo-java-code-snippets/blob/master/ExampleClass.java
163
+ start_section = 'https://github.com'
164
+
165
+ # Insert "blob/master" and strip ".repos"
166
+ repo_path = '\\0blob/master/'
167
+ file_section = code['source'].sub('.repos', '').sub(%r{(-quickstart|-code-snippets|-code-snippets)/}, repo_path)
168
+
169
+ # Line highlighting
170
+ line_section = ''
171
+ if code['from_line']
172
+ line_section += "#L#{code['from_line']}"
173
+ if code['to_line']
174
+ # If we've provided a to_line, use that
175
+ line_section += "-L#{code['to_line']}" if code['to_line']
176
+ else
177
+ # By default we read to the end of the file
178
+ line_section += "-L#{File.read(code['source']).lines.count}"
179
+ end
180
+ end
181
+
182
+ start_section + file_section + line_section
183
+ end
184
+ end
185
+
186
+ end
187
+ end
@@ -0,0 +1,26 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class CodeSnippetListFilter < Banzai::Filter
4
+ def call(input)
5
+ input.gsub(/```code_snippet_list(.+?)```/m) do |_s|
6
+ config = YAML.safe_load($1)
7
+ @product = config['product']
8
+ @blocks = Nexmo::Markdown::CodeSnippet.by_product(@product)
9
+
10
+ # Top level blocks come before subfolder
11
+ @blocks.sort_by! do |b|
12
+ sort_array = []
13
+ sort_array << (b.category ? 1 : 0)
14
+ sort_array << b.navigation_weight
15
+ sort_array
16
+ end
17
+
18
+ erb = File.read("#{GEM_ROOT}/lib/nexmo_markdown_renderer/views/code_snippets/list/plain.html.erb")
19
+ html = ERB.new(erb).result(binding)
20
+ "FREEZESTART#{Base64.urlsafe_encode64(html)}FREEZEEND"
21
+ end
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,170 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class CodeSnippetsFilter < Banzai::Filter
4
+ def call(input)
5
+ input.gsub(/^(\s*)```code_snippets(.+?)```/m) do |_s|
6
+ @indentation = $1
7
+ @config = YAML.safe_load($2)
8
+ validate_config
9
+ html
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def create_tabs(content)
16
+ tab = Nokogiri::XML::Element.new 'li', @document
17
+ tab['class'] = 'Vlt-tabs__link'
18
+ tab['class'] += ' Vlt-tabs__link_active' if content[:active]
19
+ tab['aria-selected'] = 'true' if content[:active]
20
+
21
+ if content['language']
22
+ tab['data-language'] = content['language']
23
+ tab['data-language-type'] = content['language_type']
24
+ tab['data-language-linkable'] = true
25
+ end
26
+
27
+ tab_link = Nokogiri::XML::Element.new 'a', @document
28
+ tab_link.inner_html = "<svg><use xlink:href=\"/assets/images/brands/#{content['icon']}.svg##{content['icon']}\" /></svg><span>" + content['title'] + '</span>'
29
+ tab_link['class'] = 'tab-link'
30
+
31
+ tab.add_child(tab_link)
32
+ @tabs.add_child(tab)
33
+ end
34
+
35
+ def create_content(content)
36
+ element = Nokogiri::XML::Element.new 'div', @document
37
+ element['id'] = content['id']
38
+ element['class'] = 'Vlt-tabs__panel'
39
+ element['class'] += ' Vlt-tabs__panel_active' if content[:active]
40
+ element.inner_html = content[:body]
41
+
42
+ @tabs_content.add_child(element)
43
+ end
44
+
45
+ def html
46
+ html = <<~HEREDOC
47
+ <div class="Vlt-tabs">
48
+ <div class="Vlt-tabs__header--bordered"></div>
49
+ <div class="Vlt-tabs__content"></div>
50
+ </div>
51
+ HEREDOC
52
+
53
+ generate_html_content(html)
54
+ end
55
+
56
+ def generate_html_content(html)
57
+ @document = Nokogiri::HTML::DocumentFragment.parse(html)
58
+ @tabs = @document.at_css('.Vlt-tabs__header--bordered')
59
+ @tabs_content = @document.at_css('.Vlt-tabs__content')
60
+
61
+ tab_maker
62
+
63
+ source = @document.to_html
64
+
65
+ "#{@indentation}FREEZESTART#{Base64.urlsafe_encode64(source)}FREEZEEND"
66
+ end
67
+
68
+ def contents
69
+ list = content_from_source
70
+ list ||= []
71
+
72
+ list = sort_contents(list)
73
+ resolve_active_tab(list)
74
+
75
+ list
76
+ end
77
+
78
+ def tab_maker
79
+ contents.each do |content|
80
+ create_tabs(content)
81
+ create_content(content)
82
+ end
83
+ end
84
+
85
+ def validate_config
86
+ return if @config && @config['source']
87
+
88
+ raise 'A source key must be present in this building_blocks config'
89
+ end
90
+
91
+ def content_from_source
92
+ source_path = "#{ENV['DOCS_BASE_PATH']}/#{@config['source']}/*.yml"
93
+
94
+ files = Dir[source_path]
95
+ raise "No .yml files found for #{@config['source']} code snippets" if files.empty?
96
+
97
+ generate_content(files)
98
+ end
99
+
100
+ def generate_content(files)
101
+ files.map do |content_path|
102
+ source = File.read(content_path)
103
+
104
+ # Load the defaults for this language
105
+ filename = File.basename(content_path, '.yml')
106
+ defaults = Nexmo::Markdown::CodeLanguage.find(filename)
107
+
108
+ content = YAML.safe_load(source)
109
+ content['source'] = source
110
+ content['id'] = SecureRandom.hex
111
+ content['title'] ||= defaults.label
112
+ content['language'] ||= defaults.key
113
+ content['language_type'] ||= defaults.type
114
+ content['dependencies'] ||= defaults.dependencies
115
+ content['icon'] = defaults.icon
116
+ content['weight'] ||= defaults.weight
117
+ content['run_command'] ||= defaults.run_command
118
+ content['unindent'] = defaults.unindent || false
119
+
120
+ # If we don't have a file_name in config, use the one in the repo
121
+ content['file_name'] ||= File.basename(content['code']['source'])
122
+
123
+ parent_config = { 'code_only' => @config['code_only'], 'source' => @config['source'].gsub('_examples/', '') }
124
+ if @config['application']
125
+ parent_config = parent_config.merge({ 'application' => @config['application'] })
126
+ end
127
+
128
+ parent_config = parent_config.to_yaml.lines[1..-1].join
129
+
130
+ source = render_single_snippet(content, parent_config)
131
+
132
+ content[:body] = Nexmo::Markdown::Renderer.new(options).call(source)
133
+
134
+ content
135
+ end
136
+ end
137
+
138
+ def render_single_snippet(content, parent_config)
139
+ <<~HEREDOC
140
+ ```single_code_snippet
141
+ #{content.to_yaml}\n#{parent_config}
142
+ ```
143
+ HEREDOC
144
+ end
145
+
146
+ def sort_contents(contents)
147
+ contents.sort_by do |content|
148
+ content['weight']
149
+ end
150
+ end
151
+
152
+ def resolve_active_tab(contents)
153
+ active_index = nil
154
+
155
+ if options[:code_language]
156
+ contents.each_with_index do |content, index|
157
+ active_index = index if content['language'] == options[:code_language].key
158
+ end
159
+ end
160
+
161
+ @tabs['data-has-initial-tab'] = active_index.present?
162
+ @tabs['class'] += ' skip-pushstate' if @tabs['data-has-initial-tab']
163
+ active_index ||= 0
164
+
165
+ contents[active_index][:active] = true
166
+ end
167
+ end
168
+
169
+ end
170
+ end