class SCSSLint::Config
Loads and manages application configuration.
Constants
- DEFAULT_FILE
- FILE_NAME
- SEVERITIES
Attributes
Public Class Methods
# File lib/scss_lint/config.rb, line 14 def default load(DEFAULT_FILE, merge_with_default: false) end
# File lib/scss_lint/config.rb, line 45 def linter_name(linter) (linter.is_a?(Class) ? linter : linter.class).simple_name end
Loads a configuration from a file, merging it with the default configuration.
# File lib/scss_lint/config.rb, line 20 def load(file, options = {}) config_options = load_options_hash_from_file(file) config = new(config_options, file) # Need to call this before merging with the default configuration so # that plugins can override the default configuration while still being # overridden by the repo's configuration. config.load_plugins if options.fetch(:merge_with_default, true) config = default.extend(config) end config end
# File lib/scss_lint/config.rb, line 204 def initialize(options, file = Config.user_file) @options = options @warnings = [] @file = file validate_linters end
Returns the location of the user-wide scss-lint configuration.
This needs to be a method instead of a constant so that we can change the user's home directory in tests.
# File lib/scss_lint/config.rb, line 41 def user_file File.join(Dir.home, FILE_NAME) end
Private Class Methods
# File lib/scss_lint/config.rb, line 108 def apply_options_to_matching_linters(class_name_glob, current_options, linter_options) linter_names_matching_glob(class_name_glob).each do |linter_name| old_options = current_options['linters'].fetch(linter_name, {}) current_options['linters'][linter_name] = smart_merge(old_options, linter_options) end end
Convert any config options that accept a single value or an array to an array form so that merging works.
# File lib/scss_lint/config.rb, line 82 def convert_single_options_to_arrays(options) options = options.dup if options['exclude'] # Ensure exclude is an array, since we allow user to specify a single # string. options['exclude'] = [options['exclude']].flatten end options end
# File lib/scss_lint/config.rb, line 51 def default_options_hash @default_options_hash ||= load_options_hash_from_file(DEFAULT_FILE) end
Ensure all excludes are absolute paths
# File lib/scss_lint/config.rb, line 136 def ensure_exclude_paths_are_absolute(options, original_file) options = options.dup if options['exclude'] excludes = [options['exclude']].flatten options['exclude'] = excludes.map do |exclusion_glob| if exclusion_glob.start_with?('/') exclusion_glob else # Expand the path assuming it is relative to the config file itself File.expand_path(exclusion_glob, File.expand_path(File.dirname(original_file))) end end end options end
# File lib/scss_lint/config.rb, line 122 def ensure_linter_exclude_paths_are_absolute(options, original_file) options = options.dup options['linters'] ||= {} options['linters'].each_key do |linter_name| options['linters'][linter_name] = ensure_exclude_paths_are_absolute(options['linters'][linter_name], original_file) end options end
# File lib/scss_lint/config.rb, line 155 def ensure_severities_are_valid(options) unless severity_is_valid?(options) raise SCSSLint::Exceptions::InvalidConfiguration, 'Global `severity` configuration option must be one of [' \ "#{SEVERITIES.join(' | ')}]" end options['linters'].each do |linter_name, linter_options| next if severity_is_valid?(linter_options) raise SCSSLint::Exceptions::InvalidConfiguration, "#{linter_name} `severity` configuration option must be one " \ "of [#{SEVERITIES.join(' | ')}]" end end
# File lib/scss_lint/config.rb, line 115 def linter_names_matching_glob(class_name_glob) class_name_regex = /#{class_name_glob.gsub('*', '[^:]+')}/ LinterRegistry.linters.map { |linter_class| linter_name(linter_class) } .select { |linter_name| linter_name.match(class_name_regex) } end
For easy stubbing in tests
# File lib/scss_lint/config.rb, line 187 def load_file_contents(file) File.open(file, 'r').read end
Recursively load config files, fetching files specified by `include` directives and merging the file's config with the files specified.
# File lib/scss_lint/config.rb, line 57 def load_options_hash_from_file(file) file_contents = load_file_contents(file) begin options = if yaml = YAML.load(file_contents) yaml.to_hash else {} end rescue StandardError => e raise SCSSLint::Exceptions::InvalidConfiguration, "Invalid configuration: #{e.message}" end options = convert_single_options_to_arrays(options) options = merge_wildcard_linter_options(options) options = ensure_exclude_paths_are_absolute(options, file) options = ensure_linter_exclude_paths_are_absolute(options, file) ensure_severities_are_valid(options) options end
Merge options from wildcard linters into individual linter configs
# File lib/scss_lint/config.rb, line 95 def merge_wildcard_linter_options(options) options = options.dup # Cannot use `each_key` because the cycle adds new keys during iteration options.fetch('linters', {}).keys.each do |class_name| next unless class_name.include?('*') wildcard_options = options['linters'].delete(class_name) apply_options_to_matching_linters(class_name, options, wildcard_options) end options end
# File lib/scss_lint/config.rb, line 176 def path_relative_to_config(relative_include_path, base_config_path) if relative_include_path.start_with?('/') relative_include_path else path = File.join(File.dirname(base_config_path), relative_include_path) # Remove double backslashes appearing in Windows paths. path.sub(%r{^//}, File::SEPARATOR) end end
# File lib/scss_lint/config.rb, line 172 def severity_is_valid?(options) SEVERITIES.include?(options.fetch('severity', 'warning')) end
Merge two hashes, concatenating lists and further merging nested hashes.
# File lib/scss_lint/config.rb, line 192 def smart_merge(parent, child) parent.merge(child) do |_key, old, new| case old when Hash smart_merge(old, new) else new end end end
Public Instance Methods
Compares this configuration with another.
@param other [SCSSLint::Config] @return [true,false]
# File lib/scss_lint/config.rb, line 220 def ==(other) super || @options == other.options end
# File lib/scss_lint/config.rb, line 212 def [](key) @options[key] end
# File lib/scss_lint/config.rb, line 266 def disable_all_linters @options['linters'].each_value do |linter_config| linter_config['enabled'] = false end end
# File lib/scss_lint/config.rb, line 262 def disable_linter(linter) linter_options(linter)['enabled'] = false end
# File lib/scss_lint/config.rb, line 258 def enable_linter(linter) linter_options(linter)['enabled'] = true end
# File lib/scss_lint/config.rb, line 248 def enabled_linters LinterRegistry.extract_linters_from(@options['linters'].keys).select do |linter| linter_options(linter)['enabled'] end end
# File lib/scss_lint/config.rb, line 298 def exclude_file(file_path) abs_path = File.expand_path(file_path) @options['exclude'] ||= [] @options['exclude'] << abs_path end
# File lib/scss_lint/config.rb, line 286 def exclude_patterns @options.fetch('exclude', []) end
# File lib/scss_lint/config.rb, line 278 def excluded_file?(file_path) abs_path = File.expand_path(file_path) @options.fetch('exclude', []).any? do |exclusion_glob| File.fnmatch(exclusion_glob, abs_path) end end
# File lib/scss_lint/config.rb, line 290 def excluded_file_for_linter?(file_path, linter) abs_path = File.expand_path(file_path) linter_options(linter).fetch('exclude', []).any? do |exclusion_glob| File.fnmatch(exclusion_glob, abs_path) end end
Extend this {Config} with another configuration.
@return [SCSSLint::Config]
# File lib/scss_lint/config.rb, line 227 def extend(config) @options = self.class.send(:smart_merge, @options, config.options) @warnings += config.warnings self end
# File lib/scss_lint/config.rb, line 254 def linter_enabled?(linter) (linter_options(linter) || {}).fetch('enabled', false) end
# File lib/scss_lint/config.rb, line 272 def linter_options(linter) options = @options['linters'].fetch(self.class.linter_name(linter), {}) options['severity'] ||= @options['severity'] options end
# File lib/scss_lint/config.rb, line 233 def load_plugins previous_linters = LinterRegistry.linters plugins = SCSSLint::Plugins.new(self).load new_linters = LinterRegistry.linters - previous_linters plugins.each do |plugin| # Have the plugin options be overrideable by the local configuration @options = self.class.send(:smart_merge, plugin.config.options, @options) end # We only want to set defaults for linters introduced via plugins, # otherwise we'll accidentally enable some linters ensure_linters_have_default_options(new_linters) end
@return Array
# File lib/scss_lint/config.rb, line 306 def scss_files if (path = @options['scss_files']) && Array(path).any? Array(path).map { |p| Dir[p] }.flatten.uniq else [] end end
Private Instance Methods
# File lib/scss_lint/config.rb, line 336 def default_plugin_options(linter) { self.class.linter_name(linter) => { 'enabled' => true } } end
# File lib/scss_lint/config.rb, line 328 def ensure_linters_have_default_options(linters) linters.each do |linter| if linter_options(linter).nil? @options['linters'].merge!(default_plugin_options(linter)) end end end
# File lib/scss_lint/config.rb, line 316 def validate_linters return unless linters = @options['linters'] linters.each_key do |name| begin Linter.const_get(name) rescue NameError @warnings << "Linter #{name} does not exist; ignoring" end end end