mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Initial command structure.
This commit is contained in:
parent
b724dbbb8b
commit
6813edc7d9
25 changed files with 1182 additions and 166 deletions
|
@ -7,9 +7,11 @@ Rails::AppLoader.exec_app
|
|||
require "rails/ruby_version_check"
|
||||
Signal.trap("INT") { puts; exit(1) }
|
||||
|
||||
require "rails/command"
|
||||
|
||||
if ARGV.first == "plugin"
|
||||
ARGV.shift
|
||||
require "rails/commands/plugin"
|
||||
Rails::Command.invoke :plugin, ARGV
|
||||
else
|
||||
require "rails/commands/application"
|
||||
Rails::Command.invoke :application, ARGV
|
||||
end
|
||||
|
|
96
railties/lib/rails/command.rb
Normal file
96
railties/lib/rails/command.rb
Normal file
|
@ -0,0 +1,96 @@
|
|||
require "active_support"
|
||||
require "active_support/core_ext/enumerable"
|
||||
require "active_support/core_ext/object/blank"
|
||||
require "active_support/core_ext/hash/transform_values"
|
||||
|
||||
require "thor"
|
||||
|
||||
module Rails
|
||||
module Command
|
||||
autoload :Behavior, "rails/command/behavior"
|
||||
autoload :Base, "rails/command/base"
|
||||
|
||||
include Behavior
|
||||
|
||||
class << self
|
||||
def hidden_commands # :nodoc:
|
||||
@hidden_commands ||= []
|
||||
end
|
||||
|
||||
def environment
|
||||
ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development"
|
||||
end
|
||||
|
||||
# Receives a namespace, arguments and the behavior to invoke the command.
|
||||
def invoke(namespace, args = [], **config)
|
||||
namespace = namespace.to_s
|
||||
namespace = "help" if namespace.blank? || Thor::HELP_MAPPINGS.include?(namespace)
|
||||
namespace = "version" if %w( -v --version ).include? namespace
|
||||
|
||||
if command = find_by_namespace(namespace)
|
||||
command.perform(namespace, args, config)
|
||||
else
|
||||
find_by_namespace("rake").perform(namespace, args, config)
|
||||
end
|
||||
end
|
||||
|
||||
# Rails finds namespaces similar to thor, it only adds one rule:
|
||||
#
|
||||
# Command names must end with "_command.rb". This is required because Rails
|
||||
# looks in load paths and loads the command just before it's going to be used.
|
||||
#
|
||||
# find_by_namespace :webrat, :rails, :integration
|
||||
#
|
||||
# Will search for the following commands:
|
||||
#
|
||||
# "rails:webrat", "webrat:integration", "webrat"
|
||||
#
|
||||
# Notice that "rails:commands:webrat" could be loaded as well, what
|
||||
# Rails looks for is the first and last parts of the namespace.
|
||||
def find_by_namespace(name) # :nodoc:
|
||||
lookups = [ name, "rails:#{name}" ]
|
||||
|
||||
lookup(lookups)
|
||||
|
||||
namespaces = subclasses.index_by(&:namespace)
|
||||
namespaces[(lookups & namespaces.keys).first]
|
||||
end
|
||||
|
||||
# Returns the root of the Rails engine or app running the command.
|
||||
def root
|
||||
if defined?(ENGINE_ROOT)
|
||||
Pathname.new(ENGINE_ROOT)
|
||||
elsif defined?(APP_PATH)
|
||||
Pathname.new(File.expand_path("../..", APP_PATH))
|
||||
end
|
||||
end
|
||||
|
||||
def print_commands # :nodoc:
|
||||
sorted_groups.each { |b, n| print_list(b, n) }
|
||||
end
|
||||
|
||||
def sorted_groups # :nodoc:
|
||||
lookup!
|
||||
|
||||
groups = (subclasses - hidden_commands).group_by { |c| c.namespace.split(":").first }
|
||||
groups.transform_values! { |commands| commands.flat_map(&:printing_commands).sort }
|
||||
|
||||
rails = groups.delete("rails")
|
||||
[[ "rails", rails ]] + groups.sort.to_a
|
||||
end
|
||||
|
||||
protected
|
||||
def command_type
|
||||
@command_type ||= "command"
|
||||
end
|
||||
|
||||
def lookup_paths
|
||||
@lookup_paths ||= %w( rails/commands commands )
|
||||
end
|
||||
|
||||
def file_lookup_paths
|
||||
@file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_command.rb" ]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
18
railties/lib/rails/command/actions.rb
Normal file
18
railties/lib/rails/command/actions.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
module Rails
|
||||
module Command
|
||||
module Actions
|
||||
private
|
||||
# Change to the application's path if there is no config.ru file in current directory.
|
||||
# This allows us to run `rails server` from other directories, but still get
|
||||
# the main config.ru and properly set the tmp directory.
|
||||
def set_application_directory!
|
||||
Dir.chdir(File.expand_path("../../", APP_PATH)) unless File.exist?(File.expand_path("config.ru"))
|
||||
end
|
||||
|
||||
def require_application_and_environment!
|
||||
require APP_PATH
|
||||
Rails.application.require_environment!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
130
railties/lib/rails/command/base.rb
Normal file
130
railties/lib/rails/command/base.rb
Normal file
|
@ -0,0 +1,130 @@
|
|||
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
|
||||
|
||||
include Actions
|
||||
|
||||
class << self
|
||||
# Tries to get the description from a USAGE file one folder above the command
|
||||
# root.
|
||||
def desc(usage = nil, description = nil)
|
||||
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:
|
||||
command = nil if Thor::HELP_MAPPINGS.include?(args.first)
|
||||
|
||||
dispatch(command, args.dup, nil, config)
|
||||
end
|
||||
|
||||
def printing_commands
|
||||
namespace.sub(/^rails:/, "")
|
||||
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(base_name, command_name), __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
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
123
railties/lib/rails/command/behavior.rb
Normal file
123
railties/lib/rails/command/behavior.rb
Normal file
|
@ -0,0 +1,123 @@
|
|||
require "active_support"
|
||||
|
||||
module Rails
|
||||
module Command
|
||||
module Behavior #:nodoc:
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
class_methods do
|
||||
# Remove the color from output.
|
||||
def no_color!
|
||||
Thor::Base.shell = Thor::Shell::Basic
|
||||
end
|
||||
|
||||
# Track all command subclasses.
|
||||
def subclasses
|
||||
@subclasses ||= []
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This code is based directly on the Text gem implementation.
|
||||
# Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher.
|
||||
#
|
||||
# Returns a value representing the "cost" of transforming str1 into str2
|
||||
def levenshtein_distance(str1, str2)
|
||||
s = str1
|
||||
t = str2
|
||||
n = s.length
|
||||
m = t.length
|
||||
|
||||
return m if (0 == n)
|
||||
return n if (0 == m)
|
||||
|
||||
d = (0..m).to_a
|
||||
x = nil
|
||||
|
||||
# avoid duplicating an enumerable object in the loop
|
||||
str2_codepoint_enumerable = str2.each_codepoint
|
||||
|
||||
str1.each_codepoint.with_index do |char1, i|
|
||||
e = i+1
|
||||
|
||||
str2_codepoint_enumerable.with_index do |char2, j|
|
||||
cost = (char1 == char2) ? 0 : 1
|
||||
x = [
|
||||
d[j+1] + 1, # insertion
|
||||
e + 1, # deletion
|
||||
d[j] + cost # substitution
|
||||
].min
|
||||
d[j] = e
|
||||
e = x
|
||||
end
|
||||
|
||||
d[m] = x
|
||||
end
|
||||
|
||||
x
|
||||
end
|
||||
|
||||
# Prints a list of generators.
|
||||
def print_list(base, namespaces) #:nodoc:
|
||||
return if namespaces.empty?
|
||||
puts "#{base.camelize}:"
|
||||
|
||||
namespaces.each do |namespace|
|
||||
puts(" #{namespace}")
|
||||
end
|
||||
|
||||
puts
|
||||
end
|
||||
|
||||
# Receives namespaces in an array and tries to find matching generators
|
||||
# in the load path.
|
||||
def lookup(namespaces) #:nodoc:
|
||||
paths = namespaces_to_paths(namespaces)
|
||||
|
||||
paths.each do |raw_path|
|
||||
lookup_paths.each do |base|
|
||||
path = "#{base}/#{raw_path}_#{command_type}"
|
||||
|
||||
begin
|
||||
require path
|
||||
return
|
||||
rescue LoadError => e
|
||||
raise unless e.message =~ /#{Regexp.escape(path)}$/
|
||||
rescue Exception => e
|
||||
warn "[WARNING] Could not load #{command_type} #{path.inspect}. Error: #{e.message}.\n#{e.backtrace.join("\n")}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This will try to load any command in the load path to show in help.
|
||||
def lookup! #:nodoc:
|
||||
$LOAD_PATH.each do |base|
|
||||
Dir[File.join(base, *file_lookup_paths)].each do |path|
|
||||
begin
|
||||
path = path.sub("#{base}/", "")
|
||||
require path
|
||||
rescue Exception
|
||||
# No problem
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Convert namespaces to paths by replacing ":" for "/" and adding
|
||||
# an extra lookup. For example, "rails:model" should be searched
|
||||
# in both: "rails/model/model_generator" and "rails/model_generator".
|
||||
def namespaces_to_paths(namespaces) #:nodoc:
|
||||
paths = []
|
||||
namespaces.each do |namespace|
|
||||
pieces = namespace.split(":")
|
||||
paths << pieces.dup.push(pieces.last).join("/")
|
||||
paths << pieces.join("/")
|
||||
end
|
||||
paths.uniq!
|
||||
paths
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
32
railties/lib/rails/command/environment_argument.rb
Normal file
32
railties/lib/rails/command/environment_argument.rb
Normal file
|
@ -0,0 +1,32 @@
|
|||
require "active_support"
|
||||
|
||||
module Rails
|
||||
module Command
|
||||
module EnvironmentArgument #:nodoc:
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
argument :environment, optional: true, banner: "environment"
|
||||
end
|
||||
|
||||
private
|
||||
def extract_environment_option_from_argument
|
||||
if environment
|
||||
self.options = options.merge(environment: acceptable_environment(environment))
|
||||
end
|
||||
end
|
||||
|
||||
def acceptable_environment(env = nil)
|
||||
if available_environments.include? env
|
||||
env
|
||||
else
|
||||
%w(production development test).detect { |e| e =~ /^#{env}/ } || env
|
||||
end
|
||||
end
|
||||
|
||||
def available_environments
|
||||
Dir["config/environments/*.rb"].map { |fname| File.basename(fname, ".*") }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,4 +1,4 @@
|
|||
ARGV << "--help" if ARGV.empty?
|
||||
require "rails/command"
|
||||
|
||||
aliases = {
|
||||
"g" => "generate",
|
||||
|
@ -13,6 +13,4 @@ aliases = {
|
|||
command = ARGV.shift
|
||||
command = aliases[command] || command
|
||||
|
||||
require "rails/commands/commands_tasks"
|
||||
|
||||
Rails::CommandsTasks.new(ARGV).run_command!(command)
|
||||
Rails::Command.invoke command, ARGV
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
require "rails/generators"
|
||||
require "rails/generators/rails/app/app_generator"
|
||||
|
||||
module Rails
|
||||
module Generators
|
||||
class AppGenerator # :nodoc:
|
||||
# We want to exit on failure to be kind to other libraries
|
||||
# This is only when accessing via CLI
|
||||
def self.exit_on_failure?
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Command
|
||||
class ApplicationCommand < Base
|
||||
hide_command!
|
||||
|
||||
def help
|
||||
perform # Punt help output to the generator.
|
||||
end
|
||||
|
||||
def perform(*args)
|
||||
Rails::Generators::AppGenerator.start \
|
||||
Rails::Generators::ARGVScrubber.new(args).prepare!
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
89
railties/lib/rails/commands/console/console_command.rb
Normal file
89
railties/lib/rails/commands/console/console_command.rb
Normal file
|
@ -0,0 +1,89 @@
|
|||
require "irb"
|
||||
require "irb/completion"
|
||||
|
||||
require "rails/command/environment_argument"
|
||||
|
||||
module Rails
|
||||
class Console
|
||||
module BacktraceCleaner
|
||||
def filter_backtrace(bt)
|
||||
if result = super
|
||||
Rails.backtrace_cleaner.filter([result]).first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.start(*args)
|
||||
new(*args).start
|
||||
end
|
||||
|
||||
attr_reader :options, :app, :console
|
||||
|
||||
def initialize(app, options = {})
|
||||
@app = app
|
||||
@options = options
|
||||
|
||||
app.sandbox = sandbox?
|
||||
app.load_console
|
||||
|
||||
@console = app.config.console || IRB
|
||||
|
||||
if @console == IRB
|
||||
IRB::WorkSpace.prepend(BacktraceCleaner)
|
||||
end
|
||||
end
|
||||
|
||||
def sandbox?
|
||||
options[:sandbox]
|
||||
end
|
||||
|
||||
def environment
|
||||
options[:environment]
|
||||
end
|
||||
alias_method :environment?, :environment
|
||||
|
||||
def set_environment!
|
||||
Rails.env = environment
|
||||
end
|
||||
|
||||
def start
|
||||
set_environment! if environment?
|
||||
|
||||
if sandbox?
|
||||
puts "Loading #{Rails.env} environment in sandbox (Rails #{Rails.version})"
|
||||
puts "Any modifications you make will be rolled back on exit"
|
||||
else
|
||||
puts "Loading #{Rails.env} environment (Rails #{Rails.version})"
|
||||
end
|
||||
|
||||
if defined?(console::ExtendCommandBundle)
|
||||
console::ExtendCommandBundle.include(Rails::ConsoleMethods)
|
||||
end
|
||||
console.start
|
||||
end
|
||||
end
|
||||
|
||||
module Command
|
||||
class ConsoleCommand < Base
|
||||
include EnvironmentArgument
|
||||
|
||||
class_option :sandbox, aliases: "-s", type: :boolean, default: false,
|
||||
desc: "Rollback database modifications on exit."
|
||||
|
||||
class_option :environment, aliases: "-e", type: :string, default: Rails::Command.environment,
|
||||
desc: "Specifies the environment to run this console under (test/development/production)."
|
||||
|
||||
def perform
|
||||
extract_environment_option_from_argument
|
||||
|
||||
# RAILS_ENV needs to be set before config/application is required.
|
||||
ENV["RAILS_ENV"] = options[:environment]
|
||||
|
||||
ARGV.clear # Clear ARGV so IRB doesn't freak.
|
||||
|
||||
require_application_and_environment!
|
||||
Rails::Console.start(Rails.application, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
160
railties/lib/rails/commands/dbconsole/dbconsole_command.rb
Normal file
160
railties/lib/rails/commands/dbconsole/dbconsole_command.rb
Normal file
|
@ -0,0 +1,160 @@
|
|||
require "erb"
|
||||
require "yaml"
|
||||
|
||||
require "rails/command/environment_argument"
|
||||
|
||||
module Rails
|
||||
class DBConsole
|
||||
attr_reader :arguments
|
||||
|
||||
def self.start(*args)
|
||||
new(*args).start
|
||||
end
|
||||
|
||||
def initialize(arguments = ARGV, options = {})
|
||||
@arguments, @options = arguments, options
|
||||
end
|
||||
|
||||
def start
|
||||
ENV["RAILS_ENV"] = @options[:environment] || environment
|
||||
|
||||
case config["adapter"]
|
||||
when /^(jdbc)?mysql/
|
||||
args = {
|
||||
"host" => "--host",
|
||||
"port" => "--port",
|
||||
"socket" => "--socket",
|
||||
"username" => "--user",
|
||||
"encoding" => "--default-character-set",
|
||||
"sslca" => "--ssl-ca",
|
||||
"sslcert" => "--ssl-cert",
|
||||
"sslcapath" => "--ssl-capath",
|
||||
"sslcipher" => "--ssl-cipher",
|
||||
"sslkey" => "--ssl-key"
|
||||
}.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
|
||||
|
||||
if config["password"] && @options["include_password"]
|
||||
args << "--password=#{config['password']}"
|
||||
elsif config["password"] && !config["password"].to_s.empty?
|
||||
args << "-p"
|
||||
end
|
||||
|
||||
args << config["database"]
|
||||
|
||||
find_cmd_and_exec(["mysql", "mysql5"], *args)
|
||||
|
||||
when /^postgres|^postgis/
|
||||
ENV["PGUSER"] = config["username"] if config["username"]
|
||||
ENV["PGHOST"] = config["host"] if config["host"]
|
||||
ENV["PGPORT"] = config["port"].to_s if config["port"]
|
||||
ENV["PGPASSWORD"] = config["password"].to_s if config["password"] && @options["include_password"]
|
||||
find_cmd_and_exec("psql", config["database"])
|
||||
|
||||
when "sqlite3"
|
||||
args = []
|
||||
|
||||
args << "-#{@options['mode']}" if @options["mode"]
|
||||
args << "-header" if @options["header"]
|
||||
args << File.expand_path(config["database"], Rails.respond_to?(:root) ? Rails.root : nil)
|
||||
|
||||
find_cmd_and_exec("sqlite3", *args)
|
||||
|
||||
when "oracle", "oracle_enhanced"
|
||||
logon = ""
|
||||
|
||||
if config["username"]
|
||||
logon = config["username"]
|
||||
logon << "/#{config['password']}" if config["password"] && @options["include_password"]
|
||||
logon << "@#{config['database']}" if config["database"]
|
||||
end
|
||||
|
||||
find_cmd_and_exec("sqlplus", logon)
|
||||
|
||||
when "sqlserver"
|
||||
args = []
|
||||
|
||||
args += ["-D", "#{config['database']}"] if config["database"]
|
||||
args += ["-U", "#{config['username']}"] if config["username"]
|
||||
args += ["-P", "#{config['password']}"] if config["password"]
|
||||
|
||||
if config["host"]
|
||||
host_arg = "#{config['host']}"
|
||||
host_arg << ":#{config['port']}" if config["port"]
|
||||
args += ["-S", host_arg]
|
||||
end
|
||||
|
||||
find_cmd_and_exec("sqsh", *args)
|
||||
|
||||
else
|
||||
abort "Unknown command-line client for #{config['database']}."
|
||||
end
|
||||
end
|
||||
|
||||
def config
|
||||
@config ||= begin
|
||||
if configurations[environment].blank?
|
||||
raise ActiveRecord::AdapterNotSpecified, "'#{environment}' database is not configured. Available configuration: #{configurations.inspect}"
|
||||
else
|
||||
configurations[environment]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def environment
|
||||
Rails.respond_to?(:env) ? Rails.env : Rails::Command.environment
|
||||
end
|
||||
|
||||
protected
|
||||
def configurations
|
||||
require APP_PATH
|
||||
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
|
||||
ActiveRecord::Base.configurations
|
||||
end
|
||||
|
||||
def find_cmd_and_exec(commands, *args)
|
||||
commands = Array(commands)
|
||||
|
||||
dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
|
||||
unless (ext = RbConfig::CONFIG["EXEEXT"]).empty?
|
||||
commands = commands.map { |cmd| "#{cmd}#{ext}" }
|
||||
end
|
||||
|
||||
full_path_command = nil
|
||||
found = commands.detect do |cmd|
|
||||
dirs_on_path.detect do |path|
|
||||
full_path_command = File.join(path, cmd)
|
||||
File.file?(full_path_command) && File.executable?(full_path_command)
|
||||
end
|
||||
end
|
||||
|
||||
if found
|
||||
exec full_path_command, *args
|
||||
else
|
||||
abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Command
|
||||
class DbconsoleCommand < Base
|
||||
include EnvironmentArgument
|
||||
|
||||
class_option :include_password, aliases: "-p", type: :boolean,
|
||||
desc: "Automatically provide the password from database.yml"
|
||||
|
||||
class_option :mode, enum: %w( html list line column ), type: :string,
|
||||
desc: "Automatically put the sqlite3 database in the specified mode (html, list, line, column)."
|
||||
|
||||
class_option :header, type: :string
|
||||
|
||||
class_option :environment, aliases: "-e", type: :string, default: Rails::Command.environment,
|
||||
desc: "Specifies the environment to run this console under (test/development/production)."
|
||||
|
||||
def perform
|
||||
extract_environment_option_from_argument
|
||||
|
||||
Rails::DBConsole.start(args, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
21
railties/lib/rails/commands/destroy/destroy_command.rb
Normal file
21
railties/lib/rails/commands/destroy/destroy_command.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require "rails/generators"
|
||||
|
||||
module Rails
|
||||
module Command
|
||||
class DestroyCommand < Base
|
||||
def help # :nodoc:
|
||||
Rails::Generators.help self.class.command_name
|
||||
end
|
||||
|
||||
def perform(*)
|
||||
generator = args.shift
|
||||
return help unless generator
|
||||
|
||||
require_application_and_environment!
|
||||
Rails.application.load_generators
|
||||
|
||||
Rails::Generators.invoke generator, args, behavior: :revoke, destination_root: Rails.root
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
21
railties/lib/rails/commands/generate/generate_command.rb
Normal file
21
railties/lib/rails/commands/generate/generate_command.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require "rails/generators"
|
||||
|
||||
module Rails
|
||||
module Command
|
||||
class GenerateCommand < Base
|
||||
def help # :nodoc:
|
||||
Rails::Generators.help self.class.command_name
|
||||
end
|
||||
|
||||
def perform(*)
|
||||
generator = args.shift
|
||||
return help unless generator
|
||||
|
||||
require_application_and_environment!
|
||||
load_generators
|
||||
|
||||
Rails::Generators.invoke generator, args, behavior: :invoke, destination_root: Rails::Command.root
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
16
railties/lib/rails/commands/help/USAGE
Normal file
16
railties/lib/rails/commands/help/USAGE
Normal file
|
@ -0,0 +1,16 @@
|
|||
Usage: bin/rails COMMAND [args] [options]
|
||||
|
||||
The most common rails commands are:
|
||||
generate Generate new code (short-cut alias: "g")
|
||||
console Start the Rails console (short-cut alias: "c")
|
||||
server Start the Rails server (short-cut alias: "s")
|
||||
test Run tests (short-cut alias: "t")
|
||||
dbconsole Start a console for the database specified in config/database.yml
|
||||
(short-cut alias: "db")
|
||||
new Create a new Rails application. "rails new my_app" creates a
|
||||
new application called MyApp in "./my_app"
|
||||
|
||||
All commands can be run with -h (or --help) for more information.
|
||||
|
||||
In addition to those commands, there are:
|
||||
|
13
railties/lib/rails/commands/help/help_command.rb
Normal file
13
railties/lib/rails/commands/help/help_command.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
module Rails
|
||||
module Command
|
||||
class HelpCommand < Base
|
||||
hide_command!
|
||||
|
||||
def help(*)
|
||||
puts self.class.desc
|
||||
|
||||
Rails::Command.print_commands
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
15
railties/lib/rails/commands/new/new_command.rb
Normal file
15
railties/lib/rails/commands/new/new_command.rb
Normal file
|
@ -0,0 +1,15 @@
|
|||
module Rails
|
||||
module Command
|
||||
class NewCommand < Base
|
||||
def help
|
||||
Rails::Command.invoke :application, [ "--help" ]
|
||||
end
|
||||
|
||||
def perform(*)
|
||||
puts "Can't initialize a new Rails application within the directory of another, please change to a non-Rails directory first.\n"
|
||||
puts "Type 'rails' for help."
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
43
railties/lib/rails/commands/plugin/plugin_command.rb
Normal file
43
railties/lib/rails/commands/plugin/plugin_command.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
module Rails
|
||||
module Command
|
||||
class PluginCommand < Base
|
||||
hide_command!
|
||||
|
||||
def help
|
||||
run_plugin_generator %w( --help )
|
||||
end
|
||||
|
||||
def self.banner(*) # :nodoc:
|
||||
"#{executable} new [options]"
|
||||
end
|
||||
|
||||
class_option :rc, type: :boolean, default: File.join("~", ".railsrc"),
|
||||
desc: "Initialize the plugin command with previous defaults. Uses .railsrc in your home directory by default."
|
||||
|
||||
class_option :no_rc, desc: "Skip evaluating .railsrc."
|
||||
|
||||
def perform(type = nil, *plugin_args)
|
||||
plugin_args << "--help" unless type == "new"
|
||||
|
||||
unless options.key?("no_rc") # Thor's not so indifferent access hash.
|
||||
railsrc = File.expand_path(options[:rc])
|
||||
|
||||
if File.exist?(railsrc)
|
||||
extra_args = File.read(railsrc).split(/\n+/).flat_map(&:split)
|
||||
puts "Using #{extra_args.join(" ")} from #{railsrc}"
|
||||
plugin_args.insert(1, *extra_args)
|
||||
end
|
||||
end
|
||||
|
||||
run_plugin_generator plugin_args
|
||||
end
|
||||
|
||||
private
|
||||
def run_plugin_generator(plugin_args)
|
||||
require "rails/generators"
|
||||
require "rails/generators/rails/plugin/plugin_generator"
|
||||
Rails::Generators::PluginGenerator.start plugin_args
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
49
railties/lib/rails/commands/rake/rake_command.rb
Normal file
49
railties/lib/rails/commands/rake/rake_command.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
module Rails
|
||||
module Command
|
||||
class RakeCommand < Base
|
||||
namespace "rake"
|
||||
|
||||
class << self
|
||||
def printing_commands
|
||||
formatted_rake_tasks.map(&:first)
|
||||
end
|
||||
|
||||
def perform(task, *)
|
||||
require_rake
|
||||
|
||||
ARGV.unshift(task) # Prepend the task, so Rake knows how to run it.
|
||||
|
||||
Rake.application.standard_exception_handling do
|
||||
Rake.application.init("rails")
|
||||
Rake.application.load_rakefile
|
||||
Rake.application.top_level
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def rake_tasks
|
||||
require_rake
|
||||
|
||||
return @rake_tasks if defined?(@rake_tasks)
|
||||
|
||||
ActiveSupport::Deprecation.silence do
|
||||
Rails::Command.require_application_and_environment!
|
||||
end
|
||||
|
||||
Rake::TaskManager.record_task_metadata = true
|
||||
Rake.application.instance_variable_set(:@name, "rails")
|
||||
Rails.application.load_tasks
|
||||
@rake_tasks = Rake.application.tasks.select(&:comment)
|
||||
end
|
||||
|
||||
def formatted_rake_tasks
|
||||
rake_tasks.map { |t| [ t.name_with_args, t.comment ] }
|
||||
end
|
||||
|
||||
def require_rake
|
||||
require "rake" # Defer booting Rake until we know it's needed.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
17
railties/lib/rails/commands/runner/USAGE
Normal file
17
railties/lib/rails/commands/runner/USAGE
Normal file
|
@ -0,0 +1,17 @@
|
|||
Examples:
|
||||
|
||||
Run `puts Rails.env` after loading the app:
|
||||
|
||||
<%= executable %> 'puts Rails.env'
|
||||
|
||||
Run the Ruby file located at `path/to/filename.rb` after loading the app:
|
||||
|
||||
<%= executable %> path/to/filename.rb
|
||||
|
||||
<% if RbConfig::CONFIG['host_os'] !~ /mswin|mingw/ %>
|
||||
You can also use the runner command as a shebang line for your executables:
|
||||
|
||||
#!/usr/bin/env <%= File.expand_path(executable) %>
|
||||
|
||||
Product.all.each { |p| p.price *= 2 ; p.save! }
|
||||
<% end %>
|
45
railties/lib/rails/commands/runner/runner_command.rb
Normal file
45
railties/lib/rails/commands/runner/runner_command.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
module Rails
|
||||
module Command
|
||||
class RunnerCommand < Base
|
||||
class_option :environment, aliases: "-e", type: :string,
|
||||
default: Rails::Command.environment.dup,
|
||||
desc: "The environment for the runner to operate under (test/development/production)"
|
||||
|
||||
def help
|
||||
super
|
||||
puts self.class.desc
|
||||
end
|
||||
|
||||
def self.banner(*)
|
||||
"#{super} [<'Some.ruby(code)'> | <filename.rb>]"
|
||||
end
|
||||
|
||||
def perform(code_or_file = nil)
|
||||
unless code_or_file
|
||||
help
|
||||
exit 1
|
||||
end
|
||||
|
||||
ENV["RAILS_ENV"] = options[:environment]
|
||||
|
||||
require_application_and_environment!
|
||||
Rails.application.load_runner
|
||||
|
||||
if File.exist?(code_or_file)
|
||||
$0 = code_or_file
|
||||
Kernel.load code_or_file
|
||||
else
|
||||
begin
|
||||
eval(code_or_file, binding, __FILE__, __LINE__)
|
||||
rescue SyntaxError, NameError => error
|
||||
$stderr.puts "Please specify a valid ruby command or the path of a script to run."
|
||||
$stderr.puts "Run '#{self.class.executable} -h' for help."
|
||||
$stderr.puts
|
||||
$stderr.puts error
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
159
railties/lib/rails/commands/server/server_command.rb
Normal file
159
railties/lib/rails/commands/server/server_command.rb
Normal file
|
@ -0,0 +1,159 @@
|
|||
require "fileutils"
|
||||
require "optparse"
|
||||
require "action_dispatch"
|
||||
require "rails"
|
||||
require "rails/dev_caching"
|
||||
|
||||
module Rails
|
||||
class Server < ::Rack::Server
|
||||
class Options
|
||||
DEFAULT_PID_PATH = File.expand_path("tmp/pids/server.pid").freeze
|
||||
|
||||
def parse!(args)
|
||||
args, options = args.dup, {}
|
||||
|
||||
option_parser(options).parse! args
|
||||
|
||||
options[:log_stdout] = options[:daemonize].blank? && (options[:environment] || Rails.env) == "development"
|
||||
options[:server] = args.shift
|
||||
options
|
||||
end
|
||||
|
||||
def option_parser(options) # :nodoc:
|
||||
OptionParser.new do |opts|
|
||||
opts.banner = "Usage: rails server [mongrel, thin etc] [options]"
|
||||
|
||||
opts.separator ""
|
||||
opts.separator "Options:"
|
||||
|
||||
opts.on("-p", "--port=port", Integer,
|
||||
"Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v }
|
||||
opts.on("-b", "--binding=IP", String,
|
||||
"Binds Rails to the specified IP.", "Default: localhost") { |v| options[:Host] = v }
|
||||
opts.on("-c", "--config=file", String,
|
||||
"Uses a custom rackup configuration.") { |v| options[:config] = v }
|
||||
opts.on("-d", "--daemon", "Runs server as a Daemon.") { options[:daemonize] = true }
|
||||
opts.on("-e", "--environment=name", String,
|
||||
"Specifies the environment to run this server under (test/development/production).",
|
||||
"Default: development") { |v| options[:environment] = v }
|
||||
opts.on("-P", "--pid=pid", String,
|
||||
"Specifies the PID file.",
|
||||
"Default: tmp/pids/server.pid") { |v| options[:pid] = v }
|
||||
opts.on("-C", "--[no-]dev-caching",
|
||||
"Specifies whether to perform caching in development.",
|
||||
"true or false") { |v| options[:caching] = v }
|
||||
|
||||
opts.separator ""
|
||||
|
||||
opts.on("-h", "--help", "Shows this help message.") { puts opts; exit }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(*)
|
||||
super
|
||||
set_environment
|
||||
end
|
||||
|
||||
# TODO: this is no longer required but we keep it for the moment to support older config.ru files.
|
||||
def app
|
||||
@app ||= begin
|
||||
app = super
|
||||
app.respond_to?(:to_app) ? app.to_app : app
|
||||
end
|
||||
end
|
||||
|
||||
def opt_parser
|
||||
Options.new
|
||||
end
|
||||
|
||||
def set_environment
|
||||
ENV["RAILS_ENV"] ||= options[:environment]
|
||||
end
|
||||
|
||||
def start
|
||||
print_boot_information
|
||||
trap(:INT) { exit }
|
||||
create_tmp_directories
|
||||
setup_dev_caching
|
||||
log_to_stdout if options[:log_stdout]
|
||||
|
||||
super
|
||||
ensure
|
||||
# The '-h' option calls exit before @options is set.
|
||||
# If we call 'options' with it unset, we get double help banners.
|
||||
puts "Exiting" unless @options && options[:daemonize]
|
||||
end
|
||||
|
||||
def middleware
|
||||
Hash.new([])
|
||||
end
|
||||
|
||||
def default_options
|
||||
super.merge( Port: ENV.fetch("PORT", 3000).to_i,
|
||||
DoNotReverseLookup: true,
|
||||
environment: (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development").dup,
|
||||
daemonize: false,
|
||||
caching: nil,
|
||||
pid: Options::DEFAULT_PID_PATH,
|
||||
restart_cmd: restart_command)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def setup_dev_caching
|
||||
if options[:environment] == "development"
|
||||
Rails::DevCaching.enable_by_argument(options[:caching])
|
||||
end
|
||||
end
|
||||
|
||||
def print_boot_information
|
||||
url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}"
|
||||
puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
|
||||
puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}"
|
||||
puts "=> Run `rails server -h` for more startup options"
|
||||
end
|
||||
|
||||
def create_tmp_directories
|
||||
%w(cache pids sockets).each do |dir_to_make|
|
||||
FileUtils.mkdir_p(File.join(Rails.root, "tmp", dir_to_make))
|
||||
end
|
||||
end
|
||||
|
||||
def log_to_stdout
|
||||
wrapped_app # touch the app so the logger is set up
|
||||
|
||||
console = ActiveSupport::Logger.new(STDOUT)
|
||||
console.formatter = Rails.logger.formatter
|
||||
console.level = Rails.logger.level
|
||||
|
||||
unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDOUT)
|
||||
Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
|
||||
end
|
||||
end
|
||||
|
||||
def restart_command
|
||||
"bin/rails server #{ARGV.join(' ')}"
|
||||
end
|
||||
end
|
||||
|
||||
module Command
|
||||
class ServerCommand < Base
|
||||
def help # :nodoc:
|
||||
puts Rails::Server::Options.new.option_parser(Hash.new)
|
||||
end
|
||||
|
||||
def perform
|
||||
set_application_directory!
|
||||
|
||||
Rails::Server.new.tap do |server|
|
||||
# Require application after server sets environment to propagate
|
||||
# the --environment option.
|
||||
require APP_PATH
|
||||
Dir.chdir(Rails.application.root)
|
||||
server.start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
18
railties/lib/rails/commands/test/test_command.rb
Normal file
18
railties/lib/rails/commands/test/test_command.rb
Normal file
|
@ -0,0 +1,18 @@
|
|||
require "rails/command"
|
||||
require "rails/test_unit/minitest_plugin"
|
||||
|
||||
module Rails
|
||||
module Command
|
||||
class TestCommand < Base
|
||||
def help # :nodoc:
|
||||
perform # Hand over help printing to minitest.
|
||||
end
|
||||
|
||||
def perform(*)
|
||||
$LOAD_PATH << Rails::Command.root.join("test")
|
||||
|
||||
exit Minitest.run(ARGV)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
9
railties/lib/rails/commands/version/version_command.rb
Normal file
9
railties/lib/rails/commands/version/version_command.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
module Rails
|
||||
module Command
|
||||
class VersionCommand < Base
|
||||
def perform
|
||||
Rails::Command.invoke :application, [ "--version" ]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,6 +2,7 @@ activesupport_path = File.expand_path("../../../../activesupport/lib", __FILE__)
|
|||
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
|
||||
|
||||
require "thor/group"
|
||||
require "rails/command"
|
||||
|
||||
require "active_support"
|
||||
require "active_support/core_ext/object/blank"
|
||||
|
@ -13,6 +14,8 @@ require "active_support/core_ext/string/inflections"
|
|||
|
||||
module Rails
|
||||
module Generators
|
||||
include Rails::Command::Behavior
|
||||
|
||||
autoload :Actions, "rails/generators/actions"
|
||||
autoload :ActiveModel, "rails/generators/active_model"
|
||||
autoload :Base, "rails/generators/base"
|
||||
|
@ -127,67 +130,6 @@ module Rails
|
|||
Thor::Base.shell = Thor::Shell::Basic
|
||||
end
|
||||
|
||||
# Track all generators subclasses.
|
||||
def self.subclasses
|
||||
@subclasses ||= []
|
||||
end
|
||||
|
||||
# Rails finds namespaces similar to thor, it only adds one rule:
|
||||
#
|
||||
# Generators names must end with "_generator.rb". This is required because Rails
|
||||
# looks in load paths and loads the generator just before it's going to be used.
|
||||
#
|
||||
# find_by_namespace :webrat, :rails, :integration
|
||||
#
|
||||
# Will search for the following generators:
|
||||
#
|
||||
# "rails:webrat", "webrat:integration", "webrat"
|
||||
#
|
||||
# Notice that "rails:generators:webrat" could be loaded as well, what
|
||||
# Rails looks for is the first and last parts of the namespace.
|
||||
def self.find_by_namespace(name, base=nil, context=nil) #:nodoc:
|
||||
lookups = []
|
||||
lookups << "#{base}:#{name}" if base
|
||||
lookups << "#{name}:#{context}" if context
|
||||
|
||||
unless base || context
|
||||
unless name.to_s.include?(?:)
|
||||
lookups << "#{name}:#{name}"
|
||||
lookups << "rails:#{name}"
|
||||
end
|
||||
lookups << "#{name}"
|
||||
end
|
||||
|
||||
lookup(lookups)
|
||||
|
||||
namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }]
|
||||
|
||||
lookups.each do |namespace|
|
||||
klass = namespaces[namespace]
|
||||
return klass if klass
|
||||
end
|
||||
|
||||
invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
|
||||
end
|
||||
|
||||
# Receives a namespace, arguments and the behavior to invoke the generator.
|
||||
# It's used as the default entry point for generate, destroy and update
|
||||
# commands.
|
||||
def self.invoke(namespace, args=ARGV, config={})
|
||||
names = namespace.to_s.split(":")
|
||||
if klass = find_by_namespace(names.pop, names.any? && names.join(":"))
|
||||
args << "--help" if args.empty? && klass.arguments.any?(&:required?)
|
||||
klass.start(args, config)
|
||||
else
|
||||
options = sorted_groups.flat_map(&:last)
|
||||
suggestions = options.sort_by { |suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3)
|
||||
msg = "Could not find generator '#{namespace}'. "
|
||||
msg << "Maybe you meant #{ suggestions.map { |s| "'#{s}'" }.to_sentence(last_word_connector: " or ", locale: :en) }\n"
|
||||
msg << "Run `rails generate --help` for more options."
|
||||
puts msg
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an array of generator namespaces that are hidden.
|
||||
# Generator namespaces may be hidden for a variety of reasons.
|
||||
# Some are aliased such as "rails:migration" and can be
|
||||
|
@ -260,11 +202,13 @@ module Rails
|
|||
def self.sorted_groups
|
||||
namespaces = public_namespaces
|
||||
namespaces.sort!
|
||||
|
||||
groups = Hash.new { |h,k| h[k] = [] }
|
||||
namespaces.each do |namespace|
|
||||
base = namespace.split(":").first
|
||||
groups[base] << namespace
|
||||
end
|
||||
|
||||
rails = groups.delete("rails")
|
||||
rails.map! { |n| n.sub(/^rails:/, "") }
|
||||
rails.delete("app")
|
||||
|
@ -272,64 +216,69 @@ module Rails
|
|||
|
||||
hidden_namespaces.each { |n| groups.delete(n.to_s) }
|
||||
|
||||
[["rails", rails]] + groups.sort.to_a
|
||||
[[ "rails", rails ]] + groups.sort.to_a
|
||||
end
|
||||
|
||||
# Rails finds namespaces similar to thor, it only adds one rule:
|
||||
#
|
||||
# Generators names must end with "_generator.rb". This is required because Rails
|
||||
# looks in load paths and loads the generator just before it's going to be used.
|
||||
#
|
||||
# find_by_namespace :webrat, :rails, :integration
|
||||
#
|
||||
# Will search for the following generators:
|
||||
#
|
||||
# "rails:webrat", "webrat:integration", "webrat"
|
||||
#
|
||||
# Notice that "rails:generators:webrat" could be loaded as well, what
|
||||
# Rails looks for is the first and last parts of the namespace.
|
||||
def self.find_by_namespace(name, base = nil, context = nil) #:nodoc:
|
||||
lookups = []
|
||||
lookups << "#{base}:#{name}" if base
|
||||
lookups << "#{name}:#{context}" if context
|
||||
|
||||
unless base || context
|
||||
unless name.to_s.include?(?:)
|
||||
lookups << "#{name}:#{name}"
|
||||
lookups << "rails:#{name}"
|
||||
end
|
||||
lookups << "#{name}"
|
||||
end
|
||||
|
||||
lookup(lookups)
|
||||
|
||||
namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }]
|
||||
lookups.each do |namespace|
|
||||
|
||||
klass = namespaces[namespace]
|
||||
return klass if klass
|
||||
end
|
||||
|
||||
invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
|
||||
end
|
||||
|
||||
# Receives a namespace, arguments and the behavior to invoke the generator.
|
||||
# It's used as the default entry point for generate, destroy and update
|
||||
# commands.
|
||||
def self.invoke(namespace, args=ARGV, config={})
|
||||
names = namespace.to_s.split(":")
|
||||
if klass = find_by_namespace(names.pop, names.any? && names.join(":"))
|
||||
args << "--help" if args.empty? && klass.arguments.any?(&:required?)
|
||||
klass.start(args, config)
|
||||
else
|
||||
options = sorted_groups.flat_map(&:last)
|
||||
suggestions = options.sort_by { |suggested| levenshtein_distance(namespace.to_s, suggested) }.first(3)
|
||||
msg = "Could not find generator '#{namespace}'. "
|
||||
msg << "Maybe you meant #{ suggestions.map { |s| "'#{s}'" }.to_sentence(last_word_connector: " or ", locale: :en) }\n"
|
||||
msg << "Run `rails generate --help` for more options."
|
||||
puts msg
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This code is based directly on the Text gem implementation.
|
||||
# Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher.
|
||||
#
|
||||
# Returns a value representing the "cost" of transforming str1 into str2
|
||||
def self.levenshtein_distance(str1, str2)
|
||||
s = str1
|
||||
t = str2
|
||||
n = s.length
|
||||
m = t.length
|
||||
|
||||
return m if (0 == n)
|
||||
return n if (0 == m)
|
||||
|
||||
d = (0..m).to_a
|
||||
x = nil
|
||||
|
||||
# avoid duplicating an enumerable object in the loop
|
||||
str2_codepoint_enumerable = str2.each_codepoint
|
||||
|
||||
str1.each_codepoint.with_index do |char1, i|
|
||||
e = i+1
|
||||
|
||||
str2_codepoint_enumerable.with_index do |char2, j|
|
||||
cost = (char1 == char2) ? 0 : 1
|
||||
x = [
|
||||
d[j+1] + 1, # insertion
|
||||
e + 1, # deletion
|
||||
d[j] + cost # substitution
|
||||
].min
|
||||
d[j] = e
|
||||
e = x
|
||||
end
|
||||
|
||||
d[m] = x
|
||||
end
|
||||
|
||||
x
|
||||
end
|
||||
|
||||
# Prints a list of generators.
|
||||
def self.print_list(base, namespaces) #:nodoc:
|
||||
namespaces = namespaces.reject do |n|
|
||||
hidden_namespaces.include?(n)
|
||||
end
|
||||
|
||||
return if namespaces.empty?
|
||||
puts "#{base.camelize}:"
|
||||
|
||||
namespaces.each do |namespace|
|
||||
puts(" #{namespace}")
|
||||
end
|
||||
|
||||
puts
|
||||
def self.print_list(base, namespaces)
|
||||
namespaces = namespaces.reject { |n| hidden_namespaces.include?(n) }
|
||||
super
|
||||
end
|
||||
|
||||
# Try fallbacks for the given base.
|
||||
|
@ -348,53 +297,16 @@ module Rails
|
|||
nil
|
||||
end
|
||||
|
||||
# Receives namespaces in an array and tries to find matching generators
|
||||
# in the load path.
|
||||
def self.lookup(namespaces) #:nodoc:
|
||||
paths = namespaces_to_paths(namespaces)
|
||||
|
||||
paths.each do |raw_path|
|
||||
["rails/generators", "generators"].each do |base|
|
||||
path = "#{base}/#{raw_path}_generator"
|
||||
|
||||
begin
|
||||
require path
|
||||
return
|
||||
rescue LoadError => e
|
||||
raise unless e.message =~ /#{Regexp.escape(path)}$/
|
||||
rescue Exception => e
|
||||
warn "[WARNING] Could not load generator #{path.inspect}. Error: #{e.message}.\n#{e.backtrace.join("\n")}"
|
||||
end
|
||||
end
|
||||
end
|
||||
def self.command_type
|
||||
@command_type ||= "generator"
|
||||
end
|
||||
|
||||
# This will try to load any generator in the load path to show in help.
|
||||
def self.lookup! #:nodoc:
|
||||
$LOAD_PATH.each do |base|
|
||||
Dir[File.join(base, "{rails/generators,generators}", "**", "*_generator.rb")].each do |path|
|
||||
begin
|
||||
path = path.sub("#{base}/", "")
|
||||
require path
|
||||
rescue Exception
|
||||
# No problem
|
||||
end
|
||||
end
|
||||
end
|
||||
def self.lookup_paths
|
||||
@lookup_paths ||= %w( rails/generators generators )
|
||||
end
|
||||
|
||||
# Convert namespaces to paths by replacing ":" for "/" and adding
|
||||
# an extra lookup. For example, "rails:model" should be searched
|
||||
# in both: "rails/model/model_generator" and "rails/model_generator".
|
||||
def self.namespaces_to_paths(namespaces) #:nodoc:
|
||||
paths = []
|
||||
namespaces.each do |namespace|
|
||||
pieces = namespace.split(":")
|
||||
paths << pieces.dup.push(pieces.last).join("/")
|
||||
paths << pieces.join("/")
|
||||
end
|
||||
paths.uniq!
|
||||
paths
|
||||
def self.file_lookup_paths
|
||||
@file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_generator.rb" ]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require "active_support/core_ext/module/attribute_accessors"
|
||||
require "active_support/core_ext/hash/keys"
|
||||
require "rails/test_unit/reporter"
|
||||
require "rails/test_unit/test_requirer"
|
||||
require "shellwords"
|
||||
|
|
|
@ -82,7 +82,7 @@ module ApplicationTests
|
|||
def test_runner_detects_bad_script_name
|
||||
output = Dir.chdir(app_path) { `bin/rails runner "iuiqwiourowe" 2>&1` }
|
||||
assert_not $?.success?
|
||||
assert_match "undefined local variable or method `iuiqwiourowe' for main:Object", output
|
||||
assert_match "undefined local variable or method `iuiqwiourowe' for", output
|
||||
end
|
||||
|
||||
def test_environment_with_rails_env
|
||||
|
|
Loading…
Reference in a new issue