Class: Do::Config

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

Overview

Loads and represents the declarative TOML configuration.

The TOML file is the source of truth. Everything else do produces is a derived artifact. This class only parses the file; validation is performed by Validator.

Constant Summary collapse

KNOWN_TASK_FIELDS =
%w[
  command schedule time day working_directory environment enabled
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(raw, source: nil) ⇒ Config

Returns a new instance of Config.



45
46
47
48
49
# File 'lib/do/config.rb', line 45

def initialize(raw, source: nil)
  @raw = raw || {}
  @source = source
  @tasks = build_tasks(@raw['tasks'])
end

Instance Attribute Details

#pathObject

Returns the value of attribute path.



43
44
45
# File 'lib/do/config.rb', line 43

def path
  @path
end

#rawObject (readonly)

Returns the value of attribute raw.



42
43
44
# File 'lib/do/config.rb', line 42

def raw
  @raw
end

#tasksObject (readonly)

Returns the value of attribute tasks.



42
43
44
# File 'lib/do/config.rb', line 42

def tasks
  @tasks
end

Class Method Details

.default_pathObject

Default location when no path is given.



17
18
19
20
21
# File 'lib/do/config.rb', line 17

def self.default_path
  dir = ENV.fetch('XDG_CONFIG_HOME', nil)
  dir = File.join(Dir.home, '.config') if dir.nil? || dir.empty?
  File.join(dir, 'do', 'config.toml')
end

.load(path = default_path) ⇒ Object

Raises:



33
34
35
36
37
38
39
40
# File 'lib/do/config.rb', line 33

def self.load(path = default_path)
  raise Error, "Configuration file does not exist: #{path}" unless File.exist?(path)

  raw = File.read(path)
  config = parse(raw, source: path)
  config.path = path
  config
end

.parse(raw_text, source: 'config.toml') ⇒ Array<String>

Returns validation errors found while parsing.

Returns:

  • (Array<String>)

    validation errors found while parsing.



24
25
26
27
28
29
30
31
# File 'lib/do/config.rb', line 24

def self.parse(raw_text, source: 'config.toml')
  parsed = TomlRB.parse(raw_text)
  new(parsed, source: source)
rescue TomlRB::ParseError, Psych::SyntaxError => e
  raise ValidationError, ["Malformed TOML in #{source}: #{e.message}"]
rescue StandardError => e
  raise ValidationError, ["Failed to parse #{source}: #{e.message}"]
end

Instance Method Details

#[](key) ⇒ Object



55
56
57
# File 'lib/do/config.rb', line 55

def [](key)
  @raw[key]
end

#remove_task(task_name) ⇒ Object

Remove a task's table from the file. Used by do remove. Also removes any nested tables under the same task (e.g. [tasks.name.environment]).

Raises:



80
81
82
83
84
85
86
87
88
89
90
# File 'lib/do/config.rb', line 80

def remove_task(task_name)
  text = File.read(@path)
  idx = block_start_index(text, task_name)
  raise Error, "task '#{task_name}' not present in configuration" if idx.nil?

  lines = text.lines
  block_end = task_block_end(lines, idx, task_name)
  removed = lines[0...idx] + lines[block_end..]
  # Collapse leftover blank lines around the removed block (best effort).
  File.write(@path, removed.join.gsub(/\n{3,}/, "\n\n"))
end

#set_task_field(task_name, field, toml_value) ⇒ Object

Edit a scalar field inside a task's table without re-serializing the whole file (preserving user comments/formatting elsewhere). Used by enable/disable.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/do/config.rb', line 62

def set_task_field(task_name, field, toml_value)
  text = File.read(@path)
  idx = block_start_index(text, task_name)
  return edit_field_anywhere(text, task_name, field, toml_value) if idx.nil?

  lines = text.lines
  block_end = block_end_index(lines, idx)
  match = (idx...block_end).find { |i| lines[i] =~ /\A#{Regexp.escape(field)}\s*=/ }
  if match
    lines[match] = "#{field} = #{toml_value}\n"
  else
    lines.insert(idx + 1, "#{field} = #{toml_value}\n")
  end
  File.write(@path, lines.join)
end

#task(name) ⇒ Object



51
52
53
# File 'lib/do/config.rb', line 51

def task(name)
  @tasks.find { |t| t.name == name }
end