Class: Rubocop::Config

Inherits:
Hash
  • Object
show all
Defined in:
lib/rubocop/config.rb

Overview

This class represents the configuration of the RuboCop application and all its cops. A Config is associated with a YAML configuration file from which it was read. Several different Configs can be used during a run of the rubocop program, if files in several directories are inspected.

Defined Under Namespace

Classes: ValidationError

Constant Summary collapse

DOTFILE =
'.rubocop.yml'
RUBOCOP_HOME =
File.realpath(File.join(File.dirname(__FILE__), '..', '..'))
DEFAULT_FILE =
File.join(RUBOCOP_HOME, 'config', 'default.yml')
AUTO_GENERATED_FILE =
'rubocop-todo.yml'

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(hash = {}, loaded_path = nil) ⇒ Config

Returns a new instance of Config.



175
176
177
178
179
# File 'lib/rubocop/config.rb', line 175

def initialize(hash = {}, loaded_path = nil)
  @hash = hash
  @loaded_path = loaded_path
  super(@hash)
end

Class Attribute Details

.debugObject Also known as: debug?

Returns the value of attribute debug.



25
26
27
# File 'lib/rubocop/config.rb', line 25

def debug
  @debug
end

Instance Attribute Details

#contains_auto_generated_configObject

Returns the value of attribute contains_auto_generated_config.



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

def contains_auto_generated_config
  @contains_auto_generated_config
end

#loaded_pathObject (readonly)

Returns the value of attribute loaded_path.



21
22
23
# File 'lib/rubocop/config.rb', line 21

def loaded_path
  @loaded_path
end

Class Method Details

.add_excludes_from_higher_level(config, highest_config) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/rubocop/config.rb', line 127

def add_excludes_from_higher_level(config, highest_config)
  if highest_config['AllCops'] && highest_config['AllCops']['Excludes']
    config['AllCops'] ||= {}
    config['AllCops']['Excludes'] ||= []
    highest_config['AllCops']['Excludes'].each do |path|
      unless path.is_a?(Regexp) || path.start_with?('/')
        path = File.join(File.dirname(highest_config.loaded_path), path)
      end
      config['AllCops']['Excludes'] << path
    end
    config['AllCops']['Excludes'].uniq!
  end
end

.base_configs(path, inherit_from) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/rubocop/config.rb', line 94

def base_configs(path, inherit_from)
  base_files = case inherit_from
               when nil then []
               when String then [inherit_from]
               when Array then inherit_from
               end
  base_files.map do |f|
    f = File.join(File.dirname(path), f) unless f.start_with?('/')
    print 'Inheriting ' if debug?
    load_file(f)
  end
end

.configuration_file_for(target_dir) ⇒ Object

Returns the path of .rubocop.yml searching upwards in the directory structure starting at the given directory where the inspected file is. If no .rubocop.yml is found there, the user's home directory is checked. If there's no .rubocop.yml there either, the path to the default file is returned.



112
113
114
# File 'lib/rubocop/config.rb', line 112

def configuration_file_for(target_dir)
  config_files_in_path(target_dir).first || DEFAULT_FILE
end

.configuration_from_file(config_file) ⇒ Object



116
117
118
119
120
121
122
123
124
125
# File 'lib/rubocop/config.rb', line 116

def configuration_from_file(config_file)
  config = load_file(config_file)
  found_files = config_files_in_path(config_file)
  if found_files.any? && found_files.last != config_file
    print 'AllCops/Excludes ' if debug?
    add_excludes_from_higher_level(config, load_file(found_files.last))
  end
  make_excludes_absolute(config)
  merge_with_default(config, config_file)
end

.default_configurationObject



141
142
143
144
145
146
# File 'lib/rubocop/config.rb', line 141

def default_configuration
  @default_configuration ||= begin
                               print 'Default ' if debug?
                               load_file(DEFAULT_FILE)
                             end
end

.load_file(path) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/rubocop/config.rb', line 28

def load_file(path)
  path = File.absolute_path(path)
  hash = YAML.load_file(path)
  puts "configuration from #{path}" if debug?
  contains_auto_generated_config = false

  base_configs(path, hash['inherit_from']).reverse.each do |base_config|
    if File.basename(base_config.loaded_path) == DOTFILE
      make_excludes_absolute(base_config)
    end
    base_config.each do |key, value|
      if value.is_a?(Hash)
        hash[key] = hash.has_key?(key) ? merge(value, hash[key]) : value
      end
    end
    if base_config.loaded_path.include?(AUTO_GENERATED_FILE)
      contains_auto_generated_config = true
    end
  end

  hash.delete('inherit_from')
  config = new(hash, path)
  config.warn_unless_valid
  config.contains_auto_generated_config = contains_auto_generated_config
  config
end

.make_excludes_absolute(config) ⇒ Object



55
56
57
58
59
60
61
62
63
64
65
# File 'lib/rubocop/config.rb', line 55

def make_excludes_absolute(config)
  if config['AllCops'] && config['AllCops']['Excludes']
    config['AllCops']['Excludes'].map! do |exclude_elem|
      if exclude_elem.is_a?(String) && !exclude_elem.start_with?('/')
        File.join(File.dirname(config.loaded_path), exclude_elem)
      else
        exclude_elem
      end
    end
  end
end

.merge(base_hash, derived_hash) ⇒ Object

Return an extended merge of two hashes. That is, a normal hash merge, with the addition that any value that is a hash, and occurs in both arguments (i.e., cop names), will also be merged.



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/rubocop/config.rb', line 75

def merge(base_hash, derived_hash)
  result = {}
  base_hash.each do |key, value|
    result[key] = if derived_hash.has_key?(key)
                    if value.is_a?(Hash)
                      value.merge(derived_hash[key])
                    else
                      derived_hash[key]
                    end
                  else
                    base_hash[key]
                  end
  end
  derived_hash.each do |key, value|
    result[key] = value unless base_hash.has_key?(key)
  end
  result
end

.merge_with_default(config, config_file) ⇒ Object



148
149
150
151
152
153
# File 'lib/rubocop/config.rb', line 148

def merge_with_default(config, config_file)
  result = new(merge(default_configuration, config), config_file)
  result.contains_auto_generated_config =
    config.contains_auto_generated_config
  result
end

.relative_path(path, base) ⇒ Object



67
68
69
70
# File 'lib/rubocop/config.rb', line 67

def relative_path(path, base)
  path_name = Pathname.new(File.expand_path(path))
  path_name.relative_path_from(Pathname.new(base)).to_s
end

Instance Method Details

#cop_enabled?(cop) ⇒ Boolean

Returns:

  • (Boolean)


185
186
187
# File 'lib/rubocop/config.rb', line 185

def cop_enabled?(cop)
  self[cop].nil? || self[cop]['Enabled']
end

#file_to_exclude?(file) ⇒ Boolean

Returns:

  • (Boolean)


229
230
231
232
# File 'lib/rubocop/config.rb', line 229

def file_to_exclude?(file)
  file = File.join(Dir.pwd, file) unless file.start_with?('/')
  patterns_to_exclude.any? { |pattern| match_path?(pattern, file) }
end

#file_to_include?(file) ⇒ Boolean

Returns:

  • (Boolean)


222
223
224
225
226
227
# File 'lib/rubocop/config.rb', line 222

def file_to_include?(file)
  relative_file_path = relative_path_to_loaded_dir(file)
  patterns_to_include.any? do |pattern|
    match_path?(pattern, relative_file_path)
  end
end

#for_cop(cop) ⇒ Object



181
182
183
# File 'lib/rubocop/config.rb', line 181

def for_cop(cop)
  self[cop]
end

#patterns_to_excludeObject



238
239
240
# File 'lib/rubocop/config.rb', line 238

def patterns_to_exclude
  @hash['AllCops']['Excludes']
end

#patterns_to_includeObject



234
235
236
# File 'lib/rubocop/config.rb', line 234

def patterns_to_include
  @hash['AllCops']['Includes']
end

#validateObject

TODO: This should be a private method



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/rubocop/config.rb', line 196

def validate
  # Don't validate RuboCop's own files. Avoids inifinite recursion.
  return if @loaded_path.start_with?(RUBOCOP_HOME)

  default_config = self.class.default_configuration

  valid_cop_names, invalid_cop_names = @hash.keys.partition do |key|
    default_config.has_key?(key)
  end

  invalid_cop_names.each do |name|
    fail ValidationError,
         "unrecognized cop #{name} found in #{loaded_path || self}"
  end

  valid_cop_names.each do |name|
    @hash[name].each_key do |param|
      unless default_config[name].has_key?(param)
        fail ValidationError,
             "unrecognized parameter #{name}:#{param} found " +
             "in #{loaded_path || self}"
      end
    end
  end
end

#warn_unless_validObject



189
190
191
192
193
# File 'lib/rubocop/config.rb', line 189

def warn_unless_valid
  validate
rescue Config::ValidationError => e
  puts "Warning: #{e.message}".color(:red)
end