2017-11-13 21:20:22 +00:00
|
|
|
|
require "yaml"
|
2019-01-12 21:19:00 +00:00
|
|
|
|
require "./glob_utils"
|
2017-11-13 21:20:22 +00:00
|
|
|
|
|
2017-11-15 18:49:09 +00:00
|
|
|
|
# A configuration entry for `Ameba::Runner`.
|
|
|
|
|
#
|
|
|
|
|
# Config can be loaded from configuration YAML file and adjusted.
|
|
|
|
|
#
|
|
|
|
|
# ```
|
|
|
|
|
# config = Config.load
|
|
|
|
|
# config.formatter = my_formatter
|
|
|
|
|
# ```
|
|
|
|
|
#
|
2023-03-06 20:19:47 +00:00
|
|
|
|
# By default config loads `.ameba.yml` file located in a current
|
|
|
|
|
# working directory.
|
2017-11-15 18:49:09 +00:00
|
|
|
|
#
|
2023-03-06 20:19:47 +00:00
|
|
|
|
# If it cannot be found until reaching the root directory, then it will be
|
|
|
|
|
# searched for in the user’s global config locations, which consists of a
|
|
|
|
|
# dotfile or a config file inside the XDG Base Directory specification.
|
|
|
|
|
#
|
|
|
|
|
# - `~/.ameba.yml`
|
|
|
|
|
# - `$XDG_CONFIG_HOME/ameba/config.yml` (expands to `~/.config/ameba/config.yml`
|
|
|
|
|
# if `$XDG_CONFIG_HOME` is not set)
|
|
|
|
|
#
|
|
|
|
|
# If both files exist, the dotfile will be selected.
|
|
|
|
|
#
|
|
|
|
|
# As an example, if Ameba is invoked from inside `/path/to/project/lib/utils`,
|
|
|
|
|
# then it will use the config as specified inside the first of the following files:
|
|
|
|
|
#
|
|
|
|
|
# - `/path/to/project/lib/utils/.ameba.yml`
|
|
|
|
|
# - `/path/to/project/lib/.ameba.yml`
|
|
|
|
|
# - `/path/to/project/.ameba.yml`
|
|
|
|
|
# - `/path/to/.ameba.yml`
|
|
|
|
|
# - `/path/.ameba.yml`
|
|
|
|
|
# - `/.ameba.yml`
|
|
|
|
|
# - `~/.ameba.yml`
|
|
|
|
|
# - `~/.config/ameba/config.yml`
|
2017-11-13 21:20:22 +00:00
|
|
|
|
class Ameba::Config
|
2019-01-12 21:19:00 +00:00
|
|
|
|
include GlobUtils
|
|
|
|
|
|
2018-01-25 14:40:32 +00:00
|
|
|
|
AVAILABLE_FORMATTERS = {
|
|
|
|
|
progress: Formatter::DotFormatter,
|
|
|
|
|
todo: Formatter::TODOFormatter,
|
|
|
|
|
flycheck: Formatter::FlycheckFormatter,
|
|
|
|
|
silent: Formatter::BaseFormatter,
|
2018-01-30 13:46:11 +00:00
|
|
|
|
disabled: Formatter::DisabledFormatter,
|
2018-05-11 18:09:15 +00:00
|
|
|
|
json: Formatter::JSONFormatter,
|
2018-01-25 14:40:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-03-06 19:18:49 +00:00
|
|
|
|
XDG_CONFIG_HOME = ENV.fetch("XDG_CONFIG_HOME", "~/.config")
|
2023-03-06 19:42:58 +00:00
|
|
|
|
|
|
|
|
|
FILENAME = ".ameba.yml"
|
|
|
|
|
DEFAULT_PATH = Path[Dir.current] / FILENAME
|
|
|
|
|
DEFAULT_PATHS = {
|
|
|
|
|
Path["~"] / FILENAME,
|
2023-03-06 19:18:49 +00:00
|
|
|
|
Path[XDG_CONFIG_HOME] / "ameba/config.yml",
|
2023-03-06 04:32:55 +00:00
|
|
|
|
}
|
2019-12-28 18:59:41 +00:00
|
|
|
|
|
2023-08-05 15:15:50 +00:00
|
|
|
|
SOURCES_GLOB = "**/*.cr"
|
2019-12-28 18:59:41 +00:00
|
|
|
|
|
2017-11-23 17:49:45 +00:00
|
|
|
|
getter rules : Array(Rule::Base)
|
2019-05-11 18:17:18 +00:00
|
|
|
|
property severity = Severity::Convention
|
2017-11-13 21:20:22 +00:00
|
|
|
|
|
2019-12-28 18:59:41 +00:00
|
|
|
|
# Returns a list of paths (with wildcards) to files.
|
|
|
|
|
# Represents a list of sources to be inspected.
|
|
|
|
|
# If globs are not set, it will return default list of files.
|
|
|
|
|
#
|
|
|
|
|
# ```
|
|
|
|
|
# config = Ameba::Config.load
|
|
|
|
|
# config.globs = ["**/*.cr"]
|
|
|
|
|
# config.globs
|
|
|
|
|
# ```
|
|
|
|
|
property globs : Array(String)
|
|
|
|
|
|
2020-03-22 12:18:07 +00:00
|
|
|
|
# Represents a list of paths to exclude from globs.
|
|
|
|
|
# Can have wildcards.
|
|
|
|
|
#
|
|
|
|
|
# ```
|
|
|
|
|
# config = Ameba::Config.load
|
|
|
|
|
# config.excluded = ["spec", "src/server/*.cr"]
|
|
|
|
|
# ```
|
|
|
|
|
property excluded : Array(String)
|
|
|
|
|
|
2022-12-09 23:20:20 +00:00
|
|
|
|
# Returns `true` if correctable issues should be autocorrected.
|
2021-10-24 18:58:32 +00:00
|
|
|
|
property? autocorrect = false
|
|
|
|
|
|
2018-09-02 21:17:56 +00:00
|
|
|
|
@rule_groups : Hash(String, Array(Rule::Base))
|
2018-06-18 07:25:06 +00:00
|
|
|
|
|
2017-11-15 18:49:09 +00:00
|
|
|
|
# Creates a new instance of `Ameba::Config` based on YAML parameters.
|
|
|
|
|
#
|
|
|
|
|
# `Config.load` uses this constructor to instantiate new config by YAML file.
|
2019-12-28 18:59:41 +00:00
|
|
|
|
protected def initialize(config : YAML::Any)
|
2018-05-22 14:55:44 +00:00
|
|
|
|
@rules = Rule.rules.map &.new(config).as(Rule::Base)
|
2018-06-18 07:25:06 +00:00
|
|
|
|
@rule_groups = @rules.group_by &.group
|
2020-03-22 12:18:07 +00:00
|
|
|
|
@excluded = load_array_section(config, "Excluded")
|
2023-08-04 20:48:35 +00:00
|
|
|
|
@globs = load_array_section(config, "Globs", default_globs)
|
2018-01-25 14:40:32 +00:00
|
|
|
|
|
2021-12-09 20:33:47 +00:00
|
|
|
|
return unless formatter_name = load_formatter_name(config)
|
|
|
|
|
self.formatter = formatter_name
|
2017-11-13 21:20:22 +00:00
|
|
|
|
end
|
|
|
|
|
|
2017-11-15 18:49:09 +00:00
|
|
|
|
# Loads YAML configuration file by `path`.
|
|
|
|
|
#
|
|
|
|
|
# ```
|
|
|
|
|
# config = Ameba::Config.load
|
|
|
|
|
# ```
|
2023-03-08 13:35:31 +00:00
|
|
|
|
def self.load(path = nil, colors = true, skip_reading_config = false)
|
2019-06-06 16:10:26 +00:00
|
|
|
|
Colorize.enabled = colors
|
2023-03-08 13:35:31 +00:00
|
|
|
|
content = if skip_reading_config
|
|
|
|
|
"{}"
|
|
|
|
|
else
|
|
|
|
|
read_config(path) || "{}"
|
|
|
|
|
end
|
2017-11-30 09:27:52 +00:00
|
|
|
|
Config.new YAML.parse(content)
|
2018-01-25 14:40:32 +00:00
|
|
|
|
rescue e
|
2023-07-25 07:46:13 +00:00
|
|
|
|
raise "Unable to load config file: #{e.message}"
|
2018-01-25 14:40:32 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-03-06 20:22:47 +00:00
|
|
|
|
protected def self.read_config(path = nil)
|
2023-03-06 04:32:55 +00:00
|
|
|
|
if path
|
2023-07-25 07:43:08 +00:00
|
|
|
|
raise ArgumentError.new("Config file does not exist #{path}") unless File.exists?(path)
|
|
|
|
|
return File.read(path)
|
2023-03-06 04:32:55 +00:00
|
|
|
|
end
|
2023-03-06 20:22:47 +00:00
|
|
|
|
each_config_path do |config_path|
|
|
|
|
|
return File.read(config_path) if File.exists?(config_path)
|
|
|
|
|
end
|
|
|
|
|
end
|
2023-03-06 04:32:55 +00:00
|
|
|
|
|
2023-03-06 20:22:47 +00:00
|
|
|
|
protected def self.each_config_path(&)
|
|
|
|
|
path = Path[DEFAULT_PATH].expand(home: true)
|
2023-03-06 04:32:55 +00:00
|
|
|
|
|
2023-03-06 20:22:47 +00:00
|
|
|
|
search_paths = path.parents
|
2023-03-06 04:32:55 +00:00
|
|
|
|
search_paths.reverse_each do |search_path|
|
2023-03-06 20:22:47 +00:00
|
|
|
|
yield search_path / FILENAME
|
2023-03-06 04:32:55 +00:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
DEFAULT_PATHS.each do |default_path|
|
2023-03-06 20:22:47 +00:00
|
|
|
|
yield default_path
|
2023-03-06 04:32:55 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2018-01-25 14:40:32 +00:00
|
|
|
|
def self.formatter_names
|
2021-01-11 18:13:58 +00:00
|
|
|
|
AVAILABLE_FORMATTERS.keys.join('|')
|
2017-11-13 21:20:22 +00:00
|
|
|
|
end
|
|
|
|
|
|
2020-03-22 12:18:07 +00:00
|
|
|
|
# Returns a list of sources matching globs and excluded sections.
|
2017-11-15 18:49:09 +00:00
|
|
|
|
#
|
|
|
|
|
# ```
|
|
|
|
|
# config = Ameba::Config.load
|
2019-01-12 21:19:00 +00:00
|
|
|
|
# config.sources # => list of default sources
|
|
|
|
|
# config.globs = ["**/*.cr"]
|
2020-03-22 12:18:07 +00:00
|
|
|
|
# config.excluded = ["spec"]
|
2019-01-12 21:19:00 +00:00
|
|
|
|
# config.sources # => list of sources pointing to files found by the wildcards
|
2017-11-15 18:49:09 +00:00
|
|
|
|
# ```
|
2019-01-12 21:19:00 +00:00
|
|
|
|
def sources
|
2020-03-22 12:18:07 +00:00
|
|
|
|
(find_files_by_globs(globs) - find_files_by_globs(excluded))
|
2019-01-12 21:19:00 +00:00
|
|
|
|
.map { |path| Source.new File.read(path), path }
|
2017-11-13 21:20:22 +00:00
|
|
|
|
end
|
|
|
|
|
|
2017-11-15 18:49:09 +00:00
|
|
|
|
# Returns a formatter to be used while inspecting files.
|
|
|
|
|
# If formatter is not set, it will return default formatter.
|
|
|
|
|
#
|
|
|
|
|
# ```
|
|
|
|
|
# config = Ameba::Config.load
|
|
|
|
|
# config.formatter = custom_formatter
|
|
|
|
|
# config.formatter
|
|
|
|
|
# ```
|
2021-01-11 18:13:58 +00:00
|
|
|
|
property formatter : Formatter::BaseFormatter do
|
|
|
|
|
Formatter::DotFormatter.new
|
2017-11-13 21:20:22 +00:00
|
|
|
|
end
|
|
|
|
|
|
2018-01-25 14:40:32 +00:00
|
|
|
|
# Sets formatter by name.
|
|
|
|
|
#
|
|
|
|
|
# ```
|
|
|
|
|
# config = Ameba::Config.load
|
|
|
|
|
# config.formatter = :progress
|
|
|
|
|
# ```
|
|
|
|
|
def formatter=(name : String | Symbol)
|
2022-11-14 00:24:29 +00:00
|
|
|
|
unless formatter = AVAILABLE_FORMATTERS[name]?
|
2018-01-25 14:40:32 +00:00
|
|
|
|
raise "Unknown formatter `#{name}`. Use one of #{Config.formatter_names}."
|
|
|
|
|
end
|
2022-11-14 00:24:29 +00:00
|
|
|
|
@formatter = formatter.new
|
2018-01-25 14:40:32 +00:00
|
|
|
|
end
|
|
|
|
|
|
2017-11-23 17:49:45 +00:00
|
|
|
|
# Updates rule properties.
|
2017-11-15 18:49:09 +00:00
|
|
|
|
#
|
|
|
|
|
# ```
|
|
|
|
|
# config = Ameba::Config.load
|
2017-11-23 17:49:45 +00:00
|
|
|
|
# config.update_rule "MyRuleName", enabled: false
|
2017-11-15 18:49:09 +00:00
|
|
|
|
# ```
|
2017-11-30 21:50:07 +00:00
|
|
|
|
def update_rule(name, enabled = true, excluded = nil)
|
2021-01-18 15:45:35 +00:00
|
|
|
|
rule = @rules.find(&.name.==(name))
|
|
|
|
|
raise ArgumentError.new("Rule `#{name}` does not exist") unless rule
|
2017-11-23 17:49:45 +00:00
|
|
|
|
|
2021-01-18 15:45:35 +00:00
|
|
|
|
rule
|
|
|
|
|
.tap(&.enabled = enabled)
|
|
|
|
|
.tap(&.excluded = excluded)
|
2017-11-13 21:20:22 +00:00
|
|
|
|
end
|
|
|
|
|
|
2018-06-18 07:25:06 +00:00
|
|
|
|
# Updates rules properties.
|
|
|
|
|
#
|
|
|
|
|
# ```
|
|
|
|
|
# config = Ameba::Config.load
|
|
|
|
|
# config.update_rules %w(Rule1 Rule2), enabled: true
|
|
|
|
|
# ```
|
|
|
|
|
#
|
|
|
|
|
# also it allows to update groups of rules:
|
|
|
|
|
#
|
|
|
|
|
# ```
|
|
|
|
|
# config.update_rules %w(Group1 Group2), enabled: true
|
|
|
|
|
# ```
|
2021-01-18 15:45:35 +00:00
|
|
|
|
def update_rules(names, enabled = true, excluded = nil)
|
2018-06-18 07:25:06 +00:00
|
|
|
|
names.try &.each do |name|
|
2021-01-18 15:45:35 +00:00
|
|
|
|
if rules = @rule_groups[name]?
|
|
|
|
|
rules.each do |rule|
|
|
|
|
|
rule.enabled = enabled
|
|
|
|
|
rule.excluded = excluded
|
|
|
|
|
end
|
2018-06-18 07:25:06 +00:00
|
|
|
|
else
|
2021-01-18 15:45:35 +00:00
|
|
|
|
update_rule name, enabled, excluded
|
2018-06-18 07:25:06 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2019-12-28 18:59:41 +00:00
|
|
|
|
private def load_formatter_name(config)
|
|
|
|
|
name = config["Formatter"]?.try &.["Name"]?
|
2021-01-11 18:13:58 +00:00
|
|
|
|
name.try(&.to_s)
|
2017-11-13 21:20:22 +00:00
|
|
|
|
end
|
|
|
|
|
|
2020-03-22 12:18:07 +00:00
|
|
|
|
private def load_array_section(config, section_name, default = [] of String)
|
|
|
|
|
case value = config[section_name]?
|
2020-03-22 12:58:10 +00:00
|
|
|
|
when .nil? then default
|
2020-03-22 12:18:07 +00:00
|
|
|
|
when .as_s? then [value.to_s]
|
|
|
|
|
when .as_a? then value.as_a.map(&.as_s)
|
2019-12-28 18:59:41 +00:00
|
|
|
|
else
|
2022-11-14 00:24:29 +00:00
|
|
|
|
raise "Incorrect '#{section_name}' section in a config files"
|
2019-12-28 18:59:41 +00:00
|
|
|
|
end
|
2017-11-13 21:20:22 +00:00
|
|
|
|
end
|
|
|
|
|
|
2023-08-04 20:48:35 +00:00
|
|
|
|
private def default_globs
|
2023-08-05 15:15:50 +00:00
|
|
|
|
[SOURCES_GLOB].tap do |globs|
|
2023-08-05 18:28:47 +00:00
|
|
|
|
globs.push("!lib") unless Dir["lib/**/*.cr"].empty?
|
2023-08-04 20:48:35 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2017-11-23 08:54:56 +00:00
|
|
|
|
# :nodoc:
|
2017-11-23 17:49:45 +00:00
|
|
|
|
module RuleConfig
|
2023-06-08 12:03:35 +00:00
|
|
|
|
# Define rule properties
|
2017-11-22 06:44:29 +00:00
|
|
|
|
macro properties(&block)
|
|
|
|
|
{% definitions = [] of NamedTuple %}
|
|
|
|
|
{% if block.body.is_a? Assign %}
|
|
|
|
|
{% definitions << {var: block.body.target, value: block.body.value} %}
|
2018-05-03 15:57:47 +00:00
|
|
|
|
{% elsif block.body.is_a? Call %}
|
2023-06-14 13:05:52 +00:00
|
|
|
|
{% definitions << {var: block.body.name, value: block.body.args.first} %}
|
2017-11-22 06:44:29 +00:00
|
|
|
|
{% elsif block.body.is_a? TypeDeclaration %}
|
|
|
|
|
{% definitions << {var: block.body.var, value: block.body.value, type: block.body.type} %}
|
|
|
|
|
{% elsif block.body.is_a? Expressions %}
|
|
|
|
|
{% for prop in block.body.expressions %}
|
|
|
|
|
{% if prop.is_a? Assign %}
|
|
|
|
|
{% definitions << {var: prop.target, value: prop.value} %}
|
2018-05-03 15:57:47 +00:00
|
|
|
|
{% elsif prop.is_a? Call %}
|
|
|
|
|
{% definitions << {var: prop.name, value: prop.args.first} %}
|
2017-11-22 06:44:29 +00:00
|
|
|
|
{% elsif prop.is_a? TypeDeclaration %}
|
|
|
|
|
{% definitions << {var: prop.var, value: prop.value, type: prop.type} %}
|
|
|
|
|
{% end %}
|
2017-11-13 21:20:22 +00:00
|
|
|
|
{% end %}
|
2017-11-22 06:44:29 +00:00
|
|
|
|
{% end %}
|
2017-11-13 21:20:22 +00:00
|
|
|
|
|
2017-11-22 06:44:29 +00:00
|
|
|
|
{% properties = {} of MacroId => NamedTuple %}
|
|
|
|
|
{% for df in definitions %}
|
|
|
|
|
{% name = df[:var].id %}
|
|
|
|
|
{% key = name.camelcase.stringify %}
|
|
|
|
|
{% value = df[:value] %}
|
|
|
|
|
{% type = df[:type] %}
|
2019-04-13 18:16:59 +00:00
|
|
|
|
{% converter = nil %}
|
|
|
|
|
|
|
|
|
|
{% if key == "Severity" %}
|
|
|
|
|
{% type = Severity %}
|
|
|
|
|
{% converter = SeverityYamlConverter %}
|
|
|
|
|
{% end %}
|
2017-11-13 21:20:22 +00:00
|
|
|
|
|
2023-06-14 13:05:52 +00:00
|
|
|
|
{% unless type %}
|
2017-11-22 06:44:29 +00:00
|
|
|
|
{% if value.is_a? BoolLiteral %}
|
|
|
|
|
{% type = Bool %}
|
|
|
|
|
{% elsif value.is_a? StringLiteral %}
|
|
|
|
|
{% type = String %}
|
|
|
|
|
{% elsif value.is_a? NumberLiteral %}
|
|
|
|
|
{% if value.kind == :i32 %}
|
|
|
|
|
{% type = Int32 %}
|
|
|
|
|
{% elsif value.kind == :i64 %}
|
|
|
|
|
{% type = Int64 %}
|
2023-06-14 13:05:52 +00:00
|
|
|
|
{% elsif value.kind == :i128 %}
|
|
|
|
|
{% type = Int128 %}
|
2017-11-22 06:44:29 +00:00
|
|
|
|
{% elsif value.kind == :f32 %}
|
|
|
|
|
{% type = Float32 %}
|
|
|
|
|
{% elsif value.kind == :f64 %}
|
|
|
|
|
{% type = Float64 %}
|
|
|
|
|
{% end %}
|
|
|
|
|
{% end %}
|
|
|
|
|
{% end %}
|
|
|
|
|
|
2019-04-13 18:16:59 +00:00
|
|
|
|
{% properties[name] = {key: key, default: value, type: type, converter: converter} %}
|
2020-06-15 11:19:23 +00:00
|
|
|
|
|
2023-06-14 13:08:19 +00:00
|
|
|
|
@[YAML::Field(key: {{ key }}, converter: {{ converter }})]
|
2022-11-22 18:49:16 +00:00
|
|
|
|
{% if type == Bool %}
|
2023-06-14 13:08:19 +00:00
|
|
|
|
property? {{ name }}{{ " : #{type}".id if type }} = {{ value }}
|
2022-11-22 18:49:16 +00:00
|
|
|
|
{% else %}
|
2023-06-14 13:08:19 +00:00
|
|
|
|
property {{ name }}{{ " : #{type}".id if type }} = {{ value }}
|
2022-11-22 18:49:16 +00:00
|
|
|
|
{% end %}
|
2017-11-22 06:44:29 +00:00
|
|
|
|
{% end %}
|
|
|
|
|
|
2022-11-26 01:16:41 +00:00
|
|
|
|
{% unless properties["enabled".id] %}
|
2020-06-15 11:19:23 +00:00
|
|
|
|
@[YAML::Field(key: "Enabled")]
|
2022-11-22 18:46:38 +00:00
|
|
|
|
property? enabled = true
|
2017-11-22 06:44:29 +00:00
|
|
|
|
{% end %}
|
2017-11-13 21:20:22 +00:00
|
|
|
|
|
2022-11-26 01:16:41 +00:00
|
|
|
|
{% unless properties["severity".id] %}
|
2020-06-15 11:19:23 +00:00
|
|
|
|
@[YAML::Field(key: "Severity", converter: Ameba::SeverityYamlConverter)]
|
2022-11-23 14:04:47 +00:00
|
|
|
|
property severity = {{ @type }}.default_severity
|
2019-04-13 19:38:37 +00:00
|
|
|
|
{% end %}
|
|
|
|
|
|
2022-11-26 01:16:41 +00:00
|
|
|
|
{% unless properties["excluded".id] %}
|
2020-06-15 11:19:23 +00:00
|
|
|
|
@[YAML::Field(key: "Excluded")]
|
|
|
|
|
property excluded : Array(String)?
|
2017-11-30 21:50:07 +00:00
|
|
|
|
{% end %}
|
2017-11-13 21:20:22 +00:00
|
|
|
|
end
|
|
|
|
|
|
2017-11-22 06:44:29 +00:00
|
|
|
|
macro included
|
2022-11-23 14:04:47 +00:00
|
|
|
|
GROUP_SEVERITY = {
|
2023-11-08 17:31:12 +00:00
|
|
|
|
Documentation: Ameba::Severity::Warning,
|
|
|
|
|
Lint: Ameba::Severity::Warning,
|
|
|
|
|
Metrics: Ameba::Severity::Warning,
|
|
|
|
|
Performance: Ameba::Severity::Warning,
|
2022-11-23 14:04:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class_getter default_severity : Ameba::Severity do
|
|
|
|
|
GROUP_SEVERITY[group_name]? || Ameba::Severity::Convention
|
|
|
|
|
end
|
|
|
|
|
|
2017-11-22 06:44:29 +00:00
|
|
|
|
macro inherited
|
2020-06-15 11:19:23 +00:00
|
|
|
|
include YAML::Serializable
|
|
|
|
|
include YAML::Serializable::Strict
|
2017-11-22 06:44:29 +00:00
|
|
|
|
|
2017-11-23 17:49:45 +00:00
|
|
|
|
def self.new(config = nil)
|
2021-01-17 12:25:50 +00:00
|
|
|
|
if (raw = config.try &.raw).is_a?(Hash)
|
2018-02-02 20:11:18 +00:00
|
|
|
|
yaml = raw[rule_name]?.try &.to_yaml
|
2017-11-30 09:27:52 +00:00
|
|
|
|
end
|
|
|
|
|
from_yaml yaml || "{}"
|
2017-11-22 06:44:29 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
2017-11-13 21:20:22 +00:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|