Code Coverage
 
Lines
Covered
20.00% covered (danger)
20.00%
52 / 260
1
require 'pathname'
1
module TheFox
1
module Timr
# Core Class
#
# Loads and saves Models to files. Tasks are loaded to `@tasks`.
#
# Holds the [Stack](rdoc-ref:TheFox::Timr::Model::Stack) instance. Responsible to call Stack methods.
1
class Timr
1
include Model
1
include Error
# ForeignIdDb instance.
1
attr_reader :foreign_id_db
# Stack instance.
1
attr_reader :stack
1
def initialize(cwd)
# Current Working Directory
5
case cwd
when String
5
@cwd = Pathname.new(cwd)
else
0
@cwd = cwd
end
5
if @cwd && !@cwd.exist?
0
puts "Initialize Timr in #{@cwd}"
0
@cwd.mkpath
end
5
@config = Config.new
5
@config.file_path = Pathname.new('config.yml').expand_path(@cwd)
5
if @config.file_path.exist?
2
@config.load_from_file
else
3
@config.save_to_file(nil, true)
end
5
@foreign_id_db = ForeignIdDb.new
5
@foreign_id_db.file_path = Pathname.new('foreign_id_db.yml').expand_path(@cwd)
5
if @foreign_id_db.file_path.exist?
0
@foreign_id_db.load_from_file
end
5
@tasks_path = Pathname.new('tasks').expand_path(@cwd)
5
unless @tasks_path.exist?
3
@tasks_path.mkpath
end
# Holds all loaded Tasks.
5
@tasks = Hash.new
# Stack Path
5
stack_path = Pathname.new('stack.yml').expand_path(@cwd)
# Stack
5
@stack = Stack.new
5
@stack.timr = self
5
@stack.file_path = stack_path
5
if stack_path.exist?
1
@stack.load_from_file
end
end
# Removes all previous [Tracks](rdoc-ref:TheFox::Timr::Model::Track) and starts a new one.
#
# Options:
#
# - `:foreign_id` (String)
# - `:task_id` (String)
# - `:track_id` (String)
1
def start(options = Hash.new)
0
foreign_id_opt = options.fetch(:foreign_id, nil)
0
task_id_opt = options.fetch(:task_id, nil)
0
track_id_opt = options.fetch(:track_id, nil)
# Get current Track from Stack.
0
old_track = @stack.current_track
# Stop current running Track.
0
if old_track
# Get Task from Track.
0
old_task = old_track.task
0
unless old_task
0
raise TrackError, "Track #{old_track.short_id} has no Task."
end
# Stop Task
0
old_task.stop
# Save Task
0
old_task.save_to_file
0
old_task = nil
end
0
if task_id_opt
0
task = get_task_by_id(task_id_opt)
0
if foreign_id_opt
# Throws exception when Foreign ID already exists in DB.
# Break before new Track creation.
0
@foreign_id_db.add_task(task, foreign_id_opt)
0
@foreign_id_db.save_to_file
end
0
track = task.start(options)
0
task.save_to_file
0
@stack.start(track)
0
@stack.save_to_file
else
0
if track_id_opt
# Seach Track ID the long way. Should be avoided.
# Searches all files.
0
track = Track.find_track_by_id(@tasks_path, track_id_opt)
0
if track
0
options[:track_id] = track.id
# Get Task from Track.
0
task = track.task
0
unless task
0
raise TrackError, "Track #{track.short_id} has no Task."
end
# Start Task
0
track = task.start(options)
# Save Task
0
task.save_to_file
0
@stack.start(track)
0
@stack.save_to_file
end
else
# Create completely new Task.
0
task = Task.create_task_from_hash(options)
0
if foreign_id_opt
# Throws exception when Foreign ID already exists in DB.
# Break before new Track creation.
0
@foreign_id_db.add_task(task, foreign_id_opt)
0
@foreign_id_db.save_to_file
end
# Start Task
0
track = task.start(options)
# Task Path
0
task_file_path = BasicModel.create_path_by_id(@tasks_path, task.id)
# Save Task to file.
0
task.save_to_file(task_file_path)
0
@stack.start(track)
0
@stack.save_to_file
end
end
0
track
end
# Stops the current running [Track](rdoc-ref:TheFox::Timr::Model::Track) and removes it from the Stack.
1
def stop(options = Hash.new)
# Get current Track from Stack.
0
track = @stack.current_track
0
unless track
0
return
end
# Get Task from Track.
0
task = track.task
# Stop Task
0
task.stop(options)
# Save Task
0
task.save_to_file
0
@stack.stop
0
@stack.save_to_file
0
track
end
# Stops the current running [Track](rdoc-ref:TheFox::Timr::Model::Track) but does not remove it from the Stack.
1
def pause(options = Hash.new)
# Get current Track from Stack.
0
track = @stack.current_track
0
unless track
0
return
end
# Track Status
0
if track.stopped?
0
raise TrackError, "Cannot pause current Track #{track.short_id}, is not running."
end
# Get Task from Track.
0
task = track.task
# Pause Task
0
track = task.pause(options)
# Save Task
0
task.save_to_file
# Do nothing on the Stack.
0
track
end
# Continues the Top [Track](rdoc-ref:TheFox::Timr::Model::Track).
#
# Options:
#
# - `:track` (Track)
1
def continue(options = Hash.new)
# Get current Track from Stack.
0
track = @stack.current_track
0
unless track
0
return
end
0
options[:track] = track
# Get Task from Track.
0
task = track.task
# Continue Task
0
track = task.continue(options)
# Save Task
0
task.save_to_file
0
@stack.stop
0
@stack.push(track)
0
@stack.save_to_file
0
track
end
# Starts a new [Track](rdoc-ref:TheFox::Timr::Model::Track) and pauses the underlying one.
#
# Options:
#
# - `:foreign_id` (String)
# - `:task_id` (String)
# - `:track_id` (String)
1
def push(options = Hash.new)
0
foreign_id_opt = options.fetch(:foreign_id, nil)
0
task_id_opt = options.fetch(:task_id, nil)
0
track_id_opt = options.fetch(:track_id, nil)
# Get current Track from Stack.
0
old_track = @stack.current_track
# Stop current running Track.
0
if old_track
# Get Task from Track.
0
old_task = old_track.task
0
unless old_task
0
raise TrackError, "Track #{old_track.short_id} has no Task."
end
# Stop Task here because on pop we need to take the
# current Track from Stack instead from Task.
# You can push another Track from an already existing
# Task on the Stack. A Task can hold only one current Track.
0
old_task.stop
# Save Task
0
old_task.save_to_file
0
old_task = nil
end
0
if task_id_opt
0
task = get_task_by_id(task_id_opt)
0
if foreign_id_opt
# Throws exception when Foreign ID already exists in DB.
# Break before new Track creation.
0
@foreign_id_db.add_task(task, foreign_id_opt)
0
@foreign_id_db.save_to_file
end
# Start Task
0
track = task.start(options)
# Save Task
0
task.save_to_file
0
@stack.push(track)
0
@stack.save_to_file
else
0
if track_id_opt
# The long way. Should be avoided.
# Search all files.
0
track = Track.find_track_by_id(@tasks_path, track_id_opt)
0
if track
0
options[:track_id] = track.id
# Get Task from Track.
0
task = track.task
0
unless task
0
raise TrackError, "Track #{track.short_id} has no Task."
end
# Start Task
0
track = task.start(options)
# Save Task
0
task.save_to_file
0
@stack.push(track)
0
@stack.save_to_file
end
else
# Create completely new Task.
0
task = Task.create_task_from_hash(options)
0
if foreign_id_opt
# Throws exception when Foreign ID already exists in DB.
# Break before new Track creation.
0
@foreign_id_db.add_task(task, foreign_id_opt)
0
@foreign_id_db.save_to_file
end
# Start Task
0
track = task.start(options)
# Task Path
0
task_file_path = BasicModel.create_path_by_id(@tasks_path, task.id)
# Save Task to file.
0
task.save_to_file(task_file_path)
0
@stack.push(track)
0
@stack.save_to_file
end
end
0
track
end
# Stops the Top [Track](rdoc-ref:TheFox::Timr::Model::Track), removes it from the [Stack](rdoc-ref:TheFox::Timr::Model::Stack) and
# continues the next underlying (new Top) Track.
1
def pop(options = Hash.new)
0
stop(options)
0
continue(options)
end
# Remove current running Track.
#
# Options:
#
# - `:stack` (Boolean) Reset the Stack.
1
def reset(options = Hash.new)
0
stack_opt = options.fetch(:stack, false)
0
track = @stack.current_track
0
if track && track.running?
0
task = track.task
0
if task
0
task.reset
end
0
track.remove
# Save Task to file.
0
task.save_to_file
0
@stack.remove(track)
end
0
if stack_opt
0
@stack.tracks.each do |track|
0
task = track.task
0
if task
0
task.reset
# Save Task to file.
0
task.save_to_file
end
end
0
@stack.reset
end
0
@stack.save_to_file
nil
end
# Create a new [Task](rdoc-ref:TheFox::Timr::Model::Task) based on the `options` Hash. Will not be started or something else.
#
# Uses [Task#create_task_from_hash](rdoc-ref:TheFox::Timr::Model::Task::create_task_from_hash) to create a new Task instance and [BasicModel#create_path_by_id](rdoc-ref:TheFox::Timr::Model::BasicModel.create_path_by_id) to create a new file path.
#
# Returns the new created Task instance.
#
# Options:
#
# - `:foreign_id` (String)
1
def add_task(options = Hash.new)
0
foreign_id_opt = options.fetch(:foreign_id, nil)
0
task = Task.create_task_from_hash(options)
0
if foreign_id_opt
# Throws exception when Foreign ID already exists in DB.
# Break before Task save.
0
@foreign_id_db.add_task(task, foreign_id_opt)
0
@foreign_id_db.save_to_file
end
# Task Path
0
task_file_path = BasicModel.create_path_by_id(@tasks_path, task.id)
# Save Task to file.
0
task.save_to_file(task_file_path)
# Leave Stack untouched.
0
task
end
# Remove a [Task](rdoc-ref:TheFox::Timr::Model::Task).
#
# Options:
#
# - `:task_id` (String) Can be either a internal ID (hex) or Foreign ID, because `get_task_by_id` searches also the Foreign ID DB.
1
def remove_task(options = Hash.new)
0
task_id_opt = options.fetch(:task_id, nil)
0
unless task_id_opt
0
raise TaskError, 'No Task ID given.'
end
0
task = get_task_by_id(task_id_opt)
0
@tasks.delete(task.id)
# Get running Tracks and remove these from Stack.
0
task.tracks({:status => ?R}).each do |track_id, track|
0
@stack.remove(track)
end
0
@stack.save_to_file
# Remove Task from Foreign ID DB.
0
@foreign_id_db.remove_task(task)
0
@foreign_id_db.save_to_file
0
task.delete_file
0
task
end
# Remove a Track.
#
# Options:
#
# - `:track_id` (String)
1
def remove_track(options = Hash.new)
0
track_id_opt = options.fetch(:track_id, nil)
0
unless track_id_opt
0
raise TrackError, 'No Track ID given.'
end
0
track = get_track_by_id(track_id_opt)
0
unless track
0
raise TrackError, "Track for ID '#{track_id_opt}' not found."
end
0
task = track.task
0
track.remove
0
task.save_to_file
# Remove Track from Stack.
0
@stack.remove(track)
0
@stack.save_to_file
{
:task => task,
:track => track,
0
}
end
# Find a [Task](rdoc-ref:TheFox::Timr::Model::Task) by ID (internal or foreign).
#
# Tasks always should be loaded with this methods to check if a Task instance already exist at `@tasks`. This is like a cache.
#
# `task_id` can be a short ID or a Foreign ID. If a Task is already loaded with full ID another search by short ID would lead to generate a new object_id. Then there would be two Tasks instances loaded for the same Task ID. The Check Cache if condition prohibits this.
1
def get_task_by_id(task_id)
# First search in Foreign ID DB.
0
tmp_task_id = @foreign_id_db.get_task_id(task_id)
0
if tmp_task_id
0
task_id = tmp_task_id
end
0
task = @tasks[task_id]
0
if task
# Take Task from cache.
else
0
task = Task.load_task_from_file_with_id(@tasks_path, task_id)
# Check cache.
0
if @tasks[task.id]
# Task already loaded.
0
task = @tasks[task.id]
else
# Set new loaded Task.
0
@tasks[task.id] = task
end
end
0
task
end
# Find a [Track](rdoc-ref:TheFox::Timr::Model::Track) by ID.
1
def get_track_by_id(track_id)
0
@tasks.each do |task_id, task|
0
track = task.find_track_by_id(track_id)
0
if track
0
return track
end
end
nil
end
# Get a Track by a specific Task ID and Track ID.
1
def get_track_by_task_id(task_id, track_id)
0
if task_id && track_id
0
task = get_task_by_id(task_id)
0
if task
0
return task.find_track_by_id(track_id)
end
end
nil
end
# Get all [Tasks](rdoc-ref:TheFox::Timr::Model::Task).
1
def tasks
0
load_all_tracks
0
@tasks
end
# Get all [Tracks](rdoc-ref:TheFox::Timr::Model::Track).
#
# Options:
#
# - `:sort` (Boolean)
1
def tracks(options = Hash.new)
0
sort_opt = options.fetch(:sort, true)
0
load_all_tracks
0
filtered_tracks = Hash.new
0
@tasks.each do |task_id, task|
0
tracks = task.tracks(options)
0
filtered_tracks.merge!(tracks)
end
0
if sort_opt
# Sort ASC by Begin DateTime, End DateTime.
filtered_tracks.sort{ |t1, t2|
0
t1 = t1.last
0
t2 = t2.last
0
cmp1 = t1.begin_datetime <=> t2.begin_datetime
0
if cmp1 == 0
0
t1.end_datetime <=> t2.end_datetime
else
0
cmp1
end
0
}.to_h
else
0
filtered_tracks
end
end
# Alias for `ForeignIdDb#match_task_with_foreign_id`.
# def match_task_with_foreign_id(task, foreign_id)
# end
# Save [Stack](rdoc-ref:TheFox::Timr::Model::Stack) and [Config](rdoc-ref:TheFox::Timr::Model::Config).
1
def shutdown
# Save Stack
2
@stack.save_to_file
# Save config
2
@config.save_to_file
# Save Foreign ID DB
2
@foreign_id_db.save_to_file
end
# Load all [Tracks](rdoc-ref:TheFox::Timr::Model::Track) using `get_task_by_id`.
1
def load_all_tracks
# Iterate all files.
0
@tasks_path.find.each do |file|
# Filter all directories.
0
unless file.file?
0
next
end
# Filter all non-yaml files.
0
unless file.basename.fnmatch('*.yml')
0
next
end
0
track_id = BasicModel.get_id_from_path(@tasks_path, file)
# Loads the Task from file into @tasks.
0
get_task_by_id(track_id)
end
end
1
def to_s
1
'Timr'
end
end # class Timr
end # module Timr
end # module TheFox