rubocop-legion 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.rspec +3 -0
- data/.rubocop.yml +22 -0
- data/CHANGELOG.md +13 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +37 -0
- data/Rakefile +9 -0
- data/config/default.yml +233 -0
- data/lib/rubocop/cop/legion/constant_safety/bare_data_define.rb +55 -0
- data/lib/rubocop/cop/legion/constant_safety/bare_json.rb +55 -0
- data/lib/rubocop/cop/legion/constant_safety/bare_process.rb +59 -0
- data/lib/rubocop/cop/legion/constant_safety/inherit_param.rb +41 -0
- data/lib/rubocop/cop/legion/extension/actor_singular_module.rb +73 -0
- data/lib/rubocop/cop/legion/extension/core_extend_guard.rb +46 -0
- data/lib/rubocop/cop/legion/extension/data_required_without_migrations.rb +37 -0
- data/lib/rubocop/cop/legion/extension/llm_ask_kwargs.rb +47 -0
- data/lib/rubocop/cop/legion/extension/runner_include_helpers.rb +114 -0
- data/lib/rubocop/cop/legion/extension/runner_must_be_module.rb +47 -0
- data/lib/rubocop/cop/legion/extension/runner_return_hash.rb +58 -0
- data/lib/rubocop/cop/legion/extension/self_contained_actor_runner_class.rb +76 -0
- data/lib/rubocop/cop/legion/extension/settings_bracket_multi_arg.rb +55 -0
- data/lib/rubocop/cop/legion/extension/settings_key_method.rb +59 -0
- data/lib/rubocop/cop/legion/framework/api_string_keys.rb +67 -0
- data/lib/rubocop/cop/legion/framework/cache_time_coercion.rb +36 -0
- data/lib/rubocop/cop/legion/framework/eager_sequel_model.rb +49 -0
- data/lib/rubocop/cop/legion/framework/faraday_xml_middleware.rb +37 -0
- data/lib/rubocop/cop/legion/framework/module_function_private.rb +67 -0
- data/lib/rubocop/cop/legion/framework/sinatra_host_auth.rb +64 -0
- data/lib/rubocop/cop/legion/framework/thor_reserved_run.rb +60 -0
- data/lib/rubocop/cop/legion/helper_migration/direct_cache.rb +55 -0
- data/lib/rubocop/cop/legion/helper_migration/direct_crypt.rb +52 -0
- data/lib/rubocop/cop/legion/helper_migration/direct_json.rb +52 -0
- data/lib/rubocop/cop/legion/helper_migration/direct_local_cache.rb +52 -0
- data/lib/rubocop/cop/legion/helper_migration/direct_logging.rb +46 -0
- data/lib/rubocop/cop/legion/helper_migration/old_logging_methods.rb +55 -0
- data/lib/rubocop/cop/legion/rescue_logging/bare_rescue.rb +41 -0
- data/lib/rubocop/cop/legion/rescue_logging/no_capture.rb +46 -0
- data/lib/rubocop/cop/legion/rescue_logging/silent_capture.rb +68 -0
- data/lib/rubocop/cop/legion/singleton/use_instance.rb +47 -0
- data/lib/rubocop/legion/plugin.rb +32 -0
- data/lib/rubocop/legion/version.rb +7 -0
- data/lib/rubocop/legion.rb +17 -0
- data/lib/rubocop-legion.rb +50 -0
- data/rubocop-legion.gemspec +39 -0
- metadata +149 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 95a23a8136fd63ac2b841d9a8812c04f261854fac9e752f40e9817f3e8288fc0
|
|
4
|
+
data.tar.gz: 4512d622e170d83a4fb5d1911d6f91d12130157bdd4adbbd422d8aca5e401649
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: febd09cfcb352235365fead149fc2679fec7a4f7c2836ca9f85c06a9e2fc25a3bdb4a6e3914ef79cfc3f636acba681c9d84cdf3b22945be6e43bd8b276a01bd7
|
|
7
|
+
data.tar.gz: efe2d65b67d0bfa191d0db52d894e87f1cec09c14f0a1bab8a286bc496ad67d1bac8ea98e856dff89c16fe61c0793c766e2ef0711867eae5d746d8b5953c3841
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
NewCops: enable
|
|
3
|
+
TargetRubyVersion: 3.4
|
|
4
|
+
SuggestExtensions: false
|
|
5
|
+
|
|
6
|
+
Style/Documentation:
|
|
7
|
+
Enabled: false
|
|
8
|
+
|
|
9
|
+
Metrics/MethodLength:
|
|
10
|
+
Max: 20
|
|
11
|
+
|
|
12
|
+
Naming/FileName:
|
|
13
|
+
Exclude:
|
|
14
|
+
- 'lib/rubocop-legion.rb'
|
|
15
|
+
|
|
16
|
+
Gemspec/DevelopmentDependencies:
|
|
17
|
+
Enabled: false
|
|
18
|
+
|
|
19
|
+
Metrics/BlockLength:
|
|
20
|
+
Exclude:
|
|
21
|
+
- 'spec/**/*'
|
|
22
|
+
- '*.gemspec'
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.1.0] - 2026-03-29
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Initial release with 31 cops across 6 departments
|
|
7
|
+
- New RuboCop Plugin API (1.72+, lint_roller based)
|
|
8
|
+
- **Legion/HelperMigration** (6 cops): enforce `log.method`, `json_load`/`json_dump`, `cache_get`/`cache_set`, `vault_get`/`vault_exist?` helpers over direct singleton calls. All auto-correctable.
|
|
9
|
+
- **Legion/ConstantSafety** (4 cops): prevent `Data.define`, `Process`, `JSON` namespace resolution bugs inside `module Legion`; enforce `const_defined?(name, false)`. All auto-correctable.
|
|
10
|
+
- **Legion/Singleton** (1 cop): enforce `.instance` over `.new` for configurable list of singleton classes. Auto-correctable.
|
|
11
|
+
- **Legion/RescueLogging** (3 cops): require rescue blocks to capture exceptions and log or re-raise them. Partial auto-correct (adds `=> e` capture).
|
|
12
|
+
- **Legion/Framework** (7 cops): catch Sequel eager model loading, Sinatra 4.0 host auth, Thor reserved `run`, Faraday XML middleware removal, `module_function`+`private` conflict, cache time coercion, API string keys. `ApiStringKeys` auto-correctable.
|
|
13
|
+
- **Legion/Extension** (10 cops): enforce LEX structural conventions — `module Actor` singular, `extend Core` guard, runner module structure, self-contained actor `runner_class`, `Settings` API correctness, `LLM.ask` signature, `data_required?` migration check. 4 auto-correctable.
|
data/Gemfile
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LegionIO
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# rubocop-legion
|
|
2
|
+
|
|
3
|
+
LegionIO code quality cops for [RuboCop](https://rubocop.org/).
|
|
4
|
+
|
|
5
|
+
Custom cops for the LegionIO async job engine ecosystem. Enforces helper usage, constant safety, rescue logging, framework conventions, and LEX extension structure.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Add to your Gemfile:
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
gem 'rubocop-legion', require: false, group: :development
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Add to your `.rubocop.yml`:
|
|
18
|
+
|
|
19
|
+
```yaml
|
|
20
|
+
plugins:
|
|
21
|
+
- rubocop-legion
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Departments
|
|
25
|
+
|
|
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 |
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
MIT
|
data/Rakefile
ADDED
data/config/default.yml
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# Legion/HelperMigration — use per-extension helpers, not global singletons
|
|
2
|
+
|
|
3
|
+
Legion/HelperMigration:
|
|
4
|
+
Enabled: true
|
|
5
|
+
Include:
|
|
6
|
+
- 'lib/**/*.rb'
|
|
7
|
+
Exclude:
|
|
8
|
+
- 'spec/**/*'
|
|
9
|
+
|
|
10
|
+
Legion/HelperMigration/DirectLogging:
|
|
11
|
+
Description: 'Use `log.method` helper instead of `Legion::Logging.method`.'
|
|
12
|
+
Enabled: true
|
|
13
|
+
Severity: warning
|
|
14
|
+
VersionAdded: '0.1'
|
|
15
|
+
|
|
16
|
+
Legion/HelperMigration/OldLoggingMethods:
|
|
17
|
+
Description: 'Use `log.method` instead of deprecated `log_method` helpers.'
|
|
18
|
+
Enabled: true
|
|
19
|
+
Severity: warning
|
|
20
|
+
VersionAdded: '0.1'
|
|
21
|
+
|
|
22
|
+
Legion/HelperMigration/DirectJson:
|
|
23
|
+
Description: 'Use `json_load`/`json_dump` helpers instead of `Legion::JSON.load`/`.dump`.'
|
|
24
|
+
Enabled: true
|
|
25
|
+
Severity: convention
|
|
26
|
+
VersionAdded: '0.1'
|
|
27
|
+
|
|
28
|
+
Legion/HelperMigration/DirectCache:
|
|
29
|
+
Description: 'Use `cache_get`/`cache_set`/`cache_delete` helpers instead of `Legion::Cache` methods.'
|
|
30
|
+
Enabled: true
|
|
31
|
+
Severity: warning
|
|
32
|
+
VersionAdded: '0.1'
|
|
33
|
+
|
|
34
|
+
Legion/HelperMigration/DirectLocalCache:
|
|
35
|
+
Description: 'Use `local_cache_get`/`local_cache_set` helpers instead of `Legion::Cache::Local` methods.'
|
|
36
|
+
Enabled: true
|
|
37
|
+
Severity: warning
|
|
38
|
+
VersionAdded: '0.1'
|
|
39
|
+
|
|
40
|
+
Legion/HelperMigration/DirectCrypt:
|
|
41
|
+
Description: 'Use `vault_get`/`vault_exist?` helpers instead of `Legion::Crypt` methods.'
|
|
42
|
+
Enabled: true
|
|
43
|
+
Severity: warning
|
|
44
|
+
VersionAdded: '0.1'
|
|
45
|
+
|
|
46
|
+
# Legion/ConstantSafety — prevent namespace resolution bugs inside module Legion
|
|
47
|
+
|
|
48
|
+
Legion/ConstantSafety:
|
|
49
|
+
Enabled: true
|
|
50
|
+
|
|
51
|
+
Legion/ConstantSafety/BareDataDefine:
|
|
52
|
+
Description: 'Use `::Data.define` inside `module Legion` to avoid resolving to `Legion::Data`.'
|
|
53
|
+
Enabled: true
|
|
54
|
+
Severity: error
|
|
55
|
+
VersionAdded: '0.1'
|
|
56
|
+
|
|
57
|
+
Legion/ConstantSafety/BareProcess:
|
|
58
|
+
Description: 'Use `::Process` inside `module Legion` to avoid resolving to `Legion::Process`.'
|
|
59
|
+
Enabled: true
|
|
60
|
+
Severity: error
|
|
61
|
+
VersionAdded: '0.1'
|
|
62
|
+
|
|
63
|
+
Legion/ConstantSafety/BareJson:
|
|
64
|
+
Description: 'Use `::JSON` inside `module Legion` to avoid resolving to `Legion::JSON`.'
|
|
65
|
+
Enabled: true
|
|
66
|
+
Severity: error
|
|
67
|
+
VersionAdded: '0.1'
|
|
68
|
+
|
|
69
|
+
Legion/ConstantSafety/InheritParam:
|
|
70
|
+
Description: 'Pass `false` as inherit param to `const_defined?`/`const_get` on dynamic modules.'
|
|
71
|
+
Enabled: true
|
|
72
|
+
Severity: convention
|
|
73
|
+
VersionAdded: '0.1'
|
|
74
|
+
|
|
75
|
+
# Legion/Singleton — enforce .instance on singleton classes
|
|
76
|
+
|
|
77
|
+
Legion/Singleton:
|
|
78
|
+
Enabled: true
|
|
79
|
+
|
|
80
|
+
Legion/Singleton/UseInstance:
|
|
81
|
+
Description: 'Use `.instance` instead of `.new` for singleton classes.'
|
|
82
|
+
Enabled: true
|
|
83
|
+
Severity: error
|
|
84
|
+
VersionAdded: '0.1'
|
|
85
|
+
SingletonClasses:
|
|
86
|
+
- TokenCache
|
|
87
|
+
- Registry
|
|
88
|
+
- Catalog
|
|
89
|
+
|
|
90
|
+
# Legion/RescueLogging — every rescue must log or re-raise
|
|
91
|
+
|
|
92
|
+
Legion/RescueLogging:
|
|
93
|
+
Enabled: true
|
|
94
|
+
Include:
|
|
95
|
+
- 'lib/**/*.rb'
|
|
96
|
+
Exclude:
|
|
97
|
+
- 'spec/**/*'
|
|
98
|
+
|
|
99
|
+
Legion/RescueLogging/BareRescue:
|
|
100
|
+
Description: 'Bare `rescue` swallows exceptions silently. Capture with `rescue => e`.'
|
|
101
|
+
Enabled: true
|
|
102
|
+
Severity: warning
|
|
103
|
+
VersionAdded: '0.1'
|
|
104
|
+
|
|
105
|
+
Legion/RescueLogging/NoCapture:
|
|
106
|
+
Description: 'Exception class specified but not captured. Use `rescue Error => e`.'
|
|
107
|
+
Enabled: true
|
|
108
|
+
Severity: convention
|
|
109
|
+
VersionAdded: '0.1'
|
|
110
|
+
|
|
111
|
+
Legion/RescueLogging/SilentCapture:
|
|
112
|
+
Description: 'Captured exception variable is never logged or re-raised.'
|
|
113
|
+
Enabled: true
|
|
114
|
+
Severity: warning
|
|
115
|
+
VersionAdded: '0.1'
|
|
116
|
+
|
|
117
|
+
# Legion/Framework — Sequel, Sinatra, Thor, Faraday, concurrency, cache, API gotchas
|
|
118
|
+
|
|
119
|
+
Legion/Framework:
|
|
120
|
+
Enabled: true
|
|
121
|
+
|
|
122
|
+
Legion/Framework/EagerSequelModel:
|
|
123
|
+
Description: '`Sequel::Model(:table)` introspects schema at require time. Use lazy define pattern.'
|
|
124
|
+
Enabled: true
|
|
125
|
+
Severity: warning
|
|
126
|
+
VersionAdded: '0.1'
|
|
127
|
+
|
|
128
|
+
Legion/Framework/SinatraHostAuth:
|
|
129
|
+
Description: 'Sinatra 4.0+ requires `set :host_authorization` or all requests get 403.'
|
|
130
|
+
Enabled: true
|
|
131
|
+
Severity: convention
|
|
132
|
+
VersionAdded: '0.1'
|
|
133
|
+
|
|
134
|
+
Legion/Framework/ThorReservedRun:
|
|
135
|
+
Description: 'Thor 1.5+ reserves `run`. Rename the method or use `map`.'
|
|
136
|
+
Enabled: true
|
|
137
|
+
Severity: warning
|
|
138
|
+
VersionAdded: '0.1'
|
|
139
|
+
Include:
|
|
140
|
+
- 'lib/**/cli/**/*.rb'
|
|
141
|
+
|
|
142
|
+
Legion/Framework/FaradayXmlMiddleware:
|
|
143
|
+
Description: 'Faraday >= 2.0 removed built-in `:xml` middleware.'
|
|
144
|
+
Enabled: true
|
|
145
|
+
Severity: error
|
|
146
|
+
VersionAdded: '0.1'
|
|
147
|
+
|
|
148
|
+
Legion/Framework/ModuleFunctionPrivate:
|
|
149
|
+
Description: '`private` after `module_function` resets visibility. Do not use both together.'
|
|
150
|
+
Enabled: true
|
|
151
|
+
Severity: convention
|
|
152
|
+
VersionAdded: '0.1'
|
|
153
|
+
|
|
154
|
+
Legion/Framework/CacheTimeCoercion:
|
|
155
|
+
Description: 'Time objects become Strings after cache round-trip. Coerce at read boundaries.'
|
|
156
|
+
Enabled: true
|
|
157
|
+
Severity: convention
|
|
158
|
+
VersionAdded: '0.1'
|
|
159
|
+
|
|
160
|
+
Legion/Framework/ApiStringKeys:
|
|
161
|
+
Description: '`Legion::JSON.load` returns symbol keys. Use `body[:key]` not `body["key"]`.'
|
|
162
|
+
Enabled: true
|
|
163
|
+
Severity: warning
|
|
164
|
+
VersionAdded: '0.1'
|
|
165
|
+
Include:
|
|
166
|
+
- 'lib/**/*.rb'
|
|
167
|
+
Exclude:
|
|
168
|
+
- 'spec/**/*'
|
|
169
|
+
|
|
170
|
+
# Legion/Extension — LEX structural convention enforcement
|
|
171
|
+
|
|
172
|
+
Legion/Extension:
|
|
173
|
+
Enabled: true
|
|
174
|
+
|
|
175
|
+
Legion/Extension/ActorSingularModule:
|
|
176
|
+
Description: 'Use `module Actor` (singular). Framework discovers actors in `Actor`, not `Actors`.'
|
|
177
|
+
Enabled: true
|
|
178
|
+
Severity: error
|
|
179
|
+
VersionAdded: '0.1'
|
|
180
|
+
|
|
181
|
+
Legion/Extension/CoreExtendGuard:
|
|
182
|
+
Description: 'Guard `extend Core` with `const_defined?` for standalone test compatibility.'
|
|
183
|
+
Enabled: true
|
|
184
|
+
Severity: error
|
|
185
|
+
VersionAdded: '0.1'
|
|
186
|
+
|
|
187
|
+
Legion/Extension/RunnerMustBeModule:
|
|
188
|
+
Description: 'Runners must be modules, not classes.'
|
|
189
|
+
Enabled: true
|
|
190
|
+
Severity: warning
|
|
191
|
+
VersionAdded: '0.1'
|
|
192
|
+
|
|
193
|
+
Legion/Extension/RunnerIncludeHelpers:
|
|
194
|
+
Description: 'Runner modules need `include Helpers::Lex` or `extend self`.'
|
|
195
|
+
Enabled: true
|
|
196
|
+
Severity: convention
|
|
197
|
+
VersionAdded: '0.1'
|
|
198
|
+
|
|
199
|
+
Legion/Extension/SelfContainedActorRunnerClass:
|
|
200
|
+
Description: 'Self-contained actors must override `runner_class`.'
|
|
201
|
+
Enabled: true
|
|
202
|
+
Severity: warning
|
|
203
|
+
VersionAdded: '0.1'
|
|
204
|
+
|
|
205
|
+
Legion/Extension/RunnerReturnHash:
|
|
206
|
+
Description: 'Runner methods must return a Hash.'
|
|
207
|
+
Enabled: true
|
|
208
|
+
Severity: convention
|
|
209
|
+
VersionAdded: '0.1'
|
|
210
|
+
|
|
211
|
+
Legion/Extension/SettingsKeyMethod:
|
|
212
|
+
Description: '`Legion::Settings` has no `key?` method. Use `!Settings[:key].nil?`.'
|
|
213
|
+
Enabled: true
|
|
214
|
+
Severity: error
|
|
215
|
+
VersionAdded: '0.1'
|
|
216
|
+
|
|
217
|
+
Legion/Extension/SettingsBracketMultiArg:
|
|
218
|
+
Description: '`Settings#[]` takes 1 arg. Use `Settings.dig(...)` for nested access.'
|
|
219
|
+
Enabled: true
|
|
220
|
+
Severity: error
|
|
221
|
+
VersionAdded: '0.1'
|
|
222
|
+
|
|
223
|
+
Legion/Extension/LlmAskKwargs:
|
|
224
|
+
Description: '`Legion::LLM.ask` only accepts `message:`. No extra kwargs.'
|
|
225
|
+
Enabled: true
|
|
226
|
+
Severity: error
|
|
227
|
+
VersionAdded: '0.1'
|
|
228
|
+
|
|
229
|
+
Legion/Extension/DataRequiredWithoutMigrations:
|
|
230
|
+
Description: '`data_required?` returns true but migrations may be missing.'
|
|
231
|
+
Enabled: true
|
|
232
|
+
Severity: warning
|
|
233
|
+
VersionAdded: '0.1'
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module ConstantSafety
|
|
7
|
+
# Detects bare `Data.define` inside `module Legion` namespaces where it
|
|
8
|
+
# resolves to `Legion::Data.define` instead of the stdlib `Data.define`.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad (inside module Legion)
|
|
12
|
+
# module Legion
|
|
13
|
+
# Point = Data.define(:x, :y)
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# # good
|
|
17
|
+
# module Legion
|
|
18
|
+
# Point = ::Data.define(:x, :y)
|
|
19
|
+
# end
|
|
20
|
+
class BareDataDefine < RuboCop::Cop::Base
|
|
21
|
+
extend AutoCorrector
|
|
22
|
+
|
|
23
|
+
MSG = 'Inside `module Legion`, bare `Data.define` resolves to `Legion::Data.define`. ' \
|
|
24
|
+
'Use `::Data.define`.'
|
|
25
|
+
|
|
26
|
+
RESTRICT_ON_SEND = %i[define].freeze
|
|
27
|
+
|
|
28
|
+
# @!method bare_data_define?(node)
|
|
29
|
+
def_node_matcher :bare_data_define?, <<~PATTERN
|
|
30
|
+
(send (const nil? :Data) :define ...)
|
|
31
|
+
PATTERN
|
|
32
|
+
|
|
33
|
+
def on_send(node)
|
|
34
|
+
return unless bare_data_define?(node)
|
|
35
|
+
return unless inside_legion_namespace?(node)
|
|
36
|
+
|
|
37
|
+
receiver = node.receiver
|
|
38
|
+
add_offense(receiver) do |corrector|
|
|
39
|
+
corrector.replace(receiver.source_range, '::Data')
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def inside_legion_namespace?(node)
|
|
46
|
+
node.each_ancestor(:module, :class).any? do |ancestor|
|
|
47
|
+
name = ancestor.identifier.source
|
|
48
|
+
name == 'Legion' || name.start_with?('Legion::')
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module ConstantSafety
|
|
7
|
+
# Detects bare `JSON` method calls inside `module Legion` namespaces
|
|
8
|
+
# where `JSON` resolves to `Legion::JSON` instead of the stdlib.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad (inside module Legion)
|
|
12
|
+
# module Legion
|
|
13
|
+
# JSON.parse(raw)
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# # good
|
|
17
|
+
# module Legion
|
|
18
|
+
# ::JSON.parse(raw)
|
|
19
|
+
# end
|
|
20
|
+
class BareJson < RuboCop::Cop::Base
|
|
21
|
+
extend AutoCorrector
|
|
22
|
+
|
|
23
|
+
MSG = 'Inside `module Legion`, bare `JSON` resolves to `Legion::JSON`. ' \
|
|
24
|
+
'Use `::JSON` for stdlib.'
|
|
25
|
+
|
|
26
|
+
RESTRICT_ON_SEND = %i[parse generate pretty_generate dump load fast_generate].freeze
|
|
27
|
+
|
|
28
|
+
# @!method bare_json_send?(node)
|
|
29
|
+
def_node_matcher :bare_json_send?, <<~PATTERN
|
|
30
|
+
(send (const nil? :JSON) _ ...)
|
|
31
|
+
PATTERN
|
|
32
|
+
|
|
33
|
+
def on_send(node)
|
|
34
|
+
return unless bare_json_send?(node)
|
|
35
|
+
return unless inside_legion_namespace?(node)
|
|
36
|
+
|
|
37
|
+
receiver = node.receiver
|
|
38
|
+
add_offense(receiver) do |corrector|
|
|
39
|
+
corrector.replace(receiver.source_range, '::JSON')
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def inside_legion_namespace?(node)
|
|
46
|
+
node.each_ancestor(:module, :class).any? do |ancestor|
|
|
47
|
+
name = ancestor.identifier.source
|
|
48
|
+
name == 'Legion' || name.start_with?('Legion::')
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module ConstantSafety
|
|
7
|
+
# Detects bare `Process` method calls inside `module Legion` namespaces
|
|
8
|
+
# where `Process` resolves to `Legion::Process` instead of the stdlib.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad (inside module Legion)
|
|
12
|
+
# module Legion
|
|
13
|
+
# Process.pid
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# # good
|
|
17
|
+
# module Legion
|
|
18
|
+
# ::Process.pid
|
|
19
|
+
# end
|
|
20
|
+
class BareProcess < RuboCop::Cop::Base
|
|
21
|
+
extend AutoCorrector
|
|
22
|
+
|
|
23
|
+
MSG = 'Inside `module Legion`, bare `Process` resolves to `Legion::Process`. ' \
|
|
24
|
+
'Use `::Process`.'
|
|
25
|
+
|
|
26
|
+
RESTRICT_ON_SEND = %i[
|
|
27
|
+
pid ppid kill detach fork wait wait2 waitpid waitpid2
|
|
28
|
+
getpgid setpgid daemon exit spawn times groups
|
|
29
|
+
uid gid euid egid
|
|
30
|
+
].freeze
|
|
31
|
+
|
|
32
|
+
# @!method bare_process_send?(node)
|
|
33
|
+
def_node_matcher :bare_process_send?, <<~PATTERN
|
|
34
|
+
(send (const nil? :Process) _ ...)
|
|
35
|
+
PATTERN
|
|
36
|
+
|
|
37
|
+
def on_send(node)
|
|
38
|
+
return unless bare_process_send?(node)
|
|
39
|
+
return unless inside_legion_namespace?(node)
|
|
40
|
+
|
|
41
|
+
receiver = node.receiver
|
|
42
|
+
add_offense(receiver) do |corrector|
|
|
43
|
+
corrector.replace(receiver.source_range, '::Process')
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def inside_legion_namespace?(node)
|
|
50
|
+
node.each_ancestor(:module, :class).any? do |ancestor|
|
|
51
|
+
name = ancestor.identifier.source
|
|
52
|
+
name == 'Legion' || name.start_with?('Legion::')
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module ConstantSafety
|
|
7
|
+
# Detects `const_defined?` or `const_get` called with only one argument.
|
|
8
|
+
# Without `false` as the second argument, Ruby searches the entire inheritance
|
|
9
|
+
# chain including `Object`, which can cause false positives and namespace leaks.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad
|
|
13
|
+
# mod.const_defined?('Foo')
|
|
14
|
+
# mod.const_get('Bar')
|
|
15
|
+
#
|
|
16
|
+
# # good
|
|
17
|
+
# mod.const_defined?('Foo', false)
|
|
18
|
+
# mod.const_get('Bar', false)
|
|
19
|
+
class InheritParam < RuboCop::Cop::Base
|
|
20
|
+
extend AutoCorrector
|
|
21
|
+
|
|
22
|
+
MSG = 'Pass `false` as inherit parameter: `%<method>s(%<arg>s, false)`. ' \
|
|
23
|
+
'Default `true` leaks through `Object`.'
|
|
24
|
+
|
|
25
|
+
RESTRICT_ON_SEND = %i[const_defined? const_get].freeze
|
|
26
|
+
|
|
27
|
+
def on_send(node)
|
|
28
|
+
return unless node.arguments.size == 1
|
|
29
|
+
|
|
30
|
+
arg = node.first_argument
|
|
31
|
+
message = format(MSG, method: node.method_name, arg: arg.source)
|
|
32
|
+
|
|
33
|
+
add_offense(node, message: message) do |corrector|
|
|
34
|
+
corrector.insert_after(arg.source_range, ', false')
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Legion
|
|
6
|
+
module Extension
|
|
7
|
+
# Detects `module Actors` (plural) inside `Legion::Extensions::*` namespaces
|
|
8
|
+
# and auto-corrects it to `module Actor` (singular), which is what the LEX
|
|
9
|
+
# framework uses when discovering actor classes.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# # bad
|
|
13
|
+
# module Legion::Extensions::Foo
|
|
14
|
+
# module Actors
|
|
15
|
+
# end
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# # good
|
|
19
|
+
# module Legion::Extensions::Foo
|
|
20
|
+
# module Actor
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
class ActorSingularModule < RuboCop::Cop::Base
|
|
24
|
+
extend AutoCorrector
|
|
25
|
+
|
|
26
|
+
MSG = 'Use `module Actor` (singular), not `module Actors`. ' \
|
|
27
|
+
'The framework discovers actors inside `Actor`.'
|
|
28
|
+
|
|
29
|
+
def on_module(node)
|
|
30
|
+
name_node = node.identifier
|
|
31
|
+
return unless name_node.short_name == :Actors
|
|
32
|
+
|
|
33
|
+
return unless inside_legion_extensions_namespace?(node)
|
|
34
|
+
|
|
35
|
+
add_offense(name_node) do |corrector|
|
|
36
|
+
corrector.replace(name_node.loc.name, 'Actor')
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def inside_legion_extensions_namespace?(node)
|
|
43
|
+
# Build the full namespace path from all ancestor module/class nodes.
|
|
44
|
+
# This handles both compact (`module Legion::Extensions::Foo`) and
|
|
45
|
+
# nested (`module Legion; module Extensions; module Foo`) forms.
|
|
46
|
+
full_path = ancestor_namespace_parts(node).join('::')
|
|
47
|
+
full_path.include?('Legion') && full_path.include?('Extensions')
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def ancestor_namespace_parts(node)
|
|
51
|
+
parts = []
|
|
52
|
+
current = node.parent
|
|
53
|
+
while current
|
|
54
|
+
parts.unshift(*resolve_const_parts(current.identifier)) if current.module_type? || current.class_type?
|
|
55
|
+
current = current.parent
|
|
56
|
+
end
|
|
57
|
+
parts
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def resolve_const_parts(const_node)
|
|
61
|
+
parts = []
|
|
62
|
+
node = const_node
|
|
63
|
+
while node&.const_type?
|
|
64
|
+
parts.unshift(node.short_name.to_s)
|
|
65
|
+
node = node.namespace
|
|
66
|
+
end
|
|
67
|
+
parts
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|