mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
183 lines
5 KiB
Ruby
183 lines
5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "thor"
|
|
require "erb"
|
|
|
|
require "active_support/core_ext/string/filters"
|
|
require "active_support/core_ext/string/inflections"
|
|
|
|
require "rails/command/actions"
|
|
|
|
module Rails
|
|
module Command
|
|
class Base < Thor
|
|
class Error < Thor::Error # :nodoc:
|
|
end
|
|
|
|
class CorrectableError < Error # :nodoc:
|
|
attr_reader :key, :options
|
|
|
|
def initialize(message, key, options)
|
|
@key = key
|
|
@options = options
|
|
super(message)
|
|
end
|
|
|
|
if defined?(DidYouMean::SpellChecker) && defined?(DidYouMean::Correctable)
|
|
include DidYouMean::Correctable
|
|
|
|
def corrections
|
|
@corrections ||= DidYouMean::SpellChecker.new(dictionary: options).correct(key)
|
|
end
|
|
end
|
|
end
|
|
|
|
include Actions
|
|
|
|
class << self
|
|
def exit_on_failure? # :nodoc:
|
|
false
|
|
end
|
|
|
|
# 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), trim_mode: "-").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.end_with?("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
|
|
"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 ||= if base = name.to_s.split("::").first
|
|
base.underscore
|
|
end
|
|
end
|
|
|
|
# Return command name without namespaces.
|
|
#
|
|
# Rails::Command::TestCommand.command_name # => 'test'
|
|
def command_name
|
|
@command_name ||= if command = name.to_s.split("::").last
|
|
command.chomp!("Command")
|
|
command.underscore
|
|
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 <tt>rails/command/test_command.rb</tt>
|
|
# would return <tt>rails/test</tt>.
|
|
def default_command_root
|
|
path = File.expand_path(relative_command_path, __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)).join(":")
|
|
end
|
|
|
|
def relative_command_path
|
|
File.join("../commands", *command_root_namespace.split(":"))
|
|
end
|
|
|
|
def namespaced_commands
|
|
commands.keys.map do |key|
|
|
if command_root_namespace.match?(/(\A|:)#{key}\z/)
|
|
command_root_namespace
|
|
else
|
|
"#{command_root_namespace}:#{key}"
|
|
end
|
|
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
|