Class: Herb::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/herb/configuration.rb,
sig/herb/configuration.rbs

Constant Summary collapse

OPTIONS_PATH =

: String

Returns:

  • (String)
File.expand_path("../../config/options.yml", __dir__ || __FILE__).freeze
OPTIONS =

: Hash[String, untyped]

Returns:

  • (Hash[String, untyped])
YAML.safe_load_file(OPTIONS_PATH).freeze
VALID_FRAMEWORKS =

: Array

Returns:

  • (Array[String])
OPTIONS["framework"]["values"].freeze
VALID_TEMPLATE_ENGINES =

: Array

Returns:

  • (Array[String])
OPTIONS["template_engine"]["values"].freeze
CONFIG_FILENAMES =

Returns:

  • (Object)
[".herb.yml"].freeze
PROJECT_INDICATORS =

Returns:

  • (Object)
[
  ".git",
  ".herb",
  ".herb.yml",
  "Gemfile",
  "package.json",
  "Rakefile",
  "README.md",
  "*.gemspec",
  "config/application.rb"
].freeze
DEFAULTS_PATH =

Returns:

  • (Object)
File.expand_path("defaults.yml", __dir__ || __FILE__).freeze
DEFAULTS =

Returns:

  • (Object)
YAML.safe_load_file(DEFAULTS_PATH).freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project_path = nil) ⇒ Configuration

Returns a new instance of Configuration.

Parameters:

  • project_path (Object) (defaults to: nil)


33
34
35
36
37
38
# File 'lib/herb/configuration.rb', line 33

def initialize(project_path = nil)
  @start_path = project_path ? Pathname.new(project_path) : Pathname.pwd
  @config_path, @project_root = find_config_file
  @user_config = load_user_config
  @config = deep_merge(DEFAULTS, @user_config)
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.

Returns:

  • (Object)


31
32
33
# File 'lib/herb/configuration.rb', line 31

def config
  @config
end

#config_pathObject (readonly)

Returns the value of attribute config_path.

Returns:

  • (Object)


31
32
33
# File 'lib/herb/configuration.rb', line 31

def config_path
  @config_path
end

#project_rootObject (readonly)

Returns the value of attribute project_root.

Returns:

  • (Object)


31
32
33
# File 'lib/herb/configuration.rb', line 31

def project_root
  @project_root
end

#user_configObject (readonly)

Returns the value of attribute user_config.

Returns:

  • (Object)


31
32
33
# File 'lib/herb/configuration.rb', line 31

def user_config
  @user_config
end

Class Method Details

.defaultObject

Returns:

  • (Object)


217
218
219
# File 'lib/herb/configuration.rb', line 217

def default
  @default ||= new
end

.default_exclude_patternsObject

Returns:

  • (Object)


225
226
227
# File 'lib/herb/configuration.rb', line 225

def default_exclude_patterns
  DEFAULTS.dig("files", "exclude") || []
end

.default_file_patternsObject

Returns:

  • (Object)


221
222
223
# File 'lib/herb/configuration.rb', line 221

def default_file_patterns
  DEFAULTS.dig("files", "include") || []
end

.load(project_path = nil) ⇒ Object

Parameters:

  • project_path (Object) (defaults to: nil)

Returns:

  • (Object)


213
214
215
# File 'lib/herb/configuration.rb', line 213

def load(project_path = nil)
  new(project_path)
end

Instance Method Details

#[](key) ⇒ Object

Parameters:

  • key (Object)

Returns:

  • (Object)


40
41
42
# File 'lib/herb/configuration.rb', line 40

def [](key)
  @config[key.to_s]
end

#deep_merge(base, override, additive_keys: ["include", "exclude"]) ⇒ Object

Parameters:

  • base (Object)
  • override (Object)
  • additive_keys: (Object) (defaults to: ["include", "exclude"])

Returns:

  • (Object)


272
273
274
275
276
277
278
279
280
281
282
# File 'lib/herb/configuration.rb', line 272

def deep_merge(base, override, additive_keys: ["include", "exclude"])
  base.merge(override) do |key, old_value, new_value|
    if old_value.is_a?(Hash) && new_value.is_a?(Hash)
      deep_merge(old_value, new_value, additive_keys: additive_keys)
    elsif old_value.is_a?(Array) && new_value.is_a?(Array) && additive_keys.include?(key)
      old_value + new_value
    else
      new_value
    end
  end
end

#dig(*keys) ⇒ Object

Parameters:

  • keys (Object)

Returns:

  • (Object)


44
45
46
# File 'lib/herb/configuration.rb', line 44

def dig(*keys)
  @config.dig(*keys.map(&:to_s))
end

#enabled_for_path?(path, tool) ⇒ Boolean

Parameters:

  • path (Object)
  • tool (Object)

Returns:

  • (Boolean)


143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/herb/configuration.rb', line 143

def enabled_for_path?(path, tool)
  tool_config = send(tool.to_s)
  tool_include = tool_config["include"] || []
  tool_exclude = tool_config["exclude"] || []

  if tool_include.any? && path_included?(path, tool_include)
    return !path_excluded?(path, tool_exclude)
  end

  exclude_patterns = exclude_patterns_for(tool)

  !path_excluded?(path, exclude_patterns)
end

#enabled_validators(overrides = {}) ⇒ Object

Parameters:

  • overrides (Object) (defaults to: {})

Returns:

  • (Object)


101
102
103
104
105
106
107
108
109
110
111
# File 'lib/herb/configuration.rb', line 101

def enabled_validators(overrides = {})
  config = dig("engine", "validators") || {}

  {
    security: config.fetch("security", true),
    nesting: config.fetch("nesting", true),
    accessibility: config.fetch("accessibility", true),
  }.merge(
    overrides.to_h { |key, value| [key.to_sym, !!value] }
  )
end

#engineObject

Returns:

  • (Object)


92
93
94
# File 'lib/herb/configuration.rb', line 92

def engine
  @config["engine"] || {}
end

#engine_option(key, default = nil) ⇒ Object

: (String, untyped) -> untyped

Parameters:

  • (String)
  • (Object)

Returns:

  • (Object)


97
98
99
# File 'lib/herb/configuration.rb', line 97

def engine_option(key, default = nil)
  engine.fetch(key.to_s, default)
end

#exclude_patterns_for(tool) ⇒ Object

Parameters:

  • tool (Object)

Returns:

  • (Object)


122
123
124
125
# File 'lib/herb/configuration.rb', line 122

def exclude_patterns_for(tool)
  tool_config = send(tool.to_s)
  file_exclude_patterns + (tool_config["exclude"] || [])
end

#file_exclude_patternsObject

Returns:

  • (Object)


84
85
86
# File 'lib/herb/configuration.rb', line 84

def file_exclude_patterns
  files["exclude"] || DEFAULTS.dig("files", "exclude") || []
end

#file_include_patternsObject

Returns:

  • (Object)


80
81
82
# File 'lib/herb/configuration.rb', line 80

def file_include_patterns
  files["include"] || DEFAULTS.dig("files", "include") || []
end

#filesObject

Returns:

  • (Object)


76
77
78
# File 'lib/herb/configuration.rb', line 76

def files
  @config["files"] || {}
end

#find_config_fileObject

Returns:

  • (Object)


232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# File 'lib/herb/configuration.rb', line 232

def find_config_file
  search_path = @start_path
  search_path = search_path.parent if search_path.file?

  while search_path.to_s != "/"
    CONFIG_FILENAMES.each do |filename|
      config_file = search_path / filename
      return [config_file, search_path] if config_file.exist?
    end

    return [nil, search_path] if project_root?(search_path)

    search_path = search_path.parent
  end

  [nil, @start_path]
end

#find_files(search_path = nil) ⇒ Object

Parameters:

  • search_path (Object) (defaults to: nil)

Returns:

  • (Object)


173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/herb/configuration.rb', line 173

def find_files(search_path = nil)
  search_path ||= @project_root || @start_path
  expanded_path = File.expand_path(search_path.to_s)

  all_files = file_include_patterns.flat_map do |pattern|
    Dir[File.join(expanded_path, pattern)]
  end.uniq

  all_files.reject do |file|
    relative = file.sub("#{expanded_path}/", "")
    path_excluded?(relative, file_exclude_patterns)
  end.sort
end

#find_files_for_formatter(search_path = nil) ⇒ Object

Parameters:

  • search_path (Object) (defaults to: nil)

Returns:

  • (Object)


208
209
210
# File 'lib/herb/configuration.rb', line 208

def find_files_for_formatter(search_path = nil)
  find_files_for_tool(:formatter, search_path)
end

#find_files_for_linter(search_path = nil) ⇒ Object

Parameters:

  • search_path (Object) (defaults to: nil)

Returns:

  • (Object)


204
205
206
# File 'lib/herb/configuration.rb', line 204

def find_files_for_linter(search_path = nil)
  find_files_for_tool(:linter, search_path)
end

#find_files_for_tool(tool, search_path = nil) ⇒ Object

Parameters:

  • tool (Object)
  • search_path (Object) (defaults to: nil)

Returns:

  • (Object)


187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/herb/configuration.rb', line 187

def find_files_for_tool(tool, search_path = nil)
  search_path ||= @project_root || @start_path
  expanded_path = File.expand_path(search_path.to_s)

  include_patterns = include_patterns_for(tool)
  exclude_patterns = exclude_patterns_for(tool)

  all_files = include_patterns.flat_map do |pattern|
    Dir[File.join(expanded_path, pattern)]
  end.uniq

  all_files.reject do |file|
    relative = file.sub("#{expanded_path}/", "")
    path_excluded?(relative, exclude_patterns)
  end.sort
end

#formatterObject

Returns:

  • (Object)


113
114
115
# File 'lib/herb/configuration.rb', line 113

def formatter
  @config["formatter"] || {}
end

#formatter_enabled_for_path?(path) ⇒ Boolean

Parameters:

  • path (Object)

Returns:

  • (Boolean)


161
162
163
# File 'lib/herb/configuration.rb', line 161

def formatter_enabled_for_path?(path)
  enabled_for_path?(path, :formatter)
end

#formatter_exclude_patternsObject

Returns:

  • (Object)


139
140
141
# File 'lib/herb/configuration.rb', line 139

def formatter_exclude_patterns
  exclude_patterns_for(:formatter)
end

#formatter_include_patternsObject

Returns:

  • (Object)


135
136
137
# File 'lib/herb/configuration.rb', line 135

def formatter_include_patterns
  include_patterns_for(:formatter)
end

#frameworkString

: () -> String

Returns:

  • (String)


53
54
55
56
57
58
59
60
61
62
# File 'lib/herb/configuration.rb', line 53

def framework
  value = @config["framework"] || "ruby"

  unless VALID_FRAMEWORKS.include?(value)
    warn "[Herb] Unknown framework: #{value.inspect}. Valid values: #{VALID_FRAMEWORKS.join(", ")}. Defaulting to 'ruby'."
    return "ruby"
  end

  value
end

#include_patterns_for(tool) ⇒ Object

Parameters:

  • tool (Object)

Returns:

  • (Object)


117
118
119
120
# File 'lib/herb/configuration.rb', line 117

def include_patterns_for(tool)
  tool_config = send(tool.to_s)
  file_include_patterns + (tool_config["include"] || [])
end

#linterObject

Returns:

  • (Object)


88
89
90
# File 'lib/herb/configuration.rb', line 88

def linter
  @config["linter"] || {}
end

#linter_enabled_for_path?(path) ⇒ Boolean

Parameters:

  • path (Object)

Returns:

  • (Boolean)


157
158
159
# File 'lib/herb/configuration.rb', line 157

def linter_enabled_for_path?(path)
  enabled_for_path?(path, :linter)
end

#linter_exclude_patternsObject

Returns:

  • (Object)


131
132
133
# File 'lib/herb/configuration.rb', line 131

def linter_exclude_patterns
  exclude_patterns_for(:linter)
end

#linter_include_patternsObject

Returns:

  • (Object)


127
128
129
# File 'lib/herb/configuration.rb', line 127

def linter_include_patterns
  include_patterns_for(:linter)
end

#load_user_configObject

Returns:

  • (Object)


260
261
262
263
264
265
266
267
268
269
270
# File 'lib/herb/configuration.rb', line 260

def load_user_config
  return {} unless @config_path&.exist?

  begin
    YAML.safe_load_file(@config_path, permitted_classes: [Symbol]) || {}
  rescue Psych::SyntaxError => e
    warn "Warning: Invalid YAML in #{@config_path}: #{e.message}"

    {}
  end
end

#path_excluded?(path, patterns) ⇒ Boolean

Parameters:

  • path (Object)
  • patterns (Object)

Returns:

  • (Boolean)


165
166
167
# File 'lib/herb/configuration.rb', line 165

def path_excluded?(path, patterns)
  patterns.any? { |pattern| File.fnmatch?(pattern, path, File::FNM_PATHNAME) }
end

#path_included?(path, patterns) ⇒ Boolean

Parameters:

  • path (Object)
  • patterns (Object)

Returns:

  • (Boolean)


169
170
171
# File 'lib/herb/configuration.rb', line 169

def path_included?(path, patterns)
  patterns.any? { |pattern| File.fnmatch?(pattern, path, File::FNM_PATHNAME) }
end

#project_root?(path) ⇒ Boolean

Parameters:

  • path (Object)

Returns:

  • (Boolean)


250
251
252
253
254
255
256
257
258
# File 'lib/herb/configuration.rb', line 250

def project_root?(path)
  PROJECT_INDICATORS.any? do |indicator|
    if indicator.include?("*")
      Dir.glob(path / indicator).any?
    else
      (path / indicator).exist?
    end
  end
end

#template_engineString

: () -> String

Returns:

  • (String)


65
66
67
68
69
70
71
72
73
74
# File 'lib/herb/configuration.rb', line 65

def template_engine
  value = @config["template_engine"] || "erubi"

  unless VALID_TEMPLATE_ENGINES.include?(value)
    warn "[Herb] Unknown template_engine: #{value.inspect}. Valid values: #{VALID_TEMPLATE_ENGINES.join(", ")}. Defaulting to 'erubi'."
    return "erubi"
  end

  value
end

#versionObject

Returns:

  • (Object)


48
49
50
# File 'lib/herb/configuration.rb', line 48

def version
  @config["version"]
end