Helper Methods¶
Not every method needs to be a CLI command. Asgard (via Thor) provides two mechanisms to define callable helper methods that are excluded from asgard help and cannot be invoked directly from the command line.
Private Methods¶
Methods declared after private are callable from any task in the same class but are invisible to Thor's command dispatcher. They will not appear in --help output and cannot be called from the CLI:
class Tasks
desc "Compile and package"
def build
compile("src")
package(app_version)
end
desc "Build and publish to RubyGems"
def release
build
sh "gem push pkg/myapp-#{app_version}.gem"
end
private
def compile(dir)
sh "gcc -O2 -o bin/myapp #{dir}/*.c"
end
def package(ver)
sh "tar czf pkg/myapp-#{ver}.tar.gz bin/"
end
def app_version
`git describe --tags`.strip
end
end
Note
In Ruby, private applies to all methods defined after it in the same class body. You can group all helpers at the bottom of the class after a single private declaration.
The no_commands Block¶
Thor's no_commands block marks public methods as excluded from CLI discovery. Unlike private, these methods are still publicly accessible from Ruby code (e.g., from a subclass or a module). They are useful for methods that must be public for technical reasons but should not appear as commands:
class Tasks
desc "Compile the project"
def build
puts "Revision: #{current_sha}"
sh "rake build"
end
desc "Deploy to production"
def deploy
puts "Deploying revision #{current_sha}..."
sh "cap production deploy"
end
no_commands do
def current_sha
`git rev-parse --short HEAD`.strip
end
def timestamp
Time.now.strftime("%Y%m%d-%H%M%S")
end
end
end
Choosing Between private and no_commands¶
private |
no_commands |
|
|---|---|---|
Hidden from --help |
Yes | Yes |
| Blocked from CLI | Yes | Yes |
| Accessible from subclass | No | Yes |
| Accessible from module include | No | Yes |
| Ruby idiom | Familiar | Thor-specific |
For most helpers, private is the right choice. Use no_commands when the helper must remain technically public (e.g., it will be inherited by a subcommand class).
Sharing Helpers Across Files¶
Within a project¶
Extract shared helpers into a plain Ruby module and load it from .loki using require_relative:
# shared/helpers.rb
module BuildHelpers
private
def compile(dir)
sh "gcc -O2 -o bin/myapp #{dir}/*.c"
end
def dist_path(ver)
"pkg/myapp-#{ver}.tar.gz"
end
end
# .loki
require_relative "shared/helpers"
class Tasks
include BuildHelpers
desc "Compile the project"
def build = compile("src")
desc "Create distribution archive"
def package = sh "tar czf #{dist_path(app_version)} bin/"
end
Because include in the class body makes the module methods available as instance methods, and they are declared private inside the module, they remain invisible to Thor.
Tip
Helpers in a shared module can call sh, shebang, and other Asgard DSL methods because those are included in Tasks (via Asgard::Base and Asgard::Shell) and are available in self when the module method is invoked.
Across projects with import_up¶
If helpers are defined as tasks in a shared .loki file, use import_up to load them from any sub-project without knowing the absolute path:
~/sandbox/
shared_helpers.loki ← defines helper tasks available to all sub-projects
projectA/
.loki
projectB/
.loki
# projectA/.loki (and identically in projectB/.loki)
import_up "shared_helpers.loki"
class Tasks
# shared tasks are already defined in Tasks by here
end
import_up walks up from Dir.pwd until it finds shared_helpers.loki, then loads it. It returns false without raising if the file is not found, making it safe to use in projects where the shared file may not always be present.
Helper Methods in Subcommands¶
Subcommand classes that inherit from Tasks also inherit all private helpers and no_commands methods defined on Tasks. You can also define helpers local to the subcommand class: