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"
|
require "rails/ruby_version_check"
|
||||||
Signal.trap("INT") { puts; exit(1) }
|
Signal.trap("INT") { puts; exit(1) }
|
||||||
|
|
||||||
|
require "rails/command"
|
||||||
|
|
||||||
if ARGV.first == "plugin"
|
if ARGV.first == "plugin"
|
||||||
ARGV.shift
|
ARGV.shift
|
||||||
require "rails/commands/plugin"
|
Rails::Command.invoke :plugin, ARGV
|
||||||
else
|
else
|
||||||
require "rails/commands/application"
|
Rails::Command.invoke :application, ARGV
|
||||||
end
|
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 = {
|
aliases = {
|
||||||
"g" => "generate",
|
"g" => "generate",
|
||||||
|
@ -13,6 +13,4 @@ aliases = {
|
||||||
command = ARGV.shift
|
command = ARGV.shift
|
||||||
command = aliases[command] || command
|
command = aliases[command] || command
|
||||||
|
|
||||||
require "rails/commands/commands_tasks"
|
Rails::Command.invoke command, ARGV
|
||||||
|
|
||||||
Rails::CommandsTasks.new(ARGV).run_command!(command)
|
|
||||||
|
|
|
@ -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)
|
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
|
||||||
|
|
||||||
require "thor/group"
|
require "thor/group"
|
||||||
|
require "rails/command"
|
||||||
|
|
||||||
require "active_support"
|
require "active_support"
|
||||||
require "active_support/core_ext/object/blank"
|
require "active_support/core_ext/object/blank"
|
||||||
|
@ -13,6 +14,8 @@ require "active_support/core_ext/string/inflections"
|
||||||
|
|
||||||
module Rails
|
module Rails
|
||||||
module Generators
|
module Generators
|
||||||
|
include Rails::Command::Behavior
|
||||||
|
|
||||||
autoload :Actions, "rails/generators/actions"
|
autoload :Actions, "rails/generators/actions"
|
||||||
autoload :ActiveModel, "rails/generators/active_model"
|
autoload :ActiveModel, "rails/generators/active_model"
|
||||||
autoload :Base, "rails/generators/base"
|
autoload :Base, "rails/generators/base"
|
||||||
|
@ -127,67 +130,6 @@ module Rails
|
||||||
Thor::Base.shell = Thor::Shell::Basic
|
Thor::Base.shell = Thor::Shell::Basic
|
||||||
end
|
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.
|
# Returns an array of generator namespaces that are hidden.
|
||||||
# Generator namespaces may be hidden for a variety of reasons.
|
# Generator namespaces may be hidden for a variety of reasons.
|
||||||
# Some are aliased such as "rails:migration" and can be
|
# Some are aliased such as "rails:migration" and can be
|
||||||
|
@ -260,11 +202,13 @@ module Rails
|
||||||
def self.sorted_groups
|
def self.sorted_groups
|
||||||
namespaces = public_namespaces
|
namespaces = public_namespaces
|
||||||
namespaces.sort!
|
namespaces.sort!
|
||||||
|
|
||||||
groups = Hash.new { |h,k| h[k] = [] }
|
groups = Hash.new { |h,k| h[k] = [] }
|
||||||
namespaces.each do |namespace|
|
namespaces.each do |namespace|
|
||||||
base = namespace.split(":").first
|
base = namespace.split(":").first
|
||||||
groups[base] << namespace
|
groups[base] << namespace
|
||||||
end
|
end
|
||||||
|
|
||||||
rails = groups.delete("rails")
|
rails = groups.delete("rails")
|
||||||
rails.map! { |n| n.sub(/^rails:/, "") }
|
rails.map! { |n| n.sub(/^rails:/, "") }
|
||||||
rails.delete("app")
|
rails.delete("app")
|
||||||
|
@ -275,61 +219,66 @@ module Rails
|
||||||
[[ "rails", rails ]] + groups.sort.to_a
|
[[ "rails", rails ]] + groups.sort.to_a
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
# Rails finds namespaces similar to thor, it only adds one rule:
|
||||||
|
|
||||||
# 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
|
# Generators names must end with "_generator.rb". This is required because Rails
|
||||||
def self.levenshtein_distance(str1, str2)
|
# looks in load paths and loads the generator just before it's going to be used.
|
||||||
s = str1
|
#
|
||||||
t = str2
|
# find_by_namespace :webrat, :rails, :integration
|
||||||
n = s.length
|
#
|
||||||
m = t.length
|
# 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
|
||||||
|
|
||||||
return m if (0 == n)
|
unless base || context
|
||||||
return n if (0 == m)
|
unless name.to_s.include?(?:)
|
||||||
|
lookups << "#{name}:#{name}"
|
||||||
d = (0..m).to_a
|
lookups << "rails:#{name}"
|
||||||
x = nil
|
end
|
||||||
|
lookups << "#{name}"
|
||||||
# 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
|
end
|
||||||
|
|
||||||
d[m] = x
|
lookup(lookups)
|
||||||
|
|
||||||
|
namespaces = Hash[subclasses.map { |klass| [klass.namespace, klass] }]
|
||||||
|
lookups.each do |namespace|
|
||||||
|
|
||||||
|
klass = namespaces[namespace]
|
||||||
|
return klass if klass
|
||||||
end
|
end
|
||||||
|
|
||||||
x
|
invoke_fallbacks_for(name, base) || invoke_fallbacks_for(context, name)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Prints a list of generators.
|
# Receives a namespace, arguments and the behavior to invoke the generator.
|
||||||
def self.print_list(base, namespaces) #:nodoc:
|
# It's used as the default entry point for generate, destroy and update
|
||||||
namespaces = namespaces.reject do |n|
|
# commands.
|
||||||
hidden_namespaces.include?(n)
|
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
|
end
|
||||||
|
|
||||||
return if namespaces.empty?
|
protected
|
||||||
puts "#{base.camelize}:"
|
def self.print_list(base, namespaces)
|
||||||
|
namespaces = namespaces.reject { |n| hidden_namespaces.include?(n) }
|
||||||
namespaces.each do |namespace|
|
super
|
||||||
puts(" #{namespace}")
|
|
||||||
end
|
|
||||||
|
|
||||||
puts
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Try fallbacks for the given base.
|
# Try fallbacks for the given base.
|
||||||
|
@ -348,53 +297,16 @@ module Rails
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
# Receives namespaces in an array and tries to find matching generators
|
def self.command_type
|
||||||
# in the load path.
|
@command_type ||= "generator"
|
||||||
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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# This will try to load any generator in the load path to show in help.
|
def self.lookup_paths
|
||||||
def self.lookup! #:nodoc:
|
@lookup_paths ||= %w( rails/generators generators )
|
||||||
$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
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Convert namespaces to paths by replacing ":" for "/" and adding
|
def self.file_lookup_paths
|
||||||
# an extra lookup. For example, "rails:model" should be searched
|
@file_lookup_paths ||= [ "{#{lookup_paths.join(',')}}", "**", "*_generator.rb" ]
|
||||||
# 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
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
require "active_support/core_ext/module/attribute_accessors"
|
require "active_support/core_ext/module/attribute_accessors"
|
||||||
|
require "active_support/core_ext/hash/keys"
|
||||||
require "rails/test_unit/reporter"
|
require "rails/test_unit/reporter"
|
||||||
require "rails/test_unit/test_requirer"
|
require "rails/test_unit/test_requirer"
|
||||||
require "shellwords"
|
require "shellwords"
|
||||||
|
|
|
@ -82,7 +82,7 @@ module ApplicationTests
|
||||||
def test_runner_detects_bad_script_name
|
def test_runner_detects_bad_script_name
|
||||||
output = Dir.chdir(app_path) { `bin/rails runner "iuiqwiourowe" 2>&1` }
|
output = Dir.chdir(app_path) { `bin/rails runner "iuiqwiourowe" 2>&1` }
|
||||||
assert_not $?.success?
|
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
|
end
|
||||||
|
|
||||||
def test_environment_with_rails_env
|
def test_environment_with_rails_env
|
||||||
|
|
Loading…
Reference in a new issue