sleeping_king_studios-docs 0.2.0 → 0.3.0

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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -0
  3. data/README.md +30 -14
  4. data/lib/sleeping_king_studios/docs/data/base.rb +24 -4
  5. data/lib/sleeping_king_studios/docs/data/constant_object.rb +24 -0
  6. data/lib/sleeping_king_studios/docs/data/metadata.rb +0 -2
  7. data/lib/sleeping_king_studios/docs/data/method_object.rb +32 -0
  8. data/lib/sleeping_king_studios/docs/data/module_object.rb +1 -1
  9. data/lib/sleeping_king_studios/docs/data/namespace_object.rb +26 -2
  10. data/lib/sleeping_king_studios/docs/data/see_tags/reference_tag.rb +1 -1
  11. data/lib/sleeping_king_studios/docs/data/types/parser.rb +5 -5
  12. data/lib/sleeping_king_studios/docs/data/types/type.rb +3 -6
  13. data/lib/sleeping_king_studios/docs/errors/outdated_documentation.rb +79 -0
  14. data/lib/sleeping_king_studios/docs/errors/registry_error.rb +13 -0
  15. data/lib/sleeping_king_studios/docs/errors.rb +4 -0
  16. data/lib/sleeping_king_studios/docs/jekyll/commands/backfill_versions.rb +177 -0
  17. data/lib/sleeping_king_studios/docs/jekyll/commands/check.rb +347 -0
  18. data/lib/sleeping_king_studios/docs/jekyll/commands/clobber.rb +99 -0
  19. data/lib/sleeping_king_studios/docs/jekyll/commands/document_version.rb +217 -0
  20. data/lib/sleeping_king_studios/docs/jekyll/commands/generate.rb +202 -0
  21. data/lib/sleeping_king_studios/docs/jekyll/commands/install.rb +271 -0
  22. data/lib/sleeping_king_studios/docs/jekyll/commands/install_templates.rb +125 -0
  23. data/lib/sleeping_king_studios/docs/jekyll/commands/install_workflow.rb +55 -0
  24. data/lib/sleeping_king_studios/docs/jekyll/commands/reference.rb +158 -0
  25. data/lib/sleeping_king_studios/docs/jekyll/commands/update.rb +32 -0
  26. data/lib/sleeping_king_studios/docs/jekyll/commands.rb +44 -0
  27. data/lib/sleeping_king_studios/docs/jekyll/generators/data_generator.rb +85 -0
  28. data/lib/sleeping_king_studios/docs/jekyll/generators/reference_generator.rb +40 -0
  29. data/lib/sleeping_king_studios/docs/jekyll/generators/version_generator.rb +66 -0
  30. data/lib/sleeping_king_studios/docs/jekyll/generators.rb +15 -0
  31. data/lib/sleeping_king_studios/docs/jekyll.rb +26 -1
  32. data/lib/sleeping_king_studios/docs/version.rb +1 -1
  33. data/lib/sleeping_king_studios/docs/yard/build.rb +42 -0
  34. data/lib/sleeping_king_studios/docs/{commands → yard}/parse.rb +18 -11
  35. data/lib/sleeping_king_studios/docs/yard/registry.rb +54 -0
  36. data/lib/sleeping_king_studios/docs/{registry_query.rb → yard/registry_query.rb} +9 -7
  37. data/lib/sleeping_king_studios/docs/yard.rb +13 -0
  38. data/lib/sleeping_king_studios/docs.rb +29 -17
  39. metadata +66 -83
  40. data/lib/sleeping_king_studios/docs/commands/generate.rb +0 -268
  41. data/lib/sleeping_king_studios/docs/commands/generators/base.rb +0 -97
  42. data/lib/sleeping_king_studios/docs/commands/generators/data_generator.rb +0 -68
  43. data/lib/sleeping_king_studios/docs/commands/generators/reference_generator.rb +0 -59
  44. data/lib/sleeping_king_studios/docs/commands/generators.rb +0 -15
  45. data/lib/sleeping_king_studios/docs/commands/installation/install_jekyll.rb +0 -273
  46. data/lib/sleeping_king_studios/docs/commands/installation/install_templates.rb +0 -125
  47. data/lib/sleeping_king_studios/docs/commands/installation/install_workflow.rb +0 -121
  48. data/lib/sleeping_king_studios/docs/commands/installation.rb +0 -15
  49. data/lib/sleeping_king_studios/docs/commands/write_file.rb +0 -89
  50. data/lib/sleeping_king_studios/docs/commands.rb +0 -14
  51. data/lib/sleeping_king_studios/docs/registry.rb +0 -22
  52. data/lib/sleeping_king_studios/docs/tasks/base.rb +0 -20
  53. data/lib/sleeping_king_studios/docs/tasks/generate.rb +0 -39
  54. data/lib/sleeping_king_studios/docs/tasks/installation/install_jekyll.rb +0 -67
  55. data/lib/sleeping_king_studios/docs/tasks/installation/install_templates.rb +0 -39
  56. data/lib/sleeping_king_studios/docs/tasks/installation/install_workflow.rb +0 -53
  57. data/lib/sleeping_king_studios/docs/tasks/installation.rb +0 -8
  58. data/lib/sleeping_king_studios/docs/tasks/update.rb +0 -35
  59. data/lib/sleeping_king_studios/docs/tasks.rb +0 -14
  60. data/lib/sleeping_king_studios/docs/templates/config.yml.erb +0 -24
  61. data/lib/sleeping_king_studios/docs/templates/deploy-pages.yml.erb +0 -67
  62. data/lib/sleeping_king_studios/docs/templates/includes/breadcrumbs.md +0 -7
  63. data/lib/sleeping_king_studios/docs/templates/includes/reference/attribute.md +0 -23
  64. data/lib/sleeping_king_studios/docs/templates/includes/reference/attributes/heading.md +0 -15
  65. data/lib/sleeping_king_studios/docs/templates/includes/reference/class.md +0 -27
  66. data/lib/sleeping_king_studios/docs/templates/includes/reference/constant.md +0 -8
  67. data/lib/sleeping_king_studios/docs/templates/includes/reference/constants/heading.md +0 -6
  68. data/lib/sleeping_king_studios/docs/templates/includes/reference/constants/inherited.md +0 -4
  69. data/lib/sleeping_king_studios/docs/templates/includes/reference/constants/overview.md +0 -60
  70. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/breadcrumbs.md +0 -21
  71. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/class_attributes.md +0 -9
  72. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/class_methods.md +0 -9
  73. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/constants.md +0 -9
  74. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/constructor.md +0 -12
  75. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/definitions.md +0 -23
  76. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/details.md +0 -34
  77. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/instance_attributes.md +0 -9
  78. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/instance_methods.md +0 -17
  79. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/overview.md +0 -74
  80. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/subclasses.md +0 -10
  81. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/table-of-contents/class_attributes.md +0 -10
  82. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/table-of-contents/class_methods.md +0 -10
  83. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/table-of-contents/constants.md +0 -10
  84. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/table-of-contents/instance_attributes.md +0 -10
  85. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/table-of-contents/instance_methods.md +0 -10
  86. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/table-of-contents/overview.md +0 -41
  87. data/lib/sleeping_king_studios/docs/templates/includes/reference/definitions/table-of-contents.md +0 -27
  88. data/lib/sleeping_king_studios/docs/templates/includes/reference/method.md +0 -27
  89. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/aliases.md +0 -4
  90. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/heading.md +0 -14
  91. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/inherited.md +0 -4
  92. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/overload.md +0 -18
  93. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/overloads.md +0 -12
  94. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/overview.md +0 -41
  95. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/parameters.md +0 -31
  96. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/post_overview.md +0 -51
  97. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/raises.md +0 -14
  98. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/return_types.md +0 -9
  99. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/returns.md +0 -14
  100. data/lib/sleeping_king_studios/docs/templates/includes/reference/methods/yields.md +0 -42
  101. data/lib/sleeping_king_studios/docs/templates/includes/reference/module.md +0 -23
  102. data/lib/sleeping_king_studios/docs/templates/includes/reference/namespace.md +0 -33
  103. data/lib/sleeping_king_studios/docs/templates/includes/reference/parent_link.md +0 -6
  104. data/lib/sleeping_king_studios/docs/templates/includes/reference/reference_link.md +0 -7
  105. data/lib/sleeping_king_studios/docs/templates/includes/reference/see_link.md +0 -7
  106. data/lib/sleeping_king_studios/docs/templates/includes/reference/type.md +0 -14
  107. data/lib/sleeping_king_studios/docs/templates/includes/reference/type_list.md +0 -4
  108. data/lib/sleeping_king_studios/docs/templates/includes/reference/types/array_type.md +0 -1
  109. data/lib/sleeping_king_studios/docs/templates/includes/reference/types/hash_type.md +0 -1
  110. data/lib/sleeping_king_studios/docs/templates/includes/reference/types/ordered_type.md +0 -1
  111. data/lib/sleeping_king_studios/docs/templates/pages/index.md.erb +0 -22
  112. data/lib/sleeping_king_studios/docs/templates/pages/reference.md.erb +0 -14
  113. data/lib/sleeping_king_studios/docs/templates/pages/versions.md.erb +0 -13
@@ -0,0 +1,217 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sleeping_king_studios/docs/jekyll/commands'
4
+
5
+ module SleepingKingStudios::Docs::Jekyll::Commands
6
+ # Generates pinned documentation for the specified project version.
7
+ #
8
+ # - Adds the version to the _versions collection.
9
+ # - Generates reference documentation for the version.
10
+ # - Copies all static documentation files to the respective versions/*
11
+ # subdirectory.
12
+ class DocumentVersion < Cuprum::Cli::Command # rubocop:disable Metrics/ClassLength
13
+ dependency :file_system
14
+ dependency :standard_io
15
+
16
+ include Cuprum::Cli::Dependencies::StandardIo::Helpers
17
+ include Cuprum::Cli::Options::Quiet
18
+ include Cuprum::Cli::Options::Verbose
19
+
20
+ argument :version, type: :string, required: true
21
+
22
+ option :docs_path, default: 'docs'
23
+
24
+ option :dry_run, type: :boolean, default: false
25
+
26
+ full_name 'docs:jekyll:document_version'
27
+
28
+ description 'Generates pinned documentation for the specified version'
29
+
30
+ private
31
+
32
+ def copy_static_file(original_path)
33
+ relative_path = original_path.sub(%r{#{docs_path}/}, '')
34
+ updated_path = File.join(docs_path, 'versions', version, relative_path)
35
+
36
+ contents = file_system.read_file(original_path)
37
+ metadata, text = step { extract_front_matter(contents) }
38
+ metadata = update_metadata(metadata:, relative_path:)
39
+ contents = generate_contents(metadata:, text:)
40
+
41
+ files = step { write_static_file(contents:, updated_path:) }
42
+
43
+ @files.concat(files)
44
+ end
45
+
46
+ def copy_static_files
47
+ say "\nCopying static documentation..."
48
+ say("\n", verbose: true)
49
+
50
+ find_static_docs.each { |file_path| copy_static_file(file_path) }
51
+ end
52
+
53
+ def default_breadcrumbs_for(relative_path)
54
+ *dir_path, _ = relative_path.split(File::SEPARATOR)
55
+
56
+ return [] if dir_path.empty?
57
+
58
+ dir_path
59
+ .each
60
+ .map do |segment, index|
61
+ name = tools.string_tools.camelize(segment.tr('-_', ''))
62
+ path = "#{File::SEPARATOR}#{File.join(dir_path[0..index])}"
63
+
64
+ { 'name' => name, 'path' => path }
65
+ end
66
+ end
67
+
68
+ def exclude_static_file?(file_path)
69
+ return false if file_path == "#{docs_path}/reference/index.md"
70
+
71
+ file_path.start_with?("#{docs_path}/_includes/") ||
72
+ file_path.start_with?("#{docs_path}/reference") ||
73
+ file_path.start_with?("#{docs_path}/versions")
74
+ end
75
+
76
+ def extract_front_matter(contents)
77
+ return [{}, contents] unless contents.start_with?("---\n")
78
+
79
+ match = /^---\n$/.match(contents[4..])
80
+
81
+ return [{}, contents] unless match
82
+
83
+ text = match.post_match
84
+ yaml = YAML.safe_load(match.pre_match) || {}
85
+
86
+ [yaml, text]
87
+ end
88
+
89
+ def generate_contents(metadata:, text:)
90
+ "#{YAML.safe_dump(metadata)}---\n#{text}"
91
+ end
92
+
93
+ def generate_reference_files # rubocop:disable Metrics/MethodLength
94
+ say "\nGenerating data and reference files:"
95
+ say("\n", verbose: true)
96
+
97
+ files = step do
98
+ SleepingKingStudios::Docs::Jekyll::Commands::Generate
99
+ .new(file_system:, standard_io:)
100
+ .call(
101
+ docs_path:,
102
+ dry_run: dry_run?,
103
+ quiet: quiet?,
104
+ verbose: verbose?,
105
+ version:
106
+ )
107
+ end
108
+
109
+ @files.concat(files)
110
+ end
111
+
112
+ def generate_version # rubocop:disable Metrics/MethodLength
113
+ say "\nGenerating version file:"
114
+ say("\n", verbose: true)
115
+
116
+ files = step do
117
+ SleepingKingStudios::Docs::Jekyll::Generators::VersionGenerator
118
+ .new(
119
+ file_system:,
120
+ standard_io:,
121
+ docs_path:,
122
+ dry_run: dry_run?,
123
+ quiet: quiet?,
124
+ verbose: verbose?,
125
+ version:
126
+ )
127
+ .call
128
+ end
129
+
130
+ @files.concat(files)
131
+ end
132
+
133
+ def find_static_docs
134
+ file_system
135
+ .each_file("#{docs_path}/**/*.md")
136
+ .reject { |file_path| exclude_static_file?(file_path) }
137
+ .to_a
138
+ end
139
+
140
+ def process
141
+ say "Generating pinned documentation for version #{version}"
142
+
143
+ @files = []
144
+
145
+ step { generate_reference_files }
146
+
147
+ step { generate_version }
148
+
149
+ step { copy_static_files }
150
+
151
+ say "\nSuccess!"
152
+
153
+ @files
154
+ end
155
+
156
+ def top_level_breadcrumb?(obj)
157
+ obj.is_a?(Hash) && obj['path'] == File::SEPARATOR
158
+ end
159
+
160
+ def version_breadcrumbs # rubocop:disable Metrics/MethodLength
161
+ @version_breadcrumbs ||= [
162
+ {
163
+ 'name' => 'Documentation',
164
+ 'path' => File::SEPARATOR
165
+ },
166
+ {
167
+ 'name' => 'Versions',
168
+ 'path' => "#{File::SEPARATOR}versions"
169
+ },
170
+ {
171
+ 'name' => "Version #{version}",
172
+ 'path' => "#{File::SEPARATOR}#{File.join('versions', version)}"
173
+ }
174
+ ]
175
+ end
176
+
177
+ def update_breadcrumbs(breadcrumbs:, relative_path:)
178
+ prefix = "#{File::SEPARATOR}#{File.join('versions', version)}"
179
+ breadcrumbs = breadcrumbs[1..] if top_level_breadcrumb?(breadcrumbs.first)
180
+ breadcrumbs = breadcrumbs.map do |hsh|
181
+ hsh.merge('path' => File.join(prefix, hsh['path']))
182
+ end
183
+
184
+ if breadcrumbs.empty? && relative_path == 'index.md'
185
+ return version_breadcrumbs[...-1]
186
+ end
187
+
188
+ version_breadcrumbs + breadcrumbs
189
+ end
190
+
191
+ def update_metadata(metadata:, relative_path:)
192
+ breadcrumbs = update_breadcrumbs(
193
+ breadcrumbs: metadata.fetch(
194
+ 'breadcrumbs',
195
+ default_breadcrumbs_for(relative_path)
196
+ ),
197
+ relative_path:
198
+ )
199
+
200
+ metadata.merge('breadcrumbs' => breadcrumbs, 'version' => version)
201
+ end
202
+
203
+ def write_static_file(contents:, updated_path:) # rubocop:disable Metrics/MethodLength
204
+ Cuprum::Cli::Files::Generators::BasicGenerator
205
+ .new(
206
+ file_system:,
207
+ standard_io:,
208
+ contents:,
209
+ dry_run: dry_run?,
210
+ file_path: updated_path,
211
+ quiet: quiet?,
212
+ verbose: verbose?
213
+ )
214
+ .call
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sleeping_king_studios/docs/jekyll/commands'
4
+
5
+ module SleepingKingStudios::Docs::Jekyll::Commands
6
+ # Generates new reference documentation.
7
+ class Generate < SleepingKingStudios::Docs::Jekyll::Commands::Reference # rubocop:disable Metrics/ClassLength
8
+ full_name 'docs:jekyll:generate'
9
+
10
+ description 'Generates reference documentation for the current version'
11
+
12
+ private
13
+
14
+ attr_reader :errors
15
+
16
+ attr_reader :files
17
+
18
+ attr_reader :registry
19
+
20
+ def build_command
21
+ @build_command ||= SleepingKingStudios::Docs::Yard::Build.new
22
+ end
23
+
24
+ def clear_data_directory(path) # rubocop:disable Metrics/MethodLength
25
+ return unless file_system.directory?(path)
26
+
27
+ if dry_run?
28
+ step { ensure_data_directory_empty(path) }
29
+
30
+ return
31
+ end
32
+
33
+ each_data_directory(path) do |child|
34
+ file_system.delete_directory(child, recursive: true)
35
+ end
36
+ rescue Cuprum::Cli::Dependencies::FileSystem::FileError => exception
37
+ error = SleepingKingStudios::Docs::Errors::FileError.new(
38
+ message: exception.message,
39
+ path:
40
+ )
41
+ failure(error)
42
+ end
43
+
44
+ def clear_directories
45
+ step { clear_reference_directory }
46
+
47
+ step { clear_data_directory(class_data_directory) }
48
+ step { clear_data_directory(constant_data_directory) }
49
+ step { clear_data_directory(method_data_directory) }
50
+ step { clear_data_directory(module_data_directory) }
51
+ step { clear_data_directory(namespace_data_directory) }
52
+ end
53
+
54
+ def clear_reference_directory # rubocop:disable Metrics/MethodLength
55
+ return unless file_system.directory?(reference_directory)
56
+
57
+ if dry_run?
58
+ step { ensure_reference_directory_empty }
59
+
60
+ return
61
+ end
62
+
63
+ each_reference_directory do |path|
64
+ file_system.delete_directory(path, recursive: true)
65
+ end
66
+ rescue Cuprum::Cli::Dependencies::FileSystem::FileError => exception
67
+ error = SleepingKingStudios::Docs::Errors::FileError.new(
68
+ message: exception.message,
69
+ path: reference_directory
70
+ )
71
+ failure(error)
72
+ end
73
+
74
+ def ensure_data_directory_empty(path)
75
+ matching = each_data_file(path).first
76
+
77
+ return unless matching
78
+
79
+ path = File.dirname(matching)
80
+
81
+ message = "unable to delete directory #{path} - directory is not empty"
82
+ error = SleepingKingStudios::Docs::Errors::FileError.new(
83
+ message:,
84
+ path:
85
+ )
86
+
87
+ failure(error)
88
+ end
89
+
90
+ def ensure_reference_directory_empty # rubocop:disable Metrics/MethodLength
91
+ matching = each_reference_file.first
92
+
93
+ return unless matching
94
+
95
+ path = File.dirname(matching)
96
+
97
+ message =
98
+ "unable to delete directory #{path} - directory is " \
99
+ 'not empty'
100
+ error = SleepingKingStudios::Docs::Errors::FileError.new(
101
+ message:,
102
+ path:
103
+ )
104
+
105
+ failure(error)
106
+ end
107
+
108
+ def generator_for(object)
109
+ namespace = SleepingKingStudios::Docs::Jekyll::Generators
110
+ generator_class =
111
+ if object.is_a?(SleepingKingStudios::Docs::Data::ModuleObject)
112
+ namespace::ReferenceGenerator
113
+ else
114
+ namespace::DataGenerator
115
+ end
116
+
117
+ generator_class.new(object:, **generator_options)
118
+ end
119
+
120
+ def generator_options
121
+ @generator_options ||= {
122
+ file_system:,
123
+ standard_io:,
124
+ docs_path:,
125
+ dry_run: dry_run?,
126
+ quiet: quiet?,
127
+ verbose: verbose?,
128
+ version:
129
+ }
130
+ end
131
+
132
+ def handle_generator_result
133
+ result = yield
134
+
135
+ if result.success?
136
+ files.concat(result.value)
137
+
138
+ return
139
+ end
140
+
141
+ errors << result.error
142
+ end
143
+
144
+ def parse_registry
145
+ @registry = step do
146
+ SleepingKingStudios::Docs::Yard::Parse.new.call
147
+ end
148
+
149
+ SleepingKingStudios::Docs::Yard::Registry
150
+ .provider
151
+ .set(:registry, registry)
152
+ rescue Plumbum::Errors::ImmutableError => exception
153
+ message = "#{exception.class}: #{exception.message}"
154
+ error = SleepingKingStudios::Docs::Errors::RegistryError.new(message:)
155
+
156
+ failure(error)
157
+ end
158
+
159
+ def process # rubocop:disable Metrics/MethodLength
160
+ @files = []
161
+ @errors = []
162
+
163
+ step { clear_directories }
164
+
165
+ step { parse_registry }
166
+
167
+ registry.each do |native|
168
+ data = step { build_command.call(native) }
169
+
170
+ next unless data.public?
171
+
172
+ generator = generator_for(data)
173
+
174
+ handle_generator_result { generator.call }
175
+ end
176
+
177
+ report_errors
178
+ end
179
+
180
+ def report_errors # rubocop:disable Metrics/MethodLength
181
+ if errors.empty?
182
+ say 'Success!'
183
+
184
+ return files
185
+ end
186
+
187
+ warn 'Failures:'
188
+ warn "\n"
189
+
190
+ errors.each do |error|
191
+ warn " - Unable to generate file #{error.path} - #{error.message}"
192
+ end
193
+
194
+ warn "\n"
195
+
196
+ message = 'unable to generate documentation files'
197
+ error = Cuprum::Errors::MultipleErrors.new(errors:, message:)
198
+
199
+ failure(error)
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,271 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cuprum/cli'
4
+
5
+ require 'sleeping_king_studios/docs/jekyll/commands'
6
+
7
+ module SleepingKingStudios::Docs::Jekyll::Commands
8
+ # Installs the Jekyll application.
9
+ class Install < Cuprum::Cli::Command # rubocop:disable Metrics/ClassLength
10
+ dependency :file_system
11
+ dependency :standard_io
12
+
13
+ include Cuprum::Cli::Dependencies::StandardIo::Helpers
14
+ include Cuprum::Cli::Options::Quiet
15
+ include Cuprum::Cli::Options::Verbose
16
+
17
+ TEMPLATES_PATH = SleepingKingStudios::Docs::Jekyll::Commands.templates_path
18
+ private_constant :TEMPLATES_PATH
19
+
20
+ # Generator for writing the Jekyll static pages.
21
+ class Generator < Cuprum::Cli::Files::Generator
22
+ output '%<docs_path>s/_config.yml',
23
+ key: :config,
24
+ template: File.join(TEMPLATES_PATH, 'config.yml.erb')
25
+
26
+ output '%<docs_path>s/_layouts/page.html',
27
+ key: :layout,
28
+ template: File.join(TEMPLATES_PATH, 'layouts', 'page.html.erb')
29
+
30
+ output '%<docs_path>s/index.md',
31
+ key: :index,
32
+ template: File.join(TEMPLATES_PATH, 'pages', 'index.md.erb')
33
+
34
+ output '%<docs_path>s/reference/index.md',
35
+ key: :reference,
36
+ template: File.join(TEMPLATES_PATH, 'pages', 'reference.md.erb')
37
+
38
+ output '%<docs_path>s/versions/index.md',
39
+ key: :versions,
40
+ template: File.join(TEMPLATES_PATH, 'pages', 'versions.md.erb')
41
+
42
+ option :docs_path, default: 'docs'
43
+
44
+ option :project_description, default: ''
45
+
46
+ option :project_name, default: 'Project Name'
47
+
48
+ option :project_repository, default: ''
49
+ end
50
+
51
+ full_name 'docs:jekyll:install'
52
+
53
+ description 'Installs the Jekyll application'
54
+
55
+ option :docs_path, type: :string, default: 'docs'
56
+
57
+ option :dry_run, type: :boolean, default: false
58
+
59
+ option :gemfile, type: :boolean, default: true
60
+
61
+ option :gitignore, type: :boolean, default: true
62
+
63
+ option :templates, type: :boolean, default: true
64
+
65
+ option :workflow, type: :boolean, default: false
66
+
67
+ private
68
+
69
+ attr_reader :errors
70
+
71
+ attr_reader :output_paths
72
+
73
+ def create_directory
74
+ return if file_system.directory?(docs_path)
75
+
76
+ say "Generating directory #{docs_path}..."
77
+ say("\n", verbose: true)
78
+
79
+ file_system.create_directory(docs_path, recursive: true) unless dry_run?
80
+ end
81
+
82
+ def create_gemfile
83
+ gems = included_gems.map { |k, v| " gem '#{k}', '#{v}'" }.join("\n")
84
+ contents = <<~GEMFILE
85
+ # frozen_string_literal: true
86
+
87
+ source 'https://rubygems.org'
88
+
89
+ group: :docs do
90
+ #{gems}
91
+ end
92
+ GEMFILE
93
+
94
+ say 'Generating Gemfile...'
95
+ say("\n#{indent(contents, 2)}\n", verbose: true)
96
+
97
+ file_system.write_file('Gemfile', contents) unless dry_run?
98
+ end
99
+
100
+ def create_gitignore
101
+ contents = "# Ignore Jekyll site and temporary files.\n"
102
+
103
+ ignored_paths.each { |path| contents = "#{contents}#{path}\n" }
104
+
105
+ say 'Generating .gitignore...'
106
+ say("\n#{indent(contents, 2)}\n", verbose: true)
107
+
108
+ file_system.write_file('.gitignore', contents) unless dry_run?
109
+ end
110
+
111
+ def create_static_files # rubocop:disable Metrics/MethodLength
112
+ static_files = step do
113
+ Generator
114
+ .new(
115
+ file_system:,
116
+ standard_io:,
117
+ docs_path:,
118
+ dry_run: dry_run?,
119
+ directories: true,
120
+ quiet: quiet?,
121
+ verbose: verbose?
122
+ )
123
+ .call
124
+ end
125
+
126
+ output_paths.concat(static_files)
127
+ end
128
+
129
+ def handle_file_error(file_path)
130
+ yield
131
+
132
+ success(file_path)
133
+ rescue Cuprum::Cli::Dependencies::FileSystem::FileError => exception
134
+ warn(" ! #{exception.message}")
135
+
136
+ error = Cuprum::Cli::Files::Errors::FileNotWriteable.new(
137
+ file_path:,
138
+ message: exception.message
139
+ )
140
+ failure(error)
141
+ end
142
+
143
+ def ignored_paths
144
+ [
145
+ "#{docs_path}/_site",
146
+ "#{docs_path}/.sass-cache",
147
+ "#{docs_path}/.jekyll-cache",
148
+ "#{docs_path}/.jekyll-metadata",
149
+ 'vendor'
150
+ ]
151
+ end
152
+
153
+ def included_gems
154
+ {
155
+ 'jekyll' => '~> 4.4',
156
+ 'kramdown-parser-gfm' => '~> 1.1',
157
+ 'webrick' => '~> 1.8'
158
+ }
159
+ end
160
+
161
+ def indent(str, count) = tools.string_tools.indent(str, count)
162
+
163
+ def install_templates # rubocop:disable Metrics/MethodLength
164
+ return unless templates?
165
+
166
+ template_files = step do
167
+ SleepingKingStudios::Docs::Jekyll::Commands::InstallTemplates
168
+ .new(file_system:, standard_io:)
169
+ .call(
170
+ docs_path:,
171
+ dry_run: dry_run?,
172
+ quiet: quiet?,
173
+ verbose: verbose?
174
+ )
175
+ end
176
+
177
+ say("\n", verbose: true)
178
+
179
+ output_paths.concat(template_files)
180
+ end
181
+
182
+ def install_workflow # rubocop:disable Metrics/MethodLength
183
+ return unless workflow?
184
+
185
+ workflow_files = step do
186
+ SleepingKingStudios::Docs::Jekyll::Commands::InstallWorkflow
187
+ .new(file_system:, standard_io:)
188
+ .call(
189
+ dry_run: dry_run?,
190
+ quiet: quiet?,
191
+ verbose: verbose?
192
+ )
193
+ end
194
+
195
+ output_paths.concat(workflow_files)
196
+ end
197
+
198
+ def merge_gemfile # rubocop:disable Metrics/AbcSize
199
+ contents = file_system.read_file('Gemfile')
200
+ existing = contents.each_line.map(&:strip)
201
+ added = included_gems.reject { |k, _| existing.include?("gem '#{k}'") }
202
+
203
+ return if added.empty?
204
+
205
+ gems = added.map { |k, v| " gem '#{k}', '#{v}'" }.join("\n")
206
+
207
+ contents << "\n" unless contents.end_with?("\n\n")
208
+ contents << "group :docs do\n#{gems}\nend\n"
209
+
210
+ say 'Updating Gemfile...'
211
+ say("\n#{indent(contents, 2)}\n", verbose: true)
212
+
213
+ file_system.write_file('Gemfile', contents) unless dry_run?
214
+ end
215
+
216
+ def merge_gitignore # rubocop:disable Metrics/AbcSize
217
+ contents = file_system.read_file('.gitignore')
218
+ existing = contents.each_line.map(&:strip)
219
+ added = ignored_paths.reject { |path| existing.include?(path) }
220
+
221
+ return if added.empty?
222
+
223
+ contents << "\n" unless contents.end_with?("\n\n")
224
+ contents << "# Ignore Jekyll site and temporary files.\n"
225
+
226
+ added.each { |path| contents << path << "\n" }
227
+
228
+ say 'Updating .gitignore...'
229
+ say("\n#{indent(contents, 2)}\n", verbose: true)
230
+
231
+ file_system.write_file('.gitignore', contents) unless dry_run?
232
+ end
233
+
234
+ def process
235
+ @output_paths = []
236
+
237
+ step { update_gitignore }
238
+ step { update_gemfile }
239
+ step { create_directory }
240
+ step { create_static_files }
241
+ step { install_templates }
242
+ step { install_workflow }
243
+
244
+ output_paths
245
+ end
246
+
247
+ def update_gemfile
248
+ return unless gemfile?
249
+
250
+ step do
251
+ handle_file_error('Gemfile') do
252
+ file_system.file?('Gemfile') ? merge_gemfile : create_gemfile
253
+ end
254
+ end
255
+
256
+ output_paths << 'Gemfile'
257
+ end
258
+
259
+ def update_gitignore
260
+ return unless gitignore?
261
+
262
+ step do
263
+ handle_file_error('.gitignore') do
264
+ file_system.file?('.gitignore') ? merge_gitignore : create_gitignore
265
+ end
266
+ end
267
+
268
+ output_paths << '.gitignore'
269
+ end
270
+ end
271
+ end