ratatui_ruby-devtools 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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/AGENTS.md +1 -1
  4. data/CHANGELOG.md +63 -6
  5. data/README.md +49 -21
  6. data/Rakefile +1 -1
  7. data/bin/setup +20 -6
  8. data/bin/setup.ps1 +74 -0
  9. data/doc/contributors/documentation_style.md +3 -4
  10. data/exe/scaffold +8 -342
  11. data/lib/ratatui_ruby/devtools/scaffold/scaffold_config.rb +67 -0
  12. data/lib/ratatui_ruby/devtools/scaffold/template_renderer.rb +282 -0
  13. data/lib/ratatui_ruby/devtools/site_tasks/build.rake +10 -15
  14. data/lib/ratatui_ruby/devtools/task_scope.rb +24 -0
  15. data/lib/ratatui_ruby/devtools/tasks/autodoc_tasks.rb +31 -0
  16. data/lib/ratatui_ruby/devtools/tasks/bump/bump_workflow.rb +71 -0
  17. data/lib/ratatui_ruby/devtools/tasks/bump/changelog.rb +60 -3
  18. data/lib/ratatui_ruby/devtools/tasks/bump/github_project.rb +26 -0
  19. data/lib/ratatui_ruby/devtools/tasks/bump/history.rb +3 -3
  20. data/lib/ratatui_ruby/devtools/tasks/bump/links.rb +39 -24
  21. data/lib/ratatui_ruby/devtools/tasks/bump/manifest.rb +9 -17
  22. data/lib/ratatui_ruby/devtools/tasks/bump/no_crate.rb +19 -0
  23. data/lib/ratatui_ruby/devtools/tasks/bump/no_links.rb +22 -0
  24. data/lib/ratatui_ruby/devtools/tasks/bump/patch_release.rb +25 -0
  25. data/lib/ratatui_ruby/devtools/tasks/bump/release_branch.rb +17 -0
  26. data/lib/ratatui_ruby/devtools/tasks/bump/release_from_trunk.rb +48 -0
  27. data/lib/ratatui_ruby/devtools/tasks/bump/repository.rb +143 -0
  28. data/lib/ratatui_ruby/devtools/tasks/bump/ruby_gem.rb +65 -59
  29. data/lib/ratatui_ruby/devtools/tasks/bump/rust_crate.rb +42 -0
  30. data/lib/ratatui_ruby/devtools/tasks/bump/sem_ver.rb +127 -10
  31. data/lib/ratatui_ruby/devtools/tasks/bump/source_hut_project.rb +26 -0
  32. data/lib/ratatui_ruby/devtools/tasks/bump/unbranched_release.rb +20 -0
  33. data/lib/ratatui_ruby/devtools/tasks/bump/unreleased_section.rb +19 -1
  34. data/lib/ratatui_ruby/devtools/tasks/bump_tasks.rb +47 -0
  35. data/lib/ratatui_ruby/devtools/tasks/cargo_tasks.rb +57 -0
  36. data/lib/ratatui_ruby/devtools/tasks/doc/documentation.rb +59 -0
  37. data/lib/ratatui_ruby/devtools/tasks/doc/link/file_url.rb +30 -0
  38. data/lib/ratatui_ruby/devtools/tasks/doc/link/relative_path.rb +61 -0
  39. data/lib/ratatui_ruby/devtools/tasks/doc/link/web_url.rb +55 -0
  40. data/lib/ratatui_ruby/devtools/tasks/doc/link.rb +52 -0
  41. data/lib/ratatui_ruby/devtools/tasks/doc/link_audit.rb +116 -0
  42. data/lib/ratatui_ruby/devtools/tasks/doc/problem.rb +40 -0
  43. data/lib/ratatui_ruby/devtools/tasks/doc/source_file.rb +93 -0
  44. data/lib/ratatui_ruby/devtools/tasks/doc_tasks.rb +937 -0
  45. data/lib/ratatui_ruby/devtools/tasks/license/blame.rb +72 -0
  46. data/lib/ratatui_ruby/devtools/tasks/license/changed_files.rb +34 -0
  47. data/lib/ratatui_ruby/devtools/tasks/license/contribution.rb +12 -0
  48. data/lib/ratatui_ruby/devtools/tasks/license/copyright.rb +47 -0
  49. data/lib/ratatui_ruby/devtools/tasks/license/directory_licensing.rb +42 -0
  50. data/lib/ratatui_ruby/devtools/tasks/license/existing_header.rb +21 -0
  51. data/lib/ratatui_ruby/devtools/tasks/license/header_revision.rb +45 -0
  52. data/lib/ratatui_ruby/devtools/tasks/license/licensed_file.rb +39 -0
  53. data/lib/ratatui_ruby/devtools/tasks/license/licensing.rb +35 -0
  54. data/lib/ratatui_ruby/devtools/tasks/license/markdown_document.rb +23 -0
  55. data/lib/ratatui_ruby/devtools/tasks/license/markdown_files.rb +31 -0
  56. data/lib/ratatui_ruby/devtools/tasks/license/markdown_header_syntax.rb +104 -0
  57. data/lib/ratatui_ruby/devtools/tasks/license/markdown_snippet_syntax.rb +159 -0
  58. data/lib/ratatui_ruby/devtools/tasks/license/missing_header.rb +18 -0
  59. data/lib/ratatui_ruby/devtools/tasks/license/rbs_files.rb +27 -0
  60. data/lib/ratatui_ruby/devtools/tasks/license/rbs_header_syntax.rb +31 -0
  61. data/lib/ratatui_ruby/devtools/tasks/license/rdoc_comment.rb +51 -0
  62. data/lib/ratatui_ruby/devtools/tasks/license/rdoc_examples.rb +41 -0
  63. data/lib/ratatui_ruby/devtools/tasks/license/rdoc_snippet_syntax.rb +119 -0
  64. data/lib/ratatui_ruby/devtools/tasks/license/renewal.rb +17 -0
  65. data/lib/ratatui_ruby/devtools/tasks/license/reuse_annotated_file.rb +12 -0
  66. data/lib/ratatui_ruby/devtools/tasks/license/reuse_tool.rb +34 -0
  67. data/lib/ratatui_ruby/devtools/tasks/license/ruby_comment_header.rb +49 -0
  68. data/lib/ratatui_ruby/devtools/tasks/license/ruby_files.rb +36 -0
  69. data/lib/ratatui_ruby/devtools/tasks/license/ruby_header_syntax.rb +39 -0
  70. data/lib/ratatui_ruby/devtools/tasks/license/rust_files.rb +27 -0
  71. data/lib/ratatui_ruby/devtools/tasks/license/rust_header_syntax.rb +59 -0
  72. data/lib/ratatui_ruby/devtools/tasks/license/snippet.rb +44 -0
  73. data/lib/ratatui_ruby/devtools/tasks/license/snippet_file.rb +29 -0
  74. data/lib/ratatui_ruby/devtools/tasks/license/snippet_opening.rb +39 -0
  75. data/lib/ratatui_ruby/devtools/tasks/license/snippet_revision.rb +75 -0
  76. data/lib/ratatui_ruby/devtools/tasks/license/spdx_header.rb +42 -0
  77. data/lib/ratatui_ruby/devtools/tasks/license/unknown_file.rb +12 -0
  78. data/lib/ratatui_ruby/devtools/tasks/license/unlicensed_files.rb +69 -0
  79. data/lib/ratatui_ruby/devtools/tasks/license/wrapped_snippet.rb +30 -0
  80. data/lib/ratatui_ruby/devtools/tasks/license_tasks.rb +71 -0
  81. data/lib/ratatui_ruby/devtools/tasks/lint/gem_lockfile.rb +29 -0
  82. data/lib/ratatui_ruby/devtools/tasks/lint/supported_rubies.rb +18 -0
  83. data/lib/ratatui_ruby/devtools/tasks/lint_tasks.rb +100 -0
  84. data/lib/ratatui_ruby/devtools/tasks/release/platform_gem.rb +21 -12
  85. data/lib/ratatui_ruby/devtools/tasks/release/versioned_binary.rb +1 -1
  86. data/lib/ratatui_ruby/devtools/tasks/release_tasks.rb +80 -0
  87. data/lib/ratatui_ruby/devtools/tasks/reuse_tasks.rb +39 -0
  88. data/lib/ratatui_ruby/devtools/tasks/{test.rake → test_tasks.rb} +13 -4
  89. data/lib/ratatui_ruby/devtools/tasks/website/hosted_source_tree.rb +24 -0
  90. data/lib/ratatui_ruby/devtools/tasks/website/index_page.rb +1 -4
  91. data/lib/ratatui_ruby/devtools/tasks/website/source_tree.rb +63 -0
  92. data/lib/ratatui_ruby/devtools/tasks/website/versioned_documentation.rb +14 -13
  93. data/lib/ratatui_ruby/devtools/tasks/website_tasks.rb +49 -0
  94. data/lib/ratatui_ruby/devtools/templates/.github/workflows/build-gems.yml.erb +7 -1
  95. data/lib/ratatui_ruby/devtools/templates/.github/workflows/ci.yml.erb +2 -2
  96. data/lib/ratatui_ruby/devtools/templates/.gitignore.erb +2 -1
  97. data/lib/ratatui_ruby/devtools/templates/.rubocop.yml.erb +1 -1
  98. data/lib/ratatui_ruby/devtools/templates/AGENTS.md.erb +1 -1
  99. data/lib/ratatui_ruby/devtools/templates/CHANGELOG.md.erb +3 -1
  100. data/lib/ratatui_ruby/devtools/templates/CONTRIBUTING.md.erb +3 -3
  101. data/lib/ratatui_ruby/devtools/templates/Gemfile.erb +2 -3
  102. data/lib/ratatui_ruby/devtools/templates/README.md.erb +7 -7
  103. data/lib/ratatui_ruby/devtools/templates/REUSE.toml.erb +1 -1
  104. data/lib/ratatui_ruby/devtools/templates/bin/setup.erb +5 -1
  105. data/lib/ratatui_ruby/devtools/templates/bin/setup.ps1.erb +5 -1
  106. data/lib/ratatui_ruby/devtools/templates/doc/concepts/application_architecture.md.erb +2 -2
  107. data/lib/ratatui_ruby/devtools/templates/doc/concepts/application_testing.md.erb +3 -3
  108. data/lib/ratatui_ruby/devtools/templates/doc/getting_started/quickstart.md.erb +4 -4
  109. data/lib/ratatui_ruby/devtools/templates/ext/Cargo.toml.erb +1 -1
  110. data/lib/ratatui_ruby/devtools/templates/gemspec.erb +1 -1
  111. data/lib/ratatui_ruby/devtools/templates/mise.toml.erb +3 -3
  112. data/lib/ratatui_ruby/devtools/version.rb +1 -1
  113. data/lib/ratatui_ruby/devtools.rb +54 -71
  114. data/mise.toml +2 -2
  115. data/sig/ratatui_ruby/devtools.rbs +16 -1
  116. metadata +93 -34
  117. data/.builds/ruby-4.0.yml +0 -38
  118. data/lib/ratatui_ruby/devtools/tasks/autodoc.rake +0 -21
  119. data/lib/ratatui_ruby/devtools/tasks/bump.rake +0 -80
  120. data/lib/ratatui_ruby/devtools/tasks/cargo.rake +0 -47
  121. data/lib/ratatui_ruby/devtools/tasks/doc.rake +0 -887
  122. data/lib/ratatui_ruby/devtools/tasks/license/headers_md.rb +0 -276
  123. data/lib/ratatui_ruby/devtools/tasks/license/headers_rb.rb +0 -236
  124. data/lib/ratatui_ruby/devtools/tasks/license/license_utils.rb +0 -143
  125. data/lib/ratatui_ruby/devtools/tasks/license/snippets_md.rb +0 -353
  126. data/lib/ratatui_ruby/devtools/tasks/license/snippets_rdoc.rb +0 -186
  127. data/lib/ratatui_ruby/devtools/tasks/license.rake +0 -91
  128. data/lib/ratatui_ruby/devtools/tasks/lint.rake +0 -80
  129. data/lib/ratatui_ruby/devtools/tasks/release.rake +0 -87
  130. data/lib/ratatui_ruby/devtools/tasks/resources/rubies.yml +0 -7
  131. data/lib/ratatui_ruby/devtools/tasks/reuse.rake +0 -104
  132. data/lib/ratatui_ruby/devtools/tasks/website.rake +0 -37
  133. data/lib/ratatui_ruby/devtools/templates/.builds/ruby.yml.erb +0 -47
  134. data/lib/ratatui_ruby/devtools/templates/tasks/resources/index.html.erb +0 -45
  135. data/lib/ratatui_ruby/devtools/templates/tasks/resources/rubies.yml.erb +0 -8
data/exe/scaffold CHANGED
@@ -28,7 +28,7 @@
28
28
  #
29
29
  # --author NAME Copyright holder name (default: from git config)
30
30
  # --email EMAIL Copyright holder email (default: from git config)
31
- # --license ID SPDX license identifier (default: AGPL-3.0-or-later)
31
+ # --license ID SPDX license identifier (default: LGPL-3.0-or-later)
32
32
  # --dir PATH Parent directory for new gem (default: current directory)
33
33
  # --update Update existing project (diff + prompt for conflicts)
34
34
  # --force Remove existing directory if present
@@ -42,347 +42,11 @@
42
42
  # scaffold ratatui_ruby-framework --dir ~/Developer
43
43
  # scaffold rooibos --update --dir ~/Developer
44
44
 
45
- require "erb"
46
45
  require "fileutils"
47
46
  require "optparse"
48
47
 
49
- # Configuration for scaffold generation.
50
- #
51
- # Holds all parameters needed to render templates. Auto-discovers author info
52
- # from git config if not provided.
53
- class ScaffoldConfig
54
- # The gem name to scaffold.
55
- attr_accessor :gem_name
56
-
57
- # The copyright holder's name.
58
- attr_accessor :author
59
-
60
- # The copyright holder's email.
61
- attr_accessor :email
62
-
63
- # The SPDX license identifier.
64
- attr_accessor :license
65
-
66
- # The output directory path.
67
- attr_accessor :output_dir
68
-
69
- # Whether this gem includes a Rust native extension.
70
- attr_accessor :has_rust
71
-
72
- # Creates a new config with defaults from git.
73
- #
74
- # Reads author name and email from git config. Sets default license to
75
- # LGPL-3.0-or-later. Override any accessor after creation.
76
- def initialize
77
- @license = "LGPL-3.0-or-later"
78
- @author = `git config user.name`.strip
79
- @email = `git config user.email`.strip
80
- @has_rust = false
81
- end
82
-
83
- # The gem name with underscores (for module names).
84
- def module_name
85
- gem_name.split("-").map { |s| s.split("_").map(&:capitalize).join }.join("::")
86
- end
87
-
88
- # The gem name with slashes (for require paths).
89
- def require_path
90
- gem_name.tr("-", "/")
91
- end
92
-
93
- # Copyright holder string for SPDX headers.
94
- def copyright_holder
95
- "#{author} <#{email}>"
96
- end
97
-
98
- # Current year for copyright.
99
- def year
100
- Time.now.year
101
- end
102
-
103
- # Ruby versions to test in CI.
104
- def ruby_versions
105
- %w[3.2 3.3 3.4 4.0.1]
106
- end
107
- end
108
-
109
- # Renders ERB templates into the target directory.
110
- #
111
- # Templates use ERB to inject gem-specific values. This class handles the
112
- # rendering and file creation with proper directory structure.
113
- class TemplateRenderer
114
- # Creates a new renderer with config and paths.
115
- #
116
- # [config] The ScaffoldConfig with gem parameters.
117
- # [templates_dir] Path to the ERB templates.
118
- # [output_dir] Path where rendered files are written.
119
- def initialize(config, templates_dir, output_dir, update_mode: false)
120
- @config = config
121
- @templates_dir = templates_dir
122
- @output_dir = output_dir
123
- @update_mode = update_mode
124
- end
125
-
126
- # Renders all templates into the output directory.
127
- def render_all
128
- FileUtils.mkdir_p(@output_dir)
129
-
130
- render_template(".gitignore.erb", ".gitignore")
131
- render_template(".pre-commit-config.yaml.erb", ".pre-commit-config.yaml")
132
- render_template(".rubocop.yml.erb", ".rubocop.yml")
133
- render_template("AGENTS.md.erb", "AGENTS.md")
134
- render_template("CHANGELOG.md.erb", "CHANGELOG.md") unless @update_mode
135
- render_template("CONTRIBUTING.md.erb", "CONTRIBUTING.md")
136
- render_template("CODE_OF_CONDUCT.md.erb", "CODE_OF_CONDUCT.md")
137
- render_template("Gemfile.erb", "Gemfile")
138
- render_template("README.md.erb", "README.md")
139
- render_template("REUSE.toml.erb", "REUSE.toml")
140
- render_template("Rakefile.erb", "Rakefile")
141
- render_template("gemspec.erb", "#{@config.gem_name}.gemspec")
142
- render_template("mise.toml.erb", "mise.toml")
143
-
144
- # bin/ scripts
145
- render_template("bin/setup.erb", "bin/setup", executable: true)
146
- render_template("bin/setup.ps1.erb", "bin/setup.ps1")
147
- render_template("bin/console.erb", "bin/console", executable: true)
148
-
149
- # GitHub Actions CI workflow
150
- render_template(".github/workflows/ci.yml.erb", ".github/workflows/ci.yml")
151
-
152
- # GitHub Actions build-gems workflow (Rust only)
153
- render_template(".github/workflows/build-gems.yml.erb", ".github/workflows/build-gems.yml") if @config.has_rust
154
-
155
- # task resources
156
- copy_task_resources
157
-
158
- # doc files (RDoc styling)
159
- copy_doc_files
160
-
161
- # lib/ structure
162
- create_lib_structure
163
-
164
- # Rust extension (if enabled)
165
- create_rust_extension if @config.has_rust
166
-
167
- puts "Scaffolded #{@config.gem_name} in #{@output_dir}"
168
- end
169
-
170
- private def render_template(template_name, output_name, executable: false)
171
- template_path = File.join(@templates_dir, template_name)
172
- output_path = File.join(@output_dir, output_name)
173
-
174
- FileUtils.mkdir_p(File.dirname(output_path))
175
-
176
- unless File.exist?(template_path)
177
- warn " WARNING: Template not found: #{template_name}"
178
- return
179
- end
180
-
181
- content = if template_name.end_with?(".erb")
182
- ERB.new(File.read(template_path), trim_mode: "-").result(binding_for_config)
183
- else
184
- File.read(template_path)
185
- end
186
-
187
- if @update_mode && File.exist?(output_path)
188
- existing = File.read(output_path)
189
- if existing == content
190
- puts " Unchanged #{output_name}"
191
- return
192
- end
193
- puts " Conflict #{output_name}"
194
- show_diff(output_path, content)
195
- print " Overwrite #{output_name}? [y/N] "
196
- answer = $stdin.gets&.strip
197
- unless answer&.match?(/^[Yy]/)
198
- puts " Skipped #{output_name}"
199
- return
200
- end
201
- end
202
-
203
- File.write(output_path, content)
204
- FileUtils.chmod(0o755, output_path) if executable
205
- puts " Created #{output_name}"
206
- end
207
-
208
- private def show_diff(existing_path, new_content)
209
- require "tempfile"
210
- Tempfile.create(["scaffold", File.extname(existing_path)]) do |tmp|
211
- tmp.write(new_content)
212
- tmp.flush
213
- system("git", "--no-pager", "diff", "--no-index", "--color", existing_path, tmp.path)
214
- end
215
- end
216
-
217
- private def copy_task_resources
218
- resources_dir = File.join(@output_dir, "tasks", "resources")
219
- tasks_dir = File.join(@output_dir, "tasks")
220
- FileUtils.mkdir_p(resources_dir)
221
-
222
- # Render ERB templates to tasks/resources/
223
- render_template("tasks/resources/rubies.yml.erb", "tasks/resources/rubies.yml")
224
- render_template("tasks/resources/index.html.erb", "tasks/resources/index.html.erb")
225
-
226
- # Render example_viewer.html.erb to tasks/
227
- render_template("tasks/example_viewer.html.erb", "tasks/example_viewer.html.erb")
228
-
229
- # Create exe/.gitkeep
230
- exe_dir = File.join(@output_dir, "exe")
231
- FileUtils.mkdir_p(exe_dir)
232
- gitkeep = File.join(exe_dir, ".gitkeep")
233
- FileUtils.touch(gitkeep)
234
- puts " Created exe/.gitkeep"
235
- end
236
-
237
- private def copy_doc_files
238
- doc_dir = File.join(@output_dir, "doc")
239
- FileUtils.mkdir_p(doc_dir)
240
- FileUtils.mkdir_p(File.join(doc_dir, "getting_started"))
241
- FileUtils.mkdir_p(File.join(doc_dir, "concepts"))
242
- FileUtils.mkdir_p(File.join(doc_dir, "images"))
243
-
244
- # Render custom.css (now an ERB template)
245
- render_template("doc/custom.css.erb", "doc/custom.css")
246
-
247
- # Create images/.gitkeep
248
- FileUtils.touch(File.join(doc_dir, "images", ".gitkeep"))
249
- puts " Created doc/images/.gitkeep"
250
-
251
- # Render doc templates
252
- render_template("doc/index.md.erb", "doc/index.md")
253
- render_template("doc/getting_started/quickstart.md.erb", "doc/getting_started/quickstart.md")
254
- render_template("doc/concepts/application_architecture.md.erb", "doc/concepts/application_architecture.md")
255
- render_template("doc/concepts/application_testing.md.erb", "doc/concepts/application_testing.md")
256
- end
257
-
258
- private def create_lib_structure
259
- lib_path = File.join(@output_dir, "lib", @config.require_path)
260
- FileUtils.mkdir_p(lib_path)
261
-
262
- # RuboCop config shipped inside the gem (resolved via inherit_gem)
263
- render_template("lib/rubocop.yml", "lib/#{@config.require_path}/rubocop.yml")
264
-
265
- # Main require file
266
- render_template("lib/main.rb.erb", "lib/#{@config.require_path}.rb")
267
-
268
- # Version file
269
- render_template("lib/version.rb.erb", "lib/#{@config.require_path}/version.rb")
270
-
271
- # Test helper shipped inside the gem
272
- render_template("lib/test_helper.rb.erb", "lib/#{@config.require_path}/test_helper.rb")
273
-
274
- # Test directory with placeholder
275
- FileUtils.mkdir_p(File.join(@output_dir, "test"))
276
- render_template("test/test_helper.rb.erb", "test/test_helper.rb")
277
- end
278
-
279
- private def create_rust_extension
280
- ext_name = @config.gem_name.tr("-", "_")
281
-
282
- render_template("ext/Cargo.toml.erb", "ext/#{ext_name}/Cargo.toml")
283
- render_template("ext/extconf.rb.erb", "ext/#{ext_name}/extconf.rb")
284
- render_template("ext/.cargo/config.toml.erb", "ext/#{ext_name}/.cargo/config.toml")
285
- render_template("ext/.gitignore.erb", "ext/#{ext_name}/.gitignore")
286
- render_template("ext/clippy.toml.erb", "ext/#{ext_name}/clippy.toml")
287
- render_template("ext/src/lib.rs.erb", "ext/#{ext_name}/src/lib.rs")
288
- end
289
-
290
- private def generate_nested_module(parts, content, indent = 0, is_outermost = true)
291
- return content if parts.empty?
292
-
293
- prefix = " " * indent
294
- first, *rest = parts
295
- inner = generate_nested_module(rest, content, indent + 1, false)
296
- inner_prefix = " " * (indent + 1)
297
-
298
- if rest.empty?
299
- # Innermost module - content is a doc comment that goes BEFORE the module
300
- if content.empty?
301
- "#{prefix}module #{first}\n#{prefix}end"
302
- else
303
- "#{prefix}#{content}\n#{prefix}module #{first}\n#{prefix}end"
304
- end
305
- else
306
- # Outer modules get :nodoc: to avoid "missing documentation" warnings
307
- # (they're documented in the main require file or the core gem)
308
- nodoc = is_outermost ? " # :nodoc: Documented in the ratatui_ruby gem." : ""
309
- "#{prefix}module #{first}#{nodoc}\n#{inner}\n#{prefix}end"
310
- end
311
- end
312
-
313
- private def generate_nested_module_with_error(parts, indent = 0)
314
- prefix = " " * indent
315
- if parts.length == 1
316
- # Innermost module with documented Error class
317
- <<~RUBY.chomp
318
- #{prefix}module #{parts.first}
319
- #{prefix} # Base error class for this library.
320
- #{prefix} #
321
- #{prefix} # All library-specific exceptions inherit from this class.
322
- #{prefix} # Catch this to handle any library error generically.
323
- #{prefix} class Error < StandardError; end
324
- #{prefix}end
325
- RUBY
326
- else
327
- first, *rest = parts
328
- inner = generate_nested_module_with_error(rest, indent + 1)
329
- "#{prefix}module #{first}\n#{inner}\n#{prefix}end"
330
- end
331
- end
332
-
333
- private def generate_nested_module_with_version(parts, indent = 0, is_outermost = true)
334
- prefix = " " * indent
335
- if parts.length == 1
336
- # Innermost module with documented VERSION constant
337
- <<~RUBY.chomp
338
- #{prefix}module #{parts.first}
339
- #{prefix} # The version of this gem.
340
- #{prefix} # See https://semver.org/spec/v2.0.0.html
341
- #{prefix} VERSION = "0.0.1.pre.1"
342
- #{prefix}end
343
- RUBY
344
- else
345
- first, *rest = parts
346
- inner = generate_nested_module_with_version(rest, indent + 1, false)
347
- # Outer modules get :nodoc: to avoid "missing documentation" warnings
348
- # (they're documented in the main require file or the core gem)
349
- nodoc = is_outermost ? " # :nodoc: Documented in the ratatui_ruby gem." : ""
350
- "#{prefix}module #{first}#{nodoc}\n#{inner}\n#{prefix}end"
351
- end
352
- end
353
-
354
- private def binding_for_config
355
- module_parts = @config.module_name.split("::")
356
- ext_name = @config.gem_name.tr("-", "_")
357
-
358
- # Create a binding with all config values available to templates
359
- b = binding
360
- b.local_variable_set(:gem_name, @config.gem_name)
361
- b.local_variable_set(:gem_display_name, @config.module_name)
362
- b.local_variable_set(:module_name, @config.module_name)
363
- b.local_variable_set(:require_path, @config.require_path)
364
- b.local_variable_set(:copyright_holder, @config.copyright_holder)
365
- b.local_variable_set(:license, @config.license)
366
- b.local_variable_set(:author, @config.author)
367
- b.local_variable_set(:email, @config.email)
368
- b.local_variable_set(:year, @config.year)
369
- b.local_variable_set(:has_rdoc, true)
370
- b.local_variable_set(:has_rust, @config.has_rust)
371
- b.local_variable_set(:rubies, @config.ruby_versions)
372
- b.local_variable_set(:ext_name, ext_name)
373
- b.local_variable_set(:nested_module, generate_nested_module(module_parts, "# Namespace for library functionality."))
374
- b.local_variable_set(:nested_module_with_version, generate_nested_module_with_version(module_parts))
375
- b.local_variable_set(:update_mode, @update_mode)
376
- b.local_variable_set(:dependencies, [])
377
- b.local_variable_set(:summary, "Part of the RatatuiRuby ecosystem")
378
- b.local_variable_set(:description, "#{@config.module_name} - part of the RatatuiRuby TUI framework ecosystem")
379
- b.local_variable_set(:github_repo, "setdef/#{@config.module_name}")
380
- b.local_variable_set(:project_name, @config.module_name)
381
- b.local_variable_set(:version_file_require, "lib/#{@config.require_path}/version")
382
- b.local_variable_set(:version_file, "lib/#{@config.require_path}/version.rb")
383
- b
384
- end
385
- end
48
+ require_relative "../lib/ratatui_ruby/devtools/scaffold/scaffold_config"
49
+ require_relative "../lib/ratatui_ruby/devtools/scaffold/template_renderer"
386
50
 
387
51
  # Parse command line options
388
52
  config = ScaffoldConfig.new
@@ -533,11 +197,13 @@ else
533
197
  system("git commit --allow-empty -m 'chore: scaffold #{config.gem_name} with ratatui_ruby-devtools'")
534
198
  end
535
199
 
536
- # Create tag and stable branch
537
- system("git tag v0.0.1.pre.1 -m 'Initial scaffold release'")
200
+ # Create tag and stable branch. A gem in initial development still makes
201
+ # normal releases; a prerelease is a candidate users must opt into, so the
202
+ # gem starts at 0.0.0 rather than at a prerelease of 0.0.1.
203
+ system("git tag v0.0.0 -m 'Initial scaffold release'")
538
204
  system("git branch stable")
539
205
  puts " Created branches: trunk (default), stable"
540
- puts " Created tag: v0.0.1.pre.1"
206
+ puts " Created tag: v0.0.0"
541
207
  end
542
208
 
543
209
  puts ""
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ #--
4
+ # SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
5
+ # SPDX-License-Identifier: AGPL-3.0-or-later
6
+ #++
7
+
8
+ # Configuration for scaffold generation.
9
+ #
10
+ # Holds all parameters needed to render templates. Auto-discovers author info
11
+ # from git config if not provided.
12
+ class ScaffoldConfig
13
+ # The gem name to scaffold.
14
+ attr_accessor :gem_name
15
+
16
+ # The copyright holder's name.
17
+ attr_accessor :author
18
+
19
+ # The copyright holder's email.
20
+ attr_accessor :email
21
+
22
+ # The SPDX license identifier.
23
+ attr_accessor :license
24
+
25
+ # The output directory path.
26
+ attr_accessor :output_dir
27
+
28
+ # Whether this gem includes a Rust native extension.
29
+ attr_accessor :has_rust
30
+
31
+ # Creates a new config with defaults from git.
32
+ #
33
+ # Reads author name and email from git config. Sets default license to
34
+ # LGPL-3.0-or-later. Override any accessor after creation.
35
+ def initialize
36
+ @license = "LGPL-3.0-or-later"
37
+ @author = `git config user.name`.strip
38
+ @email = `git config user.email`.strip
39
+ @has_rust = false
40
+ end
41
+
42
+ # The gem name with underscores (for module names).
43
+ def module_name
44
+ gem_name.split("-").map { |s| s.split("_").map(&:capitalize).join }.join("::")
45
+ end
46
+
47
+ # The gem name with slashes (for require paths).
48
+ def require_path
49
+ gem_name.tr("-", "/")
50
+ end
51
+
52
+ # Copyright holder string for SPDX headers.
53
+ def copyright_holder
54
+ "#{author} <#{email}>"
55
+ end
56
+
57
+ # Current year for copyright.
58
+ def year
59
+ Time.now.year
60
+ end
61
+
62
+ # Ruby versions to test in CI. Each names a minor version, so CI tests its
63
+ # newest patch release.
64
+ def ruby_versions
65
+ %w[3.3 3.4 4.0]
66
+ end
67
+ end