Class: Asgard::Base
Direct Known Subclasses
Class Method Summary collapse
-
._build_dep_graph(stages) ⇒ Object
Translate stages into a DependencyGraph-compatible hash.
- ._cond ⇒ Object
- ._deps ⇒ Object
- ._done ⇒ Object
- ._ran_mutex ⇒ Object
-
._reset_ran! ⇒ Object
Reset execution tracking for a fresh asgard invocation.
- ._running ⇒ Object
- .default_task(meth = nil) ⇒ Object
-
.depends_on(*tasks) ⇒ Object
Declare dependencies for the next task.
-
.desc(usage_or_desc, description = nil, options = {}) ⇒ Object
Allow single-argument desc: desc “Run the tests” The usage string defaults to the method name when the description is the only arg.
- .dotenv(path = ".env") ⇒ Object
- .footer(text = nil) ⇒ Object
- .header(text = nil) ⇒ Object
- .helper(name) ⇒ Object
- .inherited(subclass) ⇒ Object
- .method_added(method_name) ⇒ Object
-
.no_negate(*names) ⇒ Object
Suppress [–no-name] / [–skip-name] from help for boolean class options where negation is meaningless.
- .subclasses ⇒ Object
-
.validate_deps! ⇒ Object
Validate the full dep graph for cycles using Dagwood::DependencyGraph.
Instance Method Summary collapse
-
#help(command = nil, subcommand = false) ⇒ Object
rubocop:disable Style/OptionalBooleanParameter.
Methods included from Shell
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] }
61 62 63 64 65 66 67 68 |
# File 'lib/asgard/base.rb', line 61 def _build_dep_graph(stages) graph = {} stages.each_with_index do |stage, i| prev_stage = i.positive? ? stages[i - 1] : [] stage.each { |task| graph[task] = prev_stage.dup } end graph end |
._cond ⇒ Object
40 41 42 |
# File 'lib/asgard/base.rb', line 40 def _cond @_cond ||= Hash.new { |h, k| h[k] = ConditionVariable.new } end |
._deps ⇒ Object
28 29 30 |
# File 'lib/asgard/base.rb', line 28 def _deps @_deps ||= {} end |
._done ⇒ Object
36 37 38 |
# File 'lib/asgard/base.rb', line 36 def _done @_done ||= Set.new end |
._ran_mutex ⇒ Object
44 45 46 |
# File 'lib/asgard/base.rb', line 44 def _ran_mutex @_ran_mutex ||= Mutex.new end |
._reset_ran! ⇒ Object
Reset execution tracking for a fresh asgard invocation.
49 50 51 52 53 54 55 |
# File 'lib/asgard/base.rb', line 49 def _reset_ran! _ran_mutex.synchronize do @_running = Set.new @_done = Set.new @_cond = Hash.new { |h, k| h[k] = ConditionVariable.new } end end |
._running ⇒ Object
32 33 34 |
# File 'lib/asgard/base.rb', line 32 def _running @_running ||= Set.new end |
.default_task(meth = nil) ⇒ Object
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/asgard/base.rb', line 127 def default_task(meth = nil) if meth && meth != :none && @_default_task_location here = caller_locations(1, 1).first # rubocop:disable Style/StderrPuts -- warn bypasses $stderr in Ruby 4.0, breaking capture_io in tests $stderr.puts "asgard: default_task :#{meth} at #{here.path}:#{here.lineno} " \ "overrides default_task :#{@_default_task_name} set at " \ "#{@_default_task_location.path}:#{@_default_task_location.lineno}" # rubocop:enable Style/StderrPuts end if meth && meth != :none @_default_task_location = caller_locations(1, 1).first @_default_task_name = meth end super end |
.depends_on(*tasks) ⇒ Object
Declare dependencies for the next task. 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
77 78 79 |
# File 'lib/asgard/base.rb', line 77 def depends_on(*tasks) @_pending_deps = tasks end |
.desc(usage_or_desc, description = nil, options = {}) ⇒ Object
Allow single-argument desc: desc “Run the tests” The usage string defaults to the method name when the description is the only arg.
83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/asgard/base.rb', line 83 def desc(usage_or_desc, description = nil, = {}) if description.nil? || description.is_a?(Hash) = description if description.is_a?(Hash) @_pending_single_desc = usage_or_desc @_pending_single_desc_opts = else @_pending_single_desc = nil @_pending_single_desc_opts = nil super end end |
.dotenv(path = ".env") ⇒ Object
117 118 119 120 |
# File 'lib/asgard/base.rb', line 117 def dotenv(path = ".env") require "dotenv" Dotenv.load(path) if File.exist?(path) end |
.footer(text = nil) ⇒ Object
112 113 114 115 |
# File 'lib/asgard/base.rb', line 112 def (text = nil) return @_footer if text.nil? (@_footer ||= []).unshift(text) end |
.header(text = nil) ⇒ Object
107 108 109 110 |
# File 'lib/asgard/base.rb', line 107 def header(text = nil) return @_header if text.nil? (@_header ||= []) << text end |
.helper(name) ⇒ Object
122 123 124 125 |
# File 'lib/asgard/base.rb', line 122 def helper(name, &) define_singleton_method(name, &) no_commands { private define_method(name) { |*args, **kwargs, &blk| self.class.send(name, *args, **kwargs, &blk) } } end |
.inherited(subclass) ⇒ Object
15 16 17 18 19 20 21 22 23 24 25 26 |
# File 'lib/asgard/base.rb', line 15 def inherited(subclass) super Asgard::Base.subclasses << subclass subclass.instance_variable_set(:@_deps, {}) subclass.instance_variable_set(:@_pending_deps, []) subclass.instance_variable_set(:@_pending_single_desc, nil) subclass.instance_variable_set(:@_pending_single_desc_opts, nil) subclass.instance_variable_set(:@_running, Set.new) subclass.instance_variable_set(:@_done, Set.new) subclass.instance_variable_set(:@_cond, Hash.new { |h, k| h[k] = ConditionVariable.new }) subclass.instance_variable_set(:@_ran_mutex, Mutex.new) end |
.method_added(method_name) ⇒ Object
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/asgard/base.rb', line 193 def method_added(method_name) if @_pending_single_desc && !no_commands? pending_desc = @_pending_single_desc pending_opts = @_pending_single_desc_opts || {} @_pending_single_desc = nil @_pending_single_desc_opts = nil desc(method_name.to_s, pending_desc, pending_opts) end return super unless @usage 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 |
.no_negate(*names) ⇒ Object
Suppress [–no-name] / [–skip-name] from help for boolean class options where negation is meaningless. Call after class_option declarations.
97 98 99 100 101 102 103 104 105 |
# File 'lib/asgard/base.rb', line 97 def no_negate(*names) names.each do |name| opt = [name] next unless opt opt.define_singleton_method(:usage) do |padding = 0| aliases_for_usage.ljust(padding) + "[#{switch_name}]" end end end |
.subclasses ⇒ Object
11 12 13 |
# File 'lib/asgard/base.rb', line 11 def subclasses @subclasses ||= [] end |
.validate_deps! ⇒ Object
Validate the full dep graph for cycles using Dagwood::DependencyGraph.
144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/asgard/base.rb', line 144 def validate_deps! _check_orphaned_deps! return if _deps.empty? all_task_names = all_commands.keys.map(&:to_sym) _check_undefined_deps!(all_task_names) _check_dep_arities! _build_and_sort_graph(all_task_names) rescue TSort::Cyclic => e raise Asgard::CircularDependencyError, e. end |
Instance Method Details
#help(command = nil, subcommand = false) ⇒ Object
rubocop:disable Style/OptionalBooleanParameter
216 217 218 219 220 221 |
# File 'lib/asgard/base.rb', line 216 def help(command = nil, subcommand = false) # rubocop:disable Style/OptionalBooleanParameter say self.class.header.join("\n\n") if self.class.header && command.nil? say "\n" super say self.class..join("\n\n") if self.class. && command.nil? end |