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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/config/locales/en.yml +1 -0
  3. data/lib/nexmo_markdown_renderer.rb +24 -1
  4. data/lib/nexmo_markdown_renderer/filters/block_escape_filter.rb +3 -3
  5. data/lib/nexmo_markdown_renderer/filters/code_filter.rb +17 -17
  6. data/lib/nexmo_markdown_renderer/filters/code_snippet/binding.rb +41 -0
  7. data/lib/nexmo_markdown_renderer/filters/code_snippet/create_application.rb +61 -0
  8. data/lib/nexmo_markdown_renderer/filters/code_snippet/import_dependencies.rb +38 -0
  9. data/lib/nexmo_markdown_renderer/filters/code_snippet/initialize_dependencies.rb +41 -0
  10. data/lib/nexmo_markdown_renderer/filters/code_snippet/install_dependencies.rb +38 -0
  11. data/lib/nexmo_markdown_renderer/filters/code_snippet/instructions.rb +46 -0
  12. data/lib/nexmo_markdown_renderer/filters/code_snippet/renderable.rb +62 -0
  13. data/lib/nexmo_markdown_renderer/filters/code_snippet/run.rb +29 -0
  14. data/lib/nexmo_markdown_renderer/filters/code_snippet_filter.rb +22 -159
  15. data/lib/nexmo_markdown_renderer/filters/concerns/prism_code_snippet.rb +22 -0
  16. data/lib/nexmo_markdown_renderer/filters/markdown_filter.rb +13 -10
  17. data/lib/nexmo_markdown_renderer/filters/partial_filter.rb +1 -1
  18. data/lib/nexmo_markdown_renderer/filters/screenshot_filter.rb +1 -1
  19. data/lib/nexmo_markdown_renderer/filters/tab_filter.rb +73 -70
  20. data/lib/nexmo_markdown_renderer/filters/utils.rb +50 -0
  21. data/lib/nexmo_markdown_renderer/models/code_language.rb +1 -1
  22. data/lib/nexmo_markdown_renderer/models/tutorial.rb +116 -72
  23. data/lib/nexmo_markdown_renderer/models/tutorial/file_loader.rb +32 -0
  24. data/lib/nexmo_markdown_renderer/models/tutorial/metadata.rb +57 -0
  25. data/lib/nexmo_markdown_renderer/models/tutorial/prerequisite.rb +35 -0
  26. data/lib/nexmo_markdown_renderer/models/tutorial/task.rb +49 -0
  27. data/lib/nexmo_markdown_renderer/models/use_case.rb +3 -16
  28. data/lib/nexmo_markdown_renderer/services/doc_finder.rb +7 -2
  29. data/lib/nexmo_markdown_renderer/views/code_snippets/_application_rtc.html.erb +1 -1
  30. data/lib/nexmo_markdown_renderer/views/code_snippets/_application_voice.html.erb +1 -1
  31. data/lib/nexmo_markdown_renderer/views/code_snippets/_configure_client.html.erb +2 -2
  32. data/lib/nexmo_markdown_renderer/views/code_snippets/_import_dependencies.html.erb +18 -0
  33. data/lib/version.rb +1 -1
  34. 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
@@ -72,7 +72,7 @@ module Nexmo
72
72
  end
73
73
 
74
74
  private_class_method def self.config
75
- @config ||= YAML.load_file('./config/code_languages.yml')
75
+ @config ||= YAML.load_file("#{Nexmo::Markdown::Config.docs_base_path}/config/code_languages.yml")
76
76
  end
77
77
  end
78
78
  end
@@ -2,68 +2,109 @@ module Nexmo
2
2
  module Markdown
3
3
  class Tutorial
4
4
  include ActiveModel::Model
5
- attr_accessor :raw, :name, :current_step, :current_product, :title, :description, :products, :subtasks, :prerequisites
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 raw[step_name]
50
+ raise "Invalid step: #{step_name}" unless yaml[step_name]
10
51
 
11
- return raw[step_name]['content']
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['path']
66
+ subtasks.first&.name
25
67
  end
26
68
 
27
69
  def prerequisite?
28
- prerequisites.pluck('path').include?(@current_step)
70
+ prerequisites.map(&:name).include?(@current_step)
29
71
  end
30
72
 
31
73
  def next_step
32
- current_task_index = subtasks.pluck('path').index(@current_step)
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.pluck('path').index(@current_step)
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 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
- ).path
53
- config = YAML.safe_load(File.read(document_path))
54
- current_product ||= config['products'].first
55
-
56
- 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
- })
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 self.load_subtasks(introduction, prerequisites, tasks, conclusion, current_step)
93
- tasks ||= []
133
+ def prerequisite_task
134
+ return if prerequisites.empty?
94
135
 
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
- ).path
101
- raise "Subtask not found: #{t}" unless File.exist? t_path
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
- 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
144
+ def introduction_task
145
+ return unless yaml['introduction']
111
146
 
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
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
- 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
155
+ def conclusion_task
156
+ return unless yaml['conclusion']
129
157
 
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
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
- tasks
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