nexmo_markdown_renderer 0.0.2
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +54 -0
- data/config/code_languages.yml +138 -0
- data/config/dynamic_content.yml +1 -0
- data/config/locales/en.yml +176 -0
- data/lib/nexmo_markdown_renderer.rb +28 -0
- data/lib/nexmo_markdown_renderer/core_ext/string.rb +14 -0
- data/lib/nexmo_markdown_renderer/filters/anchor_filter.rb +14 -0
- data/lib/nexmo_markdown_renderer/filters/audio_filter.rb +18 -0
- data/lib/nexmo_markdown_renderer/filters/block_escape_filter.rb +21 -0
- data/lib/nexmo_markdown_renderer/filters/break_filter.rb +10 -0
- data/lib/nexmo_markdown_renderer/filters/code_filter.rb +62 -0
- data/lib/nexmo_markdown_renderer/filters/code_snippet_filter.rb +187 -0
- data/lib/nexmo_markdown_renderer/filters/code_snippet_list_filter.rb +26 -0
- data/lib/nexmo_markdown_renderer/filters/code_snippets_filter.rb +170 -0
- data/lib/nexmo_markdown_renderer/filters/collapsible_filter.rb +27 -0
- data/lib/nexmo_markdown_renderer/filters/columns_filter.rb +47 -0
- data/lib/nexmo_markdown_renderer/filters/concept_list_filter.rb +30 -0
- data/lib/nexmo_markdown_renderer/filters/dynamic_content_filter.rb +28 -0
- data/lib/nexmo_markdown_renderer/filters/external_link_filter.rb +29 -0
- data/lib/nexmo_markdown_renderer/filters/frontmatter_filter.rb +11 -0
- data/lib/nexmo_markdown_renderer/filters/heading_filter.rb +57 -0
- data/lib/nexmo_markdown_renderer/filters/i18n/frontmatter_filter.rb +16 -0
- data/lib/nexmo_markdown_renderer/filters/i18n/smartling_converter_filter.rb +22 -0
- data/lib/nexmo_markdown_renderer/filters/icon_filter.rb +19 -0
- data/lib/nexmo_markdown_renderer/filters/indent_filter.rb +17 -0
- data/lib/nexmo_markdown_renderer/filters/inline_escape_filter.rb +14 -0
- data/lib/nexmo_markdown_renderer/filters/js_sequence_diagram_filter.rb +18 -0
- data/lib/nexmo_markdown_renderer/filters/label_filter.rb +29 -0
- data/lib/nexmo_markdown_renderer/filters/language_filter.rb +12 -0
- data/lib/nexmo_markdown_renderer/filters/markdown_filter.rb +81 -0
- data/lib/nexmo_markdown_renderer/filters/mermaid_filter.rb +29 -0
- data/lib/nexmo_markdown_renderer/filters/modal_filter.rb +37 -0
- data/lib/nexmo_markdown_renderer/filters/partial_filter.rb +29 -0
- data/lib/nexmo_markdown_renderer/filters/php_inliner_filter.rb +11 -0
- data/lib/nexmo_markdown_renderer/filters/screenshot_filter.rb +22 -0
- data/lib/nexmo_markdown_renderer/filters/tab_filter.rb +298 -0
- data/lib/nexmo_markdown_renderer/filters/techio_filter.rb +20 -0
- data/lib/nexmo_markdown_renderer/filters/tooltip_filter.rb +18 -0
- data/lib/nexmo_markdown_renderer/filters/unfreeze_filter.rb +16 -0
- data/lib/nexmo_markdown_renderer/filters/use_case_list_filter.rb +20 -0
- data/lib/nexmo_markdown_renderer/initializers/doc_finder.rb +5 -0
- data/lib/nexmo_markdown_renderer/initializers/i18n.rb +4 -0
- data/lib/nexmo_markdown_renderer/initializers/redcarpet.rb +7 -0
- data/lib/nexmo_markdown_renderer/markdown_renderer.rb +47 -0
- data/lib/nexmo_markdown_renderer/models/code_language.rb +79 -0
- data/lib/nexmo_markdown_renderer/models/code_snippet.rb +72 -0
- data/lib/nexmo_markdown_renderer/models/concept.rb +83 -0
- data/lib/nexmo_markdown_renderer/models/tutorial.rb +148 -0
- data/lib/nexmo_markdown_renderer/models/use_case.rb +81 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/android.rb +25 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/base.rb +12 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/curl.rb +29 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/dotnet.rb +23 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/java.rb +32 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/javascript.rb +23 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/kotlin.rb +25 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/objective_c.rb +25 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/php.rb +23 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/python.rb +23 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/ruby.rb +23 -0
- data/lib/nexmo_markdown_renderer/services/code_snippet_renderer/swift.rb +25 -0
- data/lib/nexmo_markdown_renderer/services/doc_finder.rb +119 -0
- data/lib/nexmo_markdown_renderer/views/code_snippets/_application_messages_dispatch.html.erb +9 -0
- data/lib/nexmo_markdown_renderer/views/code_snippets/_application_rtc.html.erb +28 -0
- data/lib/nexmo_markdown_renderer/views/code_snippets/_application_voice.html.erb +24 -0
- data/lib/nexmo_markdown_renderer/views/code_snippets/_code_only.html.erb +6 -0
- data/lib/nexmo_markdown_renderer/views/code_snippets/_configure_client.html.erb +20 -0
- data/lib/nexmo_markdown_renderer/views/code_snippets/_dependencies.html.erb +11 -0
- data/lib/nexmo_markdown_renderer/views/code_snippets/_write_code.html.erb +13 -0
- data/lib/nexmo_markdown_renderer/views/code_snippets/list/plain.html.erb +10 -0
- data/lib/nexmo_markdown_renderer/views/concepts/list/plain.html.erb +5 -0
- data/lib/nexmo_markdown_renderer/views/use_case/_index.html.erb +41 -0
- data/lib/nexmo_markdown_renderer/views/use_case/index.html.erb +48 -0
- data/lib/nexmo_markdown_renderer/views/use_case/list/plain.html.erb +5 -0
- data/lib/nexmo_markdown_renderer/views/use_case/show.html.erb +8 -0
- data/lib/version.rb +7 -0
- metadata +320 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Nexmo
|
|
2
|
+
module Markdown
|
|
3
|
+
class TooltipFilter < Banzai::Filter
|
|
4
|
+
def call(input)
|
|
5
|
+
input.gsub(/\^\[([a-zA-Z0-9\s:\-]+)\]\((.+?)\)/) do
|
|
6
|
+
tooltip = <<~HEREDOC
|
|
7
|
+
<span class="Vlt-tooltip Vlt-tooltip--top" title="#{$2}" tabindex="0">
|
|
8
|
+
#{$1}
|
|
9
|
+
<svg class="Vlt-icon Vlt-icon--smaller Vlt-icon--text-bottom Vlt-blue" aria-hidden="true"><use xlink:href="/symbol/volta-icons.svg#Vlt-icon-help-negative"/></svg>
|
|
10
|
+
</span>
|
|
11
|
+
HEREDOC
|
|
12
|
+
|
|
13
|
+
"FREEZESTART#{Base64.urlsafe_encode64(tooltip)}FREEZEEND"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Nexmo
|
|
2
|
+
module Markdown
|
|
3
|
+
class UnfreezeFilter < Banzai::Filter
|
|
4
|
+
def call(input)
|
|
5
|
+
input.gsub!('FREEZESTARTFREEZEEND', '')
|
|
6
|
+
input.gsub!('<p>FREEZESTART', 'FREEZESTART')
|
|
7
|
+
input.gsub!('FREEZEEND</p>', 'FREEZEEND')
|
|
8
|
+
|
|
9
|
+
input.gsub!(/FREEZESTART(.+?)FREEZEEND/m) do |_s|
|
|
10
|
+
Base64.urlsafe_decode64($1).force_encoding(Encoding::UTF_8)
|
|
11
|
+
end
|
|
12
|
+
input
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Nexmo
|
|
2
|
+
module Markdown
|
|
3
|
+
class UseCaseListFilter < Banzai::Filter
|
|
4
|
+
def call(input)
|
|
5
|
+
input.gsub(/```use_cases(.+?)```/m) do |_s|
|
|
6
|
+
config = YAML.safe_load($1)
|
|
7
|
+
@product = config['product']
|
|
8
|
+
@use_cases = Nexmo::Markdown::UseCase.by_product(@product)
|
|
9
|
+
|
|
10
|
+
# Default to plain layout, but allow people to override it
|
|
11
|
+
config['layout'] = 'list/plain' unless config['layout']
|
|
12
|
+
|
|
13
|
+
erb = File.read("#{GEM_ROOT}/lib/nexmo_markdown_renderer/views/use_case/#{config['layout']}.html.erb")
|
|
14
|
+
html = ERB.new(erb).result(binding)
|
|
15
|
+
"FREEZESTART#{Base64.urlsafe_encode64(html)}FREEZEEND"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Nexmo
|
|
2
|
+
module Markdown
|
|
3
|
+
class Renderer < Banzai::Pipeline
|
|
4
|
+
def initialize(options = {})
|
|
5
|
+
super(
|
|
6
|
+
# As Markdown
|
|
7
|
+
FrontmatterFilter,
|
|
8
|
+
PhpInlinerFilter,
|
|
9
|
+
InlineEscapeFilter,
|
|
10
|
+
BlockEscapeFilter,
|
|
11
|
+
ScreenshotFilter,
|
|
12
|
+
AnchorFilter,
|
|
13
|
+
AudioFilter,
|
|
14
|
+
DynamicContentFilter,
|
|
15
|
+
TooltipFilter,
|
|
16
|
+
CollapsibleFilter,
|
|
17
|
+
TabFilter.new(options),
|
|
18
|
+
CodeSnippetsFilter.new(options),
|
|
19
|
+
CodeSnippetFilter.new(options),
|
|
20
|
+
CodeFilter,
|
|
21
|
+
IndentFilter,
|
|
22
|
+
ModalFilter,
|
|
23
|
+
JsSequenceDiagramFilter,
|
|
24
|
+
MermaidFilter,
|
|
25
|
+
PartialFilter.new(options),
|
|
26
|
+
TechioFilter,
|
|
27
|
+
UseCaseListFilter,
|
|
28
|
+
CodeSnippetListFilter,
|
|
29
|
+
ConceptListFilter.new(options),
|
|
30
|
+
LanguageFilter,
|
|
31
|
+
ColumnsFilter,
|
|
32
|
+
MarkdownFilter.new(options),
|
|
33
|
+
|
|
34
|
+
# As HTML
|
|
35
|
+
HeadingFilter,
|
|
36
|
+
LabelFilter.new(options),
|
|
37
|
+
BreakFilter,
|
|
38
|
+
UnfreezeFilter,
|
|
39
|
+
IconFilter,
|
|
40
|
+
ExternalLinkFilter
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Nexmo
|
|
2
|
+
module Markdown
|
|
3
|
+
class CodeLanguage
|
|
4
|
+
include ActiveModel::Model
|
|
5
|
+
attr_accessor :key, :label, :type, :dependencies, :unindent, :icon, :run_command
|
|
6
|
+
attr_writer :weight, :linkable, :languages, :lexer
|
|
7
|
+
|
|
8
|
+
def weight
|
|
9
|
+
@weight || 999
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def linkable?
|
|
13
|
+
return true if @linkable.nil?
|
|
14
|
+
@linkable
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def lexer
|
|
18
|
+
return Rouge::Lexers::PHP.new({ start_inline: true }) if @lexer == 'php'
|
|
19
|
+
Rouge::Lexer.find(@lexer) || Rouge::Lexer.find('text')
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def languages
|
|
23
|
+
@languages ||= []
|
|
24
|
+
@languages.map do |language|
|
|
25
|
+
self.class.find(language)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.languages
|
|
30
|
+
where_type('languages')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.frameworks
|
|
34
|
+
where_type('platforms')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.terminal_programs
|
|
38
|
+
where_type('terminal_programs')
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.data
|
|
42
|
+
where_type('data')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.all
|
|
46
|
+
languages + frameworks + terminal_programs + data
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.exists?(key)
|
|
50
|
+
all.detect { |lang| lang.key == key }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.find(key)
|
|
54
|
+
raise 'Key is missing' unless key
|
|
55
|
+
code_language = all.detect { |lang| lang.key == key }
|
|
56
|
+
raise "Language #{key} does not exist." unless code_language
|
|
57
|
+
code_language
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.linkable
|
|
61
|
+
all.select(&:linkable?)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.route_constraint
|
|
65
|
+
{ code_language: Regexp.new(linkable.map(&:key).compact.join('|')) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private_class_method def self.where_type(type)
|
|
69
|
+
config[type].map do |key, attributes|
|
|
70
|
+
new(attributes.merge({ key: key, type: type }))
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private_class_method def self.config
|
|
75
|
+
@config ||= YAML.load_file('./config/code_languages.yml')
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module Nexmo
|
|
2
|
+
module Markdown
|
|
3
|
+
class CodeSnippet
|
|
4
|
+
include ActiveModel::Model
|
|
5
|
+
attr_accessor :title, :product, :category, :navigation_weight, :document_path, :url
|
|
6
|
+
|
|
7
|
+
def self.by_product(product)
|
|
8
|
+
all.select do |block|
|
|
9
|
+
block.product == product
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.all
|
|
14
|
+
blocks = files.map do |document_path|
|
|
15
|
+
document = File.read(document_path)
|
|
16
|
+
product = extract_product(document_path)
|
|
17
|
+
|
|
18
|
+
frontmatter = YAML.safe_load(document)
|
|
19
|
+
|
|
20
|
+
Nexmo::Markdown::CodeSnippet.new({
|
|
21
|
+
title: frontmatter['title'],
|
|
22
|
+
navigation_weight: frontmatter['navigation_weight'] || 999,
|
|
23
|
+
product: product,
|
|
24
|
+
document_path: document_path,
|
|
25
|
+
category: extract_category(document_path),
|
|
26
|
+
url: generate_url(document_path),
|
|
27
|
+
})
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
blocks.sort_by(&:navigation_weight)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.generate_url(path)
|
|
34
|
+
'/' + path.gsub(%r{#{origin}/\w{2}/}, '').gsub('.md', '')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.extract_product(path)
|
|
38
|
+
# Remove the prefix
|
|
39
|
+
path = path.gsub!(%r{#{origin}/\w{2}/}, '')
|
|
40
|
+
|
|
41
|
+
# Each file is in the form code-snippets/<title>.md, so let's remove everything after code-snippets
|
|
42
|
+
path = path.gsub(%r{/code-snippets/.*}, '')
|
|
43
|
+
|
|
44
|
+
path
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.extract_category(path)
|
|
48
|
+
# Remove the prefix
|
|
49
|
+
path = path.gsub(%r{#{origin}/\w{2}/}, '')
|
|
50
|
+
|
|
51
|
+
# Each file is in the form code-snippets/<title>.md, so let's capture everything after code-snippets
|
|
52
|
+
path = path.gsub(%r{.*/code-snippets/(.*)$}, '\1')
|
|
53
|
+
|
|
54
|
+
parts = path.split('/')
|
|
55
|
+
parts = parts[0...-1]
|
|
56
|
+
|
|
57
|
+
return nil if parts.empty?
|
|
58
|
+
|
|
59
|
+
parts.join('/').tr('-', ' ').humanize
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.files
|
|
63
|
+
Dir.glob("#{origin}/**/code-snippets/**/*.md")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.origin
|
|
67
|
+
"#{ENV['DOCS_BASE_PATH']}/_documentation"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module Nexmo
|
|
2
|
+
module Markdown
|
|
3
|
+
class Concept
|
|
4
|
+
include ActiveModel::Model
|
|
5
|
+
|
|
6
|
+
ORIGIN = "#{ENV['DOCS_BASE_PATH']}/_documentation".freeze
|
|
7
|
+
|
|
8
|
+
FILES = [
|
|
9
|
+
Dir.glob("#{ORIGIN}/#{::I18n.default_locale}/**/guides/**/*.md"),
|
|
10
|
+
Dir.glob("#{ORIGIN}/#{::I18n.default_locale}/**/concepts/**/*.md"),
|
|
11
|
+
].flatten
|
|
12
|
+
|
|
13
|
+
attr_accessor :title, :product, :description, :navigation_weight, :document_path, :url, :ignore_in_list
|
|
14
|
+
|
|
15
|
+
def self.by_name(names, language)
|
|
16
|
+
matches = all(language).select do |block|
|
|
17
|
+
concept = "#{block.product}/#{block.filename}"
|
|
18
|
+
match = names.include?(concept)
|
|
19
|
+
names.delete(concept) if match
|
|
20
|
+
match
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
raise "Could not find concepts: #{names.join(', ')}" unless names.empty?
|
|
24
|
+
matches
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.by_product(product, language)
|
|
28
|
+
all(language).select do |block|
|
|
29
|
+
block.product == product
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def filename
|
|
34
|
+
Pathname(document_path).basename.to_s.gsub('.md', '')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.all(language)
|
|
38
|
+
blocks = files(language).map do |document_path|
|
|
39
|
+
document = File.read(document_path)
|
|
40
|
+
product = extract_product(document_path)
|
|
41
|
+
|
|
42
|
+
frontmatter = YAML.safe_load(document)
|
|
43
|
+
|
|
44
|
+
Nexmo::Markdown::Concept.new({
|
|
45
|
+
title: frontmatter['title'],
|
|
46
|
+
description: frontmatter['description'],
|
|
47
|
+
navigation_weight: frontmatter['navigation_weight'] || 999,
|
|
48
|
+
ignore_in_list: frontmatter['ignore_in_list'],
|
|
49
|
+
product: product,
|
|
50
|
+
document_path: document_path,
|
|
51
|
+
url: generate_url(document_path, language),
|
|
52
|
+
})
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
blocks.sort_by(&:navigation_weight)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def self.generate_url(path, language)
|
|
59
|
+
'/' + path.gsub("#{ORIGIN}/#{language}/", '').gsub('.md', '')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.extract_product(path)
|
|
63
|
+
# Remove the prefix
|
|
64
|
+
path = path.gsub!(%r{#{ORIGIN}\/[a-z]{2}\/}, '')
|
|
65
|
+
|
|
66
|
+
# Each file is in the form guides/<title>.md, so let's remove the last two segments
|
|
67
|
+
parts = path.split('/')
|
|
68
|
+
parts = parts[0...-2]
|
|
69
|
+
|
|
70
|
+
# What's left once we remove the start and end of the path is our product name. This could be any number
|
|
71
|
+
# of parts, but it's generally 1-2
|
|
72
|
+
parts.join('/')
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def self.files(language)
|
|
76
|
+
FILES.each_with_object([]) do |file, array|
|
|
77
|
+
document = file.gsub("#{ORIGIN}/#{::I18n.default_locale}/", '')
|
|
78
|
+
array << Nexmo::Markdown::DocFinder.find(root: ORIGIN, document: document, language: language)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
module Nexmo
|
|
2
|
+
module Markdown
|
|
3
|
+
class Tutorial
|
|
4
|
+
include ActiveModel::Model
|
|
5
|
+
attr_accessor :raw, :name, :current_step, :current_product, :title, :description, :products, :subtasks, :prerequisites
|
|
6
|
+
|
|
7
|
+
def content_for(step_name)
|
|
8
|
+
if ['introduction', 'conclusion'].include? step_name
|
|
9
|
+
raise "Invalid step: #{step_name}" unless raw[step_name]
|
|
10
|
+
|
|
11
|
+
return raw[step_name]['content']
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
path = Nexmo::Markdown::DocFinder.find(
|
|
15
|
+
root: self.class.task_content_path,
|
|
16
|
+
document: step_name,
|
|
17
|
+
language: ::I18n.locale
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
File.read(path)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def first_step
|
|
24
|
+
subtasks.first['path']
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def prerequisite?
|
|
28
|
+
prerequisites.pluck('path').include?(@current_step)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def next_step
|
|
32
|
+
current_task_index = subtasks.pluck('path').index(@current_step)
|
|
33
|
+
return nil unless current_task_index
|
|
34
|
+
|
|
35
|
+
subtasks[current_task_index + 1]
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def previous_step
|
|
39
|
+
current_task_index = subtasks.pluck('path').index(@current_step)
|
|
40
|
+
return nil unless current_task_index
|
|
41
|
+
return nil if current_task_index <= 0
|
|
42
|
+
|
|
43
|
+
subtasks[current_task_index - 1]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def self.load(name, current_step, current_product = nil)
|
|
47
|
+
document_path = Nexmo::Markdown::DocFinder.find(
|
|
48
|
+
root: 'config/tutorials',
|
|
49
|
+
document: name,
|
|
50
|
+
language: ::I18n.default_locale,
|
|
51
|
+
format: 'yml'
|
|
52
|
+
)
|
|
53
|
+
config = YAML.safe_load(File.read(document_path))
|
|
54
|
+
current_product ||= config['products'].first
|
|
55
|
+
|
|
56
|
+
Nexmo::Markdown::Tutorial.new({
|
|
57
|
+
raw: config,
|
|
58
|
+
name: name,
|
|
59
|
+
current_step: current_step,
|
|
60
|
+
current_product: current_product,
|
|
61
|
+
title: config['title'],
|
|
62
|
+
description: config['description'],
|
|
63
|
+
products: config['products'],
|
|
64
|
+
prerequisites: load_prerequisites(config['prerequisites'], current_step),
|
|
65
|
+
subtasks: load_subtasks(config['introduction'], config['prerequisites'], config['tasks'], config['conclusion'], current_step),
|
|
66
|
+
})
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.load_prerequisites(prerequisites, current_step)
|
|
70
|
+
return [] unless prerequisites
|
|
71
|
+
|
|
72
|
+
prerequisites.map do |t|
|
|
73
|
+
t_path = Nexmo::Markdown::DocFinder.find(
|
|
74
|
+
root: task_content_path,
|
|
75
|
+
document: t,
|
|
76
|
+
language: ::I18n.locale
|
|
77
|
+
)
|
|
78
|
+
raise "Prerequisite not found: #{t}" unless File.exist? t_path
|
|
79
|
+
|
|
80
|
+
content = File.read(t_path)
|
|
81
|
+
prereq = YAML.safe_load(content)
|
|
82
|
+
{
|
|
83
|
+
'path' => t,
|
|
84
|
+
'title' => prereq['title'],
|
|
85
|
+
'description' => prereq['description'],
|
|
86
|
+
'is_active' => t == current_step,
|
|
87
|
+
'content' => content,
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def self.load_subtasks(introduction, prerequisites, tasks, conclusion, current_step)
|
|
93
|
+
tasks ||= []
|
|
94
|
+
|
|
95
|
+
tasks = tasks.map do |t|
|
|
96
|
+
t_path = Nexmo::Markdown::DocFinder.find(
|
|
97
|
+
root: task_content_path,
|
|
98
|
+
document: t,
|
|
99
|
+
language: ::I18n.locale
|
|
100
|
+
)
|
|
101
|
+
raise "Subtask not found: #{t}" unless File.exist? t_path
|
|
102
|
+
|
|
103
|
+
subtask_config = YAML.safe_load(File.read(t_path))
|
|
104
|
+
{
|
|
105
|
+
'path' => t,
|
|
106
|
+
'title' => subtask_config['title'],
|
|
107
|
+
'description' => subtask_config['description'],
|
|
108
|
+
'is_active' => t == current_step,
|
|
109
|
+
}
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
if prerequisites
|
|
113
|
+
tasks.unshift({
|
|
114
|
+
'path' => 'prerequisites',
|
|
115
|
+
'title' => 'Prerequisites',
|
|
116
|
+
'description' => 'Everything you need to complete this task',
|
|
117
|
+
'is_active' => current_step == 'prerequisites',
|
|
118
|
+
})
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
if introduction
|
|
122
|
+
tasks.unshift({
|
|
123
|
+
'path' => 'introduction',
|
|
124
|
+
'title' => introduction['title'],
|
|
125
|
+
'description' => introduction['description'],
|
|
126
|
+
'is_active' => current_step == 'introduction',
|
|
127
|
+
})
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
if conclusion
|
|
131
|
+
tasks.push({
|
|
132
|
+
'path' => 'conclusion',
|
|
133
|
+
'title' => conclusion['title'],
|
|
134
|
+
'description' => conclusion['description'],
|
|
135
|
+
'is_active' => current_step == 'conclusion',
|
|
136
|
+
})
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
tasks
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def self.task_content_path
|
|
143
|
+
"#{ENV['DOCS_BASE_PATH']}/_tutorials"
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
end
|
|
148
|
+
end
|