rubocop-legion 0.1.2 → 0.1.6
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 +4 -4
- data/CHANGELOG.md +34 -0
- data/CLAUDE.md +114 -0
- data/README.md +120 -14
- data/config/base.yml +95 -0
- data/config/core.yml +19 -0
- data/config/default.yml +78 -0
- data/config/lex.yml +18 -0
- data/lib/rubocop/cop/legion/extension/absorber_missing_absorb_method.rb +62 -0
- data/lib/rubocop/cop/legion/extension/absorber_missing_pattern.rb +64 -0
- data/lib/rubocop/cop/legion/extension/actor_enabled_side_effects.rb +56 -0
- data/lib/rubocop/cop/legion/extension/actor_inheritance.rb +65 -0
- data/lib/rubocop/cop/legion/extension/definition_call_mismatched.rb +70 -0
- data/lib/rubocop/cop/legion/extension/every_actor_requires_time.rb +68 -0
- data/lib/rubocop/cop/legion/extension/hook_missing_runner_class.rb +66 -0
- data/lib/rubocop/cop/legion/extension/runner_plural_module.rb +73 -0
- data/lib/rubocop/cop/legion/framework/mutex_nested_sync.rb +54 -0
- data/lib/rubocop/cop/legion/helper_migration/defined_transport_guard.rb +49 -0
- data/lib/rubocop/cop/legion/helper_migration/direct_data.rb +86 -0
- data/lib/rubocop/cop/legion/helper_migration/direct_llm_embed.rb +43 -0
- data/lib/rubocop/cop/legion/helper_migration/require_defined_guard.rb +76 -0
- data/lib/rubocop/legion/version.rb +1 -1
- data/lib/rubocop-legion.rb +13 -0
- data/rubocop-legion.gemspec +2 -0
- metadata +46 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f841ecd6e5821651ad020bd243791971cf7ab5dca138178d90d216e8a58a48ba
|
|
4
|
+
data.tar.gz: acd221dbf9f924e6f81b21569262ea6a8aebbe42d4753356f9750cd3cc0c13a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5933d7f5010bc89b49a95e3d4c0080d752d583fab9a4c11e5fc3a244a0e45938be35eeb6f6851386452d72eca5c135434b6fe1d7dc6cb2a5be163bf7a0213576
|
|
7
|
+
data.tar.gz: 62b31e59d175e345383a02764b3c1c32a7599e70af80e31d8f0685730579c5396d9cdd783e19fb16ca18809e43b33cead79b6ad03f444acc77045fe4c2dd6399
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.6] - 2026-03-29
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Bundled `rubocop-performance` (52 cops) — Ruby performance anti-pattern detection, all enabled by default
|
|
7
|
+
- Bundled `rubocop-thread_safety` (6 cops) — thread safety analysis for concurrent code
|
|
8
|
+
- Tuned ThreadSafety defaults: `NewThread` excludes service/connection files, `ClassInstanceVariable` excludes singletons, `RackMiddlewareInstanceVariable` disabled, `DirChdir` allows block form
|
|
9
|
+
|
|
10
|
+
## [0.1.5] - 2026-03-29
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- New cop `Legion/Framework/MutexNestedSync`: detect nested `synchronize` blocks (deadlock risk)
|
|
14
|
+
- New cop `Legion/Extension/ActorEnabledSideEffects`: flag `enabled?` in actor classes that runs during boot (keep side-effect-free)
|
|
15
|
+
|
|
16
|
+
## [0.1.4] - 2026-03-29
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- New cop `Legion/HelperMigration/DirectData`: use `data_connection`/`local_data_*` helpers instead of `Legion::Data::Connection`/`Legion::Data::Local` (auto-fix)
|
|
20
|
+
- New cop `Legion/HelperMigration/DirectLlmEmbed`: use `llm_embed` helper instead of `Legion::LLM.embed` (auto-fix)
|
|
21
|
+
- New cop `Legion/HelperMigration/RequireDefinedGuard`: remove `if defined?(Legion::...)` from require statements (auto-fix)
|
|
22
|
+
- New cop `Legion/HelperMigration/DefinedTransportGuard`: use `transport_connected?` instead of `defined?(Legion::Transport)`
|
|
23
|
+
- New cop `Legion/Extension/RunnerPluralModule`: enforce `module Runners` (plural), auto-correctable
|
|
24
|
+
- New cop `Legion/Extension/ActorInheritance`: actor must inherit from Every, Once, Poll, Subscription, Loop, or Nothing
|
|
25
|
+
- New cop `Legion/Extension/EveryActorRequiresTime`: Every/Poll actors must call `time` DSL
|
|
26
|
+
- New cop `Legion/Extension/HookMissingRunnerClass`: hook classes must override `runner_class`
|
|
27
|
+
- New cop `Legion/Extension/AbsorberMissingPattern`: absorber classes must call `pattern` DSL
|
|
28
|
+
- New cop `Legion/Extension/AbsorberMissingAbsorbMethod`: absorber classes must define `absorb` method
|
|
29
|
+
- New cop `Legion/Extension/DefinitionCallMismatched`: `definition :name` must have matching `def name`
|
|
30
|
+
|
|
31
|
+
## [0.1.3] - 2026-03-29
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- Shared config profiles: `config/lex.yml` for lex-* gems, `config/core.yml` for legion-* gems
|
|
35
|
+
- Use `inherit_gem: { rubocop-legion: config/lex.yml }` to replace 60-line .rubocop.yml with 2 lines
|
|
36
|
+
|
|
3
37
|
## [0.1.2] - 2026-03-29
|
|
4
38
|
|
|
5
39
|
### Added
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# rubocop-legion
|
|
2
|
+
|
|
3
|
+
**Parent**: `/Users/miverso2/rubymine/legion/CLAUDE.md`
|
|
4
|
+
|
|
5
|
+
## What is This?
|
|
6
|
+
|
|
7
|
+
Custom RuboCop plugin gem for the LegionIO ecosystem. Provides 45 AST-based cops across 6 departments. Uses the new RuboCop Plugin API (1.72+, lint_roller-based) with auto-discovery via gemspec metadata.
|
|
8
|
+
|
|
9
|
+
**GitHub**: https://github.com/LegionIO/rubocop-legion
|
|
10
|
+
**RubyGems**: https://rubygems.org/gems/rubocop-legion
|
|
11
|
+
**License**: MIT
|
|
12
|
+
|
|
13
|
+
## Shared Config Profiles
|
|
14
|
+
|
|
15
|
+
The gem ships shared `.rubocop.yml` profiles so repos don't duplicate config:
|
|
16
|
+
|
|
17
|
+
- `config/base.yml` — all shared settings (AllCops, Layout, Metrics, Style, Naming, Performance, ThreadSafety)
|
|
18
|
+
- `config/lex.yml` — inherits base, adds plugins (rubocop-legion + performance + thread_safety) + `ParameterLists Max: 8`
|
|
19
|
+
- `config/core.yml` — inherits base, adds plugins (rubocop-legion + performance + thread_safety) + `ParameterLists Max: 10, CountKeywordArgs: false`
|
|
20
|
+
|
|
21
|
+
### Bundled Plugins
|
|
22
|
+
|
|
23
|
+
Both profiles load `rubocop-performance` (52 cops) and `rubocop-thread_safety` (6 cops) as runtime dependencies. ThreadSafety defaults tuned for LegionIO: `NewThread` excludes service/connection files, `ClassInstanceVariable` excludes singletons, `RackMiddlewareInstanceVariable` disabled, `DirChdir` allows block form.
|
|
24
|
+
|
|
25
|
+
**LEX repos**: `inherit_gem: { rubocop-legion: config/lex.yml }`
|
|
26
|
+
**Core repos**: `inherit_gem: { rubocop-legion: config/core.yml }`
|
|
27
|
+
|
|
28
|
+
Repo-specific overrides go below the `inherit_gem` directive. Version-locked with the gem — bump gem version = all repos get updated config.
|
|
29
|
+
|
|
30
|
+
## Cop Scoping
|
|
31
|
+
|
|
32
|
+
Cops are scoped by gem type — no per-repo configuration needed:
|
|
33
|
+
|
|
34
|
+
- **Universal** (9 cops): Fire on all LegionIO gems
|
|
35
|
+
- **Library-specific** (6 cops): Fire on all gems but only trigger when using Sequel, Sinatra, Thor, Faraday, or cache
|
|
36
|
+
- **LEX-only** (28 cops): Scoped to `lib/legion/extensions/**/*.rb` via Include directive — never fire on core `legion-*` libraries
|
|
37
|
+
|
|
38
|
+
## Departments and Cops
|
|
39
|
+
|
|
40
|
+
### Universal — 10 cops
|
|
41
|
+
|
|
42
|
+
- **ConstantSafety** (4): `BareDataDefine`, `BareProcess`, `BareJson` (all error, auto-fix) — prefix with `::` inside `module Legion`. `InheritParam` (convention, auto-fix) — pass `false` to `const_defined?`/`const_get`.
|
|
43
|
+
- **RescueLogging** (3): `BareRescue` (warning, auto-fix) — capture with `=> e`. `NoCapture` (convention, no auto-fix) — exception class without capture. `SilentCapture` (warning, no auto-fix) — captured but never logged/re-raised. Skips `_`-prefixed vars. All skip inline rescue modifiers.
|
|
44
|
+
- **Singleton** (1): `UseInstance` (error, auto-fix) — `.instance` not `.new` for configurable singleton classes.
|
|
45
|
+
- **Framework/MutexNestedSync** (1): Nested `synchronize` blocks risk deadlock.
|
|
46
|
+
- **Framework/ModuleFunctionPrivate** (1): `private` after `module_function` resets visibility.
|
|
47
|
+
|
|
48
|
+
### Library-Specific — 6 cops
|
|
49
|
+
|
|
50
|
+
- `EagerSequelModel` — `Sequel::Model(:table)` at require time
|
|
51
|
+
- `SinatraHostAuth` — Sinatra 4.0+ `set :host_authorization`
|
|
52
|
+
- `ThorReservedRun` — Thor 1.5+ reserves `run`
|
|
53
|
+
- `FaradayXmlMiddleware` — Faraday 2.0+ removed `:xml`
|
|
54
|
+
- `CacheTimeCoercion` — Time→String after cache round-trip
|
|
55
|
+
- `ApiStringKeys` — `Legion::JSON.load` returns symbol keys (scoped to `lib/legion/extensions/**/*.rb`)
|
|
56
|
+
|
|
57
|
+
### LEX-Only — 29 cops
|
|
58
|
+
|
|
59
|
+
- **HelperMigration** (11): `DirectLogging`, `OldLoggingMethods`, `DirectJson`, `DirectCache`, `DirectLocalCache`, `DirectCrypt`, `DirectData`, `DirectLlmEmbed`, `RequireDefinedGuard` (all auto-fix) — use per-extension helpers, not global singletons. `LoggingGuard` (no auto-fix) — remove unnecessary `respond_to?(:log_warn)` / `defined?(Legion::Logging)` guards. `DefinedTransportGuard` (no auto-fix) — use `transport_connected?` instead of `defined?(Legion::Transport)`.
|
|
60
|
+
- **Extension** (18): `ActorSingularModule` (auto-fix), `RunnerPluralModule` (auto-fix), `CoreExtendGuard` (auto-fix), `RunnerMustBeModule`, `RunnerIncludeHelpers`, `ActorInheritance`, `EveryActorRequiresTime`, `SelfContainedActorRunnerClass`, `HookMissingRunnerClass`, `AbsorberMissingPattern`, `AbsorberMissingAbsorbMethod`, `DefinitionCallMismatched`, `RunnerReturnHash`, `SettingsKeyMethod` (auto-fix), `SettingsBracketMultiArg` (auto-fix), `LlmAskKwargs`, `ActorEnabledSideEffects`, `DataRequiredWithoutMigrations`.
|
|
61
|
+
|
|
62
|
+
## Architecture
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
rubocop-legion/
|
|
66
|
+
├── lib/
|
|
67
|
+
│ ├── rubocop-legion.rb # Entry point, requires all cops
|
|
68
|
+
│ └── rubocop/
|
|
69
|
+
│ ├── legion.rb # Namespace declarations
|
|
70
|
+
│ ├── legion/
|
|
71
|
+
│ │ ├── version.rb
|
|
72
|
+
│ │ └── plugin.rb # LintRoller::Plugin (auto-discovery)
|
|
73
|
+
│ └── cop/legion/
|
|
74
|
+
│ ├── helper_migration/ # 7 cops (lex-only)
|
|
75
|
+
│ ├── constant_safety/ # 4 cops (universal)
|
|
76
|
+
│ ├── singleton/ # 1 cop (universal)
|
|
77
|
+
│ ├── rescue_logging/ # 3 cops (universal)
|
|
78
|
+
│ ├── framework/ # 7 cops (universal + library-specific)
|
|
79
|
+
│ └── extension/ # 17 cops (lex-only)
|
|
80
|
+
├── config/
|
|
81
|
+
│ └── default.yml # All cop defaults, Include/Exclude scoping
|
|
82
|
+
└── spec/ # mirrors lib/ structure
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Key Implementation Details
|
|
86
|
+
|
|
87
|
+
- Plugin entry point: `RuboCop::Legion::Plugin` (LintRoller-based, registered via gemspec metadata `default_lint_roller_plugin`)
|
|
88
|
+
- All cops inherit from `RuboCop::Cop::Base`
|
|
89
|
+
- Auto-correctable cops use `extend AutoCorrector`
|
|
90
|
+
- AST matching via `def_node_matcher` and `def_node_search`
|
|
91
|
+
- Specs use `RuboCop::RSpec::Support` with `:config` shared context and `expect_offense`/`expect_correction`
|
|
92
|
+
- `BareRescue` and `NoCapture` skip rescue modifiers (inline `rescue`) to avoid syntax corruption
|
|
93
|
+
- `NoCapture` has no auto-correct to prevent correction loop with `Lint/UselessAssignment`
|
|
94
|
+
- `SilentCapture` skips `_`-prefixed variables (Ruby unused convention)
|
|
95
|
+
|
|
96
|
+
## Development
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
bundle install
|
|
100
|
+
bundle exec rspec # 322 specs
|
|
101
|
+
bundle exec rubocop # Self-linting
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Common Per-Repo Overrides
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
# Repos using Faraday JSON middleware (string keys, not Legion::JSON symbol keys)
|
|
108
|
+
Legion/Framework/ApiStringKeys:
|
|
109
|
+
Enabled: false
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
**Maintained By**: Matthew Iverson (@Esity)
|
data/README.md
CHANGED
|
@@ -2,35 +2,141 @@
|
|
|
2
2
|
|
|
3
3
|
LegionIO code quality cops for [RuboCop](https://rubocop.org/).
|
|
4
4
|
|
|
5
|
-
Custom cops for the LegionIO async job engine ecosystem. Enforces helper usage, constant safety, rescue logging, framework conventions, and LEX extension structure.
|
|
5
|
+
Custom cops for the LegionIO async job engine ecosystem. Enforces helper usage, constant safety, rescue logging, framework conventions, and LEX extension structure. Replaces the regex-based `lint-patterns.yml` CI workflow with precise AST-based analysis and auto-correction.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
Add to your Gemfile:
|
|
10
10
|
|
|
11
11
|
```ruby
|
|
12
|
-
gem 'rubocop-legion', require: false, group: :development
|
|
12
|
+
gem 'rubocop-legion', '~> 0.1', require: false, group: :development
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Usage
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
Use a shared config profile that includes the plugin, all standard settings, and cop defaults:
|
|
18
|
+
|
|
19
|
+
**For `lex-*` extension gems:**
|
|
20
|
+
|
|
21
|
+
```yaml
|
|
22
|
+
# .rubocop.yml
|
|
23
|
+
inherit_gem:
|
|
24
|
+
rubocop-legion: config/lex.yml
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**For `legion-*` core library gems:**
|
|
18
28
|
|
|
19
29
|
```yaml
|
|
20
|
-
|
|
21
|
-
|
|
30
|
+
# .rubocop.yml
|
|
31
|
+
inherit_gem:
|
|
32
|
+
rubocop-legion: config/core.yml
|
|
22
33
|
```
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
Add repo-specific overrides below the `inherit_gem` directive. For manual setup without shared config, use `plugins: [rubocop-legion]` directly.
|
|
36
|
+
|
|
37
|
+
Requires RuboCop 1.72+ (Plugin API with lint_roller).
|
|
38
|
+
|
|
39
|
+
### What the shared configs include
|
|
40
|
+
|
|
41
|
+
Both profiles set: `TargetRubyVersion: 3.4`, `NewCops: enable`, `LineLength: 160`, `MethodLength: 50`, `ClassLength: 1500`, `ModuleLength: 1500`, `BlockLength: 40` (spec/gemspec excluded), `AbcSize: 60`, table-aligned hashes, frozen string literals, and disable `Style/Documentation`, `Naming/FileName`, `Naming/PredicateMethod`, and `Gemspec/DevelopmentDependencies`.
|
|
42
|
+
|
|
43
|
+
Both profiles also load **rubocop-performance** (52 cops) and **rubocop-thread_safety** (6 cops) as plugins. Thread safety defaults are tuned for LegionIO's concurrent architecture: `NewThread` excludes service/connection files, `ClassInstanceVariable` excludes singletons, `RackMiddlewareInstanceVariable` is disabled, `DirChdir` allows block form.
|
|
44
|
+
|
|
45
|
+
| Profile | `ParameterLists Max` | `CountKeywordArgs` |
|
|
46
|
+
|---------|---------------------|--------------------|
|
|
47
|
+
| `lex.yml` | 8 | default (true) |
|
|
48
|
+
| `core.yml` | 10 | false |
|
|
49
|
+
|
|
50
|
+
## Cop Scoping
|
|
51
|
+
|
|
52
|
+
Cops are automatically scoped based on where they should apply:
|
|
53
|
+
|
|
54
|
+
- **Universal cops** fire on all LegionIO gems (any code inside `module Legion`)
|
|
55
|
+
- **Library-specific cops** fire on all gems but only trigger when using a specific library (Sequel, Sinatra, Thor, Faraday, etc.)
|
|
56
|
+
- **LEX-only cops** fire only on `lib/legion/extensions/**/*.rb` — they don't apply to core `legion-*` libraries
|
|
57
|
+
|
|
58
|
+
No per-repo configuration needed for scoping. If a cop doesn't apply to your gem type, it won't fire.
|
|
59
|
+
|
|
60
|
+
## Cops
|
|
25
61
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
|
29
|
-
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
62
|
+
### Universal (all LegionIO gems) — 10 cops
|
|
63
|
+
|
|
64
|
+
| Department | Cop | Severity | Auto-fix | Description |
|
|
65
|
+
|---|---|---|---|---|
|
|
66
|
+
| ConstantSafety | `BareDataDefine` | error | yes | Use `::Data.define` inside `module Legion` to avoid `Legion::Data` |
|
|
67
|
+
| ConstantSafety | `BareProcess` | error | yes | Use `::Process` inside `module Legion` to avoid `Legion::Process` |
|
|
68
|
+
| ConstantSafety | `BareJson` | error | yes | Use `::JSON` inside `module Legion` to avoid `Legion::JSON` |
|
|
69
|
+
| ConstantSafety | `InheritParam` | convention | yes | Pass `false` to `const_defined?`/`const_get` on dynamic modules |
|
|
70
|
+
| RescueLogging | `BareRescue` | warning | yes | Bare `rescue` swallows exceptions — capture with `rescue => e` |
|
|
71
|
+
| RescueLogging | `NoCapture` | convention | no | Exception class specified but not captured (`rescue Error` without `=> e`) |
|
|
72
|
+
| RescueLogging | `SilentCapture` | warning | no | Captured exception never logged or re-raised |
|
|
73
|
+
| Singleton | `UseInstance` | error | yes | Use `.instance` instead of `.new` for singleton classes |
|
|
74
|
+
| Framework | `MutexNestedSync` | warning | no | Nested `synchronize` blocks risk deadlock |
|
|
75
|
+
| Framework | `ModuleFunctionPrivate` | convention | no | `private` after `module_function` resets visibility |
|
|
76
|
+
|
|
77
|
+
### Library-Specific (all gems, triggers on library usage) — 6 cops
|
|
78
|
+
|
|
79
|
+
| Department | Cop | Triggers on | Severity | Auto-fix | Description |
|
|
80
|
+
|---|---|---|---|---|---|
|
|
81
|
+
| Framework | `EagerSequelModel` | Sequel | warning | no | `Sequel::Model(:table)` introspects schema at require time |
|
|
82
|
+
| Framework | `SinatraHostAuth` | Sinatra | convention | no | Sinatra 4.0+ requires `set :host_authorization` |
|
|
83
|
+
| Framework | `ThorReservedRun` | Thor | warning | no | Thor 1.5+ reserves `run` — rename or use `map` |
|
|
84
|
+
| Framework | `FaradayXmlMiddleware` | Faraday | error | no | Faraday 2.0+ removed built-in `:xml` middleware |
|
|
85
|
+
| Framework | `CacheTimeCoercion` | cache_get | convention | no | Time objects become Strings after cache round-trip |
|
|
86
|
+
| Framework | `ApiStringKeys` | Legion::JSON.load | warning | yes | `Legion::JSON.load` returns symbol keys — use `body[:key]` |
|
|
87
|
+
|
|
88
|
+
### LEX Extensions Only (`lib/legion/extensions/**/*.rb`) — 29 cops
|
|
89
|
+
|
|
90
|
+
| Department | Cop | Severity | Auto-fix | Description |
|
|
91
|
+
|---|---|---|---|---|
|
|
92
|
+
| HelperMigration | `DirectLogging` | warning | yes | Use `log.method` instead of `Legion::Logging.method` |
|
|
93
|
+
| HelperMigration | `OldLoggingMethods` | warning | yes | Use `log.method` instead of deprecated `log_method` helpers |
|
|
94
|
+
| HelperMigration | `DirectJson` | convention | yes | Use `json_load`/`json_dump` instead of `Legion::JSON` |
|
|
95
|
+
| HelperMigration | `DirectCache` | warning | yes | Use `cache_get`/`cache_set` instead of `Legion::Cache` |
|
|
96
|
+
| HelperMigration | `DirectLocalCache` | warning | yes | Use `local_cache_get`/`local_cache_set` instead of `Legion::Cache::Local` |
|
|
97
|
+
| HelperMigration | `DirectCrypt` | warning | yes | Use `vault_get`/`vault_exist?` instead of `Legion::Crypt` |
|
|
98
|
+
| HelperMigration | `LoggingGuard` | convention | no | Remove unnecessary `respond_to?(:log_warn)` / `defined?(Legion::Logging)` guards |
|
|
99
|
+
| HelperMigration | `DirectData` | convention | yes | Use `data_connection`/`local_data_*` instead of `Legion::Data::Connection`/`Local` |
|
|
100
|
+
| HelperMigration | `DirectLlmEmbed` | convention | yes | Use `llm_embed` instead of `Legion::LLM.embed` |
|
|
101
|
+
| HelperMigration | `RequireDefinedGuard` | convention | yes | Remove `if defined?(Legion::...)` guard from `require` statements |
|
|
102
|
+
| HelperMigration | `DefinedTransportGuard` | convention | no | Use `transport_connected?` instead of `defined?(Legion::Transport)` |
|
|
103
|
+
| Extension | `ActorSingularModule` | error | yes | Use `module Actor` (singular) — framework discovers `Actor`, not `Actors` |
|
|
104
|
+
| Extension | `RunnerPluralModule` | error | yes | Use `module Runners` (plural) — framework discovers `Runners`, not `Runner` |
|
|
105
|
+
| Extension | `CoreExtendGuard` | error | yes | Guard `extend Core` with `const_defined?` for standalone compatibility |
|
|
106
|
+
| Extension | `RunnerMustBeModule` | warning | no | Runners must be modules, not classes |
|
|
107
|
+
| Extension | `RunnerIncludeHelpers` | convention | no | Runner modules need `include Helpers::Lex` or `extend self` |
|
|
108
|
+
| Extension | `ActorInheritance` | error | no | Actor must inherit from Every, Once, Poll, Subscription, Loop, or Nothing |
|
|
109
|
+
| Extension | `EveryActorRequiresTime` | warning | no | Every/Poll actors must call `time` DSL to set the interval |
|
|
110
|
+
| Extension | `SelfContainedActorRunnerClass` | warning | no | Self-contained actors must override `runner_class` |
|
|
111
|
+
| Extension | `HookMissingRunnerClass` | error | no | Hook classes must override `runner_class` |
|
|
112
|
+
| Extension | `AbsorberMissingPattern` | warning | no | Absorber classes must call `pattern` DSL to match events |
|
|
113
|
+
| Extension | `AbsorberMissingAbsorbMethod` | warning | no | Absorber classes must define `absorb` method |
|
|
114
|
+
| Extension | `DefinitionCallMismatched` | error | no | `definition :name` must have matching `def name` method |
|
|
115
|
+
| Extension | `RunnerReturnHash` | convention | no | Runner methods must return a Hash |
|
|
116
|
+
| Extension | `SettingsKeyMethod` | error | yes | `Legion::Settings` has no `key?` — use `!Settings[:key].nil?` |
|
|
117
|
+
| Extension | `SettingsBracketMultiArg` | error | yes | `Settings#[]` takes 1 arg — use `Settings.dig(...)` for nested access |
|
|
118
|
+
| Extension | `LlmAskKwargs` | error | no | `Legion::LLM.ask` only accepts `message:` — no extra kwargs |
|
|
119
|
+
| Extension | `ActorEnabledSideEffects` | convention | no | `enabled?` runs during boot — keep side-effect-free |
|
|
120
|
+
| Extension | `DataRequiredWithoutMigrations` | warning | no | `data_required?` returns true but migrations may be missing |
|
|
121
|
+
|
|
122
|
+
**Total: 45 custom cops** across 6 departments, 19 auto-correctable.
|
|
123
|
+
|
|
124
|
+
### Bundled Plugins
|
|
125
|
+
|
|
126
|
+
Both shared config profiles also load:
|
|
127
|
+
|
|
128
|
+
- **[rubocop-performance](https://github.com/rubocop/rubocop-performance)** — 52 cops for Ruby performance anti-patterns (all enabled)
|
|
129
|
+
- **[rubocop-thread_safety](https://github.com/rubocop/rubocop-thread_safety)** — 6 cops for thread safety analysis (tuned for LegionIO)
|
|
130
|
+
|
|
131
|
+
## Per-Repo Overrides
|
|
132
|
+
|
|
133
|
+
Most repos need no overrides. Common exceptions:
|
|
134
|
+
|
|
135
|
+
```yaml
|
|
136
|
+
# Repos using Faraday JSON middleware (string keys, not Legion::JSON symbol keys)
|
|
137
|
+
Legion/Framework/ApiStringKeys:
|
|
138
|
+
Enabled: false
|
|
139
|
+
```
|
|
34
140
|
|
|
35
141
|
## License
|
|
36
142
|
|
data/config/base.yml
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Shared RuboCop configuration for all LegionIO gems.
|
|
2
|
+
# Do not use directly — inherit via config/lex.yml or config/core.yml.
|
|
3
|
+
|
|
4
|
+
AllCops:
|
|
5
|
+
TargetRubyVersion: 3.4
|
|
6
|
+
NewCops: enable
|
|
7
|
+
SuggestExtensions: false
|
|
8
|
+
|
|
9
|
+
Layout/LineLength:
|
|
10
|
+
Max: 160
|
|
11
|
+
|
|
12
|
+
Layout/SpaceAroundEqualsInParameterDefault:
|
|
13
|
+
EnforcedStyle: space
|
|
14
|
+
|
|
15
|
+
Layout/HashAlignment:
|
|
16
|
+
EnforcedHashRocketStyle: table
|
|
17
|
+
EnforcedColonStyle: table
|
|
18
|
+
|
|
19
|
+
Metrics/MethodLength:
|
|
20
|
+
Max: 50
|
|
21
|
+
|
|
22
|
+
Metrics/ClassLength:
|
|
23
|
+
Max: 1500
|
|
24
|
+
|
|
25
|
+
Metrics/ModuleLength:
|
|
26
|
+
Max: 1500
|
|
27
|
+
|
|
28
|
+
Metrics/BlockLength:
|
|
29
|
+
Max: 40
|
|
30
|
+
Exclude:
|
|
31
|
+
- 'spec/**/*'
|
|
32
|
+
- '*.gemspec'
|
|
33
|
+
|
|
34
|
+
Metrics/AbcSize:
|
|
35
|
+
Max: 60
|
|
36
|
+
|
|
37
|
+
Metrics/CyclomaticComplexity:
|
|
38
|
+
Max: 15
|
|
39
|
+
|
|
40
|
+
Metrics/PerceivedComplexity:
|
|
41
|
+
Max: 17
|
|
42
|
+
|
|
43
|
+
Style/Documentation:
|
|
44
|
+
Enabled: false
|
|
45
|
+
|
|
46
|
+
Style/SymbolArray:
|
|
47
|
+
Enabled: true
|
|
48
|
+
|
|
49
|
+
Style/FrozenStringLiteralComment:
|
|
50
|
+
Enabled: true
|
|
51
|
+
EnforcedStyle: always
|
|
52
|
+
|
|
53
|
+
Naming/FileName:
|
|
54
|
+
Enabled: false
|
|
55
|
+
|
|
56
|
+
Naming/PredicateMethod:
|
|
57
|
+
Enabled: false
|
|
58
|
+
|
|
59
|
+
Gemspec/DevelopmentDependencies:
|
|
60
|
+
Enabled: false
|
|
61
|
+
|
|
62
|
+
# --- Performance ---
|
|
63
|
+
# All Performance cops enabled by default (rubocop-performance).
|
|
64
|
+
# Override per-repo only if needed.
|
|
65
|
+
|
|
66
|
+
Performance:
|
|
67
|
+
Enabled: true
|
|
68
|
+
|
|
69
|
+
# --- Thread Safety ---
|
|
70
|
+
# LegionIO is a concurrent async engine. These cops catch real bugs.
|
|
71
|
+
|
|
72
|
+
ThreadSafety:
|
|
73
|
+
Enabled: true
|
|
74
|
+
|
|
75
|
+
ThreadSafety/NewThread:
|
|
76
|
+
Enabled: true
|
|
77
|
+
Exclude:
|
|
78
|
+
- 'lib/**/service.rb'
|
|
79
|
+
- 'lib/**/service/**/*.rb'
|
|
80
|
+
- 'lib/**/connection.rb'
|
|
81
|
+
- 'lib/**/connection/**/*.rb'
|
|
82
|
+
- 'spec/**/*'
|
|
83
|
+
|
|
84
|
+
ThreadSafety/ClassInstanceVariable:
|
|
85
|
+
Enabled: true
|
|
86
|
+
Exclude:
|
|
87
|
+
- 'lib/**/singleton/**/*.rb'
|
|
88
|
+
- 'spec/**/*'
|
|
89
|
+
|
|
90
|
+
ThreadSafety/RackMiddlewareInstanceVariable:
|
|
91
|
+
Enabled: false
|
|
92
|
+
|
|
93
|
+
ThreadSafety/DirChdir:
|
|
94
|
+
Enabled: true
|
|
95
|
+
AllowCallWithBlock: true
|
data/config/core.yml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Shared RuboCop configuration for legion-* core library gems.
|
|
2
|
+
#
|
|
3
|
+
# Usage in your .rubocop.yml:
|
|
4
|
+
#
|
|
5
|
+
# inherit_gem:
|
|
6
|
+
# rubocop-legion: config/core.yml
|
|
7
|
+
#
|
|
8
|
+
# # Repo-specific overrides below (if any)
|
|
9
|
+
|
|
10
|
+
inherit_from: base.yml
|
|
11
|
+
|
|
12
|
+
plugins:
|
|
13
|
+
- rubocop-legion
|
|
14
|
+
- rubocop-performance
|
|
15
|
+
- rubocop-thread_safety
|
|
16
|
+
|
|
17
|
+
Metrics/ParameterLists:
|
|
18
|
+
Max: 10
|
|
19
|
+
CountKeywordArgs: false
|
data/config/default.yml
CHANGED
|
@@ -49,6 +49,30 @@ Legion/HelperMigration/LoggingGuard:
|
|
|
49
49
|
Severity: convention
|
|
50
50
|
VersionAdded: '0.1.2'
|
|
51
51
|
|
|
52
|
+
Legion/HelperMigration/DirectData:
|
|
53
|
+
Description: 'Use `data_connection`/`local_data_*` helpers instead of `Legion::Data::Connection`/`Legion::Data::Local` methods.'
|
|
54
|
+
Enabled: true
|
|
55
|
+
Severity: convention
|
|
56
|
+
VersionAdded: '0.1.4'
|
|
57
|
+
|
|
58
|
+
Legion/HelperMigration/DirectLlmEmbed:
|
|
59
|
+
Description: 'Use `llm_embed` helper instead of `Legion::LLM.embed`.'
|
|
60
|
+
Enabled: true
|
|
61
|
+
Severity: convention
|
|
62
|
+
VersionAdded: '0.1.4'
|
|
63
|
+
|
|
64
|
+
Legion/HelperMigration/RequireDefinedGuard:
|
|
65
|
+
Description: 'Remove `if defined?(Legion::...)` guard from `require`/`require_relative` statements.'
|
|
66
|
+
Enabled: true
|
|
67
|
+
Severity: convention
|
|
68
|
+
VersionAdded: '0.1.4'
|
|
69
|
+
|
|
70
|
+
Legion/HelperMigration/DefinedTransportGuard:
|
|
71
|
+
Description: 'Use `transport_connected?` instead of `defined?(Legion::Transport)`.'
|
|
72
|
+
Enabled: true
|
|
73
|
+
Severity: convention
|
|
74
|
+
VersionAdded: '0.1.4'
|
|
75
|
+
|
|
52
76
|
# Legion/ConstantSafety — prevent namespace resolution bugs inside module Legion
|
|
53
77
|
|
|
54
78
|
Legion/ConstantSafety:
|
|
@@ -163,6 +187,12 @@ Legion/Framework/CacheTimeCoercion:
|
|
|
163
187
|
Severity: convention
|
|
164
188
|
VersionAdded: '0.1'
|
|
165
189
|
|
|
190
|
+
Legion/Framework/MutexNestedSync:
|
|
191
|
+
Description: 'Nested `synchronize` blocks risk deadlock if the same mutex is re-acquired.'
|
|
192
|
+
Enabled: true
|
|
193
|
+
Severity: warning
|
|
194
|
+
VersionAdded: '0.1.5'
|
|
195
|
+
|
|
166
196
|
Legion/Framework/ApiStringKeys:
|
|
167
197
|
Description: '`Legion::JSON.load` returns symbol keys. Use `body[:key]` not `body["key"]`.'
|
|
168
198
|
Enabled: true
|
|
@@ -241,3 +271,51 @@ Legion/Extension/DataRequiredWithoutMigrations:
|
|
|
241
271
|
Enabled: true
|
|
242
272
|
Severity: warning
|
|
243
273
|
VersionAdded: '0.1'
|
|
274
|
+
|
|
275
|
+
Legion/Extension/RunnerPluralModule:
|
|
276
|
+
Description: 'Use `module Runners` (plural). Framework discovers runners in `Runners`, not `Runner`.'
|
|
277
|
+
Enabled: true
|
|
278
|
+
Severity: error
|
|
279
|
+
VersionAdded: '0.1.4'
|
|
280
|
+
|
|
281
|
+
Legion/Extension/ActorInheritance:
|
|
282
|
+
Description: 'Actor must inherit from Every, Once, Poll, Subscription, Loop, or Nothing.'
|
|
283
|
+
Enabled: true
|
|
284
|
+
Severity: error
|
|
285
|
+
VersionAdded: '0.1.4'
|
|
286
|
+
|
|
287
|
+
Legion/Extension/EveryActorRequiresTime:
|
|
288
|
+
Description: 'Every/Poll actors must call the `time` DSL method to set the interval.'
|
|
289
|
+
Enabled: true
|
|
290
|
+
Severity: warning
|
|
291
|
+
VersionAdded: '0.1.4'
|
|
292
|
+
|
|
293
|
+
Legion/Extension/HookMissingRunnerClass:
|
|
294
|
+
Description: 'Hook classes must override `runner_class` or the framework dispatches to nil.'
|
|
295
|
+
Enabled: true
|
|
296
|
+
Severity: error
|
|
297
|
+
VersionAdded: '0.1.4'
|
|
298
|
+
|
|
299
|
+
Legion/Extension/AbsorberMissingPattern:
|
|
300
|
+
Description: 'Absorber classes must call the `pattern` DSL method to match events.'
|
|
301
|
+
Enabled: true
|
|
302
|
+
Severity: warning
|
|
303
|
+
VersionAdded: '0.1.4'
|
|
304
|
+
|
|
305
|
+
Legion/Extension/AbsorberMissingAbsorbMethod:
|
|
306
|
+
Description: 'Absorber classes must define an `absorb` method to handle matched events.'
|
|
307
|
+
Enabled: true
|
|
308
|
+
Severity: warning
|
|
309
|
+
VersionAdded: '0.1.4'
|
|
310
|
+
|
|
311
|
+
Legion/Extension/ActorEnabledSideEffects:
|
|
312
|
+
Description: '`enabled?` runs during extension loading. Keep it cheap and side-effect-free.'
|
|
313
|
+
Enabled: true
|
|
314
|
+
Severity: convention
|
|
315
|
+
VersionAdded: '0.1.5'
|
|
316
|
+
|
|
317
|
+
Legion/Extension/DefinitionCallMismatched:
|
|
318
|
+
Description: '`definition :name` must have a matching `def name` method in the same module.'
|
|
319
|
+
Enabled: true
|
|
320
|
+
Severity: error
|
|
321
|
+
VersionAdded: '0.1.4'
|
data/config/lex.yml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Shared RuboCop configuration for lex-* extension gems.
|
|
2
|
+
#
|
|
3
|
+
# Usage in your .rubocop.yml:
|
|
4
|
+
#
|
|
5
|
+
# inherit_gem:
|
|
6
|
+
# rubocop-legion: config/lex.yml
|
|
7
|
+
#
|
|
8
|
+
# # Repo-specific overrides below (if any)
|
|
9
|
+
|
|
10
|
+
inherit_from: base.yml
|
|
11
|
+
|
|
12
|
+
plugins:
|
|
13
|
+
- rubocop-legion
|
|
14
|
+
- rubocop-performance
|
|
15
|
+
- rubocop-thread_safety
|
|
16
|
+
|
|
17
|
+
Metrics/ParameterLists:
|
|
18
|
+
Max: 8
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Extension
|
|
7
|
+
# Detects absorber classes inside an `Absorbers` namespace that do not define
|
|
8
|
+
# the `absorb` instance method. The framework calls `absorb` when a matching
|
|
9
|
+
# event arrives — without it, the absorber silently drops events.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad
|
|
13
|
+
# module Absorbers
|
|
14
|
+
# class Foo
|
|
15
|
+
# pattern 'some.event.*'
|
|
16
|
+
# end
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# # good
|
|
20
|
+
# module Absorbers
|
|
21
|
+
# class Foo
|
|
22
|
+
# pattern 'some.event.*'
|
|
23
|
+
#
|
|
24
|
+
# def absorb(event)
|
|
25
|
+
# process(event)
|
|
26
|
+
# end
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
class AbsorberMissingAbsorbMethod < RuboCop::Cop::Base
|
|
30
|
+
MSG = 'Absorber classes must define an `absorb` method to handle matched events.'
|
|
31
|
+
|
|
32
|
+
def on_class(node)
|
|
33
|
+
return unless inside_absorbers_namespace?(node)
|
|
34
|
+
return if defines_absorb?(node)
|
|
35
|
+
|
|
36
|
+
add_offense(node.identifier)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def inside_absorbers_namespace?(node)
|
|
42
|
+
current = node.parent
|
|
43
|
+
while current
|
|
44
|
+
return true if current.module_type? && current.identifier.short_name == :Absorbers
|
|
45
|
+
|
|
46
|
+
current = current.parent
|
|
47
|
+
end
|
|
48
|
+
false
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def defines_absorb?(node)
|
|
52
|
+
return false unless node.body
|
|
53
|
+
|
|
54
|
+
node.body.each_node(:def).any? do |def_node|
|
|
55
|
+
def_node.method_name == :absorb
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|