nexmo_markdown_renderer 0.2.1 → 0.4.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/config/locales/en.yml +1 -0
- data/lib/nexmo_markdown_renderer.rb +24 -1
- data/lib/nexmo_markdown_renderer/filters/block_escape_filter.rb +3 -3
- data/lib/nexmo_markdown_renderer/filters/code_filter.rb +17 -17
- data/lib/nexmo_markdown_renderer/filters/code_snippet/binding.rb +41 -0
- data/lib/nexmo_markdown_renderer/filters/code_snippet/create_application.rb +61 -0
- data/lib/nexmo_markdown_renderer/filters/code_snippet/import_dependencies.rb +38 -0
- data/lib/nexmo_markdown_renderer/filters/code_snippet/initialize_dependencies.rb +41 -0
- data/lib/nexmo_markdown_renderer/filters/code_snippet/install_dependencies.rb +38 -0
- data/lib/nexmo_markdown_renderer/filters/code_snippet/instructions.rb +46 -0
- data/lib/nexmo_markdown_renderer/filters/code_snippet/renderable.rb +62 -0
- data/lib/nexmo_markdown_renderer/filters/code_snippet/run.rb +29 -0
- data/lib/nexmo_markdown_renderer/filters/code_snippet_filter.rb +22 -159
- data/lib/nexmo_markdown_renderer/filters/concerns/prism_code_snippet.rb +22 -0
- data/lib/nexmo_markdown_renderer/filters/markdown_filter.rb +13 -10
- data/lib/nexmo_markdown_renderer/filters/partial_filter.rb +1 -1
- data/lib/nexmo_markdown_renderer/filters/screenshot_filter.rb +1 -1
- data/lib/nexmo_markdown_renderer/filters/tab_filter.rb +73 -70
- data/lib/nexmo_markdown_renderer/filters/utils.rb +50 -0
- data/lib/nexmo_markdown_renderer/models/code_language.rb +1 -1
- data/lib/nexmo_markdown_renderer/models/tutorial.rb +116 -72
- data/lib/nexmo_markdown_renderer/models/tutorial/file_loader.rb +32 -0
- data/lib/nexmo_markdown_renderer/models/tutorial/metadata.rb +57 -0
- data/lib/nexmo_markdown_renderer/models/tutorial/prerequisite.rb +35 -0
- data/lib/nexmo_markdown_renderer/models/tutorial/task.rb +49 -0
- data/lib/nexmo_markdown_renderer/models/use_case.rb +3 -16
- data/lib/nexmo_markdown_renderer/services/doc_finder.rb +7 -2
- data/lib/nexmo_markdown_renderer/views/code_snippets/_application_rtc.html.erb +1 -1
- data/lib/nexmo_markdown_renderer/views/code_snippets/_application_voice.html.erb +1 -1
- data/lib/nexmo_markdown_renderer/views/code_snippets/_configure_client.html.erb +2 -2
- data/lib/nexmo_markdown_renderer/views/code_snippets/_import_dependencies.html.erb +18 -0
- data/lib/version.rb +1 -1
- metadata +17 -2
@@ -0,0 +1,50 @@
|
|
1
|
+
module Nexmo
|
2
|
+
module Markdown
|
3
|
+
class Utils
|
4
|
+
def self.generate_source_url(code)
|
5
|
+
# Source example: .repos/nexmo/nexmo-java-code-snippets/ExampleClass.java
|
6
|
+
# Direct link on GitHub is in form https://github.com/nexmo/nexmo-java-code-snippets/blob/master/ExampleClass.java
|
7
|
+
start_section = 'https://github.com'
|
8
|
+
|
9
|
+
# Insert "blob/master" and strip ".repos"
|
10
|
+
repo_path = '\\0blob/master/'
|
11
|
+
file_section = code['source'].sub('.repos', '').sub(%r{(-quickstart|-code-snippets|-code-snippets)/}, repo_path)
|
12
|
+
|
13
|
+
# Line highlighting
|
14
|
+
line_section = ''
|
15
|
+
if code['from_line']
|
16
|
+
line_section += "#L#{code['from_line']}"
|
17
|
+
if code['to_line']
|
18
|
+
# If we've provided a to_line, use that
|
19
|
+
line_section += "-L#{code['to_line']}" if code['to_line']
|
20
|
+
else
|
21
|
+
# By default we read to the end of the file
|
22
|
+
line_section += "-L#{File.read("#{Nexmo::Markdown::Config.docs_base_path}/#{code['source']}").lines.count}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
start_section + file_section + line_section
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.generate_code_block(language, input, unindent)
|
30
|
+
return '' unless input
|
31
|
+
filename = "#{Nexmo::Markdown::Config.docs_base_path}/#{input['source']}"
|
32
|
+
raise "CodeSnippetFilter - Could not load #{filename} for language #{language}" unless File.exist?(filename)
|
33
|
+
|
34
|
+
code = File.read(filename)
|
35
|
+
lexer = Nexmo::Markdown::CodeLanguage.find(language).lexer
|
36
|
+
|
37
|
+
total_lines = code.lines.count
|
38
|
+
|
39
|
+
# Minus one since lines are not zero-indexed
|
40
|
+
from_line = (input['from_line'] || 1) - 1
|
41
|
+
to_line = (input['to_line'] || total_lines) - 1
|
42
|
+
|
43
|
+
code = code.lines[from_line..to_line].join
|
44
|
+
code.unindent! if unindent
|
45
|
+
formatter = Rouge::Formatters::HTML.new
|
46
|
+
formatter.format(lexer.lex(code))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -2,68 +2,109 @@ module Nexmo
|
|
2
2
|
module Markdown
|
3
3
|
class Tutorial
|
4
4
|
include ActiveModel::Model
|
5
|
-
|
5
|
+
|
6
|
+
attr_reader :name, :current_step
|
7
|
+
delegate :path, :yaml, to: :@file_loader
|
8
|
+
delegate :available_code_languages, to: :metadata
|
9
|
+
|
10
|
+
def initialize(name:, current_step:, current_product: nil, code_language: nil)
|
11
|
+
@name = name
|
12
|
+
@current_step = current_step
|
13
|
+
@product = current_product
|
14
|
+
@language = code_language
|
15
|
+
@file_loader = load_file!
|
16
|
+
end
|
17
|
+
|
18
|
+
def metadata
|
19
|
+
@metadata ||= Metadata.new(name: name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def current_product
|
23
|
+
@current_product ||= @product || metadata.default_product
|
24
|
+
end
|
25
|
+
|
26
|
+
def code_language
|
27
|
+
@code_language ||= @language || metadata.code_language
|
28
|
+
end
|
29
|
+
|
30
|
+
def title
|
31
|
+
@title ||= yaml['title'] || metadata.title
|
32
|
+
end
|
33
|
+
|
34
|
+
def description
|
35
|
+
@description ||= yaml['description'] || metadata.description
|
36
|
+
end
|
37
|
+
|
38
|
+
def products
|
39
|
+
@products ||= yaml['products'] || metadata.products
|
40
|
+
end
|
41
|
+
|
42
|
+
def prerequisites
|
43
|
+
@prerequisites ||= (yaml['prerequisites'] || []).map do |prereq|
|
44
|
+
Prerequisite.new(name: prereq, code_language: code_language, current_step: current_step)
|
45
|
+
end
|
46
|
+
end
|
6
47
|
|
7
48
|
def content_for(step_name)
|
8
49
|
if ['introduction', 'conclusion'].include? step_name
|
9
|
-
raise "Invalid step: #{step_name}" unless
|
50
|
+
raise "Invalid step: #{step_name}" unless yaml[step_name]
|
10
51
|
|
11
|
-
return
|
52
|
+
return yaml[step_name]['content']
|
12
53
|
end
|
13
54
|
|
14
55
|
path = Nexmo::Markdown::DocFinder.find(
|
15
56
|
root: self.class.task_content_path,
|
16
57
|
document: step_name,
|
17
|
-
language: ::I18n.locale
|
58
|
+
language: ::I18n.locale,
|
59
|
+
code_language: code_language
|
18
60
|
).path
|
19
61
|
|
20
62
|
File.read(path)
|
21
63
|
end
|
22
64
|
|
23
65
|
def first_step
|
24
|
-
subtasks.first
|
66
|
+
subtasks.first&.name
|
25
67
|
end
|
26
68
|
|
27
69
|
def prerequisite?
|
28
|
-
prerequisites.
|
70
|
+
prerequisites.map(&:name).include?(@current_step)
|
29
71
|
end
|
30
72
|
|
31
73
|
def next_step
|
32
|
-
current_task_index = subtasks.
|
74
|
+
current_task_index = subtasks.map(&:name).index(@current_step)
|
33
75
|
return nil unless current_task_index
|
34
76
|
|
35
77
|
subtasks[current_task_index + 1]
|
36
78
|
end
|
37
79
|
|
38
80
|
def previous_step
|
39
|
-
current_task_index = subtasks.
|
81
|
+
current_task_index = subtasks.map(&:name).index(@current_step)
|
40
82
|
return nil unless current_task_index
|
41
83
|
return nil if current_task_index <= 0
|
42
84
|
|
43
85
|
subtasks[current_task_index - 1]
|
44
86
|
end
|
45
87
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
})
|
88
|
+
def subtasks
|
89
|
+
@subtasks ||= begin
|
90
|
+
tasks = []
|
91
|
+
|
92
|
+
(yaml['tasks'] || []).map do |t|
|
93
|
+
tasks.push(
|
94
|
+
Task.make_from(
|
95
|
+
name: t,
|
96
|
+
code_language: code_language,
|
97
|
+
current_step: current_step
|
98
|
+
)
|
99
|
+
)
|
100
|
+
end
|
101
|
+
|
102
|
+
tasks.unshift(prerequisite_task)
|
103
|
+
tasks.unshift(introduction_task)
|
104
|
+
tasks.push(conclusion_task)
|
105
|
+
|
106
|
+
tasks.compact
|
107
|
+
end
|
67
108
|
end
|
68
109
|
|
69
110
|
def self.load_prerequisites(prerequisites, current_step)
|
@@ -89,60 +130,63 @@ module Nexmo
|
|
89
130
|
end
|
90
131
|
end
|
91
132
|
|
92
|
-
def
|
93
|
-
|
133
|
+
def prerequisite_task
|
134
|
+
return if prerequisites.empty?
|
94
135
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
136
|
+
Task.new(
|
137
|
+
name: 'prerequisites',
|
138
|
+
title: 'Prerequisites',
|
139
|
+
description: 'Everything you need to complete this task',
|
140
|
+
current_step: current_step
|
141
|
+
)
|
142
|
+
end
|
102
143
|
|
103
|
-
|
104
|
-
|
105
|
-
'path' => t,
|
106
|
-
'title' => subtask_config['title'],
|
107
|
-
'description' => subtask_config['description'],
|
108
|
-
'is_active' => t == current_step,
|
109
|
-
}
|
110
|
-
end
|
144
|
+
def introduction_task
|
145
|
+
return unless yaml['introduction']
|
111
146
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
147
|
+
Task.new(
|
148
|
+
name: 'introduction',
|
149
|
+
title: yaml['introduction']['title'],
|
150
|
+
description: yaml['introduction']['description'],
|
151
|
+
current_step: current_step
|
152
|
+
)
|
153
|
+
end
|
120
154
|
|
121
|
-
|
122
|
-
|
123
|
-
'path' => 'introduction',
|
124
|
-
'title' => introduction['title'],
|
125
|
-
'description' => introduction['description'],
|
126
|
-
'is_active' => current_step == 'introduction',
|
127
|
-
})
|
128
|
-
end
|
155
|
+
def conclusion_task
|
156
|
+
return unless yaml['conclusion']
|
129
157
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
158
|
+
Task.new(
|
159
|
+
name: 'conclusion',
|
160
|
+
title: yaml['conclusion']['title'],
|
161
|
+
description: yaml['conclusion']['description'],
|
162
|
+
current_step: current_step
|
163
|
+
)
|
164
|
+
end
|
165
|
+
|
166
|
+
def self.load(name, current_step, current_product = nil, code_language = nil)
|
167
|
+
new(
|
168
|
+
name: name,
|
169
|
+
current_step: current_step,
|
170
|
+
current_product: current_product,
|
171
|
+
code_language: code_language
|
172
|
+
)
|
173
|
+
end
|
138
174
|
|
139
|
-
|
175
|
+
def load_file!
|
176
|
+
Tutorial::FileLoader.new(
|
177
|
+
root: self.class.tutorials_path,
|
178
|
+
code_language: code_language,
|
179
|
+
doc_name: name
|
180
|
+
)
|
140
181
|
end
|
141
182
|
|
142
183
|
def self.task_content_path
|
143
184
|
"#{Nexmo::Markdown::Config.docs_base_path}/_tutorials"
|
144
185
|
end
|
145
|
-
end
|
146
186
|
|
187
|
+
def self.tutorials_path
|
188
|
+
'config/tutorials'
|
189
|
+
end
|
190
|
+
end
|
147
191
|
end
|
148
192
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Nexmo
|
2
|
+
module Markdown
|
3
|
+
class Tutorial::FileLoader
|
4
|
+
attr_reader :root, :code_language, :doc_name, :format
|
5
|
+
|
6
|
+
def initialize(root:, code_language:, doc_name:, format: 'yml')
|
7
|
+
@root = root
|
8
|
+
@code_language = code_language
|
9
|
+
@doc_name = doc_name
|
10
|
+
@format = format
|
11
|
+
end
|
12
|
+
|
13
|
+
def path
|
14
|
+
@path ||= Nexmo::Markdown::DocFinder.find(
|
15
|
+
root: root,
|
16
|
+
document: doc_name,
|
17
|
+
language: ::I18n.locale,
|
18
|
+
code_language: code_language,
|
19
|
+
format: format
|
20
|
+
).path
|
21
|
+
end
|
22
|
+
|
23
|
+
def content
|
24
|
+
@content ||= File.read(path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def yaml
|
28
|
+
@yaml ||= YAML.safe_load(content)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Nexmo
|
2
|
+
module Markdown
|
3
|
+
class Tutorial::Metadata
|
4
|
+
attr_reader :name, :file_loader
|
5
|
+
delegate :path, :yaml, to: :file_loader
|
6
|
+
|
7
|
+
def initialize(name:)
|
8
|
+
@name = name
|
9
|
+
@file_loader = load_file!
|
10
|
+
end
|
11
|
+
|
12
|
+
def products
|
13
|
+
@products ||= yaml['products'] || []
|
14
|
+
end
|
15
|
+
|
16
|
+
def title
|
17
|
+
@title ||= yaml['title']
|
18
|
+
end
|
19
|
+
|
20
|
+
def description
|
21
|
+
@description ||= yaml['description']
|
22
|
+
end
|
23
|
+
|
24
|
+
def external_link
|
25
|
+
@external_link ||= yaml['external_link']
|
26
|
+
end
|
27
|
+
|
28
|
+
def available_code_languages
|
29
|
+
@available_code_languages ||= begin
|
30
|
+
DocFinder
|
31
|
+
.code_languages_for_tutorial(path: path.sub('.yml', '/'))
|
32
|
+
.map { |file_path| File.basename(Pathname.new(file_path).basename, '.yml') }
|
33
|
+
.sort_by { |l| CodeLanguage.find(l).weight }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def code_language
|
38
|
+
@code_language ||= begin
|
39
|
+
available_code_languages
|
40
|
+
.min_by { |k| CodeLanguage.languages.map(&:key).index(k) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def default_product
|
45
|
+
@default_product ||= products.first
|
46
|
+
end
|
47
|
+
|
48
|
+
def load_file!
|
49
|
+
Tutorial::FileLoader.new(
|
50
|
+
root: Tutorial.tutorials_path,
|
51
|
+
code_language: nil,
|
52
|
+
doc_name: @name
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Nexmo
|
2
|
+
module Markdown
|
3
|
+
class Tutorial::Prerequisite
|
4
|
+
delegate :content, :yaml, to: :@file_loader
|
5
|
+
|
6
|
+
def initialize(current_step:, code_language:, name:)
|
7
|
+
@current_step = current_step
|
8
|
+
@code_language = code_language
|
9
|
+
@name = name
|
10
|
+
@file_loader = load_file!
|
11
|
+
end
|
12
|
+
|
13
|
+
def title
|
14
|
+
@title ||= yaml['title']
|
15
|
+
end
|
16
|
+
|
17
|
+
def description
|
18
|
+
@description ||= yaml['description']
|
19
|
+
end
|
20
|
+
|
21
|
+
def active?
|
22
|
+
@name == @current_step
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_file!
|
26
|
+
Tutorial::FileLoader.new(
|
27
|
+
root: Tutorial.task_content_path,
|
28
|
+
code_language: nil,
|
29
|
+
doc_name: @name,
|
30
|
+
format: 'md'
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Nexmo
|
2
|
+
module Markdown
|
3
|
+
class Tutorial::Task
|
4
|
+
attr_reader :name, :title, :description, :current_step
|
5
|
+
|
6
|
+
def initialize(name:, title:, description:, current_step:)
|
7
|
+
@name = name
|
8
|
+
@title = title
|
9
|
+
@description = description
|
10
|
+
@current_step = current_step
|
11
|
+
end
|
12
|
+
|
13
|
+
def active?
|
14
|
+
@name == @current_step
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.make_from(name:, code_language:, current_step:)
|
18
|
+
file_loader = Tutorial::FileLoader.new(
|
19
|
+
root: Tutorial.task_content_path,
|
20
|
+
doc_name: name,
|
21
|
+
code_language: code_language,
|
22
|
+
format: 'md'
|
23
|
+
)
|
24
|
+
|
25
|
+
new(
|
26
|
+
name: name,
|
27
|
+
title: file_loader.yaml['title'],
|
28
|
+
description: file_loader.yaml['description'],
|
29
|
+
current_step: current_step
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
name == other.name &&
|
35
|
+
title == other.title &&
|
36
|
+
description == other.description &&
|
37
|
+
current_step == other.current_step
|
38
|
+
end
|
39
|
+
|
40
|
+
def eql?(other)
|
41
|
+
self == other
|
42
|
+
end
|
43
|
+
|
44
|
+
def hash
|
45
|
+
name.hash ^ title.hash ^ description.hash ^ current_step.hash
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|