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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d2c3a1967dcc9bd66a2c806d2b471b59594d51ae9fec7c2c3eaad9946898932
4
- data.tar.gz: cdd7fcb62b6b0ff1eb17d595aa6a7b76259fdefa7ae5ecd6d6b1306f98e817c1
3
+ metadata.gz: f841ecd6e5821651ad020bd243791971cf7ab5dca138178d90d216e8a58a48ba
4
+ data.tar.gz: acd221dbf9f924e6f81b21569262ea6a8aebbe42d4753356f9750cd3cc0c13a0
5
5
  SHA512:
6
- metadata.gz: 0515bb7d5ea06e80069a937b9899e23bf281366660d718cd707460b0ed63a9955ed4ff531f6e592c9b203ea090bda1ba620d5d80fbb4b758dab3b91f9190dc71
7
- data.tar.gz: 673910f539a627bd4697e1bfd86ee8c8b0f9644d257090fafcad43907bd080af80be71e961340e53be6077251dd8dd302b957fcb8d74a4418be228b3f6fd175c
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
- Add to your `.rubocop.yml`:
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
- plugins:
21
- - rubocop-legion
30
+ # .rubocop.yml
31
+ inherit_gem:
32
+ rubocop-legion: config/core.yml
22
33
  ```
23
34
 
24
- ## Departments
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
- | Department | Cops | Description |
27
- |---|---|---|
28
- | `Legion/HelperMigration` | 6 | Use per-extension helpers, not global singletons |
29
- | `Legion/ConstantSafety` | 4 | Prevent namespace resolution bugs inside `module Legion` |
30
- | `Legion/Singleton` | 1 | Enforce `.instance` on singleton classes |
31
- | `Legion/RescueLogging` | 3 | Every rescue must log or re-raise |
32
- | `Legion/Framework` | 7 | Sequel, Sinatra, Thor, Faraday, and API gotchas |
33
- | `Legion/Extension` | 10 | LEX structural convention enforcement |
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