Class: Taf

Inherits:
Object
  • Object
show all
Defined in:
lib/taf/models/taf.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(file) ⇒ Taf

Returns a new instance of Taf.



8
9
10
11
12
13
# File 'lib/taf/models/taf.rb', line 8

def initialize(file)
  @file = file
  @taf = MarkdownAdapter.read(file)
  assign_indices
  @presenter = TerminalPresenter.new
end

Class Method Details

.backup(file) ⇒ Object

Creates a backup of the file



16
17
18
# File 'lib/taf/models/taf.rb', line 16

def self.backup(file)
  FileUtils.cp(file, "#{file}.backup") if File.exist?(file)
end

.restore(file) ⇒ Object

Restores the backup file and deletes it



21
22
23
24
25
26
27
28
29
# File 'lib/taf/models/taf.rb', line 21

def self.restore(file)
  backup_file = "#{file}.backup"
  unless File.exist?(backup_file)
    raise ArgumentError, "No backup file found"
  end

  FileUtils.cp(backup_file, file)
  File.delete(backup_file)
end

Instance Method Details

#add_todo(text, tag: nil, parent_id: nil) ⇒ Object

Adds a new item

Raises:

  • (ArgumentError)


32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/taf/models/taf.rb', line 32

def add_todo(text, tag: nil, parent_id: nil)
  # Avoids flag typos
  raise ArgumentError, "Text must be #{Todo::MIN_LENGTH} or more characters" unless text.size >= Todo::MIN_LENGTH

  # Default tag if neither tag nor parent specified
  if tag.nil? && parent_id.nil?
    tag = TafHelper::DEFAULT_TAG
  end

  changed = []
  if parent_id
    parent_item = find_item_by_id(parent_id)
    unless parent_item
      raise ArgumentError, "Parent with id #{parent_id} not found"
    end

    new_item = Todo.new(
      status: "todo",
      text: text,
      children: [],
      parent: parent_item
    )
    parent_item.children << new_item
    changed = mark_ancestors_todo(parent_item)
  else
    new_item = Todo.new(
      status: "todo",
      text: text,
      children: []
    )
    if @taf[tag]
      # Tag exists, append to root level
      @taf[tag] << new_item
    else
      # Create new tag
      @taf[tag] = [new_item]
    end
  end

  changed << new_item
  assign_indices
  save

  changed.map(&:signature)
end

#cleanupObject

Sorts all items (todo items before done items) and saves



178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/taf/models/taf.rb', line 178

def cleanup
  # Sort items: within each parent, todo items before done items
  @taf.each_value do |items|
    items.each do |item|
      sort_children_recursive(item)
    end
    # Sort root level items
    items.sort_by! { |item| item.status == "done" ? 1 : 0 }
  end

  save
end

#delete(item) ⇒ Object

Deletes the item (and all its children)



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/taf/models/taf.rb', line 150

def delete(item)
  if item.parent
    # Remove from parent's children
    item.parent.children.delete(item)
  else
    # Remove from root level
    @taf.each_value do |items|
      if items.delete(item)
        break
      end
    end
  end

  save
end

#display_item_recursive(item, indent_level, highlight_signatures) ⇒ Object

Recursively displays an item and its children with proper indentation



103
104
105
106
107
108
# File 'lib/taf/models/taf.rb', line 103

def display_item_recursive(item, indent_level, highlight_signatures)
  @presenter.display_todo(item, indent_level, highlighted: highlight_signatures&.include?(item.signature))
  item.children.each do |child|
    display_item_recursive(child, indent_level + 1, highlight_signatures)
  end
end

#find_item_by_id(id) ⇒ Object

Finds a todo item by its index across all tags (searches recursively)



167
168
169
170
171
172
173
174
175
# File 'lib/taf/models/taf.rb', line 167

def find_item_by_id(id)
  @taf.each_value do |items|
    items.each do |item|
      result = find_in_subtree(item, id)
      return result if result
    end
  end
  nil
end

#mark_ancestors_todo(item) ⇒ Object

Recursively marks all ancestors as todo and returns those that changed



138
139
140
141
142
143
144
145
146
147
# File 'lib/taf/models/taf.rb', line 138

def mark_ancestors_todo(item)
  changed = []
  current = item
  while current
    changed << current if current.status != "todo"
    current.status = "todo"
    current = current.parent
  end
  changed
end

#mark_descendants_done(item) ⇒ Object

Recursively marks all descendants as done and returns those that changed



127
128
129
130
131
132
133
134
135
# File 'lib/taf/models/taf.rb', line 127

def mark_descendants_done(item)
  changed = []
  item.children.each do |child|
    changed << child if child.status != "done"
    child.status = "done"
    changed.concat(mark_descendants_done(child))
  end
  changed
end

#purgeObject

Deletes all done items (and their children) and saves



192
193
194
195
196
197
198
199
200
201
# File 'lib/taf/models/taf.rb', line 192

def purge
  @taf.each_value do |items|
    items.reject! { |item| item.status == "done" }
    items.each do |item|
      purge_done_children(item)
    end
  end

  save
end

#show_all(highlight_signatures: nil) ⇒ Object

Shows all tags and items



92
93
94
95
96
97
98
99
100
# File 'lib/taf/models/taf.rb', line 92

def show_all(highlight_signatures: nil)
  @taf.each do |key, items|
    @presenter.display_tag(key)
    items.each do |item|
      display_item_recursive(item, 0, highlight_signatures)
    end
    puts ""
  end
end

#show_tag(tag, highlight_signatures: nil) ⇒ Object

Shows given tag and its items



79
80
81
82
83
84
85
86
87
88
89
# File 'lib/taf/models/taf.rb', line 79

def show_tag(tag, highlight_signatures: nil)
  @taf.each do |key, items|
    next unless key == tag

    @presenter.display_tag(key)
    items.each do |item|
      display_item_recursive(item, 0, highlight_signatures)
    end
    puts ""
  end
end

#toggle(item) ⇒ Object

Toggles the status of the item Toggles ancestors and descendants accordingly



112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/taf/models/taf.rb', line 112

def toggle(item)
  item.status = item.status == "done" ? "todo" : "done"

  affected_items = [item]
  if item.status == "done"
    affected_items.concat(mark_descendants_done(item))
  elsif item.parent
    affected_items.concat(mark_ancestors_todo(item.parent))
  end

  save
  affected_items.map(&:signature)
end