gempilot 0.2.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.
- checksums.yaml +7 -0
- data/.claude/skills/using-command-kit/SKILL.md +119 -0
- data/.claude/skills/using-command-kit/cli-example.rb +84 -0
- data/.claude/skills/using-command-kit/generator-pattern.rb +62 -0
- data/.rspec +3 -0
- data/.rubocop.yml +281 -0
- data/.ruby-version +1 -0
- data/CLAUDE.md +45 -0
- data/LICENSE.txt +21 -0
- data/README.md +140 -0
- data/Rakefile +44 -0
- data/data/templates/gem/Gemfile.erb +23 -0
- data/data/templates/gem/LICENSE.txt.erb +21 -0
- data/data/templates/gem/README.md.erb +25 -0
- data/data/templates/gem/Rakefile.erb +36 -0
- data/data/templates/gem/bin/console.erb +7 -0
- data/data/templates/gem/bin/setup.erb +5 -0
- data/data/templates/gem/dotfiles/github/workflows/ci.yml.erb +33 -0
- data/data/templates/gem/dotfiles/gitignore +11 -0
- data/data/templates/gem/dotfiles/rubocop.yml.erb +209 -0
- data/data/templates/gem/dotfiles/ruby-version.erb +1 -0
- data/data/templates/gem/exe/gem_name.erb +3 -0
- data/data/templates/gem/gemspec.erb +27 -0
- data/data/templates/gem/lib/gem_name/version.rb.erb +7 -0
- data/data/templates/gem/lib/gem_name.rb.erb +16 -0
- data/data/templates/gem/lib/gem_name_extension.rb.erb +20 -0
- data/data/templates/gem/rspec.erb +3 -0
- data/data/templates/gem/spec/gem_name_spec.rb.erb +5 -0
- data/data/templates/gem/spec/spec_helper.rb.erb +10 -0
- data/data/templates/gem/spec/zeitwerk_spec.rb.erb +5 -0
- data/data/templates/gem/test/gem_name_test.rb.erb +7 -0
- data/data/templates/gem/test/test_helper.rb.erb +7 -0
- data/data/templates/gem/test/zeitwerk_test.rb.erb +9 -0
- data/data/templates/new/.keep +0 -0
- data/data/templates/new/command.rb.erb +15 -0
- data/docs/command_kit_comparison.md +249 -0
- data/docs/command_kit_reference.md +517 -0
- data/docs/plans/2026-02-18-gempilot-add-command.md +718 -0
- data/docs/superpowers/plans/2026-04-01-rubocop-new-config.md +838 -0
- data/docs/superpowers/plans/2026-04-06-dogfood-inflectable.md +659 -0
- data/docs/superpowers/plans/2026-04-06-inflection-tests-and-erb-rename.md +166 -0
- data/docs/superpowers/plans/2026-04-06-integrate-version-tools.md +162 -0
- data/docs/superpowers/plans/2026-04-06-new-readme.md +185 -0
- data/docs/version-management-redesign.md +44 -0
- data/exe/gempilot +12 -0
- data/issues.rec +77 -0
- data/lib/core_ext/string/inflection_methods.rb +68 -0
- data/lib/core_ext/string/refinements/inflectable.rb +15 -0
- data/lib/gempilot/cli/command.rb +17 -0
- data/lib/gempilot/cli/commands/bump.rb +49 -0
- data/lib/gempilot/cli/commands/console.rb +38 -0
- data/lib/gempilot/cli/commands/create.rb +183 -0
- data/lib/gempilot/cli/commands/destroy.rb +136 -0
- data/lib/gempilot/cli/commands/new.rb +226 -0
- data/lib/gempilot/cli/commands/release.rb +40 -0
- data/lib/gempilot/cli/gem_builder.rb +105 -0
- data/lib/gempilot/cli/gem_context.rb +40 -0
- data/lib/gempilot/cli/generator.rb +90 -0
- data/lib/gempilot/cli.rb +19 -0
- data/lib/gempilot/github_release.rb +30 -0
- data/lib/gempilot/project/version.rb +39 -0
- data/lib/gempilot/project.rb +111 -0
- data/lib/gempilot/strict_shell.rb +18 -0
- data/lib/gempilot/version.rb +3 -0
- data/lib/gempilot/version_tag.rb +53 -0
- data/lib/gempilot/version_task.rb +108 -0
- data/lib/gempilot.rb +17 -0
- data/notes.md +31 -0
- metadata +165 -0
data/README.md
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Gempilot
|
|
2
|
+
|
|
3
|
+
A CLI toolkit for creating and managing Ruby gems with modern conventions.
|
|
4
|
+
|
|
5
|
+
Gempilot scaffolds production-ready gems with Zeitwerk autoloading, RuboCop,
|
|
6
|
+
GitHub Actions CI, and integrated version management. It also generates
|
|
7
|
+
classes, modules, and commands within existing gems.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
gem install gempilot
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Requires Ruby >= 3.4.
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
gempilot create my_gem
|
|
21
|
+
cd my_gem
|
|
22
|
+
bundle exec rake
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This creates a fully configured gem with tests, linting, CI, and version
|
|
26
|
+
management — ready to develop.
|
|
27
|
+
|
|
28
|
+
## Commands
|
|
29
|
+
|
|
30
|
+
### `gempilot create`
|
|
31
|
+
|
|
32
|
+
Scaffold a new gem.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
gempilot create my_gem
|
|
36
|
+
gempilot create --test rspec --exe my_gem
|
|
37
|
+
gempilot create --test minitest --no-git my_gem
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Options:
|
|
41
|
+
|
|
42
|
+
| Option | Description | Default |
|
|
43
|
+
|--------|-------------|---------|
|
|
44
|
+
| `--test {minitest\|rspec}` | Test framework | prompted |
|
|
45
|
+
| `--[no-]exe` | Create executable in `exe/` | prompted |
|
|
46
|
+
| `--[no-]git` | Initialize git repo | prompted |
|
|
47
|
+
| `--branch NAME` | Git branch name | `master` |
|
|
48
|
+
| `--summary TEXT` | One-line gem description | prompted |
|
|
49
|
+
| `--author NAME` | Author name | `git config user.name` |
|
|
50
|
+
| `--email EMAIL` | Author email | `git config user.email` |
|
|
51
|
+
| `--ruby-version VER` | Minimum Ruby version | current Ruby |
|
|
52
|
+
|
|
53
|
+
All options are prompted interactively if omitted.
|
|
54
|
+
|
|
55
|
+
### `gempilot new`
|
|
56
|
+
|
|
57
|
+
Generate a class, module, or command in an existing gem. Run from the gem root.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
gempilot new class MyGem::Services::Authentication
|
|
61
|
+
gempilot new module MyGem::Middleware
|
|
62
|
+
gempilot new command deploy
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Creates the source file under `lib/` and a corresponding test file. For
|
|
66
|
+
commands, generates a CommandKit command class in `lib/<gem>/cli/commands/`.
|
|
67
|
+
|
|
68
|
+
### `gempilot destroy`
|
|
69
|
+
|
|
70
|
+
Remove a class, module, or command. Cleans up empty parent directories.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
gempilot destroy class MyGem::Services::Authentication
|
|
74
|
+
gempilot destroy command deploy
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### `gempilot bump`
|
|
78
|
+
|
|
79
|
+
Bump the version in `lib/<gem>/version.rb`.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
gempilot bump # patch (default)
|
|
83
|
+
gempilot bump minor
|
|
84
|
+
gempilot bump major
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### `gempilot release`
|
|
88
|
+
|
|
89
|
+
Delegates to `rake release` to build and push the gem.
|
|
90
|
+
|
|
91
|
+
### `gempilot console`
|
|
92
|
+
|
|
93
|
+
Delegates to `bin/console` for an interactive IRB session with the gem loaded.
|
|
94
|
+
|
|
95
|
+
## Generated Gem Features
|
|
96
|
+
|
|
97
|
+
Every gem scaffolded by `gempilot create` includes:
|
|
98
|
+
|
|
99
|
+
- **Zeitwerk autoloading** with `LOADER` constant and `rake zeitwerk:validate`
|
|
100
|
+
- **Test framework** — Minitest or RSpec, with a Zeitwerk eager-load test
|
|
101
|
+
- **RuboCop** with `rubocop-claude`, `rubocop-performance`, `rubocop-rake`, and
|
|
102
|
+
framework-specific plugins (`rubocop-minitest` or `rubocop-rspec`)
|
|
103
|
+
- **GitHub Actions CI** running tests and RuboCop
|
|
104
|
+
- **Version management** rake tasks (see below)
|
|
105
|
+
- **`bin/console`** and **`bin/setup`** scripts
|
|
106
|
+
- **Gemspec** with `git ls-files` and glob fallback, MFA required for RubyGems
|
|
107
|
+
|
|
108
|
+
### Version Management Tasks
|
|
109
|
+
|
|
110
|
+
Generated gems include rake tasks for the full version lifecycle:
|
|
111
|
+
|
|
112
|
+
| Task | Description |
|
|
113
|
+
|------|-------------|
|
|
114
|
+
| `rake version:current` | Display the current version |
|
|
115
|
+
| `rake version:bump` | Increment the patch version |
|
|
116
|
+
| `rake version:commit` | Commit the version file change |
|
|
117
|
+
| `rake version:tag` | Create a git tag for the version |
|
|
118
|
+
| `rake version:untag` | Delete the version git tag |
|
|
119
|
+
| `rake version:reset` | Reset the last version bump commit |
|
|
120
|
+
| `rake version:revert` | Revert the last version bump commit |
|
|
121
|
+
| `rake version:release` | Bump, commit, and tag (combined) |
|
|
122
|
+
| `rake version:unrelease` | Untag and reset (combined) |
|
|
123
|
+
| `rake version:github:release` | Push and create a GitHub release |
|
|
124
|
+
| `rake version:github:unrelease` | Delete the GitHub release |
|
|
125
|
+
| `rake version:github:list` | List GitHub releases |
|
|
126
|
+
|
|
127
|
+
## Development
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git clone https://github.com/gillisd/gempilot.git
|
|
131
|
+
cd gempilot
|
|
132
|
+
bundle install
|
|
133
|
+
bundle exec rake
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`rake` runs the Minitest suite, RSpec suite, and RuboCop.
|
|
137
|
+
|
|
138
|
+
## License
|
|
139
|
+
|
|
140
|
+
MIT License. See [LICENSE.txt](LICENSE.txt).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
Bundler::GemHelper.install_tasks name: "gempilot"
|
|
3
|
+
|
|
4
|
+
require "minitest/test_task"
|
|
5
|
+
Minitest::TestTask.create
|
|
6
|
+
|
|
7
|
+
require "rspec/core/rake_task"
|
|
8
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
9
|
+
|
|
10
|
+
require_relative 'lib/gempilot'
|
|
11
|
+
|
|
12
|
+
namespace :spec do
|
|
13
|
+
desc "Prints the specification suite in documentation format and exits"
|
|
14
|
+
task :print do
|
|
15
|
+
exec("rspec", "--format", "documentation", "--dry-run")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
namespace :zeitwerk do
|
|
20
|
+
desc "Verify all files follow Zeitwerk naming conventions"
|
|
21
|
+
task :validate do
|
|
22
|
+
ruby "-e", <<~RUBY
|
|
23
|
+
require 'gempilot'
|
|
24
|
+
Gempilot::LOADER.eager_load(force: true)
|
|
25
|
+
puts 'Zeitwerk: All files loaded successfully.'
|
|
26
|
+
RUBY
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
require "rubocop/rake_task"
|
|
31
|
+
RuboCop::RakeTask.new do |t|
|
|
32
|
+
t.patterns = [
|
|
33
|
+
'lib/*.rb',
|
|
34
|
+
'lib/**/*.rb',
|
|
35
|
+
'spec/*.rb',
|
|
36
|
+
'spec/**/*.rb',
|
|
37
|
+
'test/*.rb',
|
|
38
|
+
'test/**/*.rb',
|
|
39
|
+
]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Gempilot::VersionTask.new
|
|
43
|
+
|
|
44
|
+
task default: [:test, :spec, :rubocop]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
source "https://rubygems.org"
|
|
2
|
+
|
|
3
|
+
gemspec
|
|
4
|
+
|
|
5
|
+
gem "gempilot", require: false
|
|
6
|
+
<% if @test_framework == :minitest -%>
|
|
7
|
+
gem "minitest"
|
|
8
|
+
gem "minitest-reporters"
|
|
9
|
+
<% end -%>
|
|
10
|
+
gem "rake"
|
|
11
|
+
<% if @test_framework == :rspec -%>
|
|
12
|
+
gem "rspec", "~> 3.0"
|
|
13
|
+
<% end -%>
|
|
14
|
+
gem "rubocop"
|
|
15
|
+
gem "rubocop-claude"
|
|
16
|
+
<% if @test_framework == :minitest -%>
|
|
17
|
+
gem "rubocop-minitest"
|
|
18
|
+
<% end -%>
|
|
19
|
+
gem "rubocop-performance"
|
|
20
|
+
gem "rubocop-rake"
|
|
21
|
+
<% if @test_framework == :rspec -%>
|
|
22
|
+
gem "rubocop-rspec"
|
|
23
|
+
<% end -%>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) <%= Time.now.year %> <%= @author %>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# <%= @module_name %>
|
|
2
|
+
|
|
3
|
+
<%= @summary %>
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Install the gem and add to the application's Gemfile by executing:
|
|
8
|
+
|
|
9
|
+
$ bundle add <%= @gem_name %>
|
|
10
|
+
|
|
11
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
|
12
|
+
|
|
13
|
+
$ gem install <%= @gem_name %>
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
TODO: Write usage instructions here.
|
|
18
|
+
|
|
19
|
+
## Development
|
|
20
|
+
|
|
21
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests.
|
|
22
|
+
|
|
23
|
+
## License
|
|
24
|
+
|
|
25
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
|
|
3
|
+
<% if @test_framework == :minitest -%>
|
|
4
|
+
require "minitest/test_task"
|
|
5
|
+
<% elsif @test_framework == :rspec -%>
|
|
6
|
+
require "rspec/core/rake_task"
|
|
7
|
+
<% end -%>
|
|
8
|
+
|
|
9
|
+
<% if @test_framework == :minitest -%>
|
|
10
|
+
Minitest::TestTask.create
|
|
11
|
+
<% elsif @test_framework == :rspec -%>
|
|
12
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
13
|
+
<% end -%>
|
|
14
|
+
|
|
15
|
+
require "rubocop/rake_task"
|
|
16
|
+
RuboCop::RakeTask.new
|
|
17
|
+
|
|
18
|
+
require "gempilot/version_task"
|
|
19
|
+
Gempilot::VersionTask.new
|
|
20
|
+
|
|
21
|
+
namespace :zeitwerk do
|
|
22
|
+
desc "Verify all files follow Zeitwerk naming conventions"
|
|
23
|
+
task :validate do
|
|
24
|
+
ruby "-e", <<~RUBY
|
|
25
|
+
require '<%= @require_path %>'
|
|
26
|
+
<%= @module_name %>::LOADER.eager_load(force: true)
|
|
27
|
+
puts 'Zeitwerk: All files loaded successfully.'
|
|
28
|
+
RUBY
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
<% if @test_framework == :minitest -%>
|
|
33
|
+
task default: [:test, :rubocop]
|
|
34
|
+
<% elsif @test_framework == :rspec -%>
|
|
35
|
+
task default: [:spec, :rubocop]
|
|
36
|
+
<% end -%>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
matrix:
|
|
17
|
+
ruby-version: ['<%= @ruby_version %>']
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- name: Set up Ruby
|
|
21
|
+
uses: ruby/setup-ruby@v1
|
|
22
|
+
with:
|
|
23
|
+
ruby-version: ${{ matrix.ruby-version }}
|
|
24
|
+
bundler-cache: true
|
|
25
|
+
<% if @test_framework == :minitest -%>
|
|
26
|
+
- name: Run tests
|
|
27
|
+
run: bundle exec rake test
|
|
28
|
+
<% elsif @test_framework == :rspec -%>
|
|
29
|
+
- name: Run specs
|
|
30
|
+
run: bundle exec rake spec
|
|
31
|
+
<% end -%>
|
|
32
|
+
- name: Run RuboCop
|
|
33
|
+
run: bundle exec rake rubocop
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# ===========================================================================
|
|
2
|
+
# RuboCop Configuration
|
|
3
|
+
#
|
|
4
|
+
# Base: Stock RuboCop defaults
|
|
5
|
+
# AI guardrails: rubocop-claude plugin (all Claude/ cops + stricter metrics)
|
|
6
|
+
# Performance: rubocop-performance (with chain-hostile cops disabled)
|
|
7
|
+
#
|
|
8
|
+
# Philosophy: idiomatic Ruby, pipeline-style chaining, strict for AI agents,
|
|
9
|
+
# readable for humans.
|
|
10
|
+
# ===========================================================================
|
|
11
|
+
|
|
12
|
+
plugins:
|
|
13
|
+
- rubocop-claude
|
|
14
|
+
<% if @test_framework == :minitest -%>
|
|
15
|
+
- rubocop-minitest
|
|
16
|
+
<% elsif @test_framework == :rspec -%>
|
|
17
|
+
- rubocop-rspec
|
|
18
|
+
<% end -%>
|
|
19
|
+
- rubocop-performance
|
|
20
|
+
- rubocop-rake
|
|
21
|
+
|
|
22
|
+
AllCops:
|
|
23
|
+
NewCops: enable
|
|
24
|
+
TargetRubyVersion: <%= minor_version_for @ruby_version %>
|
|
25
|
+
Exclude:
|
|
26
|
+
- bin/*
|
|
27
|
+
- vendor/**/*
|
|
28
|
+
- lib/core_ext/**/*
|
|
29
|
+
- rakelib/project.rb
|
|
30
|
+
- rakelib/project_version.rb
|
|
31
|
+
- rakelib/version_tag.rb
|
|
32
|
+
- rakelib/github_release.rb
|
|
33
|
+
- rakelib/strict_shell.rb
|
|
34
|
+
<% if @hyphenated -%>
|
|
35
|
+
- lib/<%= @gem_name %>.rb
|
|
36
|
+
<% end -%>
|
|
37
|
+
|
|
38
|
+
# ===========================================================================
|
|
39
|
+
# Overrides from stock — personal style preferences
|
|
40
|
+
# ===========================================================================
|
|
41
|
+
|
|
42
|
+
# Double quotes everywhere. One less decision to make.
|
|
43
|
+
Style/StringLiterals:
|
|
44
|
+
EnforcedStyle: double_quotes
|
|
45
|
+
|
|
46
|
+
Style/StringLiteralsInInterpolation:
|
|
47
|
+
EnforcedStyle: double_quotes
|
|
48
|
+
|
|
49
|
+
# Frozen string literal is transitional cruft. Ruby 3.4 has chilled strings,
|
|
50
|
+
# full default freeze is coming in a future Ruby.
|
|
51
|
+
Style/FrozenStringLiteralComment:
|
|
52
|
+
EnforcedStyle: never
|
|
53
|
+
|
|
54
|
+
# Pipeline style. Chaining multi-line blocks is the whole point.
|
|
55
|
+
Style/MultilineBlockChain:
|
|
56
|
+
Enabled: false
|
|
57
|
+
|
|
58
|
+
# Block delimiters are a taste call. Pipeline code uses braces for chaining,
|
|
59
|
+
# do/end for side effects. No cop captures this nuance.
|
|
60
|
+
Style/BlockDelimiters:
|
|
61
|
+
Enabled: false
|
|
62
|
+
|
|
63
|
+
# Write arrays like arrays.
|
|
64
|
+
Style/WordArray:
|
|
65
|
+
Enabled: false
|
|
66
|
+
|
|
67
|
+
Style/SymbolArray:
|
|
68
|
+
Enabled: false
|
|
69
|
+
|
|
70
|
+
# Argument indentation: consistent 2-space indent, not aligned to first arg.
|
|
71
|
+
Layout/FirstArgumentIndentation:
|
|
72
|
+
EnforcedStyle: consistent
|
|
73
|
+
|
|
74
|
+
# Dot-aligned chaining. Dots form a visual column.
|
|
75
|
+
Layout/MultilineMethodCallIndentation:
|
|
76
|
+
EnforcedStyle: aligned
|
|
77
|
+
|
|
78
|
+
# ===========================================================================
|
|
79
|
+
# Overrides from rubocop-claude — loosen where pipeline style conflicts
|
|
80
|
+
# ===========================================================================
|
|
81
|
+
|
|
82
|
+
Claude/NoOverlyDefensiveCode:
|
|
83
|
+
MaxSafeNavigationChain: 2
|
|
84
|
+
|
|
85
|
+
Style/SafeNavigation:
|
|
86
|
+
MaxChainLength: 2
|
|
87
|
+
|
|
88
|
+
# Allow `return a, b` for tuple-style returns.
|
|
89
|
+
Style/RedundantReturn:
|
|
90
|
+
AllowMultipleReturnValues: true
|
|
91
|
+
|
|
92
|
+
# ===========================================================================
|
|
93
|
+
# Overrides from rubocop-performance — disable chain-hostile cops
|
|
94
|
+
# ===========================================================================
|
|
95
|
+
|
|
96
|
+
Performance/ChainArrayAllocation:
|
|
97
|
+
Enabled: false
|
|
98
|
+
|
|
99
|
+
Performance/MapMethodChain:
|
|
100
|
+
Enabled: false
|
|
101
|
+
|
|
102
|
+
# ===========================================================================
|
|
103
|
+
# Additional tightening — not set by stock or rubocop-claude
|
|
104
|
+
# ===========================================================================
|
|
105
|
+
|
|
106
|
+
# Short blocks push toward small chained steps instead of fat lambdas.
|
|
107
|
+
Metrics/BlockLength:
|
|
108
|
+
Max: 8
|
|
109
|
+
CountAsOne:
|
|
110
|
+
- array
|
|
111
|
+
- hash
|
|
112
|
+
- heredoc
|
|
113
|
+
- method_call
|
|
114
|
+
AllowedMethods:
|
|
115
|
+
- command
|
|
116
|
+
<% if @test_framework == :minitest -%>
|
|
117
|
+
- test
|
|
118
|
+
<% elsif @test_framework == :rspec -%>
|
|
119
|
+
- describe
|
|
120
|
+
- context
|
|
121
|
+
- shared_examples
|
|
122
|
+
- shared_examples_for
|
|
123
|
+
- shared_context
|
|
124
|
+
<% end -%>
|
|
125
|
+
Exclude:
|
|
126
|
+
- "<%= @gem_name %>.gemspec"
|
|
127
|
+
- "rakelib/**/*"
|
|
128
|
+
|
|
129
|
+
# Gemspec and rake files use patterns that trigger Claude cops legitimately.
|
|
130
|
+
Claude/NoFancyUnicode:
|
|
131
|
+
Exclude:
|
|
132
|
+
- "<%= @gem_name %>.gemspec"
|
|
133
|
+
|
|
134
|
+
Claude/MysteryRegex:
|
|
135
|
+
Exclude:
|
|
136
|
+
- "rakelib/**/*"
|
|
137
|
+
|
|
138
|
+
# Zeitwerk loaders are mutable by design — only flag literal mutable values.
|
|
139
|
+
Style/MutableConstant:
|
|
140
|
+
EnforcedStyle: literals
|
|
141
|
+
|
|
142
|
+
<% if @test_framework == :minitest -%>
|
|
143
|
+
# Minitest tests can legitimately need many assertions per method
|
|
144
|
+
# when verifying multi-step operations.
|
|
145
|
+
Minitest/MultipleAssertions:
|
|
146
|
+
Max: 10
|
|
147
|
+
<% elsif @test_framework == :rspec -%>
|
|
148
|
+
# Shared test contexts legitimately define many helpers.
|
|
149
|
+
RSpec/MultipleMemoizedHelpers:
|
|
150
|
+
Max: 10
|
|
151
|
+
<% end -%>
|
|
152
|
+
|
|
153
|
+
# Anonymous forwarding (*, **, &) breaks TruffleRuby, JRuby, and
|
|
154
|
+
# Ruby < 3.2. Named args are explicit and portable.
|
|
155
|
+
Style/ArgumentsForwarding:
|
|
156
|
+
Enabled: false
|
|
157
|
+
|
|
158
|
+
# Explicit begin/rescue/end is clearer than implicit method-body rescue.
|
|
159
|
+
Style/RedundantBegin:
|
|
160
|
+
Enabled: false
|
|
161
|
+
|
|
162
|
+
# Compact class names are fine for small files and tests.
|
|
163
|
+
Style/ClassAndModuleChildren:
|
|
164
|
+
Enabled: false
|
|
165
|
+
|
|
166
|
+
# Classes get rdoc.
|
|
167
|
+
Style/Documentation:
|
|
168
|
+
Enabled: true
|
|
169
|
+
Exclude:
|
|
170
|
+
<% if @test_framework == :minitest -%>
|
|
171
|
+
- "test/**/*"
|
|
172
|
+
<% elsif @test_framework == :rspec -%>
|
|
173
|
+
- "spec/**/*"
|
|
174
|
+
<% end -%>
|
|
175
|
+
|
|
176
|
+
# Trailing commas in multiline literals and arguments.
|
|
177
|
+
Style/TrailingCommaInArrayLiteral:
|
|
178
|
+
EnforcedStyleForMultiline: comma
|
|
179
|
+
|
|
180
|
+
Style/TrailingCommaInHashLiteral:
|
|
181
|
+
EnforcedStyleForMultiline: comma
|
|
182
|
+
|
|
183
|
+
Style/TrailingCommaInArguments:
|
|
184
|
+
EnforcedStyleForMultiline: comma
|
|
185
|
+
|
|
186
|
+
<% if @test_framework == :rspec -%>
|
|
187
|
+
# ===========================================================================
|
|
188
|
+
# RSpec — rubocop-rspec overrides
|
|
189
|
+
# ===========================================================================
|
|
190
|
+
|
|
191
|
+
# Not every describe block wraps a class.
|
|
192
|
+
RSpec/DescribeClass:
|
|
193
|
+
Enabled: false
|
|
194
|
+
|
|
195
|
+
# Subject placement is a readability call, not a rule.
|
|
196
|
+
RSpec/LeadingSubject:
|
|
197
|
+
Enabled: false
|
|
198
|
+
|
|
199
|
+
# Block style for expect { }.to change { } reads like a sentence.
|
|
200
|
+
RSpec/ExpectChange:
|
|
201
|
+
EnforcedStyle: block
|
|
202
|
+
|
|
203
|
+
RSpec/NamedSubject:
|
|
204
|
+
Enabled: false
|
|
205
|
+
|
|
206
|
+
# have_file_content matcher accepts a filename string in expect()
|
|
207
|
+
RSpec/ExpectActual:
|
|
208
|
+
Enabled: false
|
|
209
|
+
<% end -%>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<%= @ruby_version %>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require_relative "lib/<%= @require_path %>/version"
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = "<%= @gem_name %>"
|
|
5
|
+
spec.version = <%= @module_name %>::VERSION
|
|
6
|
+
spec.authors = ["<%= @author %>"]
|
|
7
|
+
spec.email = ["<%= @email %>"]
|
|
8
|
+
spec.summary = "<%= @summary %>"
|
|
9
|
+
spec.license = "MIT"
|
|
10
|
+
spec.required_ruby_version = ">= <%= @ruby_version %>"
|
|
11
|
+
|
|
12
|
+
gemspec_file = File.basename(__FILE__)
|
|
13
|
+
files = IO.popen(["git", "ls-files", "-z"], chdir: __dir__, err: IO::NULL) { |ls|
|
|
14
|
+
ls.readlines("\x0", chomp: true).reject do |f|
|
|
15
|
+
(f == gemspec_file) ||
|
|
16
|
+
f.start_with?("bin/", "test/", "spec/", "features/", ".git", "Gemfile")
|
|
17
|
+
end
|
|
18
|
+
}
|
|
19
|
+
files = Dir.glob("{lib,exe}/**/*").push("README.md", "LICENSE.txt", "Rakefile") if files.empty?
|
|
20
|
+
spec.files = files
|
|
21
|
+
spec.bindir = "exe"
|
|
22
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
23
|
+
spec.require_paths = ["lib"]
|
|
24
|
+
|
|
25
|
+
spec.add_dependency "zeitwerk"
|
|
26
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
27
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<% if @hyphenated -%>
|
|
2
|
+
require "<%= @require_path %>"
|
|
3
|
+
<% else -%>
|
|
4
|
+
require "zeitwerk"
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
# Top-level namespace for the <%= @gem_name %> gem.
|
|
8
|
+
module <%= @module_name %>
|
|
9
|
+
LOADER = Zeitwerk::Loader.for_gem
|
|
10
|
+
LOADER.setup
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
# Base error class for <%= @gem_name %>.
|
|
14
|
+
class Error < StandardError; end
|
|
15
|
+
end
|
|
16
|
+
<% end -%>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "zeitwerk"
|
|
2
|
+
|
|
3
|
+
<% @module_parts.each_with_index do |part, i| -%>
|
|
4
|
+
<%= " " * i %>##
|
|
5
|
+
<% if i == 0 -%>
|
|
6
|
+
<%= " " * i %># Top-level namespace for the <%= @gem_name %> gem.
|
|
7
|
+
<% else -%>
|
|
8
|
+
<%= " " * i %># Extension namespace for <%= @module_parts[0..i].join("::") %>.
|
|
9
|
+
<% end -%>
|
|
10
|
+
<%= " " * i %>module <%= part %>
|
|
11
|
+
<% end -%>
|
|
12
|
+
<%= " " * @module_parts.size %>LOADER = Zeitwerk::Loader.for_gem_extension(<%= @base_module %>)
|
|
13
|
+
<%= " " * @module_parts.size %>LOADER.setup
|
|
14
|
+
|
|
15
|
+
<%= " " * @module_parts.size %>##
|
|
16
|
+
<%= " " * @module_parts.size %># Base error class for <%= @gem_name %>.
|
|
17
|
+
<%= " " * @module_parts.size %>class Error < StandardError; end
|
|
18
|
+
<% @module_parts.size.times do |i| -%>
|
|
19
|
+
<%= " " * (@module_parts.size - 1 - i) %>end
|
|
20
|
+
<% end -%>
|