rubocop-legion 0.1.3 → 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 +28 -0
- data/CLAUDE.md +27 -9
- data/README.md +50 -6
- data/config/base.yml +35 -0
- data/config/core.yml +2 -0
- data/config/default.yml +78 -0
- data/config/lex.yml +2 -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 +42 -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,33 @@
|
|
|
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
|
+
|
|
3
31
|
## [0.1.3] - 2026-03-29
|
|
4
32
|
|
|
5
33
|
### Added
|
data/CLAUDE.md
CHANGED
|
@@ -4,27 +4,45 @@
|
|
|
4
4
|
|
|
5
5
|
## What is This?
|
|
6
6
|
|
|
7
|
-
Custom RuboCop plugin gem for the LegionIO ecosystem. Provides
|
|
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
8
|
|
|
9
9
|
**GitHub**: https://github.com/LegionIO/rubocop-legion
|
|
10
10
|
**RubyGems**: https://rubygems.org/gems/rubocop-legion
|
|
11
11
|
**License**: MIT
|
|
12
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
|
+
|
|
13
30
|
## Cop Scoping
|
|
14
31
|
|
|
15
32
|
Cops are scoped by gem type — no per-repo configuration needed:
|
|
16
33
|
|
|
17
34
|
- **Universal** (9 cops): Fire on all LegionIO gems
|
|
18
35
|
- **Library-specific** (6 cops): Fire on all gems but only trigger when using Sequel, Sinatra, Thor, Faraday, or cache
|
|
19
|
-
- **LEX-only** (
|
|
36
|
+
- **LEX-only** (28 cops): Scoped to `lib/legion/extensions/**/*.rb` via Include directive — never fire on core `legion-*` libraries
|
|
20
37
|
|
|
21
38
|
## Departments and Cops
|
|
22
39
|
|
|
23
|
-
### Universal —
|
|
40
|
+
### Universal — 10 cops
|
|
24
41
|
|
|
25
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`.
|
|
26
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.
|
|
27
44
|
- **Singleton** (1): `UseInstance` (error, auto-fix) — `.instance` not `.new` for configurable singleton classes.
|
|
45
|
+
- **Framework/MutexNestedSync** (1): Nested `synchronize` blocks risk deadlock.
|
|
28
46
|
- **Framework/ModuleFunctionPrivate** (1): `private` after `module_function` resets visibility.
|
|
29
47
|
|
|
30
48
|
### Library-Specific — 6 cops
|
|
@@ -36,10 +54,10 @@ Cops are scoped by gem type — no per-repo configuration needed:
|
|
|
36
54
|
- `CacheTimeCoercion` — Time→String after cache round-trip
|
|
37
55
|
- `ApiStringKeys` — `Legion::JSON.load` returns symbol keys (scoped to `lib/legion/extensions/**/*.rb`)
|
|
38
56
|
|
|
39
|
-
### LEX-Only —
|
|
57
|
+
### LEX-Only — 29 cops
|
|
40
58
|
|
|
41
|
-
- **HelperMigration** (
|
|
42
|
-
- **Extension** (
|
|
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`.
|
|
43
61
|
|
|
44
62
|
## Architecture
|
|
45
63
|
|
|
@@ -58,10 +76,10 @@ rubocop-legion/
|
|
|
58
76
|
│ ├── singleton/ # 1 cop (universal)
|
|
59
77
|
│ ├── rescue_logging/ # 3 cops (universal)
|
|
60
78
|
│ ├── framework/ # 7 cops (universal + library-specific)
|
|
61
|
-
│ └── extension/ #
|
|
79
|
+
│ └── extension/ # 17 cops (lex-only)
|
|
62
80
|
├── config/
|
|
63
81
|
│ └── default.yml # All cop defaults, Include/Exclude scoping
|
|
64
|
-
└── spec/ #
|
|
82
|
+
└── spec/ # mirrors lib/ structure
|
|
65
83
|
```
|
|
66
84
|
|
|
67
85
|
## Key Implementation Details
|
|
@@ -79,7 +97,7 @@ rubocop-legion/
|
|
|
79
97
|
|
|
80
98
|
```bash
|
|
81
99
|
bundle install
|
|
82
|
-
bundle exec rspec #
|
|
100
|
+
bundle exec rspec # 322 specs
|
|
83
101
|
bundle exec rubocop # Self-linting
|
|
84
102
|
```
|
|
85
103
|
|
data/README.md
CHANGED
|
@@ -14,15 +14,39 @@ gem 'rubocop-legion', '~> 0.1', require: false, group: :development
|
|
|
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:**
|
|
18
20
|
|
|
19
21
|
```yaml
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
# .rubocop.yml
|
|
23
|
+
inherit_gem:
|
|
24
|
+
rubocop-legion: config/lex.yml
|
|
22
25
|
```
|
|
23
26
|
|
|
27
|
+
**For `legion-*` core library gems:**
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
# .rubocop.yml
|
|
31
|
+
inherit_gem:
|
|
32
|
+
rubocop-legion: config/core.yml
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Add repo-specific overrides below the `inherit_gem` directive. For manual setup without shared config, use `plugins: [rubocop-legion]` directly.
|
|
36
|
+
|
|
24
37
|
Requires RuboCop 1.72+ (Plugin API with lint_roller).
|
|
25
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
|
+
|
|
26
50
|
## Cop Scoping
|
|
27
51
|
|
|
28
52
|
Cops are automatically scoped based on where they should apply:
|
|
@@ -35,7 +59,7 @@ No per-repo configuration needed for scoping. If a cop doesn't apply to your gem
|
|
|
35
59
|
|
|
36
60
|
## Cops
|
|
37
61
|
|
|
38
|
-
### Universal (all LegionIO gems) —
|
|
62
|
+
### Universal (all LegionIO gems) — 10 cops
|
|
39
63
|
|
|
40
64
|
| Department | Cop | Severity | Auto-fix | Description |
|
|
41
65
|
|---|---|---|---|---|
|
|
@@ -47,6 +71,7 @@ No per-repo configuration needed for scoping. If a cop doesn't apply to your gem
|
|
|
47
71
|
| RescueLogging | `NoCapture` | convention | no | Exception class specified but not captured (`rescue Error` without `=> e`) |
|
|
48
72
|
| RescueLogging | `SilentCapture` | warning | no | Captured exception never logged or re-raised |
|
|
49
73
|
| Singleton | `UseInstance` | error | yes | Use `.instance` instead of `.new` for singleton classes |
|
|
74
|
+
| Framework | `MutexNestedSync` | warning | no | Nested `synchronize` blocks risk deadlock |
|
|
50
75
|
| Framework | `ModuleFunctionPrivate` | convention | no | `private` after `module_function` resets visibility |
|
|
51
76
|
|
|
52
77
|
### Library-Specific (all gems, triggers on library usage) — 6 cops
|
|
@@ -60,7 +85,7 @@ No per-repo configuration needed for scoping. If a cop doesn't apply to your gem
|
|
|
60
85
|
| Framework | `CacheTimeCoercion` | cache_get | convention | no | Time objects become Strings after cache round-trip |
|
|
61
86
|
| Framework | `ApiStringKeys` | Legion::JSON.load | warning | yes | `Legion::JSON.load` returns symbol keys — use `body[:key]` |
|
|
62
87
|
|
|
63
|
-
### LEX Extensions Only (`lib/legion/extensions/**/*.rb`) —
|
|
88
|
+
### LEX Extensions Only (`lib/legion/extensions/**/*.rb`) — 29 cops
|
|
64
89
|
|
|
65
90
|
| Department | Cop | Severity | Auto-fix | Description |
|
|
66
91
|
|---|---|---|---|---|
|
|
@@ -71,18 +96,37 @@ No per-repo configuration needed for scoping. If a cop doesn't apply to your gem
|
|
|
71
96
|
| HelperMigration | `DirectLocalCache` | warning | yes | Use `local_cache_get`/`local_cache_set` instead of `Legion::Cache::Local` |
|
|
72
97
|
| HelperMigration | `DirectCrypt` | warning | yes | Use `vault_get`/`vault_exist?` instead of `Legion::Crypt` |
|
|
73
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)` |
|
|
74
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` |
|
|
75
105
|
| Extension | `CoreExtendGuard` | error | yes | Guard `extend Core` with `const_defined?` for standalone compatibility |
|
|
76
106
|
| Extension | `RunnerMustBeModule` | warning | no | Runners must be modules, not classes |
|
|
77
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 |
|
|
78
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 |
|
|
79
115
|
| Extension | `RunnerReturnHash` | convention | no | Runner methods must return a Hash |
|
|
80
116
|
| Extension | `SettingsKeyMethod` | error | yes | `Legion::Settings` has no `key?` — use `!Settings[:key].nil?` |
|
|
81
117
|
| Extension | `SettingsBracketMultiArg` | error | yes | `Settings#[]` takes 1 arg — use `Settings.dig(...)` for nested access |
|
|
82
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 |
|
|
83
120
|
| Extension | `DataRequiredWithoutMigrations` | warning | no | `data_required?` returns true but migrations may be missing |
|
|
84
121
|
|
|
85
|
-
**Total:
|
|
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)
|
|
86
130
|
|
|
87
131
|
## Per-Repo Overrides
|
|
88
132
|
|
data/config/base.yml
CHANGED
|
@@ -58,3 +58,38 @@ Naming/PredicateMethod:
|
|
|
58
58
|
|
|
59
59
|
Gemspec/DevelopmentDependencies:
|
|
60
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
CHANGED
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
CHANGED
|
@@ -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
|
|
@@ -0,0 +1,64 @@
|
|
|
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 call
|
|
8
|
+
# the `pattern` DSL method. Without `pattern`, the absorber will not match
|
|
9
|
+
# any events and will be silently inactive.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad
|
|
13
|
+
# module Absorbers
|
|
14
|
+
# class Foo
|
|
15
|
+
# def absorb(event)
|
|
16
|
+
# process(event)
|
|
17
|
+
# end
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# # good
|
|
22
|
+
# module Absorbers
|
|
23
|
+
# class Foo
|
|
24
|
+
# pattern 'some.event.*'
|
|
25
|
+
#
|
|
26
|
+
# def absorb(event)
|
|
27
|
+
# process(event)
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
class AbsorberMissingPattern < RuboCop::Cop::Base
|
|
32
|
+
MSG = 'Absorber classes must call the `pattern` DSL method to match events.'
|
|
33
|
+
|
|
34
|
+
def on_class(node)
|
|
35
|
+
return unless inside_absorbers_namespace?(node)
|
|
36
|
+
return if calls_pattern?(node)
|
|
37
|
+
|
|
38
|
+
add_offense(node.identifier)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def inside_absorbers_namespace?(node)
|
|
44
|
+
current = node.parent
|
|
45
|
+
while current
|
|
46
|
+
return true if current.module_type? && current.identifier.short_name == :Absorbers
|
|
47
|
+
|
|
48
|
+
current = current.parent
|
|
49
|
+
end
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def calls_pattern?(node)
|
|
54
|
+
return false unless node.body
|
|
55
|
+
|
|
56
|
+
node.body.each_node(:send).any? do |send_node|
|
|
57
|
+
send_node.method_name == :pattern && send_node.receiver.nil?
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Extension
|
|
7
|
+
# Detects `def enabled?` methods inside actor classes. The `enabled?`
|
|
8
|
+
# method runs during extension loading before `delay` is honoured,
|
|
9
|
+
# so it must be cheap and side-effect-free (no network calls, mutex
|
|
10
|
+
# locks, or I/O).
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# # bad — network call during boot
|
|
14
|
+
# module Actor
|
|
15
|
+
# class Check < Every
|
|
16
|
+
# def enabled?
|
|
17
|
+
# Legion::Transport.connected?
|
|
18
|
+
# end
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# # good — cheap Settings lookup
|
|
23
|
+
# module Actor
|
|
24
|
+
# class Check < Every
|
|
25
|
+
# def enabled?
|
|
26
|
+
# !Legion::Settings[:check].nil?
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
# end
|
|
30
|
+
class ActorEnabledSideEffects < RuboCop::Cop::Base
|
|
31
|
+
MSG = '`enabled?` runs during extension loading, before `delay`. ' \
|
|
32
|
+
'Keep it cheap and side-effect-free (no network calls, mutex locks, or I/O).'
|
|
33
|
+
|
|
34
|
+
def on_def(node)
|
|
35
|
+
return unless node.method_name == :enabled?
|
|
36
|
+
return unless inside_actor_namespace?(node)
|
|
37
|
+
|
|
38
|
+
add_offense(node)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def inside_actor_namespace?(node)
|
|
44
|
+
current = node.parent
|
|
45
|
+
while current
|
|
46
|
+
return true if current.module_type? && current.identifier.short_name == :Actor
|
|
47
|
+
|
|
48
|
+
current = current.parent
|
|
49
|
+
end
|
|
50
|
+
false
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Extension
|
|
7
|
+
# Detects actor classes inside an `Actor` namespace that do not inherit from
|
|
8
|
+
# a recognized LEX actor base class. Every actor must inherit from one of:
|
|
9
|
+
# `Every`, `Once`, `Poll`, `Subscription`, `Loop`, or `Nothing`.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad
|
|
13
|
+
# module Actor
|
|
14
|
+
# class Foo
|
|
15
|
+
# end
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# # bad
|
|
19
|
+
# module Actor
|
|
20
|
+
# class Foo < SomeOtherBase
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# # good
|
|
25
|
+
# module Actor
|
|
26
|
+
# class Foo < Legion::Extensions::Actors::Every
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
class ActorInheritance < RuboCop::Cop::Base
|
|
30
|
+
MSG = 'Actor must inherit from a recognized base: Every, Once, Poll, Subscription, Loop, or Nothing.'
|
|
31
|
+
|
|
32
|
+
RECOGNIZED_BASES = %i[Every Once Poll Subscription Loop Nothing].to_set.freeze
|
|
33
|
+
|
|
34
|
+
def on_class(node)
|
|
35
|
+
return unless inside_actor_namespace?(node)
|
|
36
|
+
|
|
37
|
+
superclass = node.parent_class
|
|
38
|
+
return if superclass && recognized_base?(superclass)
|
|
39
|
+
|
|
40
|
+
add_offense(node.identifier)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def inside_actor_namespace?(node)
|
|
46
|
+
current = node.parent
|
|
47
|
+
while current
|
|
48
|
+
return true if current.module_type? && current.identifier.short_name == :Actor
|
|
49
|
+
|
|
50
|
+
current = current.parent
|
|
51
|
+
end
|
|
52
|
+
false
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def recognized_base?(superclass_node)
|
|
56
|
+
# Handle both `< Every` and `< Legion::Extensions::Actors::Every`
|
|
57
|
+
leaf = superclass_node
|
|
58
|
+
leaf = leaf.children.last while leaf.const_type? && leaf.children.last.is_a?(Symbol) == false
|
|
59
|
+
RECOGNIZED_BASES.include?(leaf.children.last)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|