nexmo_markdown_renderer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) 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/cli.rb +13 -0
  9. data/lib/nexmo_markdown_renderer/core_ext/html.rb +7 -0
  10. data/lib/nexmo_markdown_renderer/core_ext/string.rb +14 -0
  11. data/lib/nexmo_markdown_renderer/filters/anchor_filter.rb +14 -0
  12. data/lib/nexmo_markdown_renderer/filters/audio_filter.rb +18 -0
  13. data/lib/nexmo_markdown_renderer/filters/block_escape_filter.rb +21 -0
  14. data/lib/nexmo_markdown_renderer/filters/break_filter.rb +10 -0
  15. data/lib/nexmo_markdown_renderer/filters/code_filter.rb +62 -0
  16. data/lib/nexmo_markdown_renderer/filters/code_snippet_filter.rb +187 -0
  17. data/lib/nexmo_markdown_renderer/filters/code_snippet_list_filter.rb +26 -0
  18. data/lib/nexmo_markdown_renderer/filters/code_snippets_filter.rb +170 -0
  19. data/lib/nexmo_markdown_renderer/filters/collapsible_filter.rb +27 -0
  20. data/lib/nexmo_markdown_renderer/filters/columns_filter.rb +47 -0
  21. data/lib/nexmo_markdown_renderer/filters/concept_list_filter.rb +30 -0
  22. data/lib/nexmo_markdown_renderer/filters/dynamic_content_filter.rb +28 -0
  23. data/lib/nexmo_markdown_renderer/filters/external_link_filter.rb +29 -0
  24. data/lib/nexmo_markdown_renderer/filters/frontmatter_filter.rb +11 -0
  25. data/lib/nexmo_markdown_renderer/filters/heading_filter.rb +57 -0
  26. data/lib/nexmo_markdown_renderer/filters/i18n/frontmatter_filter.rb +16 -0
  27. data/lib/nexmo_markdown_renderer/filters/i18n/smartling_converter_filter.rb +22 -0
  28. data/lib/nexmo_markdown_renderer/filters/icon_filter.rb +19 -0
  29. data/lib/nexmo_markdown_renderer/filters/indent_filter.rb +17 -0
  30. data/lib/nexmo_markdown_renderer/filters/inline_escape_filter.rb +14 -0
  31. data/lib/nexmo_markdown_renderer/filters/js_sequence_diagram_filter.rb +18 -0
  32. data/lib/nexmo_markdown_renderer/filters/label_filter.rb +29 -0
  33. data/lib/nexmo_markdown_renderer/filters/language_filter.rb +12 -0
  34. data/lib/nexmo_markdown_renderer/filters/markdown_filter.rb +81 -0
  35. data/lib/nexmo_markdown_renderer/filters/mermaid_filter.rb +29 -0
  36. data/lib/nexmo_markdown_renderer/filters/modal_filter.rb +37 -0
  37. data/lib/nexmo_markdown_renderer/filters/partial_filter.rb +29 -0
  38. data/lib/nexmo_markdown_renderer/filters/php_inliner_filter.rb +11 -0
  39. data/lib/nexmo_markdown_renderer/filters/screenshot_filter.rb +22 -0
  40. data/lib/nexmo_markdown_renderer/filters/tab_filter.rb +298 -0
  41. data/lib/nexmo_markdown_renderer/filters/techio_filter.rb +20 -0
  42. data/lib/nexmo_markdown_renderer/filters/tooltip_filter.rb +18 -0
  43. data/lib/nexmo_markdown_renderer/filters/unfreeze_filter.rb +16 -0
  44. data/lib/nexmo_markdown_renderer/filters/use_case_list_filter.rb +20 -0
  45. data/lib/nexmo_markdown_renderer/initializers/doc_finder.rb +5 -0
  46. data/lib/nexmo_markdown_renderer/initializers/i18n.rb +4 -0
  47. data/lib/nexmo_markdown_renderer/initializers/redcarpet.rb +7 -0
  48. data/lib/nexmo_markdown_renderer/markdown_renderer.rb +47 -0
  49. data/lib/nexmo_markdown_renderer/models/code_language.rb +79 -0
  50. data/lib/nexmo_markdown_renderer/models/code_snippet.rb +72 -0
  51. data/lib/nexmo_markdown_renderer/models/concept.rb +83 -0
  52. data/lib/nexmo_markdown_renderer/models/tutorial.rb +148 -0
  53. data/lib/nexmo_markdown_renderer/models/use_case.rb +81 -0
  54. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/android.rb +25 -0
  55. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/base.rb +12 -0
  56. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/curl.rb +29 -0
  57. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/dotnet.rb +23 -0
  58. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/java.rb +32 -0
  59. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/javascript.rb +23 -0
  60. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/kotlin.rb +25 -0
  61. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/objective_c.rb +25 -0
  62. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/php.rb +23 -0
  63. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/python.rb +23 -0
  64. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/ruby.rb +23 -0
  65. data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/swift.rb +25 -0
  66. data/lib/nexmo_markdown_renderer/services/doc_finder.rb +119 -0
  67. data/lib/nexmo_markdown_renderer/views/code_snippets/_application_messages_dispatch.html.erb +9 -0
  68. data/lib/nexmo_markdown_renderer/views/code_snippets/_application_rtc.html.erb +28 -0
  69. data/lib/nexmo_markdown_renderer/views/code_snippets/_application_voice.html.erb +24 -0
  70. data/lib/nexmo_markdown_renderer/views/code_snippets/_code_only.html.erb +6 -0
  71. data/lib/nexmo_markdown_renderer/views/code_snippets/_configure_client.html.erb +20 -0
  72. data/lib/nexmo_markdown_renderer/views/code_snippets/_dependencies.html.erb +11 -0
  73. data/lib/nexmo_markdown_renderer/views/code_snippets/_write_code.html.erb +13 -0
  74. data/lib/nexmo_markdown_renderer/views/code_snippets/list/plain.html.erb +10 -0
  75. data/lib/nexmo_markdown_renderer/views/concepts/list/plain.html.erb +5 -0
  76. data/lib/nexmo_markdown_renderer/views/use_case/_index.html.erb +41 -0
  77. data/lib/nexmo_markdown_renderer/views/use_case/index.html.erb +48 -0
  78. data/lib/nexmo_markdown_renderer/views/use_case/list/plain.html.erb +5 -0
  79. data/lib/nexmo_markdown_renderer/views/use_case/show.html.erb +8 -0
  80. data/lib/version.rb +7 -0
  81. metadata +322 -0
@@ -0,0 +1,29 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class MermaidFilter < Banzai::Filter
4
+ TYPES = {
5
+ 'mermaid' => '',
6
+ 'sequence_diagram' => 'sequenceDiagram',
7
+ }.freeze
8
+
9
+ def call(input)
10
+ TYPES.each do |markdown, mermaid|
11
+ input = input.gsub(/```#{markdown}(.+?)```/m) do |_s|
12
+ render_mermaid(mermaid, $1)
13
+ end
14
+ end
15
+
16
+ input
17
+ end
18
+
19
+ def render_mermaid(type, content)
20
+ diagram = <<~HEREDOC
21
+ <div class="mermaid" style="color: transparent;">#{type} #{content.gsub('\\n', '<br />').strip}
22
+ </div>
23
+ HEREDOC
24
+
25
+ "FREEZESTART#{Base64.urlsafe_encode64(diagram)}FREEZEEND"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class ModalFilter < Banzai::Filter
4
+ def call(input)
5
+ modals = []
6
+
7
+ input.gsub!(/@\[(.+?)\]\((.+?)\)/) do |_s|
8
+ id = 'M' + SecureRandom.hex(12)
9
+ modals << { document: $2, id: id }
10
+ "<a href='javascript:void(0)' data-modal='#{id}' class='Vlt-modal-trigger Vlt-text-link'>#{$1}</a>"
11
+ end
12
+
13
+ modals = modals.map do |modal|
14
+ filename = "#{ENV['DOCS_BASE_PATH']}/#{modal[:document]}"
15
+ raise "Could not find modal #{filename}" unless File.exist? filename
16
+
17
+ document = File.read(filename)
18
+ output = Nexmo::Markdown::Renderer.new.call(document)
19
+
20
+ modal = <<~HEREDOC
21
+ <div class="Vlt-modal" id="#{modal[:id]}">
22
+ <div class="Vlt-modal__panel">
23
+ <div class="Vlt-modal__content">
24
+ #{output}
25
+ </div>
26
+ </div>
27
+ </div>
28
+ HEREDOC
29
+
30
+ "FREEZESTART#{Base64.urlsafe_encode64(modal)}FREEZEEND"
31
+ end
32
+
33
+ input + modals.join("\n")
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,29 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class PartialFilter < Banzai::Filter
4
+ def call(input)
5
+ input.gsub(/```partial(.+?)```/m) do |_s|
6
+ config = YAML.safe_load($1)
7
+ file_path = if config['source'].starts_with? 'app/views'
8
+ config['source']
9
+ else
10
+ "#{ENV['DOCS_BASE_PATH']}/#{config['source']}"
11
+ end
12
+ content = File.read(file_path)
13
+
14
+ active = options[:code_language] ? options[:code_language].key == config['platform'] : false
15
+
16
+ if config['platform']
17
+ <<~HEREDOC
18
+ <div class="js-platform" data-platform="#{config['platform']}" data-active="#{active}">
19
+ #{content.render_markdown}
20
+ </div>
21
+ HEREDOC
22
+ else
23
+ content
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class PhpInlinerFilter < Banzai::Filter
4
+ def call(input)
5
+ input.gsub(/(```php)\n/) do
6
+ "#{$1}?start_inline=1\n"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class ScreenshotFilter < Banzai::Filter
4
+ def call(input)
5
+ input.gsub(/```screenshot(.+?)```/m) do |_s|
6
+ config = YAML.safe_load($1)
7
+ if config['image'] && File.file?(config['image'])
8
+ "![Screenshot](#{config['image'].gsub('public', '')})"
9
+ else
10
+ <<~HEREDOC
11
+ ## Missing image
12
+ To fix this run:
13
+ ```
14
+ $ rake screenshots:update
15
+ ```
16
+ HEREDOC
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,298 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class TabFilter < Banzai::Filter
4
+ def call(input)
5
+ input.gsub(/^(\s*)```tabbed_(examples|content|folder)(.+?)```/m) do |_s|
6
+ @indentation = $1
7
+ @mode = $2
8
+ @config = YAML.safe_load($3)
9
+
10
+ if tabbed_folder?
11
+ raise "#{@config['source']} is not a directory" unless File.directory? "#{ENV['DOCS_BASE_PATH']}/#{@config['source']}"
12
+
13
+ @tabbed_config = YAML.safe_load(File.read("#{ENV['DOCS_BASE_PATH']}/#{@config['source']}/.config.yml"))
14
+ @path = @config['source']
15
+ validate_folder_config
16
+ else
17
+ validate_config
18
+ end
19
+ html
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def create_tabs(content)
26
+ tab = Nokogiri::XML::Element.new 'div', @document
27
+ tab['class'] = 'Vlt-tabs__link'
28
+ tab['class'] += ' Vlt-tabs__link_active' if content[:active]
29
+ tab['role'] = 'tab'
30
+
31
+ if content[:language]
32
+ tab['data-language'] = content[:language].key
33
+ tab['data-language-type'] = content[:language].type
34
+ tab['data-language-linkable'] = content[:language].linkable?
35
+ end
36
+
37
+ if content[:platform]
38
+ tab['data-language'] = content[:platform].languages.map(&:key).join(',')
39
+ tab['data-platform'] = content[:platform].key
40
+ tab['data-platform-type'] = content[:platform].type
41
+ tab['data-platform-linkable'] = content[:platform].linkable?
42
+ end
43
+
44
+ tab_link = Nokogiri::XML::Element.new 'span', @document
45
+ if content[:language]
46
+ # We don't currently have icons for JSON/XML
47
+ if ['json', 'xml'].include? content[:language].key.downcase
48
+ tab_link.content = content[:tab_title]
49
+ elsif content[:language].key == 'objective_c' || content[:language].key == 'swift'
50
+ tab_link.inner_html = "<svg><use xlink:href=\"/assets/images/brands/ios.svg#ios\" /></svg><span>" + content[:tab_title] + '</span>'
51
+ else
52
+ tab_link.inner_html = "<svg><use xlink:href=\"/assets/images/brands/#{content[:language].key}.svg##{content[:language].key}\" /></svg><span>" + content[:tab_title] + '</span>'
53
+ end
54
+ elsif content[:platform]
55
+ tab_link.inner_html = "<svg><use xlink:href=\"/assets/images/brands/#{content[:platform].key}.svg##{content[:platform].key}\" /></svg><span>" + content[:tab_title] + '</span>'
56
+ else
57
+ tab_link.content = content[:tab_title]
58
+ end
59
+
60
+ tab.add_child(tab_link)
61
+ @tabs.add_child(tab)
62
+ end
63
+
64
+ def create_content(content)
65
+ tabs_panel = Nokogiri::XML::Element.new 'div', @document
66
+ tabs_panel['class'] = 'Vlt-tabs__panel'
67
+ tabs_panel['class'] += ' Vlt-tabs__panel_active' if content[:active]
68
+
69
+ element = Nokogiri::XML::Element.new 'p', @document
70
+ element['aria-labelledby'] = "\"#{content[:id]}\""
71
+ element['aria-hidden'] = true
72
+ element.inner_html = content[:body]
73
+
74
+ tabs_panel.add_child(element)
75
+ @tabs_content.add_child(tabs_panel)
76
+ end
77
+
78
+ def tabbed_code_examples?
79
+ @mode == 'examples'
80
+ end
81
+
82
+ def tabbed_content?
83
+ @mode == 'content'
84
+ end
85
+
86
+ def tabbed_folder?
87
+ @mode == 'folder'
88
+ end
89
+
90
+ def html
91
+ html = <<~HEREDOC
92
+ <div class="Vlt-tabs">
93
+ <div class="Vlt-tabs__header Vlt-tabs__header--bordered"></div>
94
+ <div class="Vlt-tabs__content">
95
+ </div>
96
+ </div>
97
+ HEREDOC
98
+
99
+ @document = Nokogiri::HTML::DocumentFragment.parse(html)
100
+ @tabs = @document.at_css('.Vlt-tabs__header')
101
+ @tabs_content = @document.at_css('.Vlt-tabs__content')
102
+
103
+ contents.each do |content|
104
+ create_tabs(content)
105
+ create_content(content)
106
+ end
107
+
108
+ source = @document.to_html
109
+
110
+ "#{@indentation}FREEZESTART#{Base64.urlsafe_encode64(source)}FREEZEEND"
111
+ end
112
+
113
+ def contents
114
+ list = content_from_folder if tabbed_folder?
115
+ list ||= content_from_source if @config['source']
116
+ list ||= content_from_tabs if @config['tabs']
117
+
118
+ list ||= []
119
+
120
+ return list unless list.any?
121
+
122
+ list = resolve_language(list)
123
+
124
+ if tabbed_code_examples?
125
+ list = format_code(list)
126
+ list = resolve_code(list)
127
+ list = resolve_tab_title(list)
128
+ end
129
+
130
+ list = sort_contents(list)
131
+ resolve_active_tab(list)
132
+
133
+ list
134
+ end
135
+
136
+ def validate_config
137
+ return if @config && (@config['source'] || @config['tabs'])
138
+
139
+ raise 'Source or tabs must be present in this tabbed_example config'
140
+ end
141
+
142
+ def validate_folder_config
143
+ return if @tabbed_config && @tabbed_config['tabbed'] == true
144
+
145
+ raise 'Tabbed must be set to true in the folder config YAML file'
146
+ end
147
+
148
+ def content_from_source
149
+ source_path = "#{ENV['DOCS_BASE_PATH']}/#{@config['source']}"
150
+ source_path += '/*' if tabbed_code_examples?
151
+ source_path += '/*.md' if tabbed_content?
152
+
153
+ files = Dir.glob(source_path)
154
+ raise "Empty content_from_source file list in #{source_path}" if files.empty?
155
+
156
+ files.map do |content_path|
157
+ raise "Could not find content_from_source file: #{content_path}" unless File.exist? content_path
158
+
159
+ source = File.read(content_path)
160
+
161
+ next generate_tabbed_code_examples(source, content_path) if tabbed_code_examples?
162
+
163
+ generate_tabbed_content(source) if tabbed_content?
164
+ end
165
+ end
166
+
167
+ def content_from_folder
168
+ source_path = "#{ENV['DOCS_BASE_PATH']}/#{@config['source']}"
169
+ source_path += '/*.md'
170
+
171
+ files = Dir.glob(source_path)
172
+ raise "Empty content_from_source file list in #{source_path}" if files.empty?
173
+
174
+ files.map do |content_path|
175
+ raise "Could not find content_from_source file: #{content_path}" unless File.exist? content_path
176
+
177
+ source = File.read(content_path)
178
+
179
+ generate_tabbed_content(source)
180
+ end
181
+ end
182
+
183
+ def content_from_tabs
184
+ @config['tabs'].map do |title, config|
185
+ raise "Could not find content_from_tabs file: #{ENV['DOCS_BASE_PATH']}/#{@config['source']}" unless File.exist? "#{ENV['DOCS_BASE_PATH']}/#{@config['source']}"
186
+
187
+ source = File.read("#{ENV['DOCS_BASE_PATH']}/#{@config['source']}")
188
+
189
+ config.symbolize_keys.merge({
190
+ id: SecureRandom.hex,
191
+ source: source,
192
+ language_key: title.dup.downcase,
193
+ })
194
+ end
195
+ end
196
+
197
+ def generate_tabbed_content(source)
198
+ content = {
199
+ id: SecureRandom.hex,
200
+ source: source,
201
+ }
202
+
203
+ content[:frontmatter] = YAML.safe_load(source)
204
+ content[:language_key] = content[:frontmatter]['language']
205
+ content[:platform_key] = content[:frontmatter]['platform']
206
+ content[:tab_title] = content[:frontmatter]['title']
207
+ content[:body] = Nexmo::Markdown::Renderer.new(options).call(source)
208
+
209
+ content
210
+ end
211
+
212
+ def generate_tabbed_code_examples(source, content_path)
213
+ content = {
214
+ id: SecureRandom.hex,
215
+ source: source,
216
+ }
217
+ language_key = File.basename(content_path, '.*').downcase
218
+ content[:language_key] = language_key
219
+
220
+ content
221
+ end
222
+
223
+ def resolve_language(contents)
224
+ contents.map do |content|
225
+ if content[:language_key]
226
+ content[:language] = CodeLanguage.find(content[:language_key])
227
+ end
228
+
229
+ if content[:platform_key]
230
+ content[:platform] = CodeLanguage.find(content[:platform_key])
231
+ end
232
+
233
+ content
234
+ end
235
+ end
236
+
237
+ def format_code(contents)
238
+ contents.each do |content|
239
+ if content[:from_line] || content[:to_line]
240
+ lines = content[:source].lines
241
+ total_lines = lines.count
242
+ from_line = (content[:from_line] || 1) - 1
243
+ to_line = (content[:to_line] || total_lines) - 1
244
+ content[:source] = lines[from_line..to_line].join
245
+ end
246
+
247
+ content[:source].unindent! if content[:unindent]
248
+ end
249
+ end
250
+
251
+ def resolve_code(contents)
252
+ contents.map do |content|
253
+ @formatter ||= Rouge::Formatters::HTML.new
254
+ lexer = content[:language].lexer
255
+ highlighted_source = @formatter.format(lexer.lex(content[:source]))
256
+ body = <<~HEREDOC
257
+ <pre class="highlight #{content[:language_key]}"><code>#{highlighted_source}</code></pre>
258
+ HEREDOC
259
+
260
+ content.merge!({ body: body })
261
+ end
262
+ end
263
+
264
+ def resolve_tab_title(contents)
265
+ contents.map do |content|
266
+ content.merge!({ tab_title: content[:language].label })
267
+ end
268
+ end
269
+
270
+ def sort_contents(contents)
271
+ contents.sort_by do |content|
272
+ next content[:language].weight if content[:language]
273
+
274
+ next content[:frontmatter]['menu_weight'] || 999 if content[:frontmatter]
275
+
276
+ 999
277
+ end
278
+ end
279
+
280
+ def resolve_active_tab(contents)
281
+ active_index = nil
282
+
283
+ if options[:code_language]
284
+ contents.each_with_index do |content, index|
285
+ %i[language_key platform_key].each do |key|
286
+ active_index = index if content[key] == options[:code_language].key
287
+ end
288
+ end
289
+ end
290
+
291
+ @tabs['data-has-initial-tab'] = active_index.present?
292
+ active_index ||= 0
293
+
294
+ contents[active_index][:active] = true
295
+ end
296
+ end
297
+ end
298
+ end
@@ -0,0 +1,20 @@
1
+ module Nexmo
2
+ module Markdown
3
+ class TechioFilter < Banzai::Filter
4
+ def call(input)
5
+ input.gsub(/```techio(.+?)```/m) do |_s|
6
+ config = YAML.safe_load($1)
7
+
8
+ techio = <<~HEREDOC
9
+ <div class="techio-container">
10
+ <iframe width="100%" frameborder="0" scrolling="no" allowtransparency="true" style="visibility: hidden" src="https://tech.io/playground-widget#{config['path']}/#{config['title']}"></iframe>
11
+ <script>if(typeof window.techioScriptInjected==="undefined"){window.techioScriptInjected=true;var d=document,s=d.createElement("script");s.src="https://files.codingame.com/codingame/iframe-v-1-4.js";(d.head||d.body).appendChild(s);}</script>
12
+ </div>
13
+ HEREDOC
14
+
15
+ "FREEZESTART#{Base64.urlsafe_encode64(techio)}FREEZEEND"
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end