# frozen_string_literal: true
require "thor"
require "erb"
require "active_support/core_ext/string/filters"
require "active_support/core_ext/string/inflections"
require_relative "actions"
module Rails
module Command
class Base < Thor
class Error < Thor::Error # :nodoc:
end
include Actions
class << self
# Returns true when the app is a Rails engine.
def engine?
defined?(ENGINE_ROOT)
end
# Tries to get the description from a USAGE file one folder above the command
# root.
def desc(usage = nil, description = nil, options = {})
if usage
super
else
@desc ||= ERB.new(File.read(usage_path)).result(binding) if usage_path
end
end
# Convenience method to get the namespace from the class name. It's the
# same as Thor default except that the Command at the end of the class
# is removed.
def namespace(name = nil)
if name
super
else
@namespace ||= super.chomp("_command").sub(/:command:/, ":")
end
end
# Convenience method to hide this command from the available ones when
# running rails command.
def hide_command!
Rails::Command.hidden_commands << self
end
def inherited(base) #:nodoc:
super
if base.name && base.name !~ /Base$/
Rails::Command.subclasses << base
end
end
def perform(command, args, config) # :nodoc:
if Rails::Command::HELP_MAPPINGS.include?(args.first)
command, args = "help", []
end
dispatch(command, args.dup, nil, config)
end
def printing_commands
namespaced_commands
end
def executable
"bin/rails #{command_name}"
end
# Use Rails' default banner.
def banner(*)
"#{executable} #{arguments.map(&:usage).join(' ')} [options]".squish
end
# Sets the base_name taking into account the current class namespace.
#
# Rails::Command::TestCommand.base_name # => 'rails'
def base_name
@base_name ||= begin
if base = name.to_s.split("::").first
base.underscore
end
end
end
# Return command name without namespaces.
#
# Rails::Command::TestCommand.command_name # => 'test'
def command_name
@command_name ||= begin
if command = name.to_s.split("::").last
command.chomp!("Command")
command.underscore
end
end
end
# Path to lookup a USAGE description in a file.
def usage_path
if default_command_root
path = File.join(default_command_root, "USAGE")
path if File.exist?(path)
end
end
# Default file root to place extra files a command might need, placed
# one folder above the command file.
#
# For a Rails::Command::TestCommand placed in rails/command/test_command.rb
# would return rails/test.
def default_command_root
path = File.expand_path(File.join("../commands", command_root_namespace), __dir__)
path if File.exist?(path)
end
private
# Allow the command method to be called perform.
def create_command(meth)
if meth == "perform"
alias_method command_name, meth
else
# Prevent exception about command without usage.
# Some commands define their documentation differently.
@usage ||= ""
@desc ||= ""
super
end
end
def command_root_namespace
(namespace.split(":") - %w( rails )).first
end
def namespaced_commands
commands.keys.map do |key|
key == command_root_namespace ? key : "#{command_root_namespace}:#{key}"
end
end
end
def help
if command_name = self.class.command_name
self.class.command_help(shell, command_name)
else
super
end
end
end
end
end