Skip to content

Commit

Permalink
feat: manager improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
AlanAlvarez21 committed Feb 6, 2025
1 parent 64f7d66 commit 3b0390a
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 86 deletions.
2 changes: 0 additions & 2 deletions lib/bas.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# frozen_string_literal: true

require_relative "bas/version"
require_relative "bas/orchestrator"

module Bas
class Error < StandardError; end
end
112 changes: 28 additions & 84 deletions lib/bas/orchestrator/manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@

module Bas
module Orchestrator
# Manages the execution of scheduled scripts, handling parallel execution
# and ensuring scripts run based on time intervals, specific times, or days.
##
# Manager class responsible for scheduling and executing scripts concurrently.
#
# This class initializes a thread pool and processes scheduled scripts based on
# time intervals, specific days, or exact times.
#
class Manager
def initialize(schedules)
@last_executions = Hash.new(0.0)
Expand All @@ -14,134 +18,74 @@ def initialize(schedules)
end

def run
until @pool.shutdown?
@actual_time = Time.now
@schedules.each { |script| process_script(script) }
sleep 1
end
@schedules.each { |script| @pool.post { process_script(script) } }

@pool.shutdown
@pool.wait_for_termination
end

private

# Determines the correct execution method based on schedule type
def process_script(script)
return unless should_execute?(script)
loop do
@actual_time = Time.new

execute(script)
update_last_execution(script)
end

# Determines if a script should be executed based on its schedule type
def should_execute?(script)
return time_elapsed?(script) if interval?(script)
return day_match?(script) && time_match?(script) if day?(script) && time?(script)
return time_match?(script) unless day?(script)

false
end
execute_interval(script) if interval?(script)
execute_day(script) if day?(script) && time?(script)
execute_time(script) if time?(script) && !day?(script)

# Updates the last execution time for the given script
def update_last_execution(script)
@last_executions[script[:path]] = time_in_milliseconds
sleep 0.1
rescue StandardError => e
puts "Error in thread: #{e.message}"
end
end

# Executes scripts that run on a time interval
def execute_interval(script)
return unless time_elapsed?(script)
return unless time_in_milliseconds - @last_executions[script[:path]] >= script[:interval]

execute(script)
@last_executions[script[:path]] = time_in_milliseconds
end

# Executes scripts that run at a specific time and day
def execute_day(script)
return unless day_match?(script) && time_match?(script)
return unless script[:day].include?(current_day) && script[:time].include?(current_time)

execute(script) unless already_executed?(script)
execute(script) unless @last_executions[script[:path]].eql?(current_time)
@last_executions[script[:path]] = current_time
end

# Executes scripts that run at a specific time only
def execute_time(script)
return unless time_match?(script) && !already_executed?(script)

execute(script)
execute(script) if script[:time].include?(current_time) && !@last_executions[script[:path]].eql?(current_time)
@last_executions[script[:path]] = current_time
end

# Checks if enough time has passed since the last execution
def time_elapsed?(script)
(time_in_milliseconds - @last_executions[script[:path]]) >= script[:interval]
end

# Checks if today matches the script's specified execution days
def day_match?(script)
script[:day]&.include?(current_day)
end

# Checks if the current time matches the script's execution time
def time_match?(script)
script[:time]&.include?(current_time)
end

# Ensures a script is not executed multiple times at the same time slot
def already_executed?(script)
@last_executions[script[:path]] == current_time
end

# Helper methods to determine schedule type
def interval?(script)
script.key?(:interval)
script[:interval]
end

def time?(script)
script.key?(:time)
script[:time]
end

def day?(script)
script.key?(:day)
script[:day]
end

# Retrieves the current time in milliseconds
def time_in_milliseconds
@actual_time.to_f * 1000
end

# Retrieves the current time as HH:MM
def current_time
@actual_time.strftime("%H:%M")
end

# Retrieves the current day name
def current_day
@actual_time.strftime("%A")
end

# Executes a script, ensuring its path is correct
def execute(script)
script_path = resolve_script_path(script[:path])
puts "Executing #{script_path} at #{current_time}"

if File.exist?(script_path)
run_script(script_path)
else
puts "File not found: #{script_path}"
end
end

# Resolves the absolute path of a script based on its relative path
def resolve_script_path(relative_path)
root = `git rev-parse --show-toplevel`.strip
File.join(root, "src", "use_cases_execution", relative_path)
end

# Runs the script and captures output
def run_script(script_path)
output = ""
IO.popen(["ruby", script_path], err: %i[child out]) do |io|
output = io.read
end
output
puts "Executing #{script[:path]} at #{current_time}"
system("ruby #{script[:path]}")

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This string construction which depends on
library input
is later used in a
shell command
.
end
end
end
Expand Down

0 comments on commit 3b0390a

Please sign in to comment.