rubocop-flexport 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/default.yml +1 -0
- data/lib/rubocop/cop/flexport/engine_api_boundary.rb +82 -29
- data/lib/rubocop/cop/flexport/global_model_access_from_engine.rb +3 -0
- data/lib/rubocop/cop/flexport_cops.rb +1 -0
- data/lib/rubocop/cop/mixin/engine_node_context.rb +26 -0
- data/lib/rubocop/flexport/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7bfc3b432b328659f2ac2dd842e48c05028482c7538fcd2f03a33dbbee4b561
|
4
|
+
data.tar.gz: b9fade3c98f628825b648b184fba3c6317c94476f2c4de1791bdf2aab7e7b372
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a7687a4bf47c4f476006a00c7852d5c008daeb88693949b2ff182e28f46c6344fba487ed6e86cda52fc454a7cc5266ae33b47b98829494c96c8e902746c8faa
|
7
|
+
data.tar.gz: 01561c084ee90550946391998f5a28bf0959a2758ece148762714ccb28bb6db2300c8829c13776544a969fab377b136b47b864a8fab76d50461f0d4af9d821f3
|
data/config/default.yml
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# rubocop:disable Metrics/ClassLength
|
3
4
|
module RuboCop
|
4
5
|
module Cop
|
5
6
|
module Flexport
|
@@ -80,6 +81,36 @@ module RuboCop
|
|
80
81
|
# end
|
81
82
|
# ```
|
82
83
|
#
|
84
|
+
# # "StronglyProtectedEngines" parameter
|
85
|
+
#
|
86
|
+
# The Engine API is not actually a network API surface. Method invocations
|
87
|
+
# may happen synchronously and assume they are part of the same
|
88
|
+
# transaction. So if your engine is using modules whitelisted by
|
89
|
+
# other engines, then you cannot extract your engine code into a
|
90
|
+
# separate network-isolated service (even though within a big Rails
|
91
|
+
# monolith using engines the cross-engine method call might have been
|
92
|
+
# acceptable).
|
93
|
+
#
|
94
|
+
# The "StronglyProtectedEngines" parameter helps in the case you want to
|
95
|
+
# extract your engine completely. If your engine is listed as a strongly
|
96
|
+
# protected engine, then the following additional restricts apply:
|
97
|
+
#
|
98
|
+
# (1) Any use of your engine's code by code outside your engine is
|
99
|
+
# considered a violation, regardless of *your* _legacy_dependents.rb,
|
100
|
+
# _whitelist.rb, or engine API module. (no inbound access)
|
101
|
+
# (2) Any use of other engines' code within your engine is considered
|
102
|
+
# a violation, regardless of *their* _legacy_dependents.rb,
|
103
|
+
# _whitelist.rb, or engine API module. (no outbound access)
|
104
|
+
#
|
105
|
+
# (Note: "EngineSpecificOverrides" parameter still has effect.)
|
106
|
+
#
|
107
|
+
# # "EngineSpecificOverrides" parameter
|
108
|
+
#
|
109
|
+
# This parameter allows defining bi-lateral private "APIs" between
|
110
|
+
# engines. See example in global_model_access_from_engine_spec.rb.
|
111
|
+
# This may be useful if you plan to extract several engines into the
|
112
|
+
# same network-isolated service.
|
113
|
+
#
|
83
114
|
# @example
|
84
115
|
#
|
85
116
|
# # bad
|
@@ -109,25 +140,27 @@ module RuboCop
|
|
109
140
|
#
|
110
141
|
class EngineApiBoundary < Cop
|
111
142
|
include EngineApi
|
143
|
+
include EngineNodeContext
|
112
144
|
|
113
145
|
MSG = 'Direct access of %<engine>s engine. ' \
|
114
146
|
'Only access engine via %<engine>s::Api.'
|
115
147
|
|
148
|
+
STRONGLY_PROTECTED_MSG = 'All direct access of ' \
|
149
|
+
'%<engine>s engine disallowed because ' \
|
150
|
+
'it is in StronglyProtectedEngines list.'
|
151
|
+
|
152
|
+
STRONGLY_PROTECTED_CURRENT_MSG = 'Direct ' \
|
153
|
+
'access of other engines is disallowed in this file because ' \
|
154
|
+
'it\'s in the %<engine>s engine, which ' \
|
155
|
+
'is in the StronglyProtectedEngines list.'
|
156
|
+
|
116
157
|
def_node_matcher :rails_association_hash_args, <<-PATTERN
|
117
158
|
(send _ {:belongs_to :has_one :has_many} sym $hash)
|
118
159
|
PATTERN
|
119
160
|
|
120
161
|
def on_const(node)
|
121
|
-
# Sometimes modules/class are declared with the same name as an
|
122
|
-
# engine. For example, you might have:
|
123
|
-
#
|
124
|
-
# /engines/foo
|
125
|
-
# /app/graph/types/foo
|
126
|
-
#
|
127
|
-
# We ignore instead of yielding false positive for the module
|
128
|
-
# declaration in the latter.
|
129
162
|
return if in_module_or_class_declaration?(node)
|
130
|
-
#
|
163
|
+
# There might be value objects that are named
|
131
164
|
# the same as engines like:
|
132
165
|
#
|
133
166
|
# Warehouse.new
|
@@ -139,7 +172,7 @@ module RuboCop
|
|
139
172
|
return unless engine
|
140
173
|
return if valid_engine_access?(node, engine)
|
141
174
|
|
142
|
-
add_offense(node, message:
|
175
|
+
add_offense(node, message: message(node, engine))
|
143
176
|
end
|
144
177
|
|
145
178
|
def on_send(node)
|
@@ -151,7 +184,7 @@ module RuboCop
|
|
151
184
|
next if engine.nil?
|
152
185
|
next if valid_engine_access?(node, engine)
|
153
186
|
|
154
|
-
add_offense(class_name_node, message:
|
187
|
+
add_offense(class_name_node, message: message(node, engine))
|
155
188
|
end
|
156
189
|
end
|
157
190
|
|
@@ -161,6 +194,16 @@ module RuboCop
|
|
161
194
|
|
162
195
|
private
|
163
196
|
|
197
|
+
def message(_node, engine)
|
198
|
+
if strongly_protected_engine?(engine)
|
199
|
+
format(STRONGLY_PROTECTED_MSG, engine: engine)
|
200
|
+
elsif strongly_protected_engine?(current_engine)
|
201
|
+
format(STRONGLY_PROTECTED_CURRENT_MSG, engine: current_engine)
|
202
|
+
else
|
203
|
+
format(MSG, engine: engine)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
164
207
|
def extract_engine(node)
|
165
208
|
return nil unless protected_engines.include?(node.const_name)
|
166
209
|
|
@@ -192,27 +235,25 @@ module RuboCop
|
|
192
235
|
names.map { |n| ActiveSupport::Inflector.camelize(n) }
|
193
236
|
end
|
194
237
|
|
195
|
-
def in_module_or_class_declaration?(node)
|
196
|
-
depth = 0
|
197
|
-
max_depth = 10
|
198
|
-
while node.const_type? && depth < max_depth
|
199
|
-
node = node.parent
|
200
|
-
depth += 1
|
201
|
-
end
|
202
|
-
node.module_type? || node.class_type?
|
203
|
-
end
|
204
|
-
|
205
238
|
def sending_method_to_namespace_itself?(node)
|
206
239
|
node.parent.send_type?
|
207
240
|
end
|
208
241
|
|
209
242
|
def valid_engine_access?(node, engine)
|
243
|
+
return true if in_engine_file?(engine)
|
244
|
+
return true if engine_specific_override?(node)
|
245
|
+
|
246
|
+
return false if strongly_protected_engine?(current_engine)
|
247
|
+
return false if strongly_protected_engine?(engine)
|
248
|
+
|
249
|
+
valid_engine_api_access?(node, engine)
|
250
|
+
end
|
251
|
+
|
252
|
+
def valid_engine_api_access?(node, engine)
|
210
253
|
(
|
211
|
-
in_engine_file?(engine) ||
|
212
254
|
in_legacy_dependent_file?(engine) ||
|
213
255
|
through_api?(node) ||
|
214
|
-
whitelisted?(node, engine)
|
215
|
-
engine_specific_override?(node)
|
256
|
+
whitelisted?(node, engine)
|
216
257
|
)
|
217
258
|
end
|
218
259
|
|
@@ -302,19 +343,31 @@ module RuboCop
|
|
302
343
|
|
303
344
|
raw_overrides.each do |raw_override|
|
304
345
|
engine = ActiveSupport::Inflector.camelize(raw_override['Engine'])
|
305
|
-
overrides_by_engine[engine] = raw_override['
|
346
|
+
overrides_by_engine[engine] = raw_override['AllowedModules']
|
306
347
|
end
|
307
348
|
overrides_by_engine
|
308
349
|
end
|
309
350
|
|
310
351
|
def engine_specific_override?(node)
|
311
|
-
|
312
|
-
|
313
|
-
return false unless
|
352
|
+
module_name = node.parent.source
|
353
|
+
module_names_allowed_by_override = overrides_by_engine[current_engine]
|
354
|
+
return false unless module_names_allowed_by_override
|
355
|
+
|
356
|
+
module_names_allowed_by_override.include?(module_name)
|
357
|
+
end
|
358
|
+
|
359
|
+
def strongly_protected_engines
|
360
|
+
@strongly_protected_engines ||= begin
|
361
|
+
strongly_protected = cop_config['StronglyProtectedEngines'] || []
|
362
|
+
camelize_all(strongly_protected)
|
363
|
+
end
|
364
|
+
end
|
314
365
|
|
315
|
-
|
366
|
+
def strongly_protected_engine?(engine)
|
367
|
+
strongly_protected_engines.include?(engine)
|
316
368
|
end
|
317
369
|
end
|
318
370
|
end
|
319
371
|
end
|
320
372
|
end
|
373
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -46,6 +46,8 @@ module RuboCop
|
|
46
46
|
# end
|
47
47
|
#
|
48
48
|
class GlobalModelAccessFromEngine < Cop
|
49
|
+
include EngineNodeContext
|
50
|
+
|
49
51
|
MSG = 'Direct access of global model `%<model>s` ' \
|
50
52
|
'from within Rails Engine.'
|
51
53
|
|
@@ -58,6 +60,7 @@ module RuboCop
|
|
58
60
|
return unless global_model_const?(node)
|
59
61
|
# The cop allows access to e.g. MyGlobalModel::MY_CONST.
|
60
62
|
return if child_of_const?(node)
|
63
|
+
return if in_module_or_class_declaration?(node)
|
61
64
|
|
62
65
|
add_offense(node, message: message(node.source))
|
63
66
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Helpers for determining the context of a node for engine violations.
|
6
|
+
module EngineNodeContext
|
7
|
+
# Sometimes modules/class are declared with the same name as an
|
8
|
+
# engine or global model. For example, you might have both:
|
9
|
+
#
|
10
|
+
# /engines/foo
|
11
|
+
# /app/graph/types/foo
|
12
|
+
#
|
13
|
+
# We ignore instead of yielding false positive for the module
|
14
|
+
# declaration in the latter.
|
15
|
+
def in_module_or_class_declaration?(node)
|
16
|
+
depth = 0
|
17
|
+
max_depth = 10
|
18
|
+
while node.const_type? && depth < max_depth
|
19
|
+
node = node.parent
|
20
|
+
depth += 1
|
21
|
+
end
|
22
|
+
node.module_type? || node.class_type?
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-flexport
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Flexport Engineering
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-01-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- lib/rubocop/cop/flexport/new_global_model.rb
|
57
57
|
- lib/rubocop/cop/flexport_cops.rb
|
58
58
|
- lib/rubocop/cop/mixin/engine_api.rb
|
59
|
+
- lib/rubocop/cop/mixin/engine_node_context.rb
|
59
60
|
- lib/rubocop/flexport.rb
|
60
61
|
- lib/rubocop/flexport/inject.rb
|
61
62
|
- lib/rubocop/flexport/version.rb
|