Class: Asgard::Base

Inherits:
Thor
  • Object
show all
Includes:
Shell
Defined in:
lib/asgard/base.rb

Direct Known Subclasses

Tasks

Class Method Summary collapse

Methods included from Shell

#sh, #shebang

Class Method Details

._build_dep_graph(stages) ⇒ Object

Translate stages into a DependencyGraph-compatible hash.

stages: [[:one], [:two, :three], [:four]]
→ { one: [], two: [:one], three: [:one], four: [:two, :three] }


51
52
53
54
55
56
57
58
# File 'lib/asgard/base.rb', line 51

def _build_dep_graph(stages)
  graph = {}
  stages.each_with_index do |stage, i|
    prev_stage = i > 0 ? stages[i - 1] : []
    stage.each { |task| graph[task] = prev_stage.dup }
  end
  graph
end

._depsObject



26
27
28
# File 'lib/asgard/base.rb', line 26

def _deps
  @_deps ||= {}
end

._ran_mutexObject



38
39
40
# File 'lib/asgard/base.rb', line 38

def _ran_mutex
  @_ran_mutex ||= Mutex.new
end

._ran_tasksObject



34
35
36
# File 'lib/asgard/base.rb', line 34

def _ran_tasks
  @_ran_tasks ||= Set.new
end

._reset_ran!Object

Reset execution tracking for a fresh asgard invocation.



43
44
45
# File 'lib/asgard/base.rb', line 43

def _reset_ran!
  _ran_mutex.synchronize { @_ran_tasks = Set.new }
end

._varsObject



30
31
32
# File 'lib/asgard/base.rb', line 30

def _vars
  @_vars ||= {}
end

.depends_on(*recipes) ⇒ Object

Declare dependencies for the next recipe. Bare symbols run sequentially; arrays within the splat run in parallel.

depends_on :build                          # sequential
depends_on :build, :lint                   # both sequential
depends_on [:build, :lint]                 # build and lint in parallel
depends_on :setup, [:build, :lint], :test  # setup, then build+lint, then test


67
68
69
# File 'lib/asgard/base.rb', line 67

def depends_on(*recipes)
  @_pending_deps = recipes
end

.dotenv(path = ".env") ⇒ Object



86
87
88
89
# File 'lib/asgard/base.rb', line 86

def dotenv(path = ".env")
  require "dotenv"
  Dotenv.load(path) if File.exist?(path)
end

.import(mod) ⇒ Object



82
83
84
# File 'lib/asgard/base.rb', line 82

def import(mod)
  include mod
end

.inherited(subclass) ⇒ Object



16
17
18
19
20
21
22
23
24
# File 'lib/asgard/base.rb', line 16

def inherited(subclass)
  super
  Asgard::Base.subclasses << subclass
  subclass.instance_variable_set(:@_deps,         {})
  subclass.instance_variable_set(:@_vars,         {})
  subclass.instance_variable_set(:@_pending_deps, [])
  subclass.instance_variable_set(:@_ran_tasks,    Set.new)
  subclass.instance_variable_set(:@_ran_mutex,    Mutex.new)
end

.method_added(method_name) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
# File 'lib/asgard/base.rb', line 105

def method_added(method_name)
  pending = Array(@_pending_deps).dup
  @_pending_deps = []

  return super if pending.empty?
  return super if method_name.to_s.start_with?("_")

  # Each element is a Symbol (sequential) or Array (parallel group).
  _deps[method_name.to_sym] = pending.map { |d| Array(d).map(&:to_sym) }
  super
end

.subclassesObject



12
13
14
# File 'lib/asgard/base.rb', line 12

def subclasses
  @subclasses ||= []
end

.validate_deps!Object

Validate the full dep graph for cycles using Dagwood::DependencyGraph.



92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/asgard/base.rb', line 92

def validate_deps!
  return if _deps.empty?

  all_tasks  = all_commands.keys.map(&:to_sym)
  full_graph = all_tasks.each_with_object({}) do |task, hash|
    hash[task] = _deps.fetch(task, []).flatten
  end

  Dagwood::DependencyGraph.new(full_graph).order
rescue TSort::Cyclic => e
  raise Asgard::CircularDependencyError, e.message
end

.var(name, value = nil, &block) ⇒ Object



71
72
73
74
75
76
77
78
79
80
# File 'lib/asgard/base.rb', line 71

def var(name, value = nil, &block)
  value = block if block_given?
  _vars[name.to_sym] = value
  no_commands do
    define_method(name) do
      v = self.class._vars[name.to_sym]
      v.respond_to?(:call) ? v.call : v
    end
  end
end