Class: RuboCop::Cop::Registry

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/rubocop/cop/registry.rb

Overview

Registry that tracks all cops by their badge and department.

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cops = [], options = {}) ⇒ Registry

Returns a new instance of Registry.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/rubocop/cop/registry.rb', line 51

def initialize(cops = [], options = {})
  @departments = Set.new
  # Maps each badge to its cop class, or to the fully qualified constant name (a `String`)
  # when the cop is registered for lazy loading and its file is not loaded yet.
  # A single insertion-ordered map keeps the registration order stable no matter when
  # a lazy-loaded cop's file gets loaded, because reassigning an existing key preserves
  # its position; the cop execution order depends on this order.
  @cops_by_badge = {}

  @enrollment_queue = cops
  @options = options

  @enabled_cache = {}.compare_by_identity
  @disabled_cache = {}.compare_by_identity
  @disabled_names_cache = {}.compare_by_identity
  @warnings = {}
end

Class Attribute Details

.globalObject (readonly)

Returns the value of attribute global.



338
339
340
# File 'lib/rubocop/cop/registry.rb', line 338

def global
  @global
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



49
50
51
# File 'lib/rubocop/cop/registry.rb', line 49

def options
  @options
end

#warningsObject (readonly)

Returns the value of attribute warnings.



49
50
51
# File 'lib/rubocop/cop/registry.rb', line 49

def warnings
  @warnings
end

Class Method Details

.allObject



22
23
24
# File 'lib/rubocop/cop/registry.rb', line 22

def self.all
  global.without_department(:Test).cops
end

.qualified_cop?(name) ⇒ Boolean

Returns:

  • (Boolean)


44
45
46
47
# File 'lib/rubocop/cop/registry.rb', line 44

def self.qualified_cop?(name)
  badge = Badge.parse(name)
  global.qualify_badge(badge).first == badge
end

.qualified_cop_name(name, origin, warn: true) ⇒ Object



26
27
28
# File 'lib/rubocop/cop/registry.rb', line 26

def self.qualified_cop_name(name, origin, warn: true)
  global.qualified_cop_name(name, origin, warn: warn)
end

.reset!Object



40
41
42
# File 'lib/rubocop/cop/registry.rb', line 40

def self.reset!
  @global = new
end

.with_temporary_global(temp_global = global.dup) ⇒ Object

Changes momentarily the global registry Intended for testing purposes



32
33
34
35
36
37
38
# File 'lib/rubocop/cop/registry.rb', line 32

def self.with_temporary_global(temp_global = global.dup)
  previous = @global
  @global = temp_global
  yield
ensure
  @global = previous
end

Instance Method Details

#==(other) ⇒ Object



290
291
292
# File 'lib/rubocop/cop/registry.rb', line 290

def ==(other)
  cops == other.cops
end

#contains_cop_matching?(names) ⇒ Boolean

Returns:

  • (Boolean)


138
139
140
# File 'lib/rubocop/cop/registry.rb', line 138

def contains_cop_matching?(names)
  registered_badges.any? { |badge| badge.match_name?(names) }
end

#copsObject



222
223
224
225
226
# File 'lib/rubocop/cop/registry.rb', line 222

def cops
  clear_enrollment_queue
  load_all_lazy_cops
  @cops_by_badge.values
end

#cops_for_department(department) ⇒ Object



280
281
282
# File 'lib/rubocop/cop/registry.rb', line 280

def cops_for_department(department)
  with_department(department.to_sym).cops
end

#department?(name) ⇒ Boolean

Returns Checks if given name is department.

Returns:

  • (Boolean)

    Checks if given name is department



134
135
136
# File 'lib/rubocop/cop/registry.rb', line 134

def department?(name)
  departments.include?(name.to_sym)
end

#department_missing?(badge, name) ⇒ Boolean

Returns:

  • (Boolean)


188
189
190
# File 'lib/rubocop/cop/registry.rb', line 188

def department_missing?(badge, name)
  !badge.qualified? && unqualified_cop_names.include?(name)
end

#departmentsArray<Symbol>

Returns list of departments for current cops.

Returns:

  • (Array<Symbol>)

    list of departments for current cops.



104
105
106
107
# File 'lib/rubocop/cop/registry.rb', line 104

def departments
  clear_enrollment_queue
  @departments.to_a
end

#disabled(config) ⇒ Object



245
246
247
# File 'lib/rubocop/cop/registry.rb', line 245

def disabled(config)
  @disabled_cache[config] ||= reject { |cop| enabled?(cop, config) }
end

#disabled_names(config) ⇒ Array<String>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the names of the disabled cops without loading any of them.

Returns:



253
254
255
256
257
258
# File 'lib/rubocop/cop/registry.rb', line 253

def disabled_names(config)
  @disabled_names_cache[config] ||= registered_badges.filter_map do |badge|
    name = badge.to_s
    name unless enabled_cop_name?(name, config)
  end
end

#dismiss(cop) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/rubocop/cop/registry.rb', line 90

def dismiss(cop)
  dismissed = !@enrollment_queue.delete(cop).nil?

  # A lazy-loaded cop that was not enlisted yet is dismissed by removing its registration.
  # Anonymous cop classes cannot have a badge.
  if !cop.name.nil? && @cops_by_badge[cop.badge].is_a?(String)
    @cops_by_badge.delete(cop.badge)
    dismissed = true
  end

  raise "Cop #{cop} could not be dismissed" unless dismissed
end

#each(&block) ⇒ Object



306
307
308
# File 'lib/rubocop/cop/registry.rb', line 306

def each(&block)
  cops.each(&block)
end

#enabled(config) ⇒ Array<Class>

Returns the enabled cop classes, loading lazy-loaded cops only when they are enabled.

Returns:

  • (Array<Class>)


236
237
238
239
240
241
242
243
# File 'lib/rubocop/cop/registry.rb', line 236

def enabled(config)
  @enabled_cache[config] ||= registered_badges.filter_map do |badge|
    next unless enabled_cop_name?(badge.to_s, config)

    cop_or_name = @cops_by_badge[badge]
    cop_or_name.is_a?(String) ? load_lazy_cop(badge) : cop_or_name
  end
end

#enabled?(cop, config) ⇒ Boolean

Returns:

  • (Boolean)


260
261
262
# File 'lib/rubocop/cop/registry.rb', line 260

def enabled?(cop, config)
  enabled_cop_name?(cop.cop_name, config)
end

#enabled_pending_cop?(cop_cfg, config, cop = nil) ⇒ Boolean

Parameters:

  • cop (Class, String, nil) (defaults to: nil)

    the cop class or the qualified cop name

Returns:

  • (Boolean)


265
266
267
268
269
270
271
272
273
# File 'lib/rubocop/cop/registry.rb', line 265

def enabled_pending_cop?(cop_cfg, config, cop = nil)
  return false if @options[:disable_pending_cops]
  return false unless cop_cfg.fetch('Enabled') == 'pending'
  return true if @options[:enable_pending_cops]
  return config.enabled_new_cops? unless cop

  cop_name = cop.is_a?(String) ? cop : cop.cop_name
  config.enabled_new_cop?(cop_name)
end

#enlist(cop) ⇒ Object



86
87
88
# File 'lib/rubocop/cop/registry.rb', line 86

def enlist(cop)
  @enrollment_queue << cop
end

#filter_by_badge(options = {}) ⇒ Registry

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a new registry containing the cops whose badge satisfies the given block, keeping lazy-loaded cops unloaded.

Returns:



124
125
126
127
128
129
130
131
# File 'lib/rubocop/cop/registry.rb', line 124

def filter_by_badge(options = {})
  clear_enrollment_queue
  copy = self.class.new([], options)
  @cops_by_badge.each do |badge, cop_or_name|
    copy.add_entry(badge, cop_or_name) if yield(badge)
  end
  copy
end

#find_by_cop_name(cop_name) ⇒ Class?

Parameters:

Returns:

  • (Class, nil)


312
313
314
315
316
317
318
# File 'lib/rubocop/cop/registry.rb', line 312

def find_by_cop_name(cop_name)
  clear_enrollment_queue
  badge = Badge.parse(cop_name)
  cop_or_name = @cops_by_badge[badge]

  cop_or_name.is_a?(String) ? load_lazy_cop(badge) : cop_or_name
end

#find_cops_by_directive(directive) ⇒ Object

When a cop name is given returns a single-element array with the cop class. When a department name is given returns an array with all the cop classes for that department.



323
324
325
326
# File 'lib/rubocop/cop/registry.rb', line 323

def find_cops_by_directive(directive)
  cop = find_by_cop_name(directive)
  cop ? [cop] : cops_for_department(directive)
end

#freezeObject



328
329
330
331
332
333
# File 'lib/rubocop/cop/registry.rb', line 328

def freeze
  clear_enrollment_queue
  load_all_lazy_cops
  unqualified_cop_names # build cache
  super
end

#lazy_load(badge, constant_name) ⇒ Object

Registers a cop by its badge and constant name without loading the class. The constant is resolved (loading the cop's file through autoload) only when the cop class itself is needed.

Parameters:

  • badge (Badge, String)

    the badge, or the qualified cop name

  • constant_name (String)

    the fully qualified constant name



75
76
77
78
79
80
81
82
83
84
# File 'lib/rubocop/cop/registry.rb', line 75

def lazy_load(badge, constant_name)
  badge = Badge.parse(badge) if badge.is_a?(String)

  # File any cops enlisted earlier first, so that the registration
  # order follows the call order.
  clear_enrollment_queue

  @departments << badge.department
  @cops_by_badge[badge] = constant_name unless @cops_by_badge[badge].is_a?(Class)
end

#lengthObject



228
229
230
231
# File 'lib/rubocop/cop/registry.rb', line 228

def length
  clear_enrollment_queue
  @cops_by_badge.size
end

#namesObject



275
276
277
278
# File 'lib/rubocop/cop/registry.rb', line 275

def names
  clear_enrollment_queue
  @cops_by_badge.keys.map(&:to_s)
end

#names_for_department(department) ⇒ Object



284
285
286
287
288
# File 'lib/rubocop/cop/registry.rb', line 284

def names_for_department(department)
  department = department.to_sym

  registered_badges.filter_map { |badge| badge.to_s if badge.department == department }
end


192
193
194
195
196
197
198
# File 'lib/rubocop/cop/registry.rb', line 192

def print_department_missing_warning(name, path)
  message = "no department given for #{name}."
  if path.end_with?('.rb')
    message += ' Run `rubocop -a --only Migration/DepartmentName` to fix.'
  end
  emit_warning(path, message)
end

#qualified_cop_name(name, path, warn: true) ⇒ String

Note:

Emits a warning if the provided name has an incorrect namespace

Convert a user provided cop name into a properly namespaced name

Examples:

gives back a correctly qualified cop name


registry = RuboCop::Cop::Registry
registry.qualified_cop_name('Layout/EndOfLine', '') # => 'Layout/EndOfLine'

fixes incorrect namespaces


registry = RuboCop::Cop::Registry
registry.qualified_cop_name('Lint/EndOfLine', '') # => 'Layout/EndOfLine'

namespaces bare cop identifiers


registry = RuboCop::Cop::Registry
registry.qualified_cop_name('EndOfLine', '') # => 'Layout/EndOfLine'

passes back unrecognized cop names


registry = RuboCop::Cop::Registry
registry.qualified_cop_name('NotACop', '') # => 'NotACop'

Parameters:

  • name (String)

    Cop name extracted from config

  • path (String, nil)

    Path of file that name was extracted from

  • warn (Boolean) (defaults to: true)

    Print a warning if no department given for name

Returns:

  • (String)

    Qualified cop name

Raises:

  • (AmbiguousCopName)

    if a bare identifier with two possible namespaces is provided



174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/rubocop/cop/registry.rb', line 174

def qualified_cop_name(name, path, warn: true)
  badge = Badge.parse(name)
  print_department_missing_warning(name, path) if warn && department_missing?(badge, name)
  return name if registered?(badge)

  potential_badges = qualify_badge(badge)

  case potential_badges.size
  when 0 then name # No namespace found. Deal with it later in caller.
  when 1 then resolve_badge(badge, potential_badges.first, path, warn: warn)
  else raise AmbiguousCopName.new(badge, path, potential_badges)
  end
end

#qualify_badge(badge) ⇒ Object



208
209
210
211
212
213
# File 'lib/rubocop/cop/registry.rb', line 208

def qualify_badge(badge)
  clear_enrollment_queue
  @departments
    .map { |department| badge.with_department(department) }
    .select { |potential_badge| registered?(potential_badge) }
end

#select(&block) ⇒ Object



302
303
304
# File 'lib/rubocop/cop/registry.rb', line 302

def select(&block)
  cops.select(&block)
end

#sort!Object



294
295
296
297
298
299
300
# File 'lib/rubocop/cop/registry.rb', line 294

def sort!
  clear_enrollment_queue
  load_all_lazy_cops
  @cops_by_badge = @cops_by_badge.sort_by { |badge, _cop| badge.cop_name }.to_h

  self
end

#to_hHash{String => Array<Class>}

Returns:

  • (Hash{String => Array<Class>})


216
217
218
219
220
# File 'lib/rubocop/cop/registry.rb', line 216

def to_h
  clear_enrollment_queue
  load_all_lazy_cops
  @cops_by_badge.to_h { |_badge, cop| [cop.cop_name, [cop]] }
end

#unqualified_cop_namesObject



200
201
202
203
204
205
206
# File 'lib/rubocop/cop/registry.rb', line 200

def unqualified_cop_names
  clear_enrollment_queue

  @unqualified_cop_names ||= @cops_by_badge.keys.to_set do |badge|
    File.basename(badge.to_s)
  end << 'RedundantCopDisableDirective'
end

#warnings?(path) ⇒ Boolean

Returns:

  • (Boolean)


341
342
343
# File 'lib/rubocop/cop/registry.rb', line 341

def warnings?(path)
  @warnings[path]
end

#with_department(department) ⇒ Registry

Returns Cops for that specific department.

Returns:

  • (Registry)

    Cops for that specific department.



110
111
112
# File 'lib/rubocop/cop/registry.rb', line 110

def with_department(department)
  filter_by_badge { |badge| badge.department == department }
end

#without_department(department) ⇒ Registry

Returns Cops not for a specific department.

Returns:

  • (Registry)

    Cops not for a specific department.



115
116
117
# File 'lib/rubocop/cop/registry.rb', line 115

def without_department(department)
  filter_by_badge { |badge| badge.department != department }
end