aboutsummaryrefslogtreecommitdiffstats
path: root/railties/lib/rails/commands
diff options
context:
space:
mode:
authorYehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com>2009-09-24 14:01:31 -0700
committerYehuda Katz + Carl Lerche <ykatz+clerche@engineyard.com>2009-09-24 16:11:41 -0700
commitf0dd77c6be6a86fe384bb0015151e0a497973d39 (patch)
treead81bb4c807c39edeaa37035882a16d9d95ebaa9 /railties/lib/rails/commands
parent610b81beca461a6fa4f00c7023e0e4315eb2b8be (diff)
downloadrails-f0dd77c6be6a86fe384bb0015151e0a497973d39.tar.gz
rails-f0dd77c6be6a86fe384bb0015151e0a497973d39.tar.bz2
rails-f0dd77c6be6a86fe384bb0015151e0a497973d39.zip
Move railties/lib/* into railties/lib/*
Diffstat (limited to 'railties/lib/rails/commands')
-rw-r--r--railties/lib/rails/commands/about.rb3
-rw-r--r--railties/lib/rails/commands/console.rb45
-rw-r--r--railties/lib/rails/commands/dbconsole.rb87
-rw-r--r--railties/lib/rails/commands/destroy.rb10
-rwxr-xr-xrailties/lib/rails/commands/generate.rb10
-rwxr-xr-xrailties/lib/rails/commands/ncgi/listener86
-rwxr-xr-xrailties/lib/rails/commands/ncgi/tracker69
-rw-r--r--railties/lib/rails/commands/performance/benchmarker.rb24
-rw-r--r--railties/lib/rails/commands/performance/profiler.rb50
-rw-r--r--railties/lib/rails/commands/plugin.rb542
-rw-r--r--railties/lib/rails/commands/runner.rb54
-rw-r--r--railties/lib/rails/commands/server.rb106
-rw-r--r--railties/lib/rails/commands/update.rb10
13 files changed, 1096 insertions, 0 deletions
diff --git a/railties/lib/rails/commands/about.rb b/railties/lib/rails/commands/about.rb
new file mode 100644
index 0000000000..bc2cfcb948
--- /dev/null
+++ b/railties/lib/rails/commands/about.rb
@@ -0,0 +1,3 @@
+require "#{RAILS_ROOT}/config/environment"
+require 'rails/info'
+puts Rails::Info
diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb
new file mode 100644
index 0000000000..31448bdf1a
--- /dev/null
+++ b/railties/lib/rails/commands/console.rb
@@ -0,0 +1,45 @@
+irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
+
+require 'optparse'
+
+options = { :sandbox => false, :irb => irb }
+OptionParser.new do |opt|
+ opt.banner = "Usage: console [environment] [options]"
+ opt.on('-s', '--sandbox', 'Rollback database modifications on exit.') { |v| options[:sandbox] = v }
+ opt.on("--irb=[#{irb}]", 'Invoke a different irb.') { |v| options[:irb] = v }
+ opt.on("--debugger", 'Enable ruby-debugging for the console.') { |v| options[:debugger] = v }
+ opt.parse!(ARGV)
+end
+
+libs = " -r irb/completion"
+libs << %( -r "#{RAILS_ROOT}/config/environment")
+libs << " -r rails/console_app"
+libs << " -r rails/console_sandbox" if options[:sandbox]
+libs << " -r rails/console_with_helpers"
+
+if options[:debugger]
+ begin
+ require 'ruby-debug'
+ libs << " -r ruby-debug"
+ puts "=> Debugger enabled"
+ rescue Exception
+ puts "You need to install ruby-debug to run the console in debugging mode. With gems, use 'gem install ruby-debug'"
+ exit
+ end
+end
+
+ENV['RAILS_ENV'] = case ARGV.first
+ when "p"; "production"
+ when "d"; "development"
+ when "t"; "test"
+ else
+ ARGV.first || ENV['RAILS_ENV'] || 'development'
+end
+
+if options[:sandbox]
+ puts "Loading #{ENV['RAILS_ENV']} environment in sandbox (Rails #{Rails.version})"
+ puts "Any modifications you make will be rolled back on exit"
+else
+ puts "Loading #{ENV['RAILS_ENV']} environment (Rails #{Rails.version})"
+end
+exec "#{options[:irb]} #{libs} --simple-prompt"
diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb
new file mode 100644
index 0000000000..e6f11a45db
--- /dev/null
+++ b/railties/lib/rails/commands/dbconsole.rb
@@ -0,0 +1,87 @@
+require 'erb'
+require 'yaml'
+require 'optparse'
+
+include_password = false
+options = {}
+
+OptionParser.new do |opt|
+ opt.banner = "Usage: dbconsole [options] [environment]"
+ opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v|
+ 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("-h", "--header") do |h|
+ options['header'] = h
+ end
+
+ opt.parse!(ARGV)
+ abort opt.to_s unless (0..1).include?(ARGV.size)
+end
+
+env = ARGV.first || ENV['RAILS_ENV'] || 'development'
+unless config = YAML::load(ERB.new(IO.read(RAILS_ROOT + "/config/database.yml")).result)[env]
+ abort "No database is configured for the environment '#{env}'"
+end
+
+
+def find_cmd(*commands)
+ dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR)
+ commands += commands.map{|cmd| "#{cmd}.exe"} if RUBY_PLATFORM =~ /win32/
+
+ full_path_command = nil
+ found = commands.detect do |cmd|
+ dir = dirs_on_path.detect do |path|
+ full_path_command = File.join(path, cmd)
+ File.executable? full_path_command
+ end
+ end
+ found ? full_path_command : abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
+end
+
+case config["adapter"]
+when "mysql"
+ args = {
+ 'host' => '--host',
+ 'port' => '--port',
+ 'socket' => '--socket',
+ 'username' => '--user',
+ 'encoding' => '--default-character-set'
+ }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact
+
+ if config['password'] && include_password
+ args << "--password=#{config['password']}"
+ elsif config['password'] && !config['password'].to_s.empty?
+ args << "-p"
+ end
+
+ args << config['database']
+
+ exec(find_cmd('mysql', 'mysql5'), *args)
+
+when "postgresql"
+ 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"] && include_password
+ exec(find_cmd('psql'), config["database"])
+
+when "sqlite"
+ exec(find_cmd('sqlite'), config["database"])
+
+when "sqlite3"
+ args = []
+
+ args << "-#{options['mode']}" if options['mode']
+ args << "-header" if options['header']
+ args << config['database']
+
+ exec(find_cmd('sqlite3'), *args)
+else
+ abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!"
+end
diff --git a/railties/lib/rails/commands/destroy.rb b/railties/lib/rails/commands/destroy.rb
new file mode 100644
index 0000000000..5013d30b83
--- /dev/null
+++ b/railties/lib/rails/commands/destroy.rb
@@ -0,0 +1,10 @@
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators'))
+require "#{RAILS_ROOT}/config/environment"
+
+if ARGV.size == 0
+ Rails::Generators.help
+ exit
+end
+
+name = ARGV.shift
+Rails::Generators.invoke name, ARGV, :behavior => :revoke
diff --git a/railties/lib/rails/commands/generate.rb b/railties/lib/rails/commands/generate.rb
new file mode 100755
index 0000000000..32cabcab10
--- /dev/null
+++ b/railties/lib/rails/commands/generate.rb
@@ -0,0 +1,10 @@
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators'))
+require "#{RAILS_ROOT}/config/environment"
+
+if ARGV.size == 0
+ Rails::Generators.help
+ exit
+end
+
+name = ARGV.shift
+Rails::Generators.invoke name, ARGV, :behavior => :invoke
diff --git a/railties/lib/rails/commands/ncgi/listener b/railties/lib/rails/commands/ncgi/listener
new file mode 100755
index 0000000000..7079ef78a6
--- /dev/null
+++ b/railties/lib/rails/commands/ncgi/listener
@@ -0,0 +1,86 @@
+#!/usr/bin/env ruby
+
+require 'stringio'
+require 'fileutils'
+require 'fcgi_handler'
+
+def message(s)
+ $stderr.puts "listener: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
+end
+
+class RemoteCGI < CGI
+ attr_accessor :stdinput, :stdoutput, :env_table
+ def initialize(env_table, input = nil, output = nil)
+ self.env_table = env_table
+ self.stdinput = input || StringIO.new
+ self.stdoutput = output || StringIO.new
+ super()
+ end
+
+ def out(stream) # Ignore the requested output stream
+ super(stdoutput)
+ end
+end
+
+class Listener
+ include DRbUndumped
+
+ def initialize(timeout, socket_path)
+ @socket = File.expand_path(socket_path)
+ @mutex = Mutex.new
+ @active = false
+ @timeout = timeout
+
+ @handler = RailsFCGIHandler.new
+ @handler.extend DRbUndumped
+
+ message 'opening socket'
+ DRb.start_service("drbunix:#{@socket}", self)
+
+ message 'entering process loop'
+ @handler.process! self
+ end
+
+ def each_cgi(&cgi_block)
+ @cgi_block = cgi_block
+ message 'entering idle loop'
+ loop do
+ sleep @timeout rescue nil
+ die! unless @active
+ @active = false
+ end
+ end
+
+ def process(env, input)
+ message 'received request'
+ @mutex.synchronize do
+ @active = true
+
+ message 'creating input stream'
+ input_stream = StringIO.new(input)
+ message 'building CGI instance'
+ cgi = RemoteCGI.new(eval(env), input_stream)
+
+ message 'yielding to fcgi handler'
+ @cgi_block.call cgi
+ message 'yield finished -- sending output'
+
+ cgi.stdoutput.seek(0)
+ output = cgi.stdoutput.read
+
+ return output
+ end
+ end
+
+ def die!
+ message 'shutting down'
+ DRb.stop_service
+ FileUtils.rm_f @socket
+ Kernel.exit 0
+ end
+end
+
+socket_path = ARGV.shift
+timeout = (ARGV.shift || 90).to_i
+
+Listener.new(timeout, socket_path)
diff --git a/railties/lib/rails/commands/ncgi/tracker b/railties/lib/rails/commands/ncgi/tracker
new file mode 100755
index 0000000000..4ca12d779b
--- /dev/null
+++ b/railties/lib/rails/commands/ncgi/tracker
@@ -0,0 +1,69 @@
+#!/usr/bin/env ruby
+
+require 'drb'
+require 'thread'
+
+def message(s)
+ $stderr.puts "tracker: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
+end
+
+class Tracker
+ include DRbUndumped
+
+ def initialize(instances, socket_path)
+ @instances = instances
+ @socket = File.expand_path(socket_path)
+ @active = false
+
+ @listeners = []
+ @instances.times { @listeners << Mutex.new }
+
+ message "using #{@listeners.length} listeners"
+ message "opening socket at #{@socket}"
+
+ @service = DRb.start_service("drbunix://#{@socket}", self)
+ end
+
+ def with_listener
+ message "listener requested"
+
+ mutex = has_lock = index = nil
+ 3.times do
+ @listeners.each_with_index do |mutex, index|
+ has_lock = mutex.try_lock
+ break if has_lock
+ end
+ break if has_lock
+ sleep 0.05
+ end
+
+ if has_lock
+ message "obtained listener #{index}"
+ @active = true
+ begin yield index
+ ensure
+ mutex.unlock
+ message "released listener #{index}"
+ end
+ else
+ message "dropping request because no listeners are available!"
+ end
+ end
+
+ def background(check_interval = nil)
+ if check_interval
+ loop do
+ sleep check_interval
+ message "Idle for #{check_interval}, shutting down" unless @active
+ @active = false
+ Kernel.exit 0
+ end
+ else DRb.thread.join
+ end
+ end
+end
+
+socket_path = ARGV.shift
+instances = ARGV.shift.to_i
+t = Tracker.new(instances, socket_path)
+t.background(ARGV.first ? ARGV.shift.to_i : 90)
diff --git a/railties/lib/rails/commands/performance/benchmarker.rb b/railties/lib/rails/commands/performance/benchmarker.rb
new file mode 100644
index 0000000000..e8804fe1bf
--- /dev/null
+++ b/railties/lib/rails/commands/performance/benchmarker.rb
@@ -0,0 +1,24 @@
+if ARGV.empty?
+ puts "Usage: ./script/performance/benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ..."
+ exit 1
+end
+
+begin
+ N = Integer(ARGV.first)
+ ARGV.shift
+rescue ArgumentError
+ N = 1
+end
+
+require RAILS_ROOT + '/config/environment'
+require 'benchmark'
+include Benchmark
+
+# Don't include compilation in the benchmark
+ARGV.each { |expression| eval(expression) }
+
+bm(6) do |x|
+ ARGV.each_with_index do |expression, idx|
+ x.report("##{idx + 1}") { N.times { eval(expression) } }
+ end
+end
diff --git a/railties/lib/rails/commands/performance/profiler.rb b/railties/lib/rails/commands/performance/profiler.rb
new file mode 100644
index 0000000000..7df840f197
--- /dev/null
+++ b/railties/lib/rails/commands/performance/profiler.rb
@@ -0,0 +1,50 @@
+if ARGV.empty?
+ $stderr.puts "Usage: ./script/performance/profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html]"
+ exit(1)
+end
+
+# Keep the expensive require out of the profile.
+$stderr.puts 'Loading Rails...'
+require RAILS_ROOT + '/config/environment'
+
+# Define a method to profile.
+if ARGV[1] and ARGV[1].to_i > 1
+ eval "def profile_me() #{ARGV[1]}.times { #{ARGV[0]} } end"
+else
+ eval "def profile_me() #{ARGV[0]} end"
+end
+
+# Use the ruby-prof extension if available. Fall back to stdlib profiler.
+begin
+ begin
+ require "ruby-prof"
+ $stderr.puts 'Using the ruby-prof extension.'
+ RubyProf.measure_mode = RubyProf::WALL_TIME
+ RubyProf.start
+ profile_me
+ results = RubyProf.stop
+ if ARGV[2]
+ printer_class = RubyProf.const_get((ARGV[2] + "_printer").classify)
+ else
+ printer_class = RubyProf::FlatPrinter
+ end
+ printer = printer_class.new(results)
+ printer.print($stderr)
+ rescue LoadError
+ require "prof"
+ $stderr.puts 'Using the old ruby-prof extension.'
+ Prof.clock_mode = Prof::GETTIMEOFDAY
+ Prof.start
+ profile_me
+ results = Prof.stop
+ require 'rubyprof_ext'
+ Prof.print_profile(results, $stderr)
+ end
+rescue LoadError
+ require 'profiler'
+ $stderr.puts 'Using the standard Ruby profiler.'
+ Profiler__.start_profile
+ profile_me
+ Profiler__.stop_profile
+ Profiler__.print_profile($stderr)
+end
diff --git a/railties/lib/rails/commands/plugin.rb b/railties/lib/rails/commands/plugin.rb
new file mode 100644
index 0000000000..159db707e7
--- /dev/null
+++ b/railties/lib/rails/commands/plugin.rb
@@ -0,0 +1,542 @@
+# Rails Plugin Manager.
+#
+# Installing plugins:
+#
+# $ ./script/plugin install continuous_builder asset_timestamping
+#
+# Specifying revisions:
+#
+# * Subversion revision is a single integer.
+#
+# * Git revision format:
+# - full - 'refs/tags/1.8.0' or 'refs/heads/experimental'
+# - short: 'experimental' (equivalent to 'refs/heads/experimental')
+# 'tag 1.8.0' (equivalent to 'refs/tags/1.8.0')
+#
+#
+# This is Free Software, copyright 2005 by Ryan Tomayko (rtomayko@gmail.com)
+# and is licensed MIT: (http://www.opensource.org/licenses/mit-license.php)
+
+$verbose = false
+
+
+require 'open-uri'
+require 'fileutils'
+require 'tempfile'
+
+include FileUtils
+
+class RailsEnvironment
+ attr_reader :root
+
+ def initialize(dir)
+ @root = dir
+ end
+
+ def self.find(dir=nil)
+ dir ||= pwd
+ while dir.length > 1
+ return new(dir) if File.exist?(File.join(dir, 'config', 'environment.rb'))
+ dir = File.dirname(dir)
+ end
+ end
+
+ def self.default
+ @default ||= find
+ end
+
+ def self.default=(rails_env)
+ @default = rails_env
+ end
+
+ def install(name_uri_or_plugin)
+ if name_uri_or_plugin.is_a? String
+ if name_uri_or_plugin =~ /:\/\//
+ plugin = Plugin.new(name_uri_or_plugin)
+ else
+ plugin = Plugins[name_uri_or_plugin]
+ end
+ else
+ plugin = name_uri_or_plugin
+ end
+ unless plugin.nil?
+ plugin.install
+ else
+ puts "Plugin not found: #{name_uri_or_plugin}"
+ end
+ end
+
+ def use_svn?
+ require 'active_support/core_ext/kernel'
+ silence_stderr {`svn --version` rescue nil}
+ !$?.nil? && $?.success?
+ end
+
+ def use_externals?
+ use_svn? && File.directory?("#{root}/vendor/plugins/.svn")
+ end
+
+ def use_checkout?
+ # this is a bit of a guess. we assume that if the rails environment
+ # is under subversion then they probably want the plugin checked out
+ # instead of exported. This can be overridden on the command line
+ File.directory?("#{root}/.svn")
+ end
+
+ def best_install_method
+ return :http unless use_svn?
+ case
+ when use_externals? then :externals
+ when use_checkout? then :checkout
+ else :export
+ end
+ end
+
+ def externals
+ return [] unless use_externals?
+ ext = `svn propget svn:externals "#{root}/vendor/plugins"`
+ lines = ext.respond_to?(:lines) ? ext.lines : ext
+ lines.reject{ |line| line.strip == '' }.map do |line|
+ line.strip.split(/\s+/, 2)
+ end
+ end
+
+ def externals=(items)
+ unless items.is_a? String
+ items = items.map{|name,uri| "#{name.ljust(29)} #{uri.chomp('/')}"}.join("\n")
+ end
+ Tempfile.open("svn-set-prop") do |file|
+ file.write(items)
+ file.flush
+ system("svn propset -q svn:externals -F \"#{file.path}\" \"#{root}/vendor/plugins\"")
+ end
+ end
+
+end
+
+class Plugin
+ attr_reader :name, :uri
+
+ def initialize(uri, name = nil)
+ @uri = uri
+ guess_name(uri)
+ end
+
+ def self.find(name)
+ new(name)
+ end
+
+ def to_s
+ "#{@name.ljust(30)}#{@uri}"
+ end
+
+ def svn_url?
+ @uri =~ /svn(?:\+ssh)?:\/\/*/
+ end
+
+ def git_url?
+ @uri =~ /^git:\/\// || @uri =~ /\.git$/
+ end
+
+ def installed?
+ File.directory?("#{rails_env.root}/vendor/plugins/#{name}") \
+ or rails_env.externals.detect{ |name, repo| self.uri == repo }
+ end
+
+ def install(method=nil, options = {})
+ method ||= rails_env.best_install_method?
+ if :http == method
+ method = :export if svn_url?
+ method = :git if git_url?
+ end
+
+ uninstall if installed? and options[:force]
+
+ unless installed?
+ send("install_using_#{method}", options)
+ run_install_hook
+ else
+ puts "already installed: #{name} (#{uri}). pass --force to reinstall"
+ end
+ end
+
+ def uninstall
+ path = "#{rails_env.root}/vendor/plugins/#{name}"
+ if File.directory?(path)
+ puts "Removing 'vendor/plugins/#{name}'" if $verbose
+ run_uninstall_hook
+ rm_r path
+ else
+ puts "Plugin doesn't exist: #{path}"
+ end
+
+ if rails_env.use_externals?
+ # clean up svn:externals
+ externals = rails_env.externals
+ externals.reject!{|n,u| name == n or name == u}
+ rails_env.externals = externals
+ end
+ end
+
+ def info
+ tmp = "#{rails_env.root}/_tmp_about.yml"
+ if svn_url?
+ cmd = "svn export #{@uri} \"#{rails_env.root}/#{tmp}\""
+ puts cmd if $verbose
+ system(cmd)
+ end
+ open(svn_url? ? tmp : File.join(@uri, 'about.yml')) do |stream|
+ stream.read
+ end rescue "No about.yml found in #{uri}"
+ ensure
+ FileUtils.rm_rf tmp if svn_url?
+ end
+
+ private
+
+ def run_install_hook
+ install_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/install.rb"
+ load install_hook_file if File.exist? install_hook_file
+ end
+
+ def run_uninstall_hook
+ uninstall_hook_file = "#{rails_env.root}/vendor/plugins/#{name}/uninstall.rb"
+ load uninstall_hook_file if File.exist? uninstall_hook_file
+ end
+
+ def install_using_export(options = {})
+ svn_command :export, options
+ end
+
+ def install_using_checkout(options = {})
+ svn_command :checkout, options
+ end
+
+ def install_using_externals(options = {})
+ externals = rails_env.externals
+ externals.push([@name, uri])
+ rails_env.externals = externals
+ install_using_checkout(options)
+ end
+
+ def install_using_http(options = {})
+ root = rails_env.root
+ mkdir_p "#{root}/vendor/plugins/#{@name}"
+ Dir.chdir "#{root}/vendor/plugins/#{@name}" do
+ puts "fetching from '#{uri}'" if $verbose
+ fetcher = RecursiveHTTPFetcher.new(uri, -1)
+ fetcher.quiet = true if options[:quiet]
+ fetcher.fetch
+ end
+ end
+
+ def install_using_git(options = {})
+ root = rails_env.root
+ mkdir_p(install_path = "#{root}/vendor/plugins/#{name}")
+ Dir.chdir install_path do
+ init_cmd = "git init"
+ init_cmd += " -q" if options[:quiet] and not $verbose
+ puts init_cmd if $verbose
+ system(init_cmd)
+ base_cmd = "git pull --depth 1 #{uri}"
+ base_cmd += " -q" if options[:quiet] and not $verbose
+ base_cmd += " #{options[:revision]}" if options[:revision]
+ puts base_cmd if $verbose
+ if system(base_cmd)
+ puts "removing: .git .gitignore" if $verbose
+ rm_rf %w(.git .gitignore)
+ else
+ rm_rf install_path
+ end
+ end
+ end
+
+ def svn_command(cmd, options = {})
+ root = rails_env.root
+ mkdir_p "#{root}/vendor/plugins"
+ base_cmd = "svn #{cmd} #{uri} \"#{root}/vendor/plugins/#{name}\""
+ base_cmd += ' -q' if options[:quiet] and not $verbose
+ base_cmd += " -r #{options[:revision]}" if options[:revision]
+ puts base_cmd if $verbose
+ system(base_cmd)
+ end
+
+ def guess_name(url)
+ @name = File.basename(url)
+ if @name == 'trunk' || @name.empty?
+ @name = File.basename(File.dirname(url))
+ end
+ @name.gsub!(/\.git$/, '') if @name =~ /\.git$/
+ end
+
+ def rails_env
+ @rails_env || RailsEnvironment.default
+ end
+end
+
+# load default environment and parse arguments
+require 'optparse'
+module Commands
+
+ class Plugin
+ attr_reader :environment, :script_name, :sources
+ def initialize
+ @environment = RailsEnvironment.default
+ @rails_root = RailsEnvironment.default.root
+ @script_name = File.basename($0)
+ @sources = []
+ end
+
+ def environment=(value)
+ @environment = value
+ RailsEnvironment.default = value
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@script_name} [OPTIONS] command"
+ o.define_head "Rails plugin manager."
+
+ o.separator ""
+ o.separator "GENERAL OPTIONS"
+
+ o.on("-r", "--root=DIR", String,
+ "Set an explicit rails app directory.",
+ "Default: #{@rails_root}") { |rails_root| @rails_root = rails_root; self.environment = RailsEnvironment.new(@rails_root) }
+ o.on("-s", "--source=URL1,URL2", Array,
+ "Use the specified plugin repositories instead of the defaults.") { |sources| @sources = sources}
+
+ o.on("-v", "--verbose", "Turn on verbose output.") { |verbose| $verbose = verbose }
+ o.on("-h", "--help", "Show this help message.") { puts o; exit }
+
+ o.separator ""
+ o.separator "COMMANDS"
+
+ o.separator " install Install plugin(s) from known repositories or URLs."
+ o.separator " remove Uninstall plugins."
+
+ o.separator ""
+ o.separator "EXAMPLES"
+ o.separator " Install a plugin:"
+ o.separator " #{@script_name} install continuous_builder\n"
+ o.separator " Install a plugin from a subversion URL:"
+ o.separator " #{@script_name} install http://dev.rubyonrails.com/svn/rails/plugins/continuous_builder\n"
+ o.separator " Install a plugin from a git URL:"
+ o.separator " #{@script_name} install git://github.com/SomeGuy/my_awesome_plugin.git\n"
+ o.separator " Install a plugin and add a svn:externals entry to vendor/plugins"
+ o.separator " #{@script_name} install -x continuous_builder\n"
+ end
+ end
+
+ def parse!(args=ARGV)
+ general, sub = split_args(args)
+ options.parse!(general)
+
+ command = general.shift
+ if command =~ /^(install|remove)$/
+ command = Commands.const_get(command.capitalize).new(self)
+ command.parse!(sub)
+ else
+ puts "Unknown command: #{command}"
+ puts options
+ exit 1
+ end
+ end
+
+ def split_args(args)
+ left = []
+ left << args.shift while args[0] and args[0] =~ /^-/
+ left << args.shift if args[0]
+ return [left, args]
+ end
+
+ def self.parse!(args=ARGV)
+ Plugin.new.parse!(args)
+ end
+ end
+
+ class Install
+ def initialize(base_command)
+ @base_command = base_command
+ @method = :http
+ @options = { :quiet => false, :revision => nil, :force => false }
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@base_command.script_name} install PLUGIN [PLUGIN [PLUGIN] ...]"
+ o.define_head "Install one or more plugins."
+ o.separator ""
+ o.separator "Options:"
+ o.on( "-x", "--externals",
+ "Use svn:externals to grab the plugin.",
+ "Enables plugin updates and plugin versioning.") { |v| @method = :externals }
+ o.on( "-o", "--checkout",
+ "Use svn checkout to grab the plugin.",
+ "Enables updating but does not add a svn:externals entry.") { |v| @method = :checkout }
+ o.on( "-e", "--export",
+ "Use svn export to grab the plugin.",
+ "Exports the plugin, allowing you to check it into your local repository. Does not enable updates, or add an svn:externals entry.") { |v| @method = :export }
+ o.on( "-q", "--quiet",
+ "Suppresses the output from installation.",
+ "Ignored if -v is passed (./script/plugin -v install ...)") { |v| @options[:quiet] = true }
+ o.on( "-r REVISION", "--revision REVISION",
+ "Checks out the given revision from subversion or git.",
+ "Ignored if subversion/git is not used.") { |v| @options[:revision] = v }
+ o.on( "-f", "--force",
+ "Reinstalls a plugin if it's already installed.") { |v| @options[:force] = true }
+ o.separator ""
+ o.separator "You can specify plugin names as given in 'plugin list' output or absolute URLs to "
+ o.separator "a plugin repository."
+ end
+ end
+
+ def determine_install_method
+ best = @base_command.environment.best_install_method
+ @method = :http if best == :http and @method == :export
+ case
+ when (best == :http and @method != :http)
+ msg = "Cannot install using subversion because `svn' cannot be found in your PATH"
+ when (best == :export and (@method != :export and @method != :http))
+ msg = "Cannot install using #{@method} because this project is not under subversion."
+ when (best != :externals and @method == :externals)
+ msg = "Cannot install using externals because vendor/plugins is not under subversion."
+ end
+ if msg
+ puts msg
+ exit 1
+ end
+ @method
+ end
+
+ def parse!(args)
+ options.parse!(args)
+ environment = @base_command.environment
+ install_method = determine_install_method
+ puts "Plugins will be installed using #{install_method}" if $verbose
+ args.each do |name|
+ ::Plugin.find(name).install(install_method, @options)
+ end
+ rescue StandardError => e
+ puts "Plugin not found: #{args.inspect}"
+ puts e.inspect if $verbose
+ exit 1
+ end
+ end
+
+ class Remove
+ def initialize(base_command)
+ @base_command = base_command
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@base_command.script_name} remove name [name]..."
+ o.define_head "Remove plugins."
+ end
+ end
+
+ def parse!(args)
+ options.parse!(args)
+ root = @base_command.environment.root
+ args.each do |name|
+ ::Plugin.new(name).uninstall
+ end
+ end
+ end
+
+ class Info
+ def initialize(base_command)
+ @base_command = base_command
+ end
+
+ def options
+ OptionParser.new do |o|
+ o.set_summary_indent(' ')
+ o.banner = "Usage: #{@base_command.script_name} info name [name]..."
+ o.define_head "Shows plugin info at {url}/about.yml."
+ end
+ end
+
+ def parse!(args)
+ options.parse!(args)
+ args.each do |name|
+ puts ::Plugin.find(name).info
+ puts
+ end
+ end
+ end
+end
+
+class RecursiveHTTPFetcher
+ attr_accessor :quiet
+ def initialize(urls_to_fetch, level = 1, cwd = ".")
+ @level = level
+ @cwd = cwd
+ @urls_to_fetch = RUBY_VERSION >= '1.9' ? urls_to_fetch.lines : urls_to_fetch.to_a
+ @quiet = false
+ end
+
+ def ls
+ @urls_to_fetch.collect do |url|
+ if url =~ /^svn(\+ssh)?:\/\/.*/
+ `svn ls #{url}`.split("\n").map {|entry| "/#{entry}"} rescue nil
+ else
+ open(url) do |stream|
+ links("", stream.read)
+ end rescue nil
+ end
+ end.flatten
+ end
+
+ def push_d(dir)
+ @cwd = File.join(@cwd, dir)
+ FileUtils.mkdir_p(@cwd)
+ end
+
+ def pop_d
+ @cwd = File.dirname(@cwd)
+ end
+
+ def links(base_url, contents)
+ links = []
+ contents.scan(/href\s*=\s*\"*[^\">]*/i) do |link|
+ link = link.sub(/href="/i, "")
+ next if link =~ /svnindex.xsl$/
+ next if link =~ /^(\w*:|)\/\// || link =~ /^\./
+ links << File.join(base_url, link)
+ end
+ links
+ end
+
+ def download(link)
+ puts "+ #{File.join(@cwd, File.basename(link))}" unless @quiet
+ open(link) do |stream|
+ File.open(File.join(@cwd, File.basename(link)), "wb") do |file|
+ file.write(stream.read)
+ end
+ end
+ end
+
+ def fetch(links = @urls_to_fetch)
+ links.each do |l|
+ (l =~ /\/$/ || links == @urls_to_fetch) ? fetch_dir(l) : download(l)
+ end
+ end
+
+ def fetch_dir(url)
+ @level += 1
+ push_d(File.basename(url)) if @level > 0
+ open(url) do |stream|
+ contents = stream.read
+ fetch(links(url, contents))
+ end
+ pop_d if @level > 0
+ @level -= 1
+ end
+end
+
+Commands::Plugin.parse!
diff --git a/railties/lib/rails/commands/runner.rb b/railties/lib/rails/commands/runner.rb
new file mode 100644
index 0000000000..510128318a
--- /dev/null
+++ b/railties/lib/rails/commands/runner.rb
@@ -0,0 +1,54 @@
+require 'optparse'
+
+options = { :environment => (ENV['RAILS_ENV'] || "development").dup }
+code_or_file = nil
+
+ARGV.clone.options do |opts|
+ script_name = File.basename($0)
+ opts.banner = "Usage: #{$0} [options] ('Some.ruby(code)' or a filename)"
+
+ 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.") { $stderr.puts opts; exit }
+
+ if RUBY_PLATFORM !~ /mswin/
+ opts.separator ""
+ opts.separator "You can also use runner as a shebang line for your scripts like this:"
+ opts.separator "-------------------------------------------------------------"
+ opts.separator "#!/usr/bin/env #{File.expand_path($0)}"
+ opts.separator ""
+ opts.separator "Product.find(: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]
+RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV)
+
+require RAILS_ROOT + '/config/environment'
+
+begin
+ if code_or_file.nil?
+ $stderr.puts "Run '#{$0} -h' for help."
+ exit 1
+ elsif File.exist?(code_or_file)
+ eval(File.read(code_or_file), nil, code_or_file)
+ else
+ eval(code_or_file)
+ end
+ensure
+ if defined? Rails
+ Rails.logger.flush if Rails.logger.respond_to?(:flush)
+ end
+end
diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb
new file mode 100644
index 0000000000..823916b1dc
--- /dev/null
+++ b/railties/lib/rails/commands/server.rb
@@ -0,0 +1,106 @@
+require 'action_controller'
+
+require 'fileutils'
+require 'optparse'
+
+options = {
+ :Port => 3000,
+ :Host => "0.0.0.0",
+ :environment => (ENV['RAILS_ENV'] || "development").dup,
+ :config => RAILS_ROOT + "/config.ru",
+ :detach => false,
+ :debugger => false,
+ :path => nil
+}
+
+ARGV.clone.options do |opts|
+ opts.on("-p", "--port=port", Integer,
+ "Runs Rails on the specified port.", "Default: #{options[:Port]}") { |v| options[:Port] = v }
+ opts.on("-b", "--binding=ip", String,
+ "Binds Rails to the specified ip.", "Default: #{options[:Host]}") { |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[:detach] = true }
+ opts.on("-u", "--debugger", "Enable ruby-debugging for the server.") { options[:debugger] = true }
+ opts.on("-e", "--environment=name", String,
+ "Specifies the environment to run this server under (test/development/production).",
+ "Default: #{options[:environment]}") { |v| options[:environment] = v }
+ opts.on("-P", "--path=/path", String, "Runs Rails app mounted at a specific path.", "Default: #{options[:path]}") { |v| options[:path] = v }
+
+ opts.separator ""
+
+ opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
+
+ opts.parse!
+end
+
+server = Rack::Handler.get(ARGV.first) rescue nil
+unless server
+ begin
+ server = Rack::Handler::Mongrel
+ rescue LoadError => e
+ server = Rack::Handler::WEBrick
+ end
+end
+
+puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}"
+puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}#{options[:path]}"
+
+%w(cache pids sessions sockets).each do |dir_to_make|
+ FileUtils.mkdir_p(File.join(RAILS_ROOT, 'tmp', dir_to_make))
+end
+
+if options[:detach]
+ Process.daemon
+ pid = "#{RAILS_ROOT}/tmp/pids/server.pid"
+ File.open(pid, 'w'){ |f| f.write(Process.pid) }
+ at_exit { File.delete(pid) if File.exist?(pid) }
+end
+
+ENV["RAILS_ENV"] = options[:environment]
+RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV)
+
+if File.exist?(options[:config])
+ config = options[:config]
+ if config =~ /\.ru$/
+ cfgfile = File.read(config)
+ if cfgfile[/^#\\(.*)/]
+ opts.parse!($1.split(/\s+/))
+ end
+ inner_app = eval("Rack::Builder.new {( " + cfgfile + "\n )}.to_app", nil, config)
+ else
+ require config
+ inner_app = Object.const_get(File.basename(config, '.rb').capitalize)
+ end
+else
+ require RAILS_ROOT + "/config/environment"
+ inner_app = ActionController::Dispatcher.new
+end
+
+if options[:path].nil?
+ map_path = "/"
+else
+ ActionController::Base.relative_url_root = options[:path]
+ map_path = options[:path]
+end
+
+app = Rack::Builder.new {
+ use Rails::Rack::LogTailer unless options[:detach]
+ use Rails::Rack::Debugger if options[:debugger]
+ map map_path do
+ use Rails::Rack::Static
+ run inner_app
+ end
+}.to_app
+
+puts "=> Call with -d to detach"
+
+trap(:INT) { exit }
+
+puts "=> Ctrl-C to shutdown server"
+
+begin
+ server.run(app, options.merge(:AccessLog => []))
+ensure
+ puts 'Exiting'
+end
diff --git a/railties/lib/rails/commands/update.rb b/railties/lib/rails/commands/update.rb
new file mode 100644
index 0000000000..f3b3ad0775
--- /dev/null
+++ b/railties/lib/rails/commands/update.rb
@@ -0,0 +1,10 @@
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators'))
+require "#{RAILS_ROOT}/config/environment"
+
+if ARGV.size == 0
+ Rails::Generators.help
+ exit
+end
+
+name = ARGV.shift
+Rails::Generators.invoke name, ARGV, :behavior => :skip