gitlab-styles 7.0.0 → 9.0.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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +11 -0
  3. data/Gemfile +3 -3
  4. data/gitlab-styles.gemspec +6 -6
  5. data/lib/gitlab/styles/common/banned_constants.rb +28 -0
  6. data/lib/gitlab/styles/rubocop/model_helpers.rb +1 -1
  7. data/lib/gitlab/styles/rubocop.rb +2 -2
  8. data/lib/gitlab/styles/version.rb +1 -1
  9. data/lib/rubocop/cop/active_record_dependent.rb +32 -0
  10. data/lib/rubocop/cop/active_record_serialize.rb +20 -0
  11. data/lib/rubocop/cop/avoid_return_from_blocks.rb +79 -0
  12. data/lib/rubocop/cop/code_reuse/active_record.rb +80 -0
  13. data/lib/rubocop/cop/custom_error_class.rb +67 -0
  14. data/lib/rubocop/cop/fips/md5.rb +27 -0
  15. data/lib/rubocop/cop/fips/open_ssl.rb +31 -0
  16. data/lib/rubocop/cop/fips/sha1.rb +27 -0
  17. data/lib/rubocop/cop/gem_fetcher.rb +37 -0
  18. data/lib/rubocop/cop/in_batches.rb +18 -0
  19. data/lib/rubocop/cop/internal_affairs/deprecate_cop_helper.rb +42 -0
  20. data/lib/rubocop/cop/line_break_after_guard_clauses.rb +101 -0
  21. data/lib/rubocop/cop/line_break_around_conditional_block.rb +127 -0
  22. data/lib/rubocop/cop/migration/update_large_table.rb +61 -0
  23. data/lib/rubocop/cop/performance/rubyzip.rb +36 -0
  24. data/lib/rubocop/cop/polymorphic_associations.rb +25 -0
  25. data/lib/rubocop/cop/rails/include_url_helper.rb +28 -0
  26. data/lib/rubocop/cop/redirect_with_status.rb +46 -0
  27. data/lib/rubocop/cop/rspec/base.rb +12 -0
  28. data/lib/rubocop/cop/rspec/empty_line_after_let_block.rb +61 -0
  29. data/lib/rubocop/cop/rspec/empty_line_after_shared_example.rb +63 -0
  30. data/lib/rubocop/cop/rspec/example_starting_character.rb +122 -0
  31. data/lib/rubocop/cop/rspec/have_link_parameters.rb +45 -0
  32. data/lib/rubocop/cop/rspec/single_line_hook.rb +43 -0
  33. data/lib/rubocop/cop/rspec/verbose_include_metadata.rb +73 -0
  34. data/lib/rubocop/cop/style/hash_transformation.rb +81 -0
  35. data/lib/rubocop/cop/style/open_struct_use.rb +40 -0
  36. data/lib/rubocop/cop/without_reactive_cache.rb +16 -0
  37. data/rubocop-default.yml +1 -0
  38. data/rubocop-fips.yml +15 -0
  39. data/rubocop-layout.yml +1 -1
  40. data/rubocop-rspec.yml +13 -5
  41. metadata +44 -47
  42. data/lib/gitlab/styles/rubocop/cop/active_record_dependent.rb +0 -36
  43. data/lib/gitlab/styles/rubocop/cop/active_record_serialize.rb +0 -24
  44. data/lib/gitlab/styles/rubocop/cop/avoid_return_from_blocks.rb +0 -81
  45. data/lib/gitlab/styles/rubocop/cop/code_reuse/active_record.rb +0 -84
  46. data/lib/gitlab/styles/rubocop/cop/custom_error_class.rb +0 -73
  47. data/lib/gitlab/styles/rubocop/cop/gem_fetcher.rb +0 -41
  48. data/lib/gitlab/styles/rubocop/cop/in_batches.rb +0 -22
  49. data/lib/gitlab/styles/rubocop/cop/internal_affairs/deprecate_cop_helper.rb +0 -43
  50. data/lib/gitlab/styles/rubocop/cop/line_break_after_guard_clauses.rb +0 -104
  51. data/lib/gitlab/styles/rubocop/cop/line_break_around_conditional_block.rb +0 -132
  52. data/lib/gitlab/styles/rubocop/cop/migration/update_large_table.rb +0 -64
  53. data/lib/gitlab/styles/rubocop/cop/performance/rubyzip.rb +0 -39
  54. data/lib/gitlab/styles/rubocop/cop/polymorphic_associations.rb +0 -29
  55. data/lib/gitlab/styles/rubocop/cop/rails/include_url_helper.rb +0 -31
  56. data/lib/gitlab/styles/rubocop/cop/redirect_with_status.rb +0 -50
  57. data/lib/gitlab/styles/rubocop/cop/rspec/base.rb +0 -18
  58. data/lib/gitlab/styles/rubocop/cop/rspec/empty_line_after_final_let_it_be.rb +0 -51
  59. data/lib/gitlab/styles/rubocop/cop/rspec/empty_line_after_let_block.rb +0 -65
  60. data/lib/gitlab/styles/rubocop/cop/rspec/empty_line_after_shared_example.rb +0 -65
  61. data/lib/gitlab/styles/rubocop/cop/rspec/example_starting_character.rb +0 -124
  62. data/lib/gitlab/styles/rubocop/cop/rspec/have_link_parameters.rb +0 -48
  63. data/lib/gitlab/styles/rubocop/cop/rspec/single_line_hook.rb +0 -45
  64. data/lib/gitlab/styles/rubocop/cop/rspec/verbose_include_metadata.rb +0 -75
  65. data/lib/gitlab/styles/rubocop/cop/style/hash_transformation.rb +0 -87
  66. data/lib/gitlab/styles/rubocop/cop/style/open_struct_use.rb +0 -43
  67. data/lib/gitlab/styles/rubocop/cop/without_reactive_cache.rb +0 -20
  68. data/lib/gitlab/styles/rubocop/rspec/helpers.rb +0 -18
@@ -1,81 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module Styles
5
- module Rubocop
6
- module Cop
7
- # Checks for return inside blocks.
8
- # For more information see: https://gitlab.com/gitlab-org/gitlab-foss/issues/42889
9
- #
10
- # @example
11
- # # bad
12
- # call do
13
- # return if something
14
- #
15
- # do_something_else
16
- # end
17
- #
18
- # # good
19
- # call do
20
- # break if something
21
- #
22
- # do_something_else
23
- # end
24
- #
25
- class AvoidReturnFromBlocks < RuboCop::Cop::Cop
26
- MSG = 'Do not return from a block, use next or break instead.'
27
- DEF_METHODS = %i[define_method lambda].freeze
28
- WHITELISTED_METHODS = %i[each each_filename times loop].freeze
29
-
30
- def on_block(node)
31
- block_body = node.body
32
-
33
- return unless block_body
34
- return unless top_block?(node)
35
-
36
- block_body.each_node(:return) do |return_node|
37
- next if parent_blocks(node, return_node).all? { |block| whitelisted?(block) }
38
-
39
- add_offense(return_node)
40
- end
41
- end
42
-
43
- private
44
-
45
- def top_block?(node)
46
- current_node = node
47
- top_block = nil
48
-
49
- while current_node && current_node.type != :def
50
- top_block = current_node if current_node.block_type?
51
- current_node = current_node.parent
52
- end
53
-
54
- top_block == node
55
- end
56
-
57
- def parent_blocks(node, current_node)
58
- blocks = []
59
-
60
- until node == current_node || def?(current_node)
61
- blocks << current_node if current_node.block_type?
62
- current_node = current_node.parent
63
- end
64
-
65
- blocks << node if node == current_node && !def?(node)
66
- blocks
67
- end
68
-
69
- def def?(node)
70
- node.def_type? || node.defs_type? ||
71
- (node.block_type? && DEF_METHODS.include?(node.method_name))
72
- end
73
-
74
- def whitelisted?(block_node)
75
- WHITELISTED_METHODS.include?(block_node.method_name)
76
- end
77
- end
78
- end
79
- end
80
- end
81
- end
@@ -1,84 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module Styles
5
- module Rubocop
6
- module Cop
7
- module CodeReuse
8
- # Cop that denies the use of ActiveRecord methods outside of models.
9
- class ActiveRecord < RuboCop::Cop::Cop
10
- MSG = 'This method can only be used inside an ActiveRecord model: ' \
11
- 'https://gitlab.com/gitlab-org/gitlab-foss/issues/49653'
12
-
13
- # Various methods from ActiveRecord::Querying that are denied. We
14
- # exclude some generic ones such as `any?` and `first`, as these may
15
- # lead to too many false positives, since `Array` also supports these
16
- # methods.
17
- #
18
- # The keys of this Hash are the denied method names. The values are
19
- # booleans that indicate if the method should only be denied if any
20
- # arguments are provided.
21
- NOT_ALLOWED = {
22
- average: true,
23
- calculate: true,
24
- count_by_sql: true,
25
- create_with: true,
26
- distinct: false,
27
- eager_load: true,
28
- exists?: true,
29
- find_by: true,
30
- find_by!: true,
31
- find_by_sql: true,
32
- find_each: true,
33
- find_in_batches: true,
34
- find_or_create_by: true,
35
- find_or_create_by!: true,
36
- find_or_initialize_by: true,
37
- first!: false,
38
- first_or_create: true,
39
- first_or_create!: true,
40
- first_or_initialize: true,
41
- from: true,
42
- group: true,
43
- having: true,
44
- ids: false,
45
- includes: true,
46
- joins: true,
47
- lock: false,
48
- many?: false,
49
- offset: true,
50
- order: true,
51
- pluck: true,
52
- preload: true,
53
- readonly: false,
54
- references: true,
55
- reorder: true,
56
- rewhere: true,
57
- take: false,
58
- take!: false,
59
- unscope: false,
60
- where: false,
61
- with: true
62
- }.freeze
63
-
64
- def on_send(node)
65
- receiver = node.children[0]
66
- send_name = node.children[1]
67
- first_arg = node.children[2]
68
-
69
- return unless receiver && NOT_ALLOWED.key?(send_name)
70
-
71
- # If the rule requires an argument to be given, but none are
72
- # provided, we won't register an offense. This prevents us from
73
- # adding offenses for `project.group`, while still covering
74
- # `Project.group(:name)`.
75
- return if NOT_ALLOWED[send_name] && !first_arg
76
-
77
- add_offense(node, location: :selector)
78
- end
79
- end
80
- end
81
- end
82
- end
83
- end
84
- end
@@ -1,73 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module Styles
5
- module Rubocop
6
- module Cop
7
- # This cop makes sure that custom error classes, when empty, are declared
8
- # with Class.new.
9
- #
10
- # @example
11
- # # bad
12
- # class FooError < StandardError
13
- # end
14
- #
15
- # # okish
16
- # class FooError < StandardError; end
17
- #
18
- # # good
19
- # FooError = Class.new(StandardError)
20
- class CustomErrorClass < RuboCop::Cop::Cop
21
- MSG = 'Use `Class.new(SuperClass)` to define an empty custom error class.'
22
-
23
- def on_class(node)
24
- parent = node.parent_class
25
- body = node.body
26
-
27
- return if body
28
-
29
- parent_klass = class_name_from_node(parent)
30
-
31
- return unless parent_klass&.to_s&.end_with?('Error')
32
-
33
- add_offense(node)
34
- end
35
-
36
- def autocorrect(node)
37
- klass = node.identifier
38
- parent = node.parent_class
39
-
40
- replacement = "#{class_name_from_node(klass)} = Class.new(#{class_name_from_node(parent)})"
41
-
42
- lambda do |corrector|
43
- corrector.replace(node.source_range, replacement)
44
- end
45
- end
46
-
47
- private
48
-
49
- # The nested constant `Foo::Bar::Baz` looks like:
50
- #
51
- # s(:const,
52
- # s(:const,
53
- # s(:const, nil, :Foo), :Bar), :Baz)
54
- #
55
- # So recurse through that to get the name as written in the source.
56
- #
57
- def class_name_from_node(node, suffix = nil)
58
- return unless node&.type == :const
59
-
60
- name = node.children[1].to_s
61
- name = "#{name}::#{suffix}" if suffix
62
-
63
- if node.children[0]
64
- class_name_from_node(node.children[0], name)
65
- else
66
- name
67
- end
68
- end
69
- end
70
- end
71
- end
72
- end
73
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module Styles
5
- module Rubocop
6
- module Cop
7
- # This cop prevents usage of the `git` and `github` arguments to `gem` in a
8
- # `Gemfile` in order to avoid additional points of failure beyond
9
- # rubygems.org.
10
- class GemFetcher < RuboCop::Cop::Cop
11
- MSG = 'Do not use gems from git repositories, only use gems from RubyGems.'
12
-
13
- GIT_KEYS = [:git, :github].freeze
14
-
15
- def on_send(node)
16
- return unless gemfile?(node)
17
-
18
- func_name = node.children[1]
19
- return unless func_name == :gem
20
-
21
- node.children.last.each_node(:pair) do |pair|
22
- key_name = pair.children[0].children[0].to_sym
23
- add_offense(node, location: pair.source_range) if GIT_KEYS.include?(key_name)
24
- end
25
- end
26
-
27
- private
28
-
29
- def gemfile?(node)
30
- node
31
- .location
32
- .expression
33
- .source_buffer
34
- .name
35
- .end_with?("Gemfile")
36
- end
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../model_helpers'
4
-
5
- module Gitlab
6
- module Styles
7
- module Rubocop
8
- module Cop
9
- # Cop that prevents the use of `in_batches`
10
- class InBatches < RuboCop::Cop::Cop
11
- MSG = 'Do not use `in_batches`, use `each_batch` from the EachBatch module instead'
12
-
13
- def on_send(node)
14
- return unless node.children[1] == :in_batches
15
-
16
- add_offense(node, location: :selector)
17
- end
18
- end
19
- end
20
- end
21
- end
22
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module Styles
5
- module Rubocop
6
- module Cop
7
- module InternalAffairs
8
- # Cop that denies the use of CopHelper.
9
- class DeprecateCopHelper < RuboCop::Cop::Cop
10
- MSG = 'Do not use `CopHelper` or methods from it, use improved patterns described in https://www.rubydoc.info/gems/rubocop/RuboCop/RSpec/ExpectOffense'
11
-
12
- def_node_matcher :cop_helper, <<~PATTERN
13
- (send nil? ${:include :extend :prepend}
14
- (const _ {:CopHelper}))
15
- PATTERN
16
-
17
- def_node_search :cop_helper_method, <<~PATTERN
18
- (send nil? {:inspect_source :inspect_source_file :parse_source :autocorrect_source_file :autocorrect_source :_investigate} ...)
19
- PATTERN
20
-
21
- def_node_search :cop_helper_method_on_instance, <<~PATTERN
22
- (send (send nil? _) {:messages :highlights :offenses} ...)
23
- PATTERN
24
-
25
- def on_send(node)
26
- cop_helper(node) do
27
- add_offense(node)
28
- end
29
-
30
- cop_helper_method(node) do
31
- add_offense(node)
32
- end
33
-
34
- cop_helper_method_on_instance(node) do
35
- add_offense(node)
36
- end
37
- end
38
- end
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,104 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module Styles
5
- module Rubocop
6
- module Cop
7
- # Ensures a line break after guard clauses.
8
- #
9
- # @example
10
- # # bad
11
- # return unless condition
12
- # do_stuff
13
- #
14
- # # good
15
- # return unless condition
16
- #
17
- # do_stuff
18
- #
19
- # # bad
20
- # raise if condition
21
- # do_stuff
22
- #
23
- # # good
24
- # raise if condition
25
- #
26
- # do_stuff
27
- #
28
- # Multiple guard clauses are allowed without
29
- # line break.
30
- #
31
- # # good
32
- # return unless condition_a
33
- # return unless condition_b
34
- #
35
- # do_stuff
36
- #
37
- # Guard clauses in case statement are allowed without
38
- # line break.
39
- #
40
- # # good
41
- # case model
42
- # when condition_a
43
- # return true unless condition_b
44
- # when
45
- # ...
46
- # end
47
- #
48
- # Guard clauses before end are allowed without
49
- # line break.
50
- #
51
- # # good
52
- # if condition_a
53
- # do_something
54
- # else
55
- # do_something_else
56
- # return unless condition
57
- # end
58
- #
59
- # do_something_more
60
- class LineBreakAfterGuardClauses < RuboCop::Cop::Cop
61
- MSG = 'Add a line break after guard clauses'
62
-
63
- def_node_matcher :guard_clause_node?, <<-PATTERN
64
- [{(send nil? {:raise :fail :throw} ...) return break next} single_line?]
65
- PATTERN
66
-
67
- def on_if(node)
68
- return unless node.single_line?
69
- return unless guard_clause?(node)
70
- return if next_line(node).blank? || clause_last_line?(next_line(node)) || guard_clause?(next_sibling(node))
71
-
72
- add_offense(node)
73
- end
74
-
75
- def autocorrect(node)
76
- lambda do |corrector|
77
- corrector.insert_after(node.loc.expression, "\n")
78
- end
79
- end
80
-
81
- private
82
-
83
- def guard_clause?(node)
84
- return false unless node.if_type?
85
-
86
- guard_clause_node?(node.if_branch)
87
- end
88
-
89
- def next_sibling(node)
90
- node.parent.children[node.sibling_index + 1]
91
- end
92
-
93
- def next_line(node)
94
- processed_source[node.loc.line]
95
- end
96
-
97
- def clause_last_line?(line)
98
- line =~ /^\s*(?:end|elsif|else|when|rescue|ensure)/
99
- end
100
- end
101
- end
102
- end
103
- end
104
- end
@@ -1,132 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module Styles
5
- module Rubocop
6
- module Cop
7
- # Ensures a line break around conditional blocks.
8
- #
9
- # @example
10
- # # bad
11
- # do_something
12
- # if condition
13
- # do_extra_stuff
14
- # end
15
- # do_something_more
16
- #
17
- # # good
18
- # do_something
19
- #
20
- # if condition
21
- # do_extra_stuff
22
- # end
23
- #
24
- # do_something_more
25
- #
26
- # # bad
27
- # do_something
28
- # unless condition
29
- # do_extra_stuff
30
- # end
31
- #
32
- # do_something_more
33
- #
34
- # # good
35
- # def a_method
36
- # if condition
37
- # do_something
38
- # end
39
- # end
40
- #
41
- # # good
42
- # on_block do
43
- # if condition
44
- # do_something
45
- # end
46
- # end
47
- class LineBreakAroundConditionalBlock < RuboCop::Cop::Cop
48
- include RuboCop::Cop::RangeHelp
49
-
50
- MSG = 'Add a line break around conditional blocks'
51
-
52
- def on_if(node)
53
- # This cop causes errors in haml files, so let's skip those
54
- return if in_haml?(node)
55
- return if node.single_line?
56
- return unless node.if? || node.unless?
57
-
58
- add_offense(node) unless previous_line_valid?(node)
59
- add_offense(node) unless last_line_valid?(node)
60
- end
61
-
62
- def autocorrect(node)
63
- lambda do |corrector|
64
- line = range_by_whole_lines(node.source_range)
65
-
66
- corrector.insert_before(line, "\n") unless previous_line_valid?(node)
67
- corrector.insert_after(line, "\n") unless last_line_valid?(node)
68
- end
69
- end
70
-
71
- private
72
-
73
- def previous_line_valid?(node)
74
- previous_line(node).empty? ||
75
- start_clause_line?(previous_line(node)) ||
76
- block_start?(previous_line(node)) ||
77
- begin_line?(previous_line(node)) ||
78
- assignment_line?(previous_line(node)) ||
79
- rescue_line?(previous_line(node))
80
- end
81
-
82
- def last_line_valid?(node)
83
- last_line(node).empty? ||
84
- end_line?(last_line(node)) ||
85
- end_clause_line?(last_line(node))
86
- end
87
-
88
- def previous_line(node)
89
- processed_source[node.loc.line - 2]
90
- end
91
-
92
- def last_line(node)
93
- processed_source[node.loc.last_line]
94
- end
95
-
96
- def start_clause_line?(line)
97
- line =~ /^\s*(def|=end|#|module|class|if|unless|else|elsif|ensure|when)/
98
- end
99
-
100
- def end_clause_line?(line)
101
- line =~ /^\s*(#|rescue|else|elsif|when)/
102
- end
103
-
104
- def begin_line?(line)
105
- # an assignment followed by a begin or ust a begin
106
- line =~ /^\s*(@?(\w|\|+|=|\[|\]|\s)+begin|begin)/
107
- end
108
-
109
- def assignment_line?(line)
110
- line =~ /^\s*.*=/
111
- end
112
-
113
- def rescue_line?(line)
114
- line =~ /^\s*rescue/
115
- end
116
-
117
- def block_start?(line)
118
- line.match(/ (do|{)( \|.*?\|)?\s?(#.+)?\z/)
119
- end
120
-
121
- def end_line?(line)
122
- line =~ /^\s*(end|})/
123
- end
124
-
125
- def in_haml?(node)
126
- node.location.expression.source_buffer.name.end_with?('.haml.rb')
127
- end
128
- end
129
- end
130
- end
131
- end
132
- end
@@ -1,64 +0,0 @@
1
- # frozen_string_literal: true
2
- require_relative '../../migration_helpers'
3
-
4
- module Gitlab
5
- module Styles
6
- module Rubocop
7
- module Cop
8
- module Migration
9
- # This cop checks for methods that may lead to batch type issues on a table that's been
10
- # explicitly denied because of its size.
11
- #
12
- # Even though though these methods perform functions to avoid
13
- # downtime, using it with tables with millions of rows still causes a
14
- # significant delay in the deploy process and is best avoided.
15
- #
16
- # See https://gitlab.com/gitlab-com/infrastructure/issues/1602 for more
17
- # information.
18
- class UpdateLargeTable < RuboCop::Cop::Cop
19
- include MigrationHelpers
20
-
21
- MSG = 'Using `%s` on the `%s` table will take a long time to ' \
22
- 'complete, and should be avoided unless absolutely ' \
23
- 'necessary'
24
-
25
- def_node_matcher :batch_update?, <<~PATTERN
26
- (send nil? ${#denied_method?}
27
- (sym $...)
28
- ...)
29
- PATTERN
30
-
31
- def on_send(node)
32
- return if denied_tables.empty? || denied_methods.empty?
33
- return unless in_migration?(node)
34
-
35
- matches = batch_update?(node)
36
- return unless matches
37
-
38
- update_method = matches.first
39
- table = matches.last.to_a.first
40
-
41
- return unless denied_tables.include?(table)
42
-
43
- add_offense(node, message: format(MSG, update_method, table))
44
- end
45
-
46
- private
47
-
48
- def denied_tables
49
- cop_config['DeniedTables'] || []
50
- end
51
-
52
- def denied_method?(method_name)
53
- denied_methods.include?(method_name)
54
- end
55
-
56
- def denied_methods
57
- cop_config['DeniedMethods'] || []
58
- end
59
- end
60
- end
61
- end
62
- end
63
- end
64
- end
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Gitlab
4
- module Styles
5
- module Rubocop
6
- module Cop
7
- module Performance
8
- # This cop flags inefficient uses of rubyzip's Zip::File, since when instantiated
9
- # it reads the file's Central Directory into memory entirely. For zips with many
10
- # files and directories, this can be very expensive even when the archive's size
11
- # in bytes is small.
12
- #
13
- # See also:
14
- # - https://github.com/rubyzip/rubyzip/issues/506
15
- # - https://github.com/rubyzip/rubyzip#notes-on-zipinputstream
16
- class Rubyzip < RuboCop::Cop::Cop
17
- MSG = 'Be careful when opening or iterating zip files via Zip::File. ' \
18
- 'Zip archives may contain many entries, and their file index is ' \
19
- 'read into memory upon construction, which can lead to ' \
20
- 'high memory use and poor performance. ' \
21
- 'Consider iterating archive entries via Zip::InputStream instead.'
22
-
23
- def_node_matcher :reads_central_directory?, <<-PATTERN
24
- (send
25
- (const
26
- (const {nil? (cbase)} :Zip) :File) {:new :open :foreach} ...)
27
- PATTERN
28
-
29
- def on_send(node)
30
- return unless reads_central_directory?(node)
31
-
32
- add_offense(node)
33
- end
34
- end
35
- end
36
- end
37
- end
38
- end
39
- end