diff options
Diffstat (limited to 'railties/lib/rails/commands')
-rw-r--r-- | railties/lib/rails/commands/about.rb | 3 | ||||
-rw-r--r-- | railties/lib/rails/commands/console.rb | 45 | ||||
-rw-r--r-- | railties/lib/rails/commands/dbconsole.rb | 87 | ||||
-rw-r--r-- | railties/lib/rails/commands/destroy.rb | 10 | ||||
-rwxr-xr-x | railties/lib/rails/commands/generate.rb | 10 | ||||
-rwxr-xr-x | railties/lib/rails/commands/ncgi/listener | 86 | ||||
-rwxr-xr-x | railties/lib/rails/commands/ncgi/tracker | 69 | ||||
-rw-r--r-- | railties/lib/rails/commands/performance/benchmarker.rb | 24 | ||||
-rw-r--r-- | railties/lib/rails/commands/performance/profiler.rb | 50 | ||||
-rw-r--r-- | railties/lib/rails/commands/plugin.rb | 542 | ||||
-rw-r--r-- | railties/lib/rails/commands/runner.rb | 54 | ||||
-rw-r--r-- | railties/lib/rails/commands/server.rb | 106 | ||||
-rw-r--r-- | railties/lib/rails/commands/update.rb | 10 |
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 |