module Thor::Util
def camel_case(str)
String
==== Returns
String
==== Parameters
Receives a string and convert it to camel case. camel_case returns CamelCase.
def camel_case(str) return str if str !~ /_/ && str =~ /[A-Z]+.*/ str.split("_").map(&:capitalize).join end
def escape_globs(path)
String
==== Returns
String
==== Parameters
Thor::Util.escape_globs('[apps]') # => '\[apps\]'
==== Examples
The glob characters are `* ? { } [ ]`.
Returns a string that has had any glob characters escaped.
def escape_globs(path) path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&') end
def escape_html(string)
String
==== Returns
String
==== Parameters
Thor::Util.escape_html('
') # => "<div>"
==== Examples
Returns a string that has had any HTML characters escaped.
==== Examples
Returns a string that has had any HTML characters escaped.
def escape_html(string) CGI.escapeHTML(string) end
def find_by_namespace(namespace)
namespace
==== Parameters
Receives a namespace and search for it in the Thor::Base subclasses.
def find_by_namespace(namespace) namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/ Thor::Base.subclasses.detect { |klass| klass.namespace == namespace } end
def find_class_and_command_by_namespace(namespace, fallback = true)
namespace
==== Parameters
Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz"
Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil
Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default command
end
class Baz::Foo < Thor::Group
end
end
def baz
class Foo::Bar < Thor
==== Examples
again. If found, returns the highest entry as the class name.
if it's not found, removes the highest entry and searches for the class
from it. It first searches for a class using the all the given namespace,
Receives a namespace and tries to retrieve a Thor or Thor::Group class
def find_class_and_command_by_namespace(namespace, fallback = true) if namespace.include?(":") # look for a namespaced command *pieces, command = namespace.split(":") namespace = pieces.join(":") namespace = "default" if namespace.empty? klass = Thor::Base.subclasses.detect { |thor| thor.namespace == namespace && thor.commands.keys.include?(command) } end unless klass # look for a Thor::Group with the right name klass = Thor::Util.find_by_namespace(namespace) command = nil end if !klass && fallback # try a command in the default namespace command = namespace klass = Thor::Util.find_by_namespace("") end [klass, command] end
def globs_for(path)
Where to look for Thor files.
def globs_for(path) path = escape_globs(path) ["#{path}/Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/**/*.thor"] end
def load_thorfile(path, content = nil, debug = false)
inside the sandbox to avoid namespacing conflicts.
Receives a path and load the thor file in the path. The file is evaluated
def load_thorfile(path, content = nil, debug = false) content ||= File.read(path) begin Thor::Sandbox.class_eval(content, path) rescue StandardError => e $stderr.puts("WARNING: unable to load thorfile #{path.inspect}: #{e.message}") if debug $stderr.puts(*e.backtrace) else $stderr.puts(e.backtrace.first) end end end
def namespace_from_thor_class(constant)
String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz"
==== Returns
constant
def namespace_from_thor_class(constant) constant = constant.to_s.gsub(/^Thor::Sandbox::/, "") constant = snake_case(constant).squeeze(":") constant end
def namespaces_in_content(contents, file = __FILE__)
Array[Object]
==== Returns
contents
==== Parameters
namespaces defined in the sandbox.
Given the contents, evaluate it inside the sandbox and returns the
def namespaces_in_content(contents, file = __FILE__) old_constants = Thor::Base.subclasses.dup Thor::Base.subclasses.clear load_thorfile(file, contents) new_constants = Thor::Base.subclasses.dup Thor::Base.subclasses.replace(old_constants) new_constants.map!(&:namespace) new_constants.compact! new_constants end
def ruby_command
installations and windows extensions.
Return the path to the ruby interpreter taking into account multiple
def ruby_command @ruby_command ||= begin ruby_name = RbConfig::CONFIG["ruby_install_name"] ruby = File.join(RbConfig::CONFIG["bindir"], ruby_name) ruby << RbConfig::CONFIG["EXEEXT"] # avoid using different name than ruby (on platforms supporting links) if ruby_name != "ruby" && File.respond_to?(:readlink) begin alternate_ruby = File.join(RbConfig::CONFIG["bindir"], "ruby") alternate_ruby << RbConfig::CONFIG["EXEEXT"] # ruby is a symlink if File.symlink? alternate_ruby linked_ruby = File.readlink alternate_ruby # symlink points to 'ruby_install_name' ruby = alternate_ruby if linked_ruby == ruby_name || linked_ruby == ruby end rescue NotImplementedError # rubocop:disable Lint/HandleExceptions # just ignore on windows end end # escape string in case path to ruby executable contain spaces. ruby.sub!(/.*\s.*/m, '"\&"') ruby end end
def snake_case(str)
String
==== Returns
String
==== Parameters
Receives a string and convert it to snake case. SnakeCase returns snake_case.
def snake_case(str) return str.downcase if str =~ /^[A-Z_]+$/ str.gsub(/\B[A-Z]/, '_\&').squeeze("_") =~ /_*(.*)/ Regexp.last_match(-1).downcase end
def thor_classes_in(klass)
Returns the thor classes declared inside the given class.
def thor_classes_in(klass) stringfied_constants = klass.constants.map(&:to_s) Thor::Base.subclasses.select do |subclass| next unless subclass.name stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", "")) end end
def thor_root
Returns the root where thor files are located, depending on the OS.
def thor_root File.join(user_home, ".thor").tr("\\", "/") end
def thor_root_glob
If we don't #gsub the \ character, Dir.glob will fail.
C:\Documents and Settings\james\.thor
like this:
Returns the files in the thor root. On Windows thor_root will be something
def thor_root_glob files = Dir["#{escape_globs(thor_root)}/*"] files.map! do |file| File.directory?(file) ? File.join(file, "main.thor") : file end end
def user_home
def user_home @@user_home ||= if ENV["HOME"] ENV["HOME"] elsif ENV["USERPROFILE"] ENV["USERPROFILE"] elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"] File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"]) elsif ENV["APPDATA"] ENV["APPDATA"] else begin File.expand_path("~") rescue if File::ALT_SEPARATOR "C:/" else "/" end end end end