rubocop-flexport 0.7.0 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1affaedaabe7b84350802f0d4222c052e94d19120c92e556250df045d9f36cc5
4
- data.tar.gz: efed93fb328d46330498aa2298e3aa5ff872faaab01dae64b06ab279f764d36e
3
+ metadata.gz: 14af72c337894273c5528d75f172b6defe96ae4f2a79f338fcaed8a02190aad5
4
+ data.tar.gz: 1ff6475e48b37ba26859de9150e30799e4d40b000ce2610b6b7dcc57d7a8cc7b
5
5
  SHA512:
6
- metadata.gz: 0737a98974780066c5b8a0822825a7a9372cf6b3c155d4b5734fafed971b023e8559e6a77d29f16c9213389b9162f61ea24948d8a2a7dd3f5aeadf820577ab9d
7
- data.tar.gz: b01a9c75229f63ce3bfef495e622c8c53450fa0cf5ac0cb1f4afddfca21785b8760c3428c90d5bb7144de4e4124112f9843d212d494cc988ddfa6b4963b428a5
6
+ metadata.gz: e43ce8e0ed08dc66aef89f753f8a53bc4e9532b46e45c0e42fd2b6d545c0d0ea62e24cad6ab1afe7728eefd0da4e5651614fd965d31bd127b3f17a5145911f89
7
+ data.tar.gz: 91ba38734c2c250fae6323c14f17c782b25bc7eb8bc6754fc3225bbfa005a0165b0d4099ac78a0b8998bf7ebbdd7273fc20084f6a541a8149160d83d1889ea9f
data/README.md CHANGED
@@ -43,7 +43,7 @@ like below and then run `bundle install`:
43
43
  gem "rubocop-flexport", path: "/Users/<user>/rubocop-flexport"
44
44
  ```
45
45
 
46
- To release a new version, update the version number in `version.rb`, and then
46
+ To release a new version, update the version number in `lib/rubocop/flexport/version.rb`, and then
47
47
  run `bundle exec rake release`, which will create a git tag for the version,
48
48
  push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
49
49
 
@@ -6,6 +6,8 @@ Flexport/EngineApiBoundary:
6
6
  UnprotectedEngines: []
7
7
  StronglyProtectedEngines: []
8
8
  EngineSpecificOverrides: []
9
+ FactoryBotEnabled: false
10
+ FactoryBotOutboundAccessAllowedEngines: []
9
11
 
10
12
  Flexport/GlobalModelAccessFromEngine:
11
13
  Description: 'Do not directly access global models from within Rails Engines.'
@@ -16,6 +18,8 @@ Flexport/GlobalModelAccessFromEngine:
16
18
  GlobalModelsPath: app/models/
17
19
  DisabledEngines: []
18
20
  AllowedGlobalModels: []
21
+ FactoryBotEnabled: false
22
+ FactoryBotGlobalAccessAllowedEngines: []
19
23
  Include:
20
24
  - '**/*.rb'
21
25
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'digest/sha1'
4
+
3
5
  # rubocop:disable Metrics/ClassLength
4
6
  module RuboCop
5
7
  module Cop
@@ -17,7 +19,7 @@ module RuboCop
17
19
  # will be accessible outside your engine. For example, adding
18
20
  # `api/foo_service.rb` will allow code outside your engine to
19
21
  # invoke eg `MyEngine::Api::FooService.bar(baz)`.
20
- # - Create a `_whitelist.rb` file in `api/`. Modules listed in
22
+ # - Create an `_allowlist.rb` or `_whitelist.rb` file in `api/`. Modules listed in
21
23
  # this file are accessible to code outside the engine. The file
22
24
  # must have this name and a particular format (see below).
23
25
  #
@@ -39,6 +41,11 @@ module RuboCop
39
41
  # The cop detects cross-engine associations as well as cross-engine
40
42
  # module access.
41
43
  #
44
+ # The cop will complain if you use FactoryBot factories defined in other
45
+ # engines in your engine's specs. You can disable this check by adding
46
+ # the engine name to `FactoryBotOutboundAccessAllowedEngines` in
47
+ # .rubocop.yml.
48
+ #
42
49
  # # Isolation guarantee
43
50
  #
44
51
  # This cop can be easily circumvented with metaprogramming, so it cannot
@@ -141,6 +148,7 @@ module RuboCop
141
148
  class EngineApiBoundary < Cop
142
149
  include EngineApi
143
150
  include EngineNodeContext
151
+ include FactoryBotUsage
144
152
 
145
153
  MSG = 'Direct access of %<accessed_engine>s engine. ' \
146
154
  'Only access engine via %<accessed_engine>s::Api.'
@@ -179,19 +187,42 @@ module RuboCop
179
187
 
180
188
  def on_send(node)
181
189
  rails_association_hash_args(node) do |assocation_hash_args|
182
- class_name_node = extract_class_name_node(assocation_hash_args)
183
- next if class_name_node.nil?
184
-
185
- accessed_engine = extract_model_engine(class_name_node)
186
- next if accessed_engine.nil?
187
- next if valid_engine_access?(node, accessed_engine)
190
+ check_for_cross_engine_rails_association(node, assocation_hash_args)
191
+ end
192
+ return unless check_for_cross_engine_factory_bot?
188
193
 
189
- add_offense(class_name_node, message: message(accessed_engine))
194
+ factory_bot_usage(node) do |factory_node|
195
+ check_for_cross_engine_factory_bot_usage(node, factory_node)
190
196
  end
191
197
  end
192
198
 
199
+ def check_for_cross_engine_rails_association(node, assocation_hash_args)
200
+ class_name_node = extract_class_name_node(assocation_hash_args)
201
+ return if class_name_node.nil?
202
+
203
+ accessed_engine = extract_model_engine(class_name_node)
204
+ return if accessed_engine.nil?
205
+ return if valid_engine_access?(node, accessed_engine)
206
+
207
+ add_offense(class_name_node, message: message(accessed_engine))
208
+ end
209
+
210
+ def check_for_cross_engine_factory_bot_usage(node, factory_node)
211
+ factory = factory_node.children[0]
212
+ accessed_engine, model_class_name = factory_engines[factory]
213
+ return if accessed_engine.nil? || !protected_engines.include?(accessed_engine)
214
+
215
+ model_class_node = parse_ast(model_class_name)
216
+ return if valid_engine_access?(model_class_node, accessed_engine)
217
+
218
+ add_offense(node, message: message(accessed_engine))
219
+ end
220
+
193
221
  def external_dependency_checksum
194
- engine_api_files_modified_time_checksum(engines_path)
222
+ checksum = engine_api_files_modified_time_checksum(engines_path)
223
+ return checksum unless check_for_cross_engine_factory_bot?
224
+
225
+ checksum + spec_factories_modified_time_checksum
195
226
  end
196
227
 
197
228
  private
@@ -268,7 +299,7 @@ module RuboCop
268
299
  (
269
300
  in_legacy_dependent_file?(accessed_engine) ||
270
301
  through_api?(node) ||
271
- whitelisted?(node, accessed_engine)
302
+ allowlisted?(node, accessed_engine)
272
303
  )
273
304
  end
274
305
 
@@ -296,14 +327,15 @@ module RuboCop
296
327
  end
297
328
 
298
329
  def current_engine
299
- @current_engine ||= begin
300
- file_path = processed_source.path
301
- if file_path&.include?(engines_path)
302
- parts = file_path.split(engines_path)
303
- engine_dir = parts.last.split('/').first
304
- ActiveSupport::Inflector.camelize(engine_dir) if engine_dir
305
- end
306
- end
330
+ @current_engine ||= engine_name_from_path(processed_source.path)
331
+ end
332
+
333
+ def engine_name_from_path(file_path)
334
+ return nil unless file_path&.include?(engines_path)
335
+
336
+ parts = file_path.split(engines_path)
337
+ engine_dir = parts.last.split('/').first
338
+ ActiveSupport::Inflector.camelize(engine_dir) if engine_dir
307
339
  end
308
340
 
309
341
  def in_engine_file?(accessed_engine)
@@ -326,15 +358,16 @@ module RuboCop
326
358
  node.parent&.const_type? && node.parent.children.last == :Api
327
359
  end
328
360
 
329
- def whitelisted?(node, engine)
330
- whitelist = read_api_file(engine, :whitelist)
331
- return false if whitelist.empty?
361
+ def allowlisted?(node, engine)
362
+ allowlist = read_api_file(engine, :allowlist)
363
+ allowlist = read_api_file(engine, :whitelist) if allowlist.empty?
364
+ return false if allowlist.empty?
332
365
 
333
366
  depth = 0
334
367
  max_depth = 5
335
- while node.const_type? && depth < max_depth
368
+ while node&.const_type? && depth < max_depth
336
369
  full_const_name = remove_leading_colons(node.source)
337
- return true if whitelist.include?(full_const_name)
370
+ return true if allowlist.include?(full_const_name)
338
371
 
339
372
  node = node.parent
340
373
  depth += 1
@@ -392,6 +425,37 @@ module RuboCop
392
425
  def strongly_protected_engine?(engine)
393
426
  strongly_protected_engines.include?(engine)
394
427
  end
428
+
429
+ def factory_bot_outbound_access_allowed_engines
430
+ @factory_bot_outbound_access_allowed_engines ||=
431
+ camelize_all(cop_config['FactoryBotOutboundAccessAllowedEngines'] || [])
432
+ end
433
+
434
+ def factory_bot_enabled?
435
+ cop_config['FactoryBotEnabled']
436
+ end
437
+
438
+ def check_for_cross_engine_factory_bot?
439
+ spec_file? &&
440
+ factory_bot_enabled? &&
441
+ !factory_bot_outbound_access_allowed_engines.include?(current_engine)
442
+ end
443
+
444
+ # Maps factories to the engine where they are defined.
445
+ def factory_engines
446
+ @factory_engines ||= find_factories.each_with_object({}) do |factory_file, h|
447
+ path, factories = factory_file
448
+ engine_name = engine_name_from_path(path)
449
+ factories.each do |factory, model_class_name|
450
+ h[factory] = [engine_name, model_class_name]
451
+ end
452
+ end
453
+ end
454
+
455
+ def spec_factories_modified_time_checksum
456
+ mtimes = factory_files.sort.map { |f| File.mtime(f) }
457
+ Digest::SHA1.hexdigest(mtimes.join)
458
+ end
395
459
  end
396
460
  end
397
461
  end
@@ -45,8 +45,14 @@ module RuboCop
45
45
  # # No direct association to global models.
46
46
  # end
47
47
  #
48
+ # This cop will also complain if you try to use global FactoryBot
49
+ # factories in your engine's specs. To disable this behavior for your
50
+ # engine, add it to the `FactoryBotGlobalAccessAllowedEngines` list in
51
+ # .rubocop.yml.
52
+ #
48
53
  class GlobalModelAccessFromEngine < Cop
49
54
  include EngineNodeContext
55
+ include FactoryBotUsage
50
56
 
51
57
  MSG = 'Direct access of global model `%<model>s` ' \
52
58
  'from within Rails Engine.'
@@ -69,19 +75,41 @@ module RuboCop
69
75
  return unless in_enforced_engine_file?
70
76
 
71
77
  rails_association_hash_args(node) do |assocation_hash_args|
72
- class_name_node = extract_class_name_node(assocation_hash_args)
73
- class_name = class_name_node&.value
74
- next unless global_model?(class_name)
78
+ check_for_rails_association_with_global_model(assocation_hash_args)
79
+ end
80
+
81
+ return unless check_for_global_factory_bot?
75
82
 
76
- add_offense(class_name_node, message: message(class_name))
83
+ factory_bot_usage(node) do |factory_node|
84
+ check_for_global_factory_bot_usage(node, factory_node)
77
85
  end
78
86
  end
79
87
 
88
+ def check_for_rails_association_with_global_model(assocation_hash_args)
89
+ class_name_node = extract_class_name_node(assocation_hash_args)
90
+ class_name = class_name_node&.value
91
+ return unless global_model?(class_name)
92
+
93
+ add_offense(class_name_node, message: message(class_name))
94
+ end
95
+
96
+ def check_for_global_factory_bot_usage(node, factory_node)
97
+ factory = factory_node.children[0]
98
+ return unless global_factory?(factory)
99
+
100
+ model_class_name = global_factories[factory]
101
+ add_offense(node, message: message(model_class_name))
102
+ end
103
+
80
104
  # Because this cop's behavior depends on the state of external files,
81
105
  # we override this method to bust the RuboCop cache when those files
82
106
  # change.
83
107
  def external_dependency_checksum
84
- Digest::SHA1.hexdigest(model_dir_paths.join)
108
+ if check_for_global_factory_bot?
109
+ Digest::SHA1.hexdigest((model_dir_paths + global_factories.keys.sort).join)
110
+ else
111
+ Digest::SHA1.hexdigest(model_dir_paths.join)
112
+ end
85
113
  end
86
114
 
87
115
  private
@@ -94,6 +122,11 @@ module RuboCop
94
122
  @global_model_names ||= calculate_global_models
95
123
  end
96
124
 
125
+ def global_factories
126
+ @global_factories ||=
127
+ find_factories.reject { |path| path.start_with?(engines_path) }.values.reduce(:merge)
128
+ end
129
+
97
130
  def model_dir_paths
98
131
  Dir[File.join(global_models_path, '**/*.rb')]
99
132
  end
@@ -127,7 +160,15 @@ module RuboCop
127
160
 
128
161
  def in_disabled_engine?(file_path)
129
162
  disabled_engines.any? do |e|
130
- file_path.include?(File.join(engines_path, e))
163
+ # Add trailing / to engine path to avoid incorrectly
164
+ # matching engines with similar names
165
+ file_path.include?(File.join(engines_path, e, ''))
166
+ end
167
+ end
168
+
169
+ def check_for_global_factory_bot?
170
+ spec_file? && factory_bot_enabled? && factory_bot_global_access_allowed_engines.none? do |engine|
171
+ processed_source.path.include?(File.join(engines_path, engine, ''))
131
172
  end
132
173
  end
133
174
 
@@ -142,6 +183,10 @@ module RuboCop
142
183
  global_model_names.include?(class_name)
143
184
  end
144
185
 
186
+ def global_factory?(factory_name)
187
+ global_factories.include?(factory_name)
188
+ end
189
+
145
190
  def child_of_const?(node)
146
191
  node.parent.const_type?
147
192
  end
@@ -163,6 +208,14 @@ module RuboCop
163
208
  end
164
209
  end
165
210
 
211
+ def factory_bot_global_access_allowed_engines
212
+ cop_config['FactoryBotGlobalAccessAllowedEngines'] || []
213
+ end
214
+
215
+ def factory_bot_enabled?
216
+ cop_config['FactoryBotEnabled']
217
+ end
218
+
166
219
  def allowed_global_models
167
220
  cop_config['AllowedGlobalModels'] || []
168
221
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative 'mixin/engine_api'
4
4
  require_relative 'mixin/engine_node_context'
5
+ require_relative 'mixin/factory_bot_usage'
5
6
 
6
7
  require_relative 'flexport/engine_api_boundary'
7
8
  require_relative 'flexport/global_model_access_from_engine'
@@ -10,9 +10,13 @@ module RuboCop
10
10
  extend NodePattern::Macros
11
11
 
12
12
  API_FILE_DETAILS = {
13
+ allowlist: {
14
+ file_basename: '_allowlist.rb',
15
+ array_matcher: :allowlist_array
16
+ },
13
17
  whitelist: {
14
18
  file_basename: '_whitelist.rb',
15
- array_matcher: :whitelist_array
19
+ array_matcher: :allowlist_array
16
20
  },
17
21
  legacy_dependents: {
18
22
  file_basename: '_legacy_dependents.rb',
@@ -58,8 +62,7 @@ module RuboCop
58
62
  File.join(engines_path, "#{raw_name}/app/api/#{raw_name}/api/")
59
63
  end
60
64
 
61
- def parse_ast(file_path)
62
- source_code = File.read(file_path)
65
+ def parse_ast(source_code)
63
66
  source = RuboCop::ProcessedSource.new(source_code, RUBY_VERSION.to_f)
64
67
  source.ast
65
68
  end
@@ -99,7 +102,7 @@ module RuboCop
99
102
  # s(:const, nil, :Trucking), :LoadTypes)), :freeze)))
100
103
  #
101
104
  # We want the :begin in the 2nd case, the :module in the 1st case.
102
- module_node = parse_ast(path)
105
+ module_node = parse_ast(File.read(path))
103
106
  module_block_node = module_node&.children&.[](1)
104
107
  if module_block_node&.begin_type?
105
108
  module_block_node
@@ -108,7 +111,7 @@ module RuboCop
108
111
  end
109
112
  end
110
113
 
111
- def_node_matcher :whitelist_array, <<-PATTERN
114
+ def_node_matcher :allowlist_array, <<-PATTERN
112
115
  (casgn nil? {:PUBLIC_MODULES :PUBLIC_SERVICES :PUBLIC_CONSTANTS :PUBLIC_TYPES} {$array (send $array ...)})
113
116
  PATTERN
114
117
 
@@ -0,0 +1,208 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/inflector'
4
+
5
+ module RuboCop
6
+ module Cop
7
+ # Helpers for detecting FactoryBot usage.
8
+ # rubocop:disable Metrics/ModuleLength
9
+ module FactoryBotUsage
10
+ extend NodePattern::Macros
11
+
12
+ Factory = Struct.new('Factory', :name, :aliases, :parent, :model_class_name)
13
+
14
+ FACTORY_BOT_METHODS = %i[
15
+ attributes_for
16
+ attributes_for_list
17
+ build
18
+ build_list
19
+ build_pair
20
+ build_stubbed
21
+ build_stubbed_list
22
+ create
23
+ create_list
24
+ create_pair
25
+ ].freeze
26
+
27
+ def_node_matcher :factory_bot_usage, <<~PATTERN
28
+ (send _ {#{FACTORY_BOT_METHODS.map(&:inspect).join(' ')}} $sym ...)
29
+ PATTERN
30
+
31
+ # Cache factories at the class level so that we don't have to fetch them
32
+ # again for every file we lint. We use class variables here so that the
33
+ # cache can be shared by all cops that include this module.
34
+ # rubocop:disable Style/ClassVars
35
+ @@factories = nil
36
+
37
+ def self.factories_cache
38
+ @@factories
39
+ end
40
+
41
+ def self.factories_cache=(factories)
42
+ @@factories = factories
43
+ end
44
+ # rubocop:enable Style/ClassVars
45
+
46
+ def spec_file?
47
+ processed_source&.path&.match?(/_spec\.rb$/) || false
48
+ end
49
+
50
+ # Parses factory definition files, returning a hash mapping factory names
51
+ # to model class names for each file.
52
+ def find_factories
53
+ RuboCop::Cop::FactoryBotUsage.factories_cache ||= begin
54
+ # We'll add factories here as we parse the factory files.
55
+ @factories = {}
56
+
57
+ # We'll add factories that specify a parent here, so we can resolve the
58
+ # reference to the parent after we have finished parsing all the files.
59
+ @parents = {}
60
+
61
+ # Parse the factory files, then resolve any parent references.
62
+ traverse_factory_files
63
+ resolve_parents
64
+
65
+ @factories
66
+ end
67
+ end
68
+
69
+ def factory_files
70
+ @factory_files ||= Dir['spec/factories/**/*.rb'] + Dir["#{engines_path}*/spec/factories/**/*.rb"]
71
+ end
72
+
73
+ def engines_path
74
+ raise NotImplementedError
75
+ end
76
+
77
+ private
78
+
79
+ def traverse_factory_files
80
+ factory_files.each do |path|
81
+ @factories[path] = {}
82
+ @parents[path] = {}
83
+
84
+ source_code = File.read(path)
85
+ source = RuboCop::ProcessedSource.new(source_code, RUBY_VERSION.to_f)
86
+ traverse_node(source.ast, path)
87
+ end
88
+ end
89
+
90
+ def resolve_parents
91
+ all_factories = @factories.values.reduce(:merge)
92
+ all_parents = @parents.values.reduce(:merge)
93
+ @parents.each do |path, parents|
94
+ parents.each do |factory, parent|
95
+ parent = all_parents[parent] while all_parents[parent]
96
+ model_class_name = all_factories[parent]
97
+ next unless model_class_name
98
+
99
+ @factories[path][factory] = model_class_name
100
+ end
101
+ end
102
+ end
103
+
104
+ def traverse_node(node, path, parent = nil, model_class_name = nil)
105
+ return unless node.is_a?(Parser::AST::Node)
106
+
107
+ factory_node = extract_factory_node(node)
108
+ if factory_node
109
+ factory = parse_factory_node(factory_node)
110
+ parent = determine_parent(factory, parent)
111
+ model_class_name = determine_model_class_name(factory, model_class_name)
112
+ if factory_node?(node)
113
+ register_factory(path, factory.name, factory.aliases, parent, model_class_name)
114
+ return
115
+ end
116
+ end
117
+
118
+ node.children.each { |child| traverse_node(child, path, parent, model_class_name) }
119
+ end
120
+
121
+ def extract_factory_node(node)
122
+ return node.children[0] if factory_block?(node)
123
+ return node if factory_node?(node)
124
+ end
125
+
126
+ def register_factory(path, factory_name, aliases, parent, model_class_name)
127
+ ([factory_name] + aliases).each do |name|
128
+ if parent
129
+ @parents[path][name] = parent
130
+ else
131
+ @factories[path][name] = model_class_name
132
+ end
133
+ end
134
+ end
135
+
136
+ def factory_block?(node)
137
+ return false if node&.type != :block
138
+
139
+ factory_node?(node.children[0])
140
+ end
141
+
142
+ def factory_node?(node)
143
+ node&.type == :send && node.children[1] == :factory
144
+ end
145
+
146
+ def parse_factory_node(node)
147
+ factory_name_node, factory_config_node = node.children[2..3]
148
+
149
+ name = factory_name_node.children[0]
150
+ aliases = extract_aliases(factory_config_node)
151
+ parent = extract_parent(factory_config_node)
152
+ model_class_name = extract_model_class_name(factory_config_node)
153
+
154
+ Factory.new(name, aliases, parent, model_class_name)
155
+ end
156
+
157
+ def extract_aliases(factory_config_hash_node)
158
+ aliases_array = extract_hash_value(factory_config_hash_node, :aliases)
159
+ return [] if aliases_array&.type != :array
160
+
161
+ aliases_array.children.map(&:value)
162
+ end
163
+
164
+ def extract_parent(factory_config_hash_node)
165
+ parent_node = extract_hash_value(factory_config_hash_node, :parent)
166
+ parent_node&.value
167
+ end
168
+
169
+ def extract_model_class_name(factory_config_hash_node)
170
+ model_class_name_node = extract_hash_value(factory_config_hash_node, :class)
171
+
172
+ case model_class_name_node&.type
173
+ when :const
174
+ model_class_name_node.source.sub(/^::/, '')
175
+ when :str
176
+ model_class_name_node.value.sub(/^::/, '')
177
+ end
178
+ end
179
+
180
+ def extract_hash_value(node, hash_key)
181
+ return nil if node&.type != :hash
182
+
183
+ pairs = node.children.select { |child| child.type == :pair }
184
+ pairs.each do |pair|
185
+ key, value = pair.children
186
+ return value if key.value == hash_key
187
+ end
188
+
189
+ nil
190
+ end
191
+
192
+ def determine_parent(factory, parent_from_surrounding_block)
193
+ # If the factory specifies an explicit model class name, we don't need
194
+ # to resolve the parent to determine the model class name.
195
+ return nil if factory.model_class_name
196
+
197
+ factory.parent || parent_from_surrounding_block
198
+ end
199
+
200
+ def determine_model_class_name(factory, model_class_name_from_surrounding_block)
201
+ factory.model_class_name ||
202
+ model_class_name_from_surrounding_block ||
203
+ ActiveSupport::Inflector.camelize(factory.name)
204
+ end
205
+ end
206
+ # rubocop:enable Metrics/ModuleLength
207
+ end
208
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Flexport
5
- VERSION = '0.7.0'
5
+ VERSION = '0.10.2'
6
6
  end
7
7
  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.7.0
4
+ version: 0.10.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Flexport Engineering
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-31 00:00:00.000000000 Z
11
+ date: 2020-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -57,6 +57,7 @@ files:
57
57
  - lib/rubocop/cop/flexport_cops.rb
58
58
  - lib/rubocop/cop/mixin/engine_api.rb
59
59
  - lib/rubocop/cop/mixin/engine_node_context.rb
60
+ - lib/rubocop/cop/mixin/factory_bot_usage.rb
60
61
  - lib/rubocop/flexport.rb
61
62
  - lib/rubocop/flexport/inject.rb
62
63
  - lib/rubocop/flexport/version.rb
@@ -64,7 +65,7 @@ homepage: https://github.com/flexport/rubocop-flexport
64
65
  licenses:
65
66
  - MIT
66
67
  metadata: {}
67
- post_install_message:
68
+ post_install_message:
68
69
  rdoc_options: []
69
70
  require_paths:
70
71
  - lib
@@ -72,16 +73,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
72
73
  requirements:
73
74
  - - ">="
74
75
  - !ruby/object:Gem::Version
75
- version: '0'
76
+ version: '2.4'
76
77
  required_rubygems_version: !ruby/object:Gem::Requirement
77
78
  requirements:
78
79
  - - ">="
79
80
  - !ruby/object:Gem::Version
80
81
  version: '0'
81
82
  requirements: []
82
- rubyforge_project:
83
- rubygems_version: 2.7.6
84
- signing_key:
83
+ rubygems_version: 3.0.3
84
+ signing_key:
85
85
  specification_version: 4
86
86
  summary: RuboCop cops used at Flexport.
87
87
  test_files: []