diff options
Diffstat (limited to 'railties/lib/rails/commands')
-rw-r--r-- | railties/lib/rails/commands/application.rb | 17 | ||||
-rw-r--r-- | railties/lib/rails/commands/commands_tasks.rb | 169 | ||||
-rw-r--r-- | railties/lib/rails/commands/console.rb | 113 | ||||
-rw-r--r-- | railties/lib/rails/commands/dbconsole.rb | 177 | ||||
-rw-r--r-- | railties/lib/rails/commands/destroy.rb | 9 | ||||
-rw-r--r-- | railties/lib/rails/commands/generate.rb | 11 | ||||
-rw-r--r-- | railties/lib/rails/commands/plugin.rb | 23 | ||||
-rw-r--r-- | railties/lib/rails/commands/runner.rb | 63 | ||||
-rw-r--r-- | railties/lib/rails/commands/server.rb | 152 | ||||
-rw-r--r-- | railties/lib/rails/commands/update.rb | 9 |
10 files changed, 743 insertions, 0 deletions
diff --git a/railties/lib/rails/commands/application.rb b/railties/lib/rails/commands/application.rb new file mode 100644 index 0000000000..c998e6b6a8 --- /dev/null +++ b/railties/lib/rails/commands/application.rb @@ -0,0 +1,17 @@ +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 +end + +args = Rails::Generators::ARGVScrubber.new(ARGV).prepare! +Rails::Generators::AppGenerator.start args diff --git a/railties/lib/rails/commands/commands_tasks.rb b/railties/lib/rails/commands/commands_tasks.rb new file mode 100644 index 0000000000..6cfbc70c51 --- /dev/null +++ b/railties/lib/rails/commands/commands_tasks.rb @@ -0,0 +1,169 @@ +module Rails + # This is a class which takes in a rails command and initiates the appropriate + # initiation sequence. + # + # Warning: This class mutates ARGV because some commands require manipulating + # it before they are run. + class CommandsTasks # :nodoc: + attr_reader :argv + + HELP_MESSAGE = <<-EOT +Usage: rails COMMAND [ARGS] + +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") + 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" + +In addition to those, there are: + destroy Undo code generated with "generate" (short-cut alias: "d") + plugin new Generates skeleton for developing a Rails plugin + runner Run a piece of code in the application environment (short-cut alias: "r") + +All commands can be run with -h (or --help) for more information. +EOT + + COMMAND_WHITELIST = %w(plugin generate destroy console server dbconsole runner new version help) + + def initialize(argv) + @argv = argv + end + + def run_command!(command) + command = parse_command(command) + if COMMAND_WHITELIST.include?(command) + send(command) + else + write_error_message(command) + end + end + + def plugin + require_command!("plugin") + end + + def generate + generate_or_destroy(:generate) + end + + def destroy + generate_or_destroy(:destroy) + end + + def console + require_command!("console") + options = Rails::Console.parse_arguments(argv) + + # RAILS_ENV needs to be set before config/application is required + ENV['RAILS_ENV'] = options[:environment] if options[:environment] + + # shift ARGV so IRB doesn't freak + shift_argv! + + require_application_and_environment! + Rails::Console.start(Rails.application, options) + end + + def server + set_application_directory! + require_command!("server") + + Rails::Server.new.tap do |server| + # We need to require application after the server sets environment, + # otherwise the --environment option given to the server won't propagate. + require APP_PATH + Dir.chdir(Rails.application.root) + server.start + end + end + + def dbconsole + require_command!("dbconsole") + Rails::DBConsole.start + end + + def runner + require_command!("runner") + end + + def new + if %w(-h --help).include?(argv.first) + require_command!("application") + else + exit_with_initialization_warning! + end + end + + def version + argv.unshift '--version' + require_command!("application") + end + + def help + write_help_message + end + + private + + def exit_with_initialization_warning! + 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 + + def shift_argv! + argv.shift if argv.first && argv.first[0] != '-' + end + + def require_command!(command) + require "rails/commands/#{command}" + end + + def generate_or_destroy(command) + require 'rails/generators' + require_application_and_environment! + Rails.application.load_generators + require "rails/commands/#{command}" + end + + # 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 + + def write_help_message + puts HELP_MESSAGE + end + + def write_error_message(command) + puts "Error: Command '#{command}' not recognized" + if %x{rake #{command} --dry-run 2>&1 } && $?.success? + puts "Did you mean: `$ rake #{command}` ?\n\n" + end + write_help_message + exit(1) + end + + def parse_command(command) + case command + when '--version', '-v' + 'version' + when '--help', '-h' + 'help' + else + command + end + end + end +end diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb new file mode 100644 index 0000000000..555d8f31e1 --- /dev/null +++ b/railties/lib/rails/commands/console.rb @@ -0,0 +1,113 @@ +require 'optparse' +require 'irb' +require 'irb/completion' + +module Rails + class Console + class << self + def start(*args) + new(*args).start + end + + def parse_arguments(arguments) + options = {} + + OptionParser.new do |opt| + opt.banner = "Usage: rails console [environment] [options]" + opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v } + opt.on("-e", "--environment=name", String, + "Specifies the environment to run this console under (test/development/production).", + "Default: development") { |v| options[:environment] = v.strip } + opt.on("--debugger", 'Enable the debugger.') do |v| + if RUBY_VERSION < '2.0.0' + options[:debugger] = v + else + puts "=> Notice: debugger option is ignored since ruby 2.0 and " \ + "it will be removed in future versions" + end + end + opt.parse!(arguments) + end + + if arguments.first && arguments.first[0] != '-' + env = arguments.first + if available_environments.include? env + options[:environment] = env + else + options[:environment] = %w(production development test).detect {|e| e =~ /^#{env}/} || env + end + end + + options + end + + private + + def available_environments + Dir['config/environments/*.rb'].map { |fname| File.basename(fname, '.*') } + end + 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 + end + + def sandbox? + options[:sandbox] + end + + def environment + options[:environment] ||= ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development' + end + + def environment? + environment + end + + def set_environment! + Rails.env = environment + end + + if RUBY_VERSION < '2.0.0' + def debugger? + options[:debugger] + end + + def require_debugger + require 'debugger' + puts "=> Debugger enabled" + rescue LoadError + puts "You're missing the 'debugger' gem. Add it to your Gemfile, bundle it and try again." + exit(1) + end + end + + def start + if RUBY_VERSION < '2.0.0' + require_debugger if debugger? + end + + 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.send :include, Rails::ConsoleMethods + end + console.start + end + end +end diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb new file mode 100644 index 0000000000..1a2613a8d0 --- /dev/null +++ b/railties/lib/rails/commands/dbconsole.rb @@ -0,0 +1,177 @@ +require 'erb' +require 'yaml' +require 'optparse' +require 'rbconfig' + +module Rails + class DBConsole + attr_reader :arguments + + def self.start + new.start + end + + def initialize(arguments = ARGV) + @arguments = arguments + end + + def start + options = parse_arguments(arguments) + 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' => '--ssh-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 "postgresql", "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 "sqlite" + find_cmd_and_exec('sqlite', 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) + + else + abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!" + 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 + if Rails.respond_to?(:env) + Rails.env + else + ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development" + end + end + + protected + + def configurations + require APP_PATH + ActiveRecord::Base.configurations = Rails.application.config.database_configuration + ActiveRecord::Base.configurations + end + + def parse_arguments(arguments) + options = {} + + OptionParser.new do |opt| + opt.banner = "Usage: rails dbconsole [environment] [options]" + opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v| + options['include_password'] = true + end + + opt.on("--mode [MODE]", ['html', 'list', 'line', 'column'], + "Automatically put the sqlite3 database in the specified mode (html, list, line, column).") do |mode| + options['mode'] = mode + end + + opt.on("--header") do |h| + options['header'] = h + end + + opt.on("-h", "--help", "Show this help message.") do + puts opt + exit + end + + opt.on("-e", "--environment=name", String, + "Specifies the environment to run this console under (test/development/production).", + "Default: development" + ) { |v| options[:environment] = v.strip } + + opt.parse!(arguments) + abort opt.to_s unless (0..1).include?(arguments.size) + end + + if arguments.first && arguments.first[0] != '-' + env = arguments.first + if available_environments.include? env + options[:environment] = env + else + options[:environment] = %w(production development test).detect {|e| e =~ /^#{env}/} || env + end + end + + options + end + + def available_environments + Dir['config/environments/*.rb'].map { |fname| File.basename(fname, '.*') } + end + + def find_cmd_and_exec(commands, *args) + commands = Array(commands) + + dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR) + commands += commands.map{|cmd| "#{cmd}.exe"} if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + + full_path_command = nil + found = commands.detect do |cmd| + dirs_on_path.detect do |path| + full_path_command = File.join(path, cmd) + 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 +end diff --git a/railties/lib/rails/commands/destroy.rb b/railties/lib/rails/commands/destroy.rb new file mode 100644 index 0000000000..5479da86a0 --- /dev/null +++ b/railties/lib/rails/commands/destroy.rb @@ -0,0 +1,9 @@ +require 'rails/generators' + +if [nil, "-h", "--help"].include?(ARGV.first) + Rails::Generators.help 'destroy' + exit +end + +name = ARGV.shift +Rails::Generators.invoke name, ARGV, behavior: :revoke, destination_root: Rails.root diff --git a/railties/lib/rails/commands/generate.rb b/railties/lib/rails/commands/generate.rb new file mode 100644 index 0000000000..351c59c645 --- /dev/null +++ b/railties/lib/rails/commands/generate.rb @@ -0,0 +1,11 @@ +require 'rails/generators' + +if [nil, "-h", "--help"].include?(ARGV.first) + Rails::Generators.help 'generate' + exit +end + +name = ARGV.shift + +root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root +Rails::Generators.invoke name, ARGV, behavior: :invoke, destination_root: root diff --git a/railties/lib/rails/commands/plugin.rb b/railties/lib/rails/commands/plugin.rb new file mode 100644 index 0000000000..95bbdd4cdf --- /dev/null +++ b/railties/lib/rails/commands/plugin.rb @@ -0,0 +1,23 @@ +if ARGV.first != "new" + ARGV[0] = "--help" +else + ARGV.shift + unless ARGV.delete("--no-rc") + customrc = ARGV.index{ |x| x.include?("--rc=") } + railsrc = if customrc + File.expand_path(ARGV.delete_at(customrc).gsub(/--rc=/, "")) + else + File.join(File.expand_path("~"), '.railsrc') + end + if File.exist?(railsrc) + extra_args_string = File.read(railsrc) + extra_args = extra_args_string.split(/\n+/).flat_map {|l| l.split} + puts "Using #{extra_args.join(" ")} from #{railsrc}" + ARGV.insert(1, *extra_args) + end + end +end + +require 'rails/generators' +require 'rails/generators/rails/plugin/plugin_generator' +Rails::Generators::PluginGenerator.start diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb new file mode 100644 index 0000000000..3a71f8d3f8 --- /dev/null +++ b/railties/lib/rails/commands/runner.rb @@ -0,0 +1,63 @@ +require 'optparse' +require 'rbconfig' + +options = { environment: (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup } +code_or_file = nil + +if ARGV.first.nil? + ARGV.push "-h" +end + +ARGV.clone.options do |opts| + opts.banner = "Usage: rails runner [options] [<'Some.ruby(code)'> | <filename.rb>]" + + opts.separator "" + + opts.on("-e", "--environment=name", String, + "Specifies the environment for the runner to operate under (test/development/production).", + "Default: development") { |v| options[:environment] = v } + + opts.separator "" + + opts.on("-h", "--help", + "Show this help message.") { $stdout.puts opts; exit } + + opts.separator "" + opts.separator "Examples: " + + opts.separator " rails runner 'puts Rails.env'" + opts.separator " This runs the code `puts Rails.env` after loading the app" + opts.separator "" + opts.separator " rails runner path/to/filename.rb" + opts.separator " This runs the Ruby file located at `path/to/filename.rb` after loading the app" + + if RbConfig::CONFIG['host_os'] !~ /mswin|mingw/ + opts.separator "" + opts.separator "You can also use runner as a shebang line for your executables:" + opts.separator " -------------------------------------------------------------" + opts.separator " #!/usr/bin/env #{File.expand_path($0)} runner" + opts.separator "" + opts.separator " Product.all.each { |p| p.price *= 2 ; p.save! }" + opts.separator " -------------------------------------------------------------" + end + + opts.order! { |o| code_or_file ||= o } rescue retry +end + +ARGV.delete(code_or_file) + +ENV["RAILS_ENV"] = options[:environment] + +require APP_PATH +Rails.application.require_environment! +Rails.application.load_runner + +if code_or_file.nil? + $stderr.puts "Run '#{$0} -h' for help." + exit 1 +elsif File.exist?(code_or_file) + $0 = code_or_file + Kernel.load code_or_file +else + eval(code_or_file, binding, __FILE__, __LINE__) +end diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb new file mode 100644 index 0000000000..c3b7bb6f84 --- /dev/null +++ b/railties/lib/rails/commands/server.rb @@ -0,0 +1,152 @@ +require 'fileutils' +require 'optparse' +require 'action_dispatch' +require 'rails' + +module Rails + class Server < ::Rack::Server + class Options + 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 + + private + + def option_parser(options) + OptionParser.new do |opts| + opts.banner = "Usage: rails server [mongrel, thin, etc] [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: 0.0.0.0") { |v| options[:Host] = v } + opts.on("-c", "--config=file", String, + "Use custom rackup configuration file") { |v| options[:config] = v } + opts.on("-d", "--daemon", "Make server run as a Daemon.") { options[:daemonize] = true } + opts.on("-u", "--debugger", "Enable the debugger") do + if RUBY_VERSION < '2.0.0' + options[:debugger] = true + else + puts "=> Notice: debugger option is ignored since ruby 2.0 and " \ + "it will be removed in future versions" + end + end + 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.separator "" + + opts.on("-h", "--help", "Show 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 + 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 + middlewares = [] + if RUBY_VERSION < '2.0.0' + middlewares << [Rails::Rack::Debugger] if options[:debugger] + end + middlewares << [::Rack::ContentLength] + + # FIXME: add Rack::Lock in the case people are using webrick. + # This is to remain backwards compatible for those who are + # running webrick in production. We should consider removing this + # in development. + if server.name == 'Rack::Handler::WEBrick' + middlewares << [::Rack::Lock] + end + + Hash.new(middlewares) + end + + def log_path + "log/#{options[:environment]}.log" + end + + def default_options + super.merge({ + Port: 3000, + DoNotReverseLookup: true, + environment: (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || "development").dup, + daemonize: false, + debugger: false, + pid: File.expand_path("tmp/pids/server.pid"), + config: File.expand_path("config.ru") + }) + end + + private + + 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" + + if options[:Host].to_s.match(/0\.0\.0\.0/) + puts "=> Notice: server is listening on all interfaces (#{options[:Host]}). Consider using 127.0.0.1 (--binding option)" + end + + puts "=> Ctrl-C to shutdown server" unless options[:daemonize] + end + + def create_tmp_directories + %w(cache pids sessions 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 + + Rails.logger.extend(ActiveSupport::Logger.broadcast(console)) + end + end +end diff --git a/railties/lib/rails/commands/update.rb b/railties/lib/rails/commands/update.rb new file mode 100644 index 0000000000..59fae5c337 --- /dev/null +++ b/railties/lib/rails/commands/update.rb @@ -0,0 +1,9 @@ +require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators')) + +if ARGV.size == 0 + Rails::Generators.help + exit +end + +name = ARGV.shift +Rails::Generators.invoke name, ARGV, behavior: :skip |