Class: ChoresKit::Chore

Inherits:
Object
  • Object
show all
Defined in:
lib/chores_kit/chore.rb

Constant Summary collapse

DEFAULT_NOTIFICATIONS =
%i[successful failed].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name) ⇒ Chore



12
13
14
15
16
17
18
19
20
# File 'lib/chores_kit/chore.rb', line 12

def initialize(name)
  @name = name
  @metadata = {}

  @dag = DAG.new(mixin: EmbeddedTask)
  @tasks = @dag.vertices

  @notifications = {}
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



8
9
10
# File 'lib/chores_kit/chore.rb', line 8

def name
  @name
end

Instance Method Details

#description(string) ⇒ Object

Metadata



23
24
25
# File 'lib/chores_kit/chore.rb', line 23

def description(string)
  @metadata[:description] = string
end

#notify(*options, &block) ⇒ Object

After-run callbacks



99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/chores_kit/chore.rb', line 99

def notify(*options, &block)
  raise "Couldn't create notifications without a block" unless block_given?

  conditions = *options
  conditions = DEFAULT_NOTIFICATIONS if options.empty?

  conditions.each do |condition|
    notification = Notification.new(condition)
    notification.instance_eval(&block)

    @notifications[condition] = notification
  end
end

#retry_failed(options) ⇒ Object

rubocop:enable Metrics/AbcSize



42
43
44
45
46
47
48
49
50
51
52
# File 'lib/chores_kit/chore.rb', line 42

def retry_failed(options)
  raise "Couldn't parse retry interval from attributes" unless options[:wait].nil? || options[:wait].is_a?(AS::Duration)

  wait = options[:wait] || 1.second
  retries = options[:retries] || 1

  @metadata[:retry_failed] = {
    wait: wait,
    retries: retries
  }
end

#run(task, options = {}) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/chores_kit/chore.rb', line 68

def run(task, options = {})
  from = options[:triggered_by] || options[:upstream] || task
  to = options[:triggers] || options[:downstream] || task

  tasks = @tasks.map(&:name)
  direction = options[:upstream] || options[:triggered_by] ? 'upstream' : 'downstream'

  # Throw an error if either up- or downstream task doesn't exist
  non_existing_tasks = ([from, to] - tasks).uniq
  raise "Couldn't set #{direction} dependency for non-existing task #{non_existing_tasks.first}" if non_existing_tasks.any?

  # Throw an error if unsupported dependencies are set
  raise "Multiple upstream tasks aren't supported" if from.is_a?(Array)
  raise "Multiple downstream tasks aren't supported" if to.is_a?(Array)

  # Set explicit root task and skip further processing if the Chore has
  # just one task defined or if only one of its tasks is set to run
  if tasks.one? || from == to
    @dag.root = @dag.find_by(name: from)
    return
  end

  v1 = @dag.vertices.detect { |vertex| vertex[:name] == from }
  v2 = @dag.vertices.detect { |vertex| vertex[:name] == to }

  @dag.add_edge(from: v1, to: v2)
  @dag.root = v1
end

#schedule(options) ⇒ Object

rubocop:disable Metrics/AbcSize



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/chores_kit/chore.rb', line 28

def schedule(options)
  raise "Couldn't parse start time from attributes" if options[:at].nil?
  raise "Couldn't parse interval from attributes" unless options[:every].nil? || options[:every].is_a?(AS::Duration)

  at_ltz = Time.parse(options[:at]) || Time.now
  at_utc = Time.utc(*at_ltz) || Date.today.to_time.utc

  @metadata[:schedule] = {
    at:    at_utc,
    every: options[:every]
  }
end

#task(options, &block) ⇒ Object

Tasks and dependencies



55
56
57
58
59
60
61
62
63
64
65
# File 'lib/chores_kit/chore.rb', line 55

def task(options, &block)
  name, params = *options

  raise "Couldn't create task without a name" if name.nil?
  raise "Couldn't create task without a block" unless block_given?

  task = Task.new(name, params)
  task.instance_eval(&block)

  @dag.add_vertex(name: name, task: task)
end