Class: Diagrams::GanttDiagram

Inherits:
Base
  • Object
show all
Defined in:
lib/diagrams/gantt_diagram.rb

Overview

Represents a Gantt Chart diagram consisting of tasks over time, grouped into sections.

Constant Summary collapse

DEFAULT_SECTION_TITLE =
'Default Section'

Instance Attribute Summary collapse

Attributes inherited from Base

#checksum, #version

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

#diff, from_hash, from_json, #to_h, #to_json

Constructor Details

#initialize(title: '', sections: [], version: 1) ⇒ GanttDiagram

Initializes a new GanttDiagram.

Parameters:

  • title (String) (defaults to: '')

    The title of the Gantt chart.

  • sections (Array<Element::GanttSection>) (defaults to: [])

    An array of section objects (containing tasks).

  • version (String, Integer, nil) (defaults to: 1)

    User-defined version identifier.



15
16
17
18
19
20
21
22
# File 'lib/diagrams/gantt_diagram.rb', line 15

def initialize(title: '', sections: [], version: 1)
  super(version:)
  @title = title.is_a?(String) ? title : ''
  @sections = Array(sections)
  ensure_default_section if @sections.empty?
  validate_elements!
  update_checksum!
end

Instance Attribute Details

#sectionsObject (readonly)

Returns the value of attribute sections.



8
9
10
# File 'lib/diagrams/gantt_diagram.rb', line 8

def sections
  @sections
end

#titleObject (readonly)

Returns the value of attribute title.



8
9
10
# File 'lib/diagrams/gantt_diagram.rb', line 8

def title
  @title
end

Class Method Details

.from_h(data_hash, version:, checksum:) ⇒ GanttDiagram

Class method to create a GanttDiagram from a hash.

Parameters:

  • data_hash (Hash)

    Hash containing :title and :sections array.

  • version (String, Integer, nil)

    Diagram version.

  • checksum (String, nil)

    Expected checksum (optional, for verification).

Returns:



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/diagrams/gantt_diagram.rb', line 132

def self.from_h(data_hash, version:, checksum:)
  title = data_hash[:title] || data_hash['title'] || ''
  sections_data = data_hash[:sections] || data_hash['sections'] || []

  sections = sections_data.map do |section_h|
    section_data = section_h.transform_keys(&:to_sym)
    tasks_data = section_data[:tasks] || [] # Expect 'tasks' key in hash
    # Map task data to Task objects
    tasks = tasks_data.map do |task_h|
      task_data = task_h.transform_keys(&:to_sym)
      # Convert status back to symbol if it's a string and present
      task_data[:status] = task_data[:status].to_sym if task_data[:status].is_a?(String)
      Elements::Task.new(task_data)
    end
    Elements::GanttSection.new(title: section_data[:title], tasks: tasks)
  end

  diagram = new(title:, sections:, version:)

  # Optional: Verify checksum
  if checksum && diagram.checksum != checksum
    warn "Checksum mismatch for loaded GanttDiagram (version: #{version}). Expected #{checksum}, got #{diagram.checksum}."
  end

  diagram
end

Instance Method Details

#add_section(section_title) ⇒ Elements::GanttSection

Adds a new section to the diagram. Subsequent tasks will be added to this section.

Parameters:

  • section_title (String)

    The title of the section.

Returns:

Raises:

  • (ArgumentError)

    if a section with the same title already exists.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/diagrams/gantt_diagram.rb', line 30

def add_section(section_title)
  clean_title = section_title.strip
  raise ArgumentError, "Section title '#{clean_title}' cannot be empty" if clean_title.empty?
  raise ArgumentError, "Section with title '#{clean_title}' already exists" if find_section(clean_title)

  # Remove default section if it's empty and we're adding a real one
  if @sections.size == 1 && @sections.first.title == DEFAULT_SECTION_TITLE && @sections.first.tasks.empty? # Check tasks for GanttSection
    @sections.clear
  end

  # Use GanttSection
  new_section = Elements::GanttSection.new(title: clean_title, tasks: [])
  @sections << new_section
  update_checksum!
  new_section
end

#add_task(id:, label:, start:, duration:, status: nil) ⇒ Elements::Task

Adds a task to the current (last) section of the diagram.

Parameters:

  • id (String)

    Unique ID for the task (used for dependencies).

  • label (String)

    Display name/label for the task.

  • status (Symbol, nil) (defaults to: nil)

    Status (:done, :active, :crit). nil implies default/future.

  • start (String)

    Start date, task ID (e.g., 'task1'), or dependency string ('after taskX').

  • duration (String)

    Duration string (e.g., '7d', '2w').

Returns:

Raises:

  • (ArgumentError)

    if required fields are missing or a task with the same ID exists.

  • (StandardError)

    if no sections exist.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/diagrams/gantt_diagram.rb', line 57

def add_task(id:, label:, start:, duration:, status: nil)
  raise ArgumentError, 'Task ID cannot be empty' if id.nil? || id.strip.empty?
  raise ArgumentError, "Task with ID '#{id}' already exists" if find_task(id)

  new_task = Elements::Task.new(
    id:,
    label:,
    status:,
    start:,
    duration:
  )

  current_section = @sections.last
  raise StandardError, 'Cannot add task: No section available.' unless current_section

  # Add task to the current section's 'tasks' array
  updated_tasks = current_section.tasks + [new_task]
  updated_section = Elements::GanttSection.new(title: current_section.title, tasks: updated_tasks)

  # Update the section in the main array
  current_section_index = @sections.index { |s| s.title == current_section.title }
  unless current_section_index
    raise StandardError,
          "Could not find index for current section '#{current_section.title}'"
  end

  @sections[current_section_index] = updated_section

  update_checksum!
  new_task
end

#find_section(section_title) ⇒ Elements::GanttSection?

Finds a section by its title.

Parameters:

  • section_title (String)

    The title of the section.

Returns:



100
101
102
# File 'lib/diagrams/gantt_diagram.rb', line 100

def find_section(section_title)
  @sections.find { |s| s.title == section_title }
end

#find_task(task_id) ⇒ Element::Task?

Finds a task by its ID across all sections.

Parameters:

  • task_id (String)

    The ID of the task to find.

Returns:

  • (Element::Task, nil)

    The found task or nil.



93
94
95
# File 'lib/diagrams/gantt_diagram.rb', line 93

def find_task(task_id)
  all_tasks.find { |t| t.id == task_id }
end

#identifiable_elementsHash{Symbol => Array<Diagrams::Elements::Task>}

Returns a hash mapping element types to their collections for diffing.

Returns:



118
119
120
121
122
123
124
# File 'lib/diagrams/gantt_diagram.rb', line 118

def identifiable_elements
  {
    # Diffing based on tasks directly might be more useful than sections here
    tasks: all_tasks
    # sections: @sections # Could also diff sections if needed
  }
end

#to_h_contentHash{Symbol => String | Array<Hash>}

Returns the specific content of the Gantt diagram as a hash.

Returns:

  • (Hash{Symbol => String | Array<Hash>})


107
108
109
110
111
112
113
# File 'lib/diagrams/gantt_diagram.rb', line 107

def to_h_content
  {
    title: @title,
    # Serialize sections, renaming 'periods' back to 'tasks' for clarity
    sections: @sections.map(&:to_h) # Use GanttSection's to_h directly
  }
end