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.
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,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}&nbsp;
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,5 @@
1
+ Nexmo::Markdown::DocFinder.configure do |config|
2
+ config.paths << "#{ENV['DOCS_BASE_PATH']}/_documentation"
3
+ config.paths << "#{ENV['DOCS_BASE_PATH']}/_use_cases"
4
+ config.paths << "#{ENV['DOCS_BASE_PATH']}/_tutorials"
5
+ end
@@ -0,0 +1,4 @@
1
+ require 'i18n'
2
+
3
+ I18n.load_path += Dir[File.join("#{GEM_ROOT}/config/locales".freeze, '*.yml'.freeze)]
4
+ I18n.default_locale = :en
@@ -0,0 +1,7 @@
1
+ require 'redcarpet'
2
+ require 'rouge'
3
+ require 'rouge/plugins/redcarpet'
4
+
5
+ class HTML < Redcarpet::Render::HTML
6
+ include Rouge::Plugins::Redcarpet
7
+ 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