diff options
author | Hongli Lai (Phusion) <hongli@phusion.nl> | 2008-12-03 19:30:35 +0100 |
---|---|---|
committer | Hongli Lai (Phusion) <hongli@phusion.nl> | 2008-12-03 19:30:35 +0100 |
commit | ccb96f2297e8783165cba764e9b5d51e1a15ff87 (patch) | |
tree | 3229e6fdddc42054615514d843c555e341003033 /railties/lib | |
parent | fb2325e35855d62abd2c76ce03feaa3ca7992e4f (diff) | |
parent | 761a633a9c0a45d76ef3ed10da97e3696c3ded79 (diff) | |
download | rails-ccb96f2297e8783165cba764e9b5d51e1a15ff87.tar.gz rails-ccb96f2297e8783165cba764e9b5d51e1a15ff87.tar.bz2 rails-ccb96f2297e8783165cba764e9b5d51e1a15ff87.zip |
Merge commit 'origin/master' into savepoints
Conflicts:
activerecord/lib/active_record/fixtures.rb
activerecord/test/cases/defaults_test.rb
Diffstat (limited to 'railties/lib')
54 files changed, 1103 insertions, 1459 deletions
diff --git a/railties/lib/commands/process/inspector.rb b/railties/lib/commands/process/inspector.rb deleted file mode 100644 index 8a6437e715..0000000000 --- a/railties/lib/commands/process/inspector.rb +++ /dev/null @@ -1,68 +0,0 @@ -require 'optparse' - -if RUBY_PLATFORM =~ /(:?mswin|mingw)/ then abort("Inspector is only for Unix") end - -OPTIONS = { - :pid_path => File.expand_path(RAILS_ROOT + '/tmp/pids'), - :pattern => "dispatch.*.pid", - :ps => "ps -o pid,state,user,start,time,pcpu,vsz,majflt,command -p %s" -} - -class Inspector - def self.inspect(pid_path, pattern) - new(pid_path, pattern).inspect - end - - def initialize(pid_path, pattern) - @pid_path, @pattern = pid_path, pattern - end - - def inspect - header = `#{OPTIONS[:ps] % 1}`.split("\n")[0] + "\n" - lines = pids.collect { |pid| `#{OPTIONS[:ps] % pid}`.split("\n")[1] } - - puts(header + lines.join("\n")) - end - - private - def pids - pid_files.collect do |pid_file| - File.read(pid_file).to_i - end - end - - def pid_files - Dir.glob(@pid_path + "/" + @pattern) - end -end - - -ARGV.options do |opts| - opts.banner = "Usage: inspector [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - Displays system information about Rails dispatchers (or other processes that use pid files) through - the ps command. - - Examples: - inspector # default ps on all tmp/pids/dispatch.*.pid files - inspector -s 'ps -o user,start,majflt,pcpu,vsz -p %s' # custom ps, %s is where the pid is interleaved - EOF - - opts.on(" Options:") - - opts.on("-s", "--ps=command", "default: #{OPTIONS[:ps]}", String) { |v| OPTIONS[:ps] = v } - opts.on("-p", "--pidpath=path", "default: #{OPTIONS[:pid_path]}", String) { |v| OPTIONS[:pid_path] = v } - opts.on("-r", "--pattern=pattern", "default: #{OPTIONS[:pattern]}", String) { |v| OPTIONS[:pattern] = v } - - opts.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -Inspector.inspect(OPTIONS[:pid_path], OPTIONS[:pattern]) diff --git a/railties/lib/commands/process/reaper.rb b/railties/lib/commands/process/reaper.rb deleted file mode 100644 index 95175d41e0..0000000000 --- a/railties/lib/commands/process/reaper.rb +++ /dev/null @@ -1,149 +0,0 @@ -require 'optparse' -require 'net/http' -require 'uri' - -if RUBY_PLATFORM =~ /(:?mswin|mingw)/ then abort("Reaper is only for Unix") end - -class Killer - class << self - # Searches for all processes matching the given keywords, and then invokes - # a specific action on each of them. This is useful for (e.g.) reloading a - # set of processes: - # - # Killer.process(:reload, "/tmp/pids", "dispatcher.*.pid") - def process(action, pid_path, pattern, keyword) - new(pid_path, pattern, keyword).process(action) - end - - # Forces the (rails) application to reload by sending a +HUP+ signal to the - # process. - def reload(pid) - `kill -s HUP #{pid}` - end - - # Force the (rails) application to restart by sending a +USR2+ signal to the - # process. - def restart(pid) - `kill -s USR2 #{pid}` - end - - # Forces the (rails) application to gracefully terminate by sending a - # +TERM+ signal to the process. - def graceful(pid) - `kill -s TERM #{pid}` - end - - # Forces the (rails) application to terminate immediately by sending a -9 - # signal to the process. - def kill(pid) - `kill -9 #{pid}` - end - - # Send a +USR1+ signal to the process. - def usr1(pid) - `kill -s USR1 #{pid}` - end - end - - def initialize(pid_path, pattern, keyword=nil) - @pid_path, @pattern, @keyword = pid_path, pattern, keyword - end - - def process(action) - pids = find_processes - - if pids.empty? - warn "Couldn't find any pid file in '#{@pid_path}' matching '#{@pattern}'" - warn "(also looked for processes matching #{@keyword.inspect})" if @keyword - else - pids.each do |pid| - puts "#{action.capitalize}ing #{pid}" - self.class.send(action, pid) - end - - delete_pid_files if terminating?(action) - end - end - - private - def terminating?(action) - [ "kill", "graceful" ].include?(action) - end - - def find_processes - files = pid_files - if files.empty? - find_processes_via_grep - else - files.collect { |pid_file| File.read(pid_file).to_i } - end - end - - def find_processes_via_grep - lines = `ps axww -o 'pid command' | grep #{@keyword}`.split(/\n/). - reject { |line| line =~ /inq|ps axww|grep|spawn-fcgi|spawner|reaper/ } - lines.map { |line| line[/^\s*(\d+)/, 1].to_i } - end - - def delete_pid_files - pid_files.each { |pid_file| File.delete(pid_file) } - end - - def pid_files - Dir.glob(@pid_path + "/" + @pattern) - end -end - - -OPTIONS = { - :action => "restart", - :pid_path => File.expand_path(RAILS_ROOT + '/tmp/pids'), - :pattern => "dispatch.[0-9]*.pid", - :dispatcher => File.expand_path("#{RAILS_ROOT}/public/dispatch.fcgi") -} - -ARGV.options do |opts| - opts.banner = "Usage: reaper [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - The reaper is used to restart, reload, gracefully exit, and forcefully exit processes - running a Rails Dispatcher (or any other process responding to the same signals). This - is commonly done when a new version of the application is available, so the existing - processes can be updated to use the latest code. - - It uses pid files to work on the processes and by default assume them to be located - in RAILS_ROOT/tmp/pids. - - The reaper actions are: - - * restart : Restarts the application by reloading both application and framework code - * reload : Only reloads the application, but not the framework (like the development environment) - * graceful: Marks all of the processes for exit after the next request - * kill : Forcefully exists all processes regardless of whether they're currently serving a request - - Restart is the most common and default action. - - Examples: - reaper # restarts the default dispatchers - reaper -a reload # reload the default dispatchers - reaper -a kill -r *.pid # kill all processes that keep pids in tmp/pids - EOF - - opts.on(" Options:") - - opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String) { |v| OPTIONS[:action] = v } - opts.on("-p", "--pidpath=path", "default: #{OPTIONS[:pid_path]}", String) { |v| OPTIONS[:pid_path] = v } - opts.on("-r", "--pattern=pattern", "default: #{OPTIONS[:pattern]}", String) { |v| OPTIONS[:pattern] = v } - opts.on("-d", "--dispatcher=path", "DEPRECATED. default: #{OPTIONS[:dispatcher]}", String) { |v| OPTIONS[:dispatcher] = v } - - opts.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -Killer.process(OPTIONS[:action], OPTIONS[:pid_path], OPTIONS[:pattern], OPTIONS[:dispatcher]) diff --git a/railties/lib/commands/process/spawner.rb b/railties/lib/commands/process/spawner.rb deleted file mode 100644 index 8bf47abb75..0000000000 --- a/railties/lib/commands/process/spawner.rb +++ /dev/null @@ -1,219 +0,0 @@ -require 'active_support' -require 'optparse' -require 'socket' -require 'fileutils' - -def daemonize #:nodoc: - exit if fork # Parent exits, child continues. - Process.setsid # Become session leader. - exit if fork # Zap session leader. See [1]. - Dir.chdir "/" # Release old working directory. - File.umask 0000 # Ensure sensible umask. Adjust as needed. - STDIN.reopen "/dev/null" # Free file descriptors and - STDOUT.reopen "/dev/null", "a" # point them somewhere sensible. - STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile. -end - -class Spawner - def self.record_pid(name = "#{OPTIONS[:process]}.spawner", id = Process.pid) - FileUtils.mkdir_p(OPTIONS[:pids]) - File.open(File.expand_path(OPTIONS[:pids] + "/#{name}.pid"), "w+") { |f| f.write(id) } - end - - def self.spawn_all - OPTIONS[:instances].times do |i| - port = OPTIONS[:port] + i - print "Checking if something is already running on #{OPTIONS[:address]}:#{port}..." - - begin - srv = TCPServer.new(OPTIONS[:address], port) - srv.close - srv = nil - - puts "NO" - puts "Starting dispatcher on port: #{OPTIONS[:address]}:#{port}" - - FileUtils.mkdir_p(OPTIONS[:pids]) - spawn(port) - rescue - puts "YES" - end - end - end -end - -class FcgiSpawner < Spawner - def self.spawn(port) - cmd = "#{OPTIONS[:spawner]} -f #{OPTIONS[:dispatcher]} -p #{port} -P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid" - cmd << " -a #{OPTIONS[:address]}" if can_bind_to_custom_address? - system(cmd) - end - - def self.can_bind_to_custom_address? - @@can_bind_to_custom_address ||= /^\s-a\s/.match `#{OPTIONS[:spawner]} -h` - end -end - -class MongrelSpawner < Spawner - def self.spawn(port) - cmd = - "mongrel_rails start -d " + - "-a #{OPTIONS[:address]} " + - "-p #{port} " + - "-P #{OPTIONS[:pids]}/#{OPTIONS[:process]}.#{port}.pid " + - "-e #{OPTIONS[:environment]} " + - "-c #{OPTIONS[:rails_root]} " + - "-l #{OPTIONS[:rails_root]}/log/mongrel.log" - - # Add prefix functionality to spawner's call to mongrel_rails - # Digging through mongrel's project subversion server, the earliest - # Tag that has prefix implemented in the bin/mongrel_rails file - # is 0.3.15 which also happens to be the earliest tag listed. - # References: http://mongrel.rubyforge.org/svn/tags - if Mongrel::Const::MONGREL_VERSION.to_f >=0.3 && !OPTIONS[:prefix].nil? - cmd = cmd + " --prefix #{OPTIONS[:prefix]}" - end - system(cmd) - end - - def self.can_bind_to_custom_address? - true - end -end - - -begin - require_library_or_gem 'fcgi' -rescue Exception - # FCGI not available -end - -begin - require_library_or_gem 'mongrel' -rescue Exception - # Mongrel not available -end - -server = case ARGV.first - when "fcgi", "mongrel" - ARGV.shift - else - if defined?(Mongrel) - "mongrel" - elsif RUBY_PLATFORM !~ /(:?mswin|mingw)/ && !silence_stderr { `spawn-fcgi -version` }.blank? && defined?(FCGI) - "fcgi" - end -end - -case server - when "fcgi" - puts "=> Starting FCGI dispatchers" - spawner_class = FcgiSpawner - when "mongrel" - puts "=> Starting mongrel dispatchers" - spawner_class = MongrelSpawner - else - puts "Neither FCGI (spawn-fcgi) nor Mongrel was installed and available!" - exit(0) -end - - - -OPTIONS = { - :environment => "production", - :spawner => '/usr/bin/env spawn-fcgi', - :dispatcher => File.expand_path(RELATIVE_RAILS_ROOT + '/public/dispatch.fcgi'), - :pids => File.expand_path(RELATIVE_RAILS_ROOT + "/tmp/pids"), - :rails_root => File.expand_path(RELATIVE_RAILS_ROOT), - :process => "dispatch", - :port => 8000, - :address => '0.0.0.0', - :instances => 3, - :repeat => nil, - :prefix => nil -} - -ARGV.options do |opts| - opts.banner = "Usage: spawner [platform] [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - The spawner is a wrapper for spawn-fcgi and mongrel that makes it - easier to start multiple processes running the Rails dispatcher. The - spawn-fcgi command is included with the lighttpd web server, but can - be used with both Apache and lighttpd (and any other web server - supporting externally managed FCGI processes). Mongrel automatically - ships with with mongrel_rails for starting dispatchers. - - The first choice you need to make is whether to spawn the Rails - dispatchers as FCGI or Mongrel. By default, this spawner will prefer - Mongrel, so if that's installed, and no platform choice is made, - Mongrel is used. - - Then decide a starting port (default is 8000) and the number of FCGI - process instances you'd like to run. So if you pick 9100 and 3 - instances, you'll start processes on 9100, 9101, and 9102. - - By setting the repeat option, you get a protection loop, which will - attempt to restart any FCGI processes that might have been exited or - outright crashed. - - You can select bind address for started processes. By default these - listen on every interface. For single machine installations you would - probably want to use 127.0.0.1, hiding them form the outside world. - - Examples: - spawner # starts instances on 8000, 8001, and 8002 - # using Mongrel if available. - spawner fcgi # starts instances on 8000, 8001, and 8002 - # using FCGI. - spawner mongrel -i 5 # starts instances on 8000, 8001, 8002, - # 8003, and 8004 using Mongrel. - spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to - # 9109 using Mongrel if available. - spawner -p 9100 -r 5 # starts 3 instances counting from 9100 to - # 9102 and attempts start them every 5 - # seconds. - spawner -a 127.0.0.1 # starts 3 instances binding to localhost - EOF - - opts.on(" Options:") - - opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |v| OPTIONS[:port] = v } - - if spawner_class.can_bind_to_custom_address? - opts.on("-a", "--address=ip", String, "Bind to IP address (default: #{OPTIONS[:address]})") { |v| OPTIONS[:address] = v } - end - - opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |v| OPTIONS[:port] = v } - opts.on("-i", "--instances=number", Integer, "Number of instances (default: #{OPTIONS[:instances]})") { |v| OPTIONS[:instances] = v } - opts.on("-r", "--repeat=seconds", Integer, "Repeat spawn attempts every n seconds (default: off)") { |v| OPTIONS[:repeat] = v } - opts.on("-e", "--environment=name", String, "test|development|production (default: #{OPTIONS[:environment]})") { |v| OPTIONS[:environment] = v } - opts.on("-P", "--prefix=path", String, "URL prefix for Rails app. [Used only with Mongrel > v0.3.15]: (default: #{OPTIONS[:prefix]})") { |v| OPTIONS[:prefix] = v } - opts.on("-n", "--process=name", String, "default: #{OPTIONS[:process]}") { |v| OPTIONS[:process] = v } - opts.on("-s", "--spawner=path", String, "default: #{OPTIONS[:spawner]}") { |v| OPTIONS[:spawner] = v } - opts.on("-d", "--dispatcher=path", String, "default: #{OPTIONS[:dispatcher]}") { |dispatcher| OPTIONS[:dispatcher] = File.expand_path(dispatcher) } - - opts.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -ENV["RAILS_ENV"] = OPTIONS[:environment] - -if OPTIONS[:repeat] - daemonize - trap("TERM") { exit } - spawner_class.record_pid - - loop do - spawner_class.spawn_all - sleep(OPTIONS[:repeat]) - end -else - spawner_class.spawn_all -end diff --git a/railties/lib/commands/process/spinner.rb b/railties/lib/commands/process/spinner.rb deleted file mode 100644 index c0b2f09a94..0000000000 --- a/railties/lib/commands/process/spinner.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'optparse' - -def daemonize #:nodoc: - exit if fork # Parent exits, child continues. - Process.setsid # Become session leader. - exit if fork # Zap session leader. See [1]. - Dir.chdir "/" # Release old working directory. - File.umask 0000 # Ensure sensible umask. Adjust as needed. - STDIN.reopen "/dev/null" # Free file descriptors and - STDOUT.reopen "/dev/null", "a" # point them somewhere sensible. - STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile. -end - -OPTIONS = { - :interval => 5.0, - :command => File.expand_path(RAILS_ROOT + '/script/process/spawner'), - :daemon => false -} - -ARGV.options do |opts| - opts.banner = "Usage: spinner [options]" - - opts.separator "" - - opts.on <<-EOF - Description: - The spinner is a protection loop for the spawner, which will attempt to restart any FCGI processes - that might have been exited or outright crashed. It's a brute-force attempt that'll just try - to run the spawner every X number of seconds, so it does pose a light load on the server. - - Examples: - spinner # attempts to run the spawner with default settings every second with output on the terminal - spinner -i 3 -d # only run the spawner every 3 seconds and detach from the terminal to become a daemon - spinner -c '/path/to/app/script/process/spawner -p 9000 -i 10' -d # using custom spawner - EOF - - opts.on(" Options:") - - opts.on("-c", "--command=path", String) { |v| OPTIONS[:command] = v } - opts.on("-i", "--interval=seconds", Float) { |v| OPTIONS[:interval] = v } - opts.on("-d", "--daemon") { |v| OPTIONS[:daemon] = v } - - opts.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -daemonize if OPTIONS[:daemon] - -trap(OPTIONS[:daemon] ? "TERM" : "INT") { exit } - -loop do - system(OPTIONS[:command]) - sleep(OPTIONS[:interval]) -end
\ No newline at end of file diff --git a/railties/lib/commands/runner.rb b/railties/lib/commands/runner.rb index 14159c3893..2411c3d270 100644 --- a/railties/lib/commands/runner.rb +++ b/railties/lib/commands/runner.rb @@ -38,11 +38,15 @@ RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV) require RAILS_ROOT + '/config/environment' -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) +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 + RAILS_DEFAULT_LOGGER.flush if RAILS_DEFAULT_LOGGER end diff --git a/railties/lib/commands/server.rb b/railties/lib/commands/server.rb index 15f417b5be..7057fcc33f 100644 --- a/railties/lib/commands/server.rb +++ b/railties/lib/commands/server.rb @@ -1,49 +1,103 @@ require 'active_support' +require 'action_controller' + require 'fileutils' +require 'optparse' +# TODO: Push Thin adapter upstream so we don't need worry about requiring it begin - require_library_or_gem 'fcgi' + require_library_or_gem 'thin' rescue Exception - # FCGI not available + # Thin not available end -begin - require_library_or_gem 'mongrel' -rescue Exception - # Mongrel not available +options = { + :Port => 3000, + :Host => "0.0.0.0", + :environment => (ENV['RAILS_ENV'] || "development").dup, + :config => RAILS_ROOT + "/config.ru", + :detach => false, + :debugger => false +} + +ARGV.clone.options do |opts| + 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[: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: development") { |v| options[:environment] = v } + + opts.separator "" + + opts.on("-h", "--help", "Show this help message.") { puts opts; exit } + + opts.parse! end -begin - require_library_or_gem 'thin' -rescue Exception - # Thin not available +server = Rack::Handler.get(ARGV.first) rescue nil +unless server + begin + server = Rack::Handler::Mongrel + rescue LoadError => e + server = Rack::Handler::WEBrick + end end -server = case ARGV.first - when "lighttpd", "mongrel", "new_mongrel", "webrick", "thin" - ARGV.shift - else - if defined?(Mongrel) - "mongrel" - elsif defined?(Thin) - "thin" - elsif RUBY_PLATFORM !~ /(:?mswin|mingw)/ && !silence_stderr { `lighttpd -version` }.blank? && defined?(FCGI) - "lighttpd" - else - "webrick" - end +puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" +puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}" + +%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 -case server - when "webrick" - puts "=> Booting WEBrick..." - when "lighttpd" - puts "=> Booting lighttpd (use 'script/server webrick' to force WEBrick)" - when "mongrel", "new_mongrel" - puts "=> Booting Mongrel (use 'script/server webrick' to force WEBrick)" - when "thin" - puts "=> Booting Thin (use 'script/server webrick' to force WEBrick)" +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 -%w(cache pids sessions sockets).each { |dir_to_make| FileUtils.mkdir_p(File.join(RAILS_ROOT, 'tmp', dir_to_make)) } -require "commands/servers/#{server}" +app = Rack::Builder.new { + use Rails::Rack::Logger + use Rails::Rack::Static + use Rails::Rack::Debugger if options[:debugger] + run inner_app +}.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/commands/servers/base.rb b/railties/lib/commands/servers/base.rb deleted file mode 100644 index 23be169a8d..0000000000 --- a/railties/lib/commands/servers/base.rb +++ /dev/null @@ -1,31 +0,0 @@ -def tail(log_file) - cursor = File.size(log_file) - last_checked = Time.now - tail_thread = Thread.new do - File.open(log_file, 'r') do |f| - loop do - f.seek cursor - if f.mtime > last_checked - last_checked = f.mtime - contents = f.read - cursor += contents.length - print contents - end - sleep 1 - end - end - end - tail_thread -end - -def start_debugger - begin - require_library_or_gem 'ruby-debug' - Debugger.start - Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings) - puts "=> Debugger enabled" - rescue Exception - puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'" - exit - end -end
\ No newline at end of file diff --git a/railties/lib/commands/servers/lighttpd.rb b/railties/lib/commands/servers/lighttpd.rb deleted file mode 100644 index c9d13e86f3..0000000000 --- a/railties/lib/commands/servers/lighttpd.rb +++ /dev/null @@ -1,94 +0,0 @@ -require 'rbconfig' -require 'commands/servers/base' - -unless RUBY_PLATFORM !~ /mswin/ && !silence_stderr { `lighttpd -version` }.blank? - puts "PROBLEM: Lighttpd is not available on your system (or not in your path)" - exit 1 -end - -unless defined?(FCGI) - puts "PROBLEM: Lighttpd requires that the FCGI Ruby bindings are installed on the system" - exit 1 -end - -require 'initializer' -configuration = Rails::Initializer.run(:initialize_logger).configuration -default_config_file = config_file = Pathname.new("#{RAILS_ROOT}/config/lighttpd.conf").cleanpath - -require 'optparse' - -detach = false -command_line_port = nil - -ARGV.options do |opt| - opt.on("-p", "--port=port", "Changes the server.port number in the config/lighttpd.conf") { |port| command_line_port = port } - opt.on('-c', "--config=#{config_file}", 'Specify a different lighttpd config file.') { |path| config_file = path } - opt.on('-h', '--help', 'Show this message.') { puts opt; exit 0 } - opt.on('-d', '-d', 'Call with -d to detach') { detach = true; puts "=> Configuration in config/lighttpd.conf" } - opt.parse! -end - -unless File.exist?(config_file) - if config_file != default_config_file - puts "=> #{config_file} not found." - exit 1 - end - - require 'fileutils' - - source = File.expand_path(File.join(File.dirname(__FILE__), - "..", "..", "..", "configs", "lighttpd.conf")) - puts "=> #{config_file} not found, copying from #{source}" - - FileUtils.cp(source, config_file) -end - -# open the config/lighttpd.conf file and add the current user defined port setting to it -if command_line_port - File.open(config_file, 'r+') do |config| - lines = config.readlines - - lines.each do |line| - line.gsub!(/^\s*server.port\s*=\s*(\d+)/, "server.port = #{command_line_port}") - end - - config.rewind - config.print(lines) - config.truncate(config.pos) - end -end - -config = IO.read(config_file) -default_port, default_ip = 3000, '0.0.0.0' -port = config.scan(/^\s*server.port\s*=\s*(\d+)/).first rescue default_port -ip = config.scan(/^\s*server.bind\s*=\s*"([^"]+)"/).first rescue default_ip -puts "=> Rails #{Rails.version} application starting on http://#{ip || default_ip}:#{port || default_port}" - -tail_thread = nil - -if !detach - puts "=> Call with -d to detach" - puts "=> Ctrl-C to shutdown server (see config/lighttpd.conf for options)" - detach = false - tail_thread = tail(configuration.log_path) -end - -trap(:INT) { exit } - -begin - `rake tmp:sockets:clear` # Needed if lighttpd crashes or otherwise leaves FCGI sockets around - `lighttpd #{!detach ? "-D " : ""}-f #{config_file}` -ensure - unless detach - tail_thread.kill if tail_thread - puts 'Exiting' - - # Ensure FCGI processes are reaped - silence_stream(STDOUT) do - ARGV.replace ['-a', 'kill'] - require 'commands/process/reaper' - end - - `rake tmp:sockets:clear` # Remove sockets on clean shutdown - end -end diff --git a/railties/lib/commands/servers/mongrel.rb b/railties/lib/commands/servers/mongrel.rb deleted file mode 100644 index 7bb110f63a..0000000000 --- a/railties/lib/commands/servers/mongrel.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'rbconfig' -require 'commands/servers/base' - -unless defined?(Mongrel) - puts "PROBLEM: Mongrel is not available on your system (or not in your path)" - exit 1 -end - -require 'optparse' - -OPTIONS = { - :port => 3000, - :ip => "0.0.0.0", - :environment => (ENV['RAILS_ENV'] || "development").dup, - :detach => false, - :debugger => false -} - -ARGV.clone.options do |opts| - 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[:ip] = 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: development") { |v| OPTIONS[:environment] = v } - - opts.separator "" - - opts.on("-h", "--help", "Show this help message.") { puts opts; exit } - - opts.parse! -end - -puts "=> Rails #{Rails.version} application starting on http://#{OPTIONS[:ip]}:#{OPTIONS[:port]}" - -parameters = [ - "start", - "-p", OPTIONS[:port].to_s, - "-a", OPTIONS[:ip].to_s, - "-e", OPTIONS[:environment], - "-P", "#{RAILS_ROOT}/tmp/pids/mongrel.pid" -] - -if OPTIONS[:detach] - `mongrel_rails #{parameters.join(" ")} -d` -else - ENV["RAILS_ENV"] = OPTIONS[:environment] - RAILS_ENV.replace(OPTIONS[:environment]) if defined?(RAILS_ENV) - - start_debugger if OPTIONS[:debugger] - - puts "=> Call with -d to detach" - puts "=> Ctrl-C to shutdown server" - - log = Pathname.new("#{File.expand_path(RAILS_ROOT)}/log/#{RAILS_ENV}.log").cleanpath - open(log, (File::WRONLY | File::APPEND | File::CREAT)) unless File.exist? log - tail_thread = tail(log) - - trap(:INT) { exit } - - begin - silence_warnings { ARGV = parameters } - load("mongrel_rails") - ensure - tail_thread.kill if tail_thread - puts 'Exiting' - end -end
\ No newline at end of file diff --git a/railties/lib/commands/servers/new_mongrel.rb b/railties/lib/commands/servers/new_mongrel.rb deleted file mode 100644 index 174dbf8a37..0000000000 --- a/railties/lib/commands/servers/new_mongrel.rb +++ /dev/null @@ -1,16 +0,0 @@ -unless defined?(Mongrel) - abort "PROBLEM: Mongrel is not available on your system (or not in your path)" -end - -require 'rails/mongrel_server/commands' - -GemPlugin::Manager.instance.load "rails::mongrel" => GemPlugin::INCLUDE, "rails" => GemPlugin::EXCLUDE - -case ARGV[0] ||= 'start' -when 'start', 'stop', 'restart' - ARGV[0] = "rails::mongrelserver::#{ARGV[0]}" -end - -if not Mongrel::Command::Registry.instance.run ARGV - exit 1 -end diff --git a/railties/lib/commands/servers/thin.rb b/railties/lib/commands/servers/thin.rb deleted file mode 100644 index 833469cab1..0000000000 --- a/railties/lib/commands/servers/thin.rb +++ /dev/null @@ -1,25 +0,0 @@ -require 'rbconfig' -require 'commands/servers/base' -require 'thin' - - -options = ARGV.clone -options.insert(0,'start') unless Thin::Runner.commands.include?(options[0]) - -thin = Thin::Runner.new(options) - -puts "=> Rails #{Rails.version} application starting on http://#{thin.options[:address]}:#{thin.options[:port]}" -puts "=> Ctrl-C to shutdown server" - -log = Pathname.new("#{File.expand_path(RAILS_ROOT)}/log/#{RAILS_ENV}.log").cleanpath -open(log, (File::WRONLY | File::APPEND | File::CREAT)) unless File.exist? log -tail_thread = tail(log) -trap(:INT) { exit } - -begin - thin.run! -ensure - tail_thread.kill if tail_thread - puts 'Exiting' -end - diff --git a/railties/lib/commands/servers/webrick.rb b/railties/lib/commands/servers/webrick.rb deleted file mode 100644 index 18c8897cc8..0000000000 --- a/railties/lib/commands/servers/webrick.rb +++ /dev/null @@ -1,66 +0,0 @@ -require 'webrick' -require 'optparse' -require 'commands/servers/base' - -OPTIONS = { - :port => 3000, - :ip => "0.0.0.0", - :environment => (ENV['RAILS_ENV'] || "development").dup, - :server_root => File.expand_path(RAILS_ROOT + "/public/"), - :server_type => WEBrick::SimpleServer, - :charset => "UTF-8", - :mime_types => WEBrick::HTTPUtils::DefaultMimeTypes, - :debugger => false - -} - -ARGV.options do |opts| - script_name = File.basename($0) - opts.banner = "Usage: ruby #{script_name} [options]" - - opts.separator "" - - 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[:ip] = v } - 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("-m", "--mime-types=filename", String, - "Specifies an Apache style mime.types configuration file to be used for mime types", - "Default: none") { |mime_types_file| OPTIONS[:mime_types] = WEBrick::HTTPUtils::load_mime_types(mime_types_file) } - - opts.on("-d", "--daemon", - "Make Rails run as a Daemon (only works if fork is available -- meaning on *nix)." - ) { OPTIONS[:server_type] = WEBrick::Daemon } - - opts.on("-u", "--debugger", "Enable ruby-debugging for the server.") { OPTIONS[:debugger] = true } - - opts.on("-c", "--charset=charset", String, - "Set default charset for output.", - "Default: UTF-8") { |v| OPTIONS[:charset] = v } - - opts.separator "" - - opts.on("-h", "--help", - "Show this help message.") { puts opts; exit } - - opts.parse! -end - -start_debugger if OPTIONS[:debugger] - -ENV["RAILS_ENV"] = OPTIONS[:environment] -RAILS_ENV.replace(OPTIONS[:environment]) if defined?(RAILS_ENV) - -require RAILS_ROOT + "/config/environment" -require 'webrick_server' - -OPTIONS['working_directory'] = File.expand_path(RAILS_ROOT) - -puts "=> Rails #{Rails.version} application started on http://#{OPTIONS[:ip]}:#{OPTIONS[:port]}" -puts "=> Ctrl-C to shutdown server; call with --help for options" if OPTIONS[:server_type] == WEBrick::SimpleServer -DispatchServlet.dispatch(OPTIONS) diff --git a/railties/lib/console_app.rb b/railties/lib/console_app.rb index 88e7962b43..96bf3117c8 100644 --- a/railties/lib/console_app.rb +++ b/railties/lib/console_app.rb @@ -1,4 +1,5 @@ -require 'action_controller/integration' +require 'active_support/test_case' +require 'action_controller' # work around the at_exit hook in test/unit, which kills IRB Test::Unit.run = true if Test::Unit.respond_to?(:run=) diff --git a/railties/lib/console_with_helpers.rb b/railties/lib/console_with_helpers.rb index be453a6896..039db667c4 100644 --- a/railties/lib/console_with_helpers.rb +++ b/railties/lib/console_with_helpers.rb @@ -1,26 +1,5 @@ -class Module - def include_all_modules_from(parent_module) - parent_module.constants.each do |const| - mod = parent_module.const_get(const) - if mod.class == Module - send(:include, mod) - include_all_modules_from(mod) - end - end - end -end - -def helper(*helper_names) - returning @helper_proxy ||= Object.new do |helper| - helper_names.each { |h| helper.extend "#{h}_helper".classify.constantize } - end -end - -require_dependency 'application' - -class << helper - include_all_modules_from ActionView +def helper + @helper ||= ApplicationController.helpers end @controller = ApplicationController.new -helper :application rescue nil diff --git a/railties/lib/fcgi_handler.rb b/railties/lib/fcgi_handler.rb index 1bb55b9275..1256ef2286 100644 --- a/railties/lib/fcgi_handler.rb +++ b/railties/lib/fcgi_handler.rb @@ -98,7 +98,7 @@ class RailsFCGIHandler with_signal_handler 'USR1' do begin - Dispatcher.dispatch(cgi) + ::Rack::Handler::FastCGI.serve(cgi, Dispatcher.new) rescue SignalException, SystemExit raise rescue Exception => error diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index e3a0e3bad1..4bb1e480b7 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -39,20 +39,21 @@ module Rails nil end end + + def backtrace_cleaner + @@backtrace_cleaner ||= begin + # Relies on ActiveSupport, so we have to lazy load to postpone definition until AS has been loaded + require 'rails/backtrace_cleaner' + Rails::BacktraceCleaner.new + end + end def root - if defined?(RAILS_ROOT) - RAILS_ROOT - else - nil - end + Pathname.new(RAILS_ROOT) if defined?(RAILS_ROOT) end def env - @_env ||= begin - require 'active_support/string_inquirer' - ActiveSupport::StringInquirer.new(RAILS_ENV) - end + @_env ||= ActiveSupport::StringInquirer.new(RAILS_ENV) end def cache @@ -131,6 +132,7 @@ module Rails add_gem_load_paths require_frameworks + preload_frameworks set_autoload_paths add_plugin_load_paths load_environment @@ -147,7 +149,10 @@ module Rails initialize_dependency_mechanism initialize_whiny_nils initialize_temporary_session_directory + initialize_time_zone + initialize_i18n + initialize_framework_settings initialize_framework_views @@ -252,10 +257,23 @@ module Rails def require_frameworks configuration.frameworks.each { |framework| require(framework.to_s) } rescue LoadError => e - # re-raise because Mongrel would swallow it + # Re-raise as RuntimeError because Mongrel would swallow LoadError. raise e.to_s end + # Preload all frameworks specified by the Configuration#frameworks. + # Used by Passenger to ensure everything's loaded before forking and + # to avoid autoload race conditions in JRuby. + def preload_frameworks + if configuration.preload_frameworks + configuration.frameworks.each do |framework| + # String#classify and #constantize aren't available yet. + toplevel = Object.const_get(framework.to_s.gsub(/(?:^|_)(.)/) { $1.upcase }) + toplevel.load_all! + end + end + end + # Add the load paths used by support functions such as the info controller def add_support_load_paths end @@ -350,9 +368,10 @@ Run `rake gems:install` to install the missing gems. def load_view_paths if configuration.frameworks.include?(:action_view) - ActionView::PathSet::Path.eager_load_templates! if configuration.cache_classes - ActionController::Base.view_paths.load if configuration.frameworks.include?(:action_controller) - ActionMailer::Base.template_root.load if configuration.frameworks.include?(:action_mailer) + if configuration.cache_classes + ActionController::Base.view_paths.load if configuration.frameworks.include?(:action_controller) + ActionMailer::Base.template_root.load if configuration.frameworks.include?(:action_mailer) + end end end @@ -464,8 +483,9 @@ Run `rake gems:install` to install the missing gems. # loading module used to lazily load controllers (Configuration#controller_paths). def initialize_routing return unless configuration.frameworks.include?(:action_controller) - ActionController::Routing.controller_paths = configuration.controller_paths - ActionController::Routing::Routes.configuration_file = configuration.routes_configuration_file + + ActionController::Routing.controller_paths += configuration.controller_paths + ActionController::Routing::Routes.add_configuration_file(configuration.routes_configuration_file) ActionController::Routing::Routes.reload end @@ -493,10 +513,15 @@ Run `rake gems:install` to install the missing gems. def initialize_time_zone if configuration.time_zone zone_default = Time.__send__(:get_zone, configuration.time_zone) + unless zone_default - raise %{Value assigned to config.time_zone not recognized. Run "rake -D time" for a list of tasks for finding appropriate time zone names.} + raise \ + 'Value assigned to config.time_zone not recognized.' + + 'Run "rake -D time" for a list of tasks for finding appropriate time zone names.' end + Time.zone_default = zone_default + if configuration.frameworks.include?(:active_record) ActiveRecord::Base.time_zone_aware_attributes = true ActiveRecord::Base.default_timezone = :utc @@ -504,6 +529,18 @@ Run `rake gems:install` to install the missing gems. end end + # Set the i18n configuration from config.i18n but special-case for the load_path which should be + # appended to what's already set instead of overwritten. + def initialize_i18n + configuration.i18n.each do |setting, value| + if setting == :load_path + I18n.load_path += value + else + I18n.send("#{setting}=", value) + end + end + end + # Initializes framework-specific settings for each of the loaded frameworks # (Configuration#frameworks). The available settings map to the accessors # on each of the corresponding Base classes. @@ -532,7 +569,7 @@ Run `rake gems:install` to install the missing gems. def load_application_initializers if gems_dependencies_loaded Dir["#{configuration.root_path}/config/initializers/**/*.rb"].sort.each do |initializer| - load(initializer) + load initializer.sub(/^#{Regexp.escape(configuration.root_path)}\//, '') end end end @@ -582,12 +619,15 @@ Run `rake gems:install` to install the missing gems. # A stub for setting options on ActiveSupport. attr_accessor :active_support + # Whether to preload all frameworks at startup. + attr_accessor :preload_frameworks + # Whether or not classes should be cached (set to false if you want # application classes to be reloaded on each request) attr_accessor :cache_classes # The list of paths that should be searched for controllers. (Defaults - # to <tt>app/controllers</tt> and <tt>components</tt>.) + # to <tt>app/controllers</tt>.) attr_accessor :controller_paths # The path to the database configuration file to use. (Defaults to @@ -732,6 +772,9 @@ Run `rake gems:install` to install the missing gems. # timezone to <tt>:utc</tt>. attr_accessor :time_zone + # Accessor for i18n settings. + attr_accessor :i18n + # Create a new Configuration instance, initialized with the default # values. def initialize @@ -745,6 +788,7 @@ Run `rake gems:install` to install the missing gems. self.log_level = default_log_level self.view_path = default_view_path self.controller_paths = default_controller_paths + self.preload_frameworks = default_preload_frameworks self.cache_classes = default_cache_classes self.dependency_loading = default_dependency_loading self.whiny_nils = default_whiny_nils @@ -755,6 +799,7 @@ Run `rake gems:install` to install the missing gems. self.database_configuration_file = default_database_configuration_file self.routes_configuration_file = default_routes_configuration_file self.gems = default_gems + self.i18n = default_i18n for framework in default_frameworks self.send("#{framework}=", Rails::OrderedOptions.new) @@ -786,6 +831,7 @@ Run `rake gems:install` to install the missing gems. # multiple database connections. Also disables automatic dependency loading # after boot def threadsafe! + self.preload_frameworks = true self.cache_classes = true self.dependency_loading = false self.action_controller.allow_concurrency = true @@ -835,6 +881,11 @@ Run `rake gems:install` to install the missing gems. end end + def middleware + require 'action_controller' + ActionController::Dispatcher.middleware + end + def builtin_directories # Include builtins only in the development environment. (environment == 'development') ? Dir["#{RAILTIES_PATH}/builtin/*/"] : [] @@ -842,10 +893,10 @@ Run `rake gems:install` to install the missing gems. def framework_paths paths = %w(railties railties/lib activesupport/lib) - paths << 'actionpack/lib' if frameworks.include? :action_controller or frameworks.include? :action_view + paths << 'actionpack/lib' if frameworks.include?(:action_controller) || frameworks.include?(:action_view) [:active_record, :action_mailer, :active_resource, :action_web_service].each do |framework| - paths << "#{framework.to_s.gsub('_', '')}/lib" if frameworks.include? framework + paths << "#{framework.to_s.gsub('_', '')}/lib" if frameworks.include?(framework) end paths.map { |dir| "#{framework_root_path}/#{dir}" }.select { |dir| File.directory?(dir) } @@ -869,9 +920,6 @@ Run `rake gems:install` to install the missing gems. # Add the app's controller directory paths.concat(Dir["#{root_path}/app/controllers/"]) - # Then components subdirectories. - paths.concat(Dir["#{root_path}/components/[_a-z]*"]) - # Followed by the standard includes. paths.concat %w( app @@ -879,9 +927,9 @@ Run `rake gems:install` to install the missing gems. app/controllers app/helpers app/services - components config lib + vendor ).map { |dir| "#{root_path}/#{dir}" }.select { |dir| File.directory?(dir) } paths.concat builtin_directories @@ -930,6 +978,10 @@ Run `rake gems:install` to install the missing gems. true end + def default_preload_frameworks + false + end + def default_cache_classes true end @@ -967,6 +1019,18 @@ Run `rake gems:install` to install the missing gems. def default_gems [] end + + def default_i18n + i18n = Rails::OrderedOptions.new + i18n.load_path = [] + + if File.exist?(File.join(RAILS_ROOT, 'config', 'locales')) + i18n.load_path << Dir[File.join(RAILS_ROOT, 'config', 'locales', '*.{rb,yml}')] + i18n.load_path.flatten! + end + + i18n + end end end diff --git a/railties/lib/rails/backtrace_cleaner.rb b/railties/lib/rails/backtrace_cleaner.rb new file mode 100644 index 0000000000..d8626aaf14 --- /dev/null +++ b/railties/lib/rails/backtrace_cleaner.rb @@ -0,0 +1,33 @@ +module Rails + class BacktraceCleaner < ActiveSupport::BacktraceCleaner + ERB_METHOD_SIG = /:in `_run_erb_.*/ + + VENDOR_DIRS = %w( vendor/plugins vendor/gems vendor/rails ) + SERVER_DIRS = %w( lib/mongrel bin/mongrel lib/rack ) + RAILS_NOISE = %w( script/server ) + RUBY_NOISE = %w( rubygems/custom_require benchmark.rb ) + + ALL_NOISE = VENDOR_DIRS + SERVER_DIRS + RAILS_NOISE + RUBY_NOISE + + def initialize + super + add_filter { |line| line.sub(RAILS_ROOT, '') } + add_filter { |line| line.sub(ERB_METHOD_SIG, '') } + add_filter { |line| line.sub('./', '/') } # for tests + add_silencer { |line| ALL_NOISE.any? { |dir| line.include?(dir) } } + end + end + + # For installing the BacktraceCleaner in the test/unit + module BacktraceFilterForTestUnit #:nodoc: + def self.included(klass) + klass.send :alias_method_chain, :filter_backtrace, :cleaning + end + + def filter_backtrace_with_cleaning(backtrace, prefix=nil) + backtrace = filter_backtrace_without_cleaning(backtrace, prefix) + backtrace = backtrace.first.split("\n") if backtrace.size == 1 + Rails.backtrace_cleaner.clean(backtrace) + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb index cd280ac023..5a07841be8 100644 --- a/railties/lib/rails/gem_dependency.rb +++ b/railties/lib/rails/gem_dependency.rb @@ -74,6 +74,7 @@ module Rails def dependencies return [] if framework_gem? + return [] if specification.nil? all_dependencies = specification.dependencies.map do |dependency| GemDependency.new(dependency.name, :requirement => dependency.version_requirements) end diff --git a/railties/lib/rails/mongrel_server/commands.rb b/railties/lib/rails/mongrel_server/commands.rb deleted file mode 100644 index d29b18712f..0000000000 --- a/railties/lib/rails/mongrel_server/commands.rb +++ /dev/null @@ -1,342 +0,0 @@ -# Copyright (c) 2005 Zed A. Shaw -# You can redistribute it and/or modify it under the same terms as Ruby. -# -# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html -# for more information. - -require 'optparse' -require 'yaml' -require 'etc' - -require 'mongrel' -require 'rails/mongrel_server/handler' - -module Rails - module MongrelServer - def self.send_signal(signal, pid_file) - pid = open(pid_file).read.to_i - print "Sending #{signal} to Mongrel at PID #{pid}..." - begin - Process.kill(signal, pid) - rescue Errno::ESRCH - puts "Process does not exist. Not running." - end - - puts "Done." - end - - class RailsConfigurator < Mongrel::Configurator - def setup_mime_types - mime = {} - - if defaults[:mime_map] - Mongrel.log("Loading additional MIME types from #{defaults[:mime_map]}") - mime = load_mime_map(defaults[:mime_map], mime) - end - - mime.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) } - end - - def mount_rails(prefix) - ENV['RAILS_ENV'] = defaults[:environment] - ::RAILS_ENV.replace(defaults[:environment]) if defined?(::RAILS_ENV) - - env_location = "#{defaults[:cwd]}/config/environment" - require env_location - - ActionController::Base.relative_url_root = defaults[:prefix] - uri prefix, :handler => Rails::MongrelServer::RailsHandler.new - end - end - - class Start < GemPlugin::Plugin "/commands" - include Mongrel::Command::Base - - def configure - options [ - ["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"], - ["-d", "--daemonize", "Run daemonized in the background", :@daemon, false], - ['-p', '--port PORT', "Which port to bind to", :@port, 3000], - ['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"], - ['-l', '--log FILE', "Where to write log messages", :@log_file, "log/mongrel.log"], - ['-P', '--pid FILE', "Where to write the PID", :@pid_file, "tmp/pids/mongrel.pid"], - ['-n', '--num-procs INT', "Number of processors active before clients denied", :@num_procs, 1024], - ['-o', '--timeout TIME', "Time to wait (in seconds) before killing a stalled thread", :@timeout, 60], - ['-t', '--throttle TIME', "Time to pause (in hundredths of a second) between accepting clients", :@throttle, 0], - ['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil], - ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, RAILS_ROOT], - ['-r', '--root PATH', "Set the document root (default 'public')", :@docroot, "public"], - ['-B', '--debug', "Enable debugging mode", :@debug, false], - ['-C', '--config PATH', "Use a config file", :@config_file, nil], - ['-S', '--script PATH', "Load the given file as an extra config script", :@config_script, nil], - ['-G', '--generate PATH', "Generate a config file for use with -C", :@generate, nil], - ['', '--user USER', "User to run as", :@user, nil], - ['', '--group GROUP', "Group to run as", :@group, nil], - ['', '--prefix PATH', "URL prefix for Rails app", :@prefix, nil], - - ['-b', '--binding ADDR', "Address to bind to (deprecated, use -a)", :@address, "0.0.0.0"], - ['-u', '--debugger', "Enable debugging mode (deprecated, use -B)", :@debug, false] - ] - end - - def validate - if @config_file - valid_exists?(@config_file, "Config file not there: #@config_file") - return false unless @valid - @config_file = File.expand_path(@config_file) - load_config - return false unless @valid - end - - @cwd = File.expand_path(@cwd) - valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd" - - # Change there to start, then we'll have to come back after daemonize - Dir.chdir(@cwd) - - valid?(@prefix[0] == ?/ && @prefix[-1] != ?/, "Prefix must begin with / and not end in /") if @prefix - valid_dir? File.dirname(@log_file), "Path to log file not valid: #@log_file" - valid_dir? File.dirname(@pid_file), "Path to pid file not valid: #@pid_file" - valid_dir? @docroot, "Path to docroot not valid: #@docroot" - valid_exists? @mime_map, "MIME mapping file does not exist: #@mime_map" if @mime_map - valid_exists? @config_file, "Config file not there: #@config_file" if @config_file - valid_dir? File.dirname(@generate), "Problem accessing directory to #@generate" if @generate - valid_user? @user if @user - valid_group? @group if @group - - return @valid - end - - def run - if @generate - @generate = File.expand_path(@generate) - Mongrel.log(:error, "** Writing config to \"#@generate\".") - open(@generate, "w") {|f| f.write(settings.to_yaml) } - Mongrel.log(:error, "** Finished. Run \"mongrel_rails start -C #@generate\" to use the config file.") - exit 0 - end - - config = RailsConfigurator.new(settings) do - defaults[:log] = $stdout if defaults[:environment] == 'development' - - Mongrel.log("=> Rails #{Rails.version} application starting on http://#{defaults[:host]}:#{defaults[:port]}") - - unless defaults[:daemon] - Mongrel.log("=> Call with -d to detach") - Mongrel.log("=> Ctrl-C to shutdown server") - start_debugger if defaults[:debug] - end - - if defaults[:daemon] - if File.exist? defaults[:pid_file] - Mongrel.log(:error, "!!! PID file #{defaults[:pid_file]} already exists. Mongrel could be running already. Check your #{defaults[:log_file]} for errors.") - Mongrel.log(:error, "!!! Exiting with error. You must stop mongrel and clear the .pid before I'll attempt a start.") - exit 1 - end - - daemonize - - Mongrel.log("Daemonized, any open files are closed. Look at #{defaults[:pid_file]} and #{defaults[:log_file]} for info.") - Mongrel.log("Settings loaded from #{@config_file} (they override command line).") if @config_file - end - - Mongrel.log("Starting Mongrel listening at #{defaults[:host]}:#{defaults[:port]}, further information can be found in log/mongrel-#{defaults[:host]}-#{defaults[:port]}.log") - - listener do - prefix = defaults[:prefix] || '/' - - if defaults[:debug] - Mongrel.log("Installing debugging prefixed filters. Look in log/mongrel_debug for the files.") - debug(prefix) - end - - setup_mime_types - dir_handler = Mongrel::DirHandler.new(defaults[:docroot], false) - dir_handler.passthrough_missing_files = true - - unless defaults[:environment] == 'production' - Mongrel.log("Mounting DirHandler at #{prefix}...") - uri prefix, :handler => dir_handler - end - - - Mongrel.log("Starting Rails with #{defaults[:environment]} environment...") - Mongrel.log("Mounting Rails at #{prefix}...") - mount_rails(prefix) - Mongrel.log("Rails loaded.") - - - Mongrel.log("Loading any Rails specific GemPlugins" ) - load_plugins - - if defaults[:config_script] - Mongrel.log("Loading #{defaults[:config_script]} external config script") - run_config(defaults[:config_script]) - end - - setup_signals - end - end - - config.run - Mongrel.log("Mongrel #{Mongrel::Const::MONGREL_VERSION} available at #{@address}:#{@port}") - - if config.defaults[:daemon] - config.write_pid_file - else - Mongrel.log("Use CTRL-C to stop.") - tail "log/#{config.defaults[:environment]}.log" - end - - config.join - - if config.needs_restart - unless RUBY_PLATFORM =~ /djgpp|(cyg|ms|bcc)win|mingw/ - cmd = "ruby #{__FILE__} start #{original_args.join(' ')}" - Mongrel.log("Restarting with arguments: #{cmd}") - config.stop(false, true) - config.remove_pid_file - - if config.defaults[:daemon] - system cmd - else - Mongrel.log(:error, "Can't restart unless in daemon mode.") - exit 1 - end - else - Mongrel.log("Win32 does not support restarts. Exiting.") - end - end - end - - def load_config - settings = {} - begin - settings = YAML.load_file(@config_file) - ensure - Mongrel.log(:error, "** Loading settings from #{@config_file} (they override command line).") unless @daemon || settings[:daemon] - end - - settings[:includes] ||= ["mongrel"] - - # Config file settings will override command line settings - settings.each do |key, value| - key = key.to_s - if config_keys.include?(key) - key = 'address' if key == 'host' - self.instance_variable_set("@#{key}", value) - else - failure "Unknown configuration setting: #{key}" - @valid = false - end - end - end - - def config_keys - @config_keys ||= - %w(address host port cwd log_file pid_file environment docroot mime_map daemon debug includes config_script num_processors timeout throttle user group prefix) - end - - def settings - config_keys.inject({}) do |hash, key| - value = self.instance_variable_get("@#{key}") - key = 'host' if key == 'address' - hash[key.to_sym] ||= value - hash - end - end - - def start_debugger - require_library_or_gem 'ruby-debug' - Debugger.start - Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings) - Mongrel.log("=> Debugger enabled") - rescue Exception - Mongrel.log(:error, "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'") - exit - end - - def tail(log_file) - cursor = File.size(log_file) - last_checked = Time.now - tail_thread = Thread.new do - File.open(log_file, 'r') do |f| - loop do - f.seek cursor - if f.mtime > last_checked - last_checked = f.mtime - contents = f.read - cursor += contents.length - print contents - end - sleep 1 - end - end - end - tail_thread - end - end - - class Stop < GemPlugin::Plugin "/commands" - include Mongrel::Command::Base - - def configure - options [ - ['-c', '--chdir PATH', "Change to dir before starting (will be expanded).", :@cwd, "."], - ['-f', '--force', "Force the shutdown (kill -9).", :@force, false], - ['-w', '--wait SECONDS', "Wait SECONDS before forcing shutdown", :@wait, "0"], - ['-P', '--pid FILE', "Where the PID file is located.", :@pid_file, "log/mongrel.pid"] - ] - end - - def validate - @cwd = File.expand_path(@cwd) - valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd" - - Dir.chdir @cwd - - valid_exists? @pid_file, "PID file #@pid_file does not exist. Not running?" - return @valid - end - - def run - if @force - @wait.to_i.times do |waiting| - exit(0) if not File.exist? @pid_file - sleep 1 - end - - Mongrel::send_signal("KILL", @pid_file) if File.exist? @pid_file - else - Mongrel::send_signal("TERM", @pid_file) - end - end - end - - - class Restart < GemPlugin::Plugin "/commands" - include Mongrel::Command::Base - - def configure - options [ - ['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, '.'], - ['-P', '--pid FILE', "Where the PID file is located", :@pid_file, "log/mongrel.pid"] - ] - end - - def validate - @cwd = File.expand_path(@cwd) - valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd" - - Dir.chdir @cwd - - valid_exists? @pid_file, "PID file #@pid_file does not exist. Not running?" - return @valid - end - - def run - MongrelServer::send_signal("USR2", @pid_file) - end - end - end -end diff --git a/railties/lib/rails/mongrel_server/handler.rb b/railties/lib/rails/mongrel_server/handler.rb deleted file mode 100644 index a19eca7259..0000000000 --- a/railties/lib/rails/mongrel_server/handler.rb +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2005 Zed A. Shaw -# You can redistribute it and/or modify it under the same terms as Ruby. -# -# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html -# for more information. - -require 'mongrel' -require 'cgi' -require 'action_controller/dispatcher' - - -module Rails - module MongrelServer - # Implements a handler that can run Rails and serve files out of the - # Rails application's public directory. This lets you run your Rails - # application with Mongrel during development and testing, then use it - # also in production behind a server that's better at serving the - # static files. - # - # The RailsHandler takes a mime_map parameter which is a simple suffix=mimetype - # mapping that it should add to the list of valid mime types. - # - # It also supports page caching directly and will try to resolve a request - # in the following order: - # - # * If the requested exact PATH_INFO exists as a file then serve it. - # * If it exists at PATH_INFO+".html" exists then serve that. - # * Finally, construct a Mongrel::CGIWrapper and run Dispatcher.dispatch to have Rails go. - # - # This means that if you are using page caching it will actually work with Mongrel - # and you should see a decent speed boost (but not as fast as if you use a static - # server like Apache or Litespeed). - class RailsHandler < Mongrel::HttpHandler - # Construct a Mongrel::CGIWrapper and dispatch. - def process(request, response) - return if response.socket.closed? - - cgi = Mongrel::CGIWrapper.new(request, response) - cgi.handler = self - # We don't want the output to be really final until we're out of the lock - cgi.default_really_final = false - - ActionController::Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body) - - # This finalizes the output using the proper HttpResponse way - cgi.out("text/html",true) {""} - rescue Errno::EPIPE - response.socket.close - rescue Object => rails_error - STDERR.puts "#{Time.now.httpdate}: Error dispatching #{rails_error.inspect}" - STDERR.puts rails_error.backtrace.join("\n") - end - end - end -end diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb index 4d983843af..4901abe808 100644 --- a/railties/lib/rails/plugin.rb +++ b/railties/lib/rails/plugin.rb @@ -28,15 +28,19 @@ module Rails end def valid? - File.directory?(directory) && (has_lib_directory? || has_init_file?) + File.directory?(directory) && (has_app_directory? || has_lib_directory? || has_init_file?) end # Returns a list of paths this plugin wishes to make available in <tt>$LOAD_PATH</tt>. def load_paths report_nonexistant_or_empty_plugin! unless valid? - has_lib_directory? ? [lib_path] : [] + + returning [] do |load_paths| + load_paths << lib_path if has_lib_directory? + load_paths << app_paths if has_app_directory? + end.flatten end - + # Evaluates a plugin's init.rb file. def load(initializer) return if loaded? @@ -56,7 +60,31 @@ module Rails def about @about ||= load_about_information end + + # Engines are plugins with an app/ directory. + def engine? + has_app_directory? + end + + # Returns true if the engine ships with a routing file + def routed? + File.exist?(routing_file) + end + + + def view_path + File.join(directory, 'app', 'views') + end + + def controller_path + File.join(directory, 'app', 'controllers') + end + + def routing_file + File.join(directory, 'config', 'routes.rb') + end + private def load_about_information about_yml_path = File.join(@directory, "about.yml") @@ -68,8 +96,13 @@ module Rails def report_nonexistant_or_empty_plugin! raise LoadError, "Can not find the plugin named: #{name}" - end - + end + + + def app_paths + [ File.join(directory, 'app', 'models'), File.join(directory, 'app', 'helpers'), controller_path ] + end + def lib_path File.join(directory, 'lib') end @@ -86,6 +119,11 @@ module Rails File.file?(gem_init_path) ? gem_init_path : classic_init_path end + + def has_app_directory? + File.directory?(File.join(directory, 'app')) + end + def has_lib_directory? File.directory?(lib_path) end @@ -94,6 +132,7 @@ module Rails File.file?(init_path) end + def evaluate_init_rb(initializer) if has_init_file? silence_warnings do diff --git a/railties/lib/rails/plugin/loader.rb b/railties/lib/rails/plugin/loader.rb index 948d497007..be81bdf4fa 100644 --- a/railties/lib/rails/plugin/loader.rb +++ b/railties/lib/rails/plugin/loader.rb @@ -22,6 +22,11 @@ module Rails @plugins ||= all_plugins.select { |plugin| should_load?(plugin) }.sort { |p1, p2| order_plugins(p1, p2) } end + # Returns the plugins that are in engine-form (have an app/ directory) + def engines + @engines ||= plugins.select(&:engine?) + end + # Returns all the plugins that could be found by the current locators. def all_plugins @all_plugins ||= locate_plugins @@ -33,6 +38,9 @@ module Rails plugin.load(initializer) register_plugin_as_loaded(plugin) end + + configure_engines + ensure_all_registered_plugins_are_loaded! end @@ -45,23 +53,49 @@ module Rails plugins.each do |plugin| plugin.load_paths.each do |path| $LOAD_PATH.insert(application_lib_index + 1, path) - ActiveSupport::Dependencies.load_paths << path + + ActiveSupport::Dependencies.load_paths << path + unless Rails.configuration.reload_plugins? ActiveSupport::Dependencies.load_once_paths << path end end end + $LOAD_PATH.uniq! - end + end + protected + def configure_engines + if engines.any? + add_engine_routing_configurations + add_engine_controller_paths + add_engine_view_paths + end + end + def add_engine_routing_configurations + engines.select(&:routed?).collect(&:routing_file).each do |routing_file| + ActionController::Routing::Routes.add_configuration_file(routing_file) + end + end + + def add_engine_controller_paths + ActionController::Routing.controller_paths += engines.collect(&:controller_path) + end + + def add_engine_view_paths + # reverse it such that the last engine can overwrite view paths from the first, like with routes + ActionController::Base.view_paths += ActionView::PathSet.new(engines.collect(&:view_path).reverse) + end + # The locate_plugins method uses each class in config.plugin_locators to # find the set of all plugins available to this Rails application. def locate_plugins - configuration.plugin_locators.map { |locator| + configuration.plugin_locators.map do |locator| locator.new(initializer).plugins - }.flatten + end.flatten # TODO: sorting based on config.plugins end diff --git a/railties/lib/rails/rack.rb b/railties/lib/rails/rack.rb index b4f32c2d95..90535674e9 100644 --- a/railties/lib/rails/rack.rb +++ b/railties/lib/rails/rack.rb @@ -1,5 +1,6 @@ module Rails module Rack + autoload :Debugger, "rails/rack/debugger" autoload :Logger, "rails/rack/logger" autoload :Static, "rails/rack/static" end diff --git a/railties/lib/rails/rack/debugger.rb b/railties/lib/rails/rack/debugger.rb new file mode 100644 index 0000000000..aa2711c616 --- /dev/null +++ b/railties/lib/rails/rack/debugger.rb @@ -0,0 +1,21 @@ +module Rails + module Rack + class Debugger + def initialize(app) + @app = app + + require_library_or_gem 'ruby-debug' + ::Debugger.start + ::Debugger.settings[:autoeval] = true if ::Debugger.respond_to?(:settings) + puts "=> Debugger enabled" + rescue Exception + puts "You need to install ruby-debug to run the server in debugging mode. With gems, use 'gem install ruby-debug'" + exit + end + + def call(env) + @app.call(env) + end + end + end +end diff --git a/railties/lib/rails/rack/static.rb b/railties/lib/rails/rack/static.rb index 45eb0e5921..ef4e2642e2 100644 --- a/railties/lib/rails/rack/static.rb +++ b/railties/lib/rails/rack/static.rb @@ -1,3 +1,5 @@ +require 'rack/utils' + module Rails module Rack class Static diff --git a/railties/lib/rails/vendor_gem_source_index.rb b/railties/lib/rails/vendor_gem_source_index.rb index dc821693ac..5b7721f303 100644 --- a/railties/lib/rails/vendor_gem_source_index.rb +++ b/railties/lib/rails/vendor_gem_source_index.rb @@ -81,7 +81,7 @@ module Rails spec.files = files else $stderr.puts("config.gem: Unpacked gem #{dir_name} in vendor/gems not in a versioned directory."+ - " Giving up.") unless @silence_spec_warnings + " Giving up.") unless @@silence_spec_warnings next end end @@ -137,4 +137,4 @@ module Rails end end -end
\ No newline at end of file +end diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index a0986a2e05..9bb4b2a96d 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -1,7 +1,7 @@ module Rails module VERSION #:nodoc: MAJOR = 2 - MINOR = 2 + MINOR = 3 TINY = 0 STRING = [MAJOR, MINOR, TINY].join('.') diff --git a/railties/lib/rails_generator/base.rb b/railties/lib/rails_generator/base.rb index b5cfe79867..aa7081f8da 100644 --- a/railties/lib/rails_generator/base.rb +++ b/railties/lib/rails_generator/base.rb @@ -154,6 +154,9 @@ module Rails File.join(destination_root, relative_destination) end + def after_generate + end + protected # Convenience method for generator subclasses to record a manifest. def record diff --git a/railties/lib/rails_generator/commands.rb b/railties/lib/rails_generator/commands.rb index 6b9a636847..cacb3807d6 100644 --- a/railties/lib/rails_generator/commands.rb +++ b/railties/lib/rails_generator/commands.rb @@ -40,6 +40,7 @@ module Rails # Replay action manifest. RewindBase subclass rewinds manifest. def invoke! manifest.replay(self) + after_generate end def dependency(generator_name, args, runtime_options = {}) diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb index 32c320385d..4a191578cf 100644 --- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb +++ b/railties/lib/rails_generator/generators/applications/app/app_generator.rb @@ -1,109 +1,46 @@ require 'rbconfig' +require File.dirname(__FILE__) + '/template_runner' require 'digest/md5' +require 'active_support/secure_random' class AppGenerator < Rails::Generator::Base - DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'], - Config::CONFIG['ruby_install_name']) + DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) - DATABASES = %w(mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db) + DATABASES = %w( mysql oracle postgresql sqlite2 sqlite3 frontbase ibm_db ) DEFAULT_DATABASE = 'sqlite3' - default_options :db => (ENV["RAILS_DEFAULT_DATABASE"] || DEFAULT_DATABASE), - :shebang => DEFAULT_SHEBANG, :freeze => false mandatory_options :source => "#{File.dirname(__FILE__)}/../../../../.." + default_options :db => (ENV["RAILS_DEFAULT_DATABASE"] || DEFAULT_DATABASE), + :shebang => DEFAULT_SHEBANG, :with_dispatchers => false, :freeze => false + def initialize(runtime_args, runtime_options = {}) super + usage if args.empty? usage("Databases supported for preconfiguration are: #{DATABASES.join(", ")}") if (options[:db] && !DATABASES.include?(options[:db])) + @destination_root = args.shift @app_name = File.basename(File.expand_path(@destination_root)) end def manifest - # Use /usr/bin/env if no special shebang was specified - script_options = { :chmod => 0755, :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] } - dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] } - - # duplicate CGI::Session#generate_unique_id - md5 = Digest::MD5.new - now = Time.now - md5 << now.to_s - md5 << String(now.usec) - md5 << String(rand(0)) - md5 << String($$) - md5 << @app_name - - # Do our best to generate a secure secret key for CookieStore - secret = ActiveSupport::SecureRandom.hex(64) - record do |m| - # Root directory and all subdirectories. - m.directory '' - BASEDIRS.each { |path| m.directory path } - - # Root - m.file "fresh_rakefile", "Rakefile" - m.file "README", "README" - - # Application - m.template "helpers/application.rb", "app/controllers/application.rb", :assigns => { :app_name => @app_name, :app_secret => md5.hexdigest } - m.template "helpers/application_helper.rb", "app/helpers/application_helper.rb" - m.template "helpers/test_helper.rb", "test/test_helper.rb" - m.template "helpers/performance_test.rb", "test/performance/browsing_test.rb" - - # database.yml and routes.rb - m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => { - :app_name => @app_name, - :socket => options[:db] == "mysql" ? mysql_socket_location : nil - } - m.template "configs/routes.rb", "config/routes.rb" - - # Initializers - m.template "configs/initializers/inflections.rb", "config/initializers/inflections.rb" - m.template "configs/initializers/mime_types.rb", "config/initializers/mime_types.rb" - m.template "configs/initializers/new_rails_defaults.rb", "config/initializers/new_rails_defaults.rb" - - # Environments - m.file "environments/boot.rb", "config/boot.rb" - m.template "environments/environment.rb", "config/environment.rb", :assigns => { :freeze => options[:freeze], :app_name => @app_name, :app_secret => secret } - m.file "environments/production.rb", "config/environments/production.rb" - m.file "environments/development.rb", "config/environments/development.rb" - m.file "environments/test.rb", "config/environments/test.rb" - - # Scripts - %w( about console dbconsole destroy generate performance/benchmarker performance/profiler performance/request process/reaper process/spawner process/inspector runner server plugin ).each do |file| - m.file "bin/#{file}", "script/#{file}", script_options - end - - # Dispatches - m.file "dispatches/dispatch.rb", "public/dispatch.rb", dispatcher_options - m.file "dispatches/dispatch.rb", "public/dispatch.cgi", dispatcher_options - m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", dispatcher_options - - # HTML files - %w(404 422 500 index).each do |file| - m.template "html/#{file}.html", "public/#{file}.html" - end - - m.template "html/favicon.ico", "public/favicon.ico" - m.template "html/robots.txt", "public/robots.txt" - m.file "html/images/rails.png", "public/images/rails.png" - - # Javascripts - m.file "html/javascripts/prototype.js", "public/javascripts/prototype.js" - m.file "html/javascripts/effects.js", "public/javascripts/effects.js" - m.file "html/javascripts/dragdrop.js", "public/javascripts/dragdrop.js" - m.file "html/javascripts/controls.js", "public/javascripts/controls.js" - m.file "html/javascripts/application.js", "public/javascripts/application.js" - - # Docs - m.file "doc/README_FOR_APP", "doc/README_FOR_APP" + create_directories(m) + create_root_files(m) + create_app_files(m) + create_config_files(m) + create_script_files(m) + create_test_files(m) + create_public_files(m) + create_documentation_file(m) + create_log_files(m) + end + end - # Logs - %w(server production development test).each { |file| - m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666 - } + def after_generate + if options[:template] + Rails::TemplateRunner.new(@destination_root, options[:template]) end end @@ -123,57 +60,199 @@ class AppGenerator < Rails::Generator::Base "Preconfigure for selected database (options: #{DATABASES.join('/')}).", "Default: #{DEFAULT_DATABASE}") { |v| options[:db] = v } + opt.on("-D", "--with-dispatchers", + "Add CGI/FastCGI/mod_ruby dispatches code to generated application skeleton", + "Default: false") { |v| options[:with_dispatchers] = v } + opt.on("-f", "--freeze", "Freeze Rails in vendor/rails from the gems generating the skeleton", "Default: false") { |v| options[:freeze] = v } + + opt.on("-m", "--template=path", String, + "Use an application template that lives at path (can be a filesystem path or URL).", + "Default: (none)") { |v| options[:template] = v } + + end + + + private + def create_directories(m) + m.directory '' + + # Intermediate directories are automatically created so don't sweat their absence here. + %w( + app/controllers + app/helpers + app/models + app/views/layouts + config/environments + config/initializers + config/locales + db + doc + lib + lib/tasks + log + public/images + public/javascripts + public/stylesheets + script/performance + test/fixtures + test/functional + test/integration + test/performance + test/unit + vendor + vendor/plugins + tmp/sessions + tmp/sockets + tmp/cache + tmp/pids + ).each { |path| m.directory(path) } + end + + def create_root_files(m) + m.file "fresh_rakefile", "Rakefile" + m.file "README", "README" + end + + def create_app_files(m) + m.file "helpers/application_controller.rb", "app/controllers/application_controller.rb" + m.file "helpers/application_helper.rb", "app/helpers/application_helper.rb" + end + + def create_config_files(m) + create_database_configuration_file(m) + create_routes_file(m) + create_locale_file(m) + create_initializer_files(m) + create_environment_files(m) + end + + def create_documentation_file(m) + m.file "doc/README_FOR_APP", "doc/README_FOR_APP" end + def create_log_files(m) + %w( server production development test ).each do |file| + m.file "configs/empty.log", "log/#{file}.log", :chmod => 0666 + end + end + + def create_public_files(m) + create_dispatch_files(m) + create_error_files(m) + create_welcome_file(m) + create_browser_convention_files(m) + create_rails_image(m) + create_javascript_files(m) + end + + def create_script_files(m) + %w( + about console dbconsole destroy generate runner server plugin + performance/benchmarker performance/profiler performance/request + ).each do |file| + m.file "bin/#{file}", "script/#{file}", { + :chmod => 0755, + :shebang => options[:shebang] == DEFAULT_SHEBANG ? nil : options[:shebang] + } + end + end + + def create_test_files(m) + m.file "helpers/test_helper.rb", "test/test_helper.rb" + m.file "helpers/performance_test.rb", "test/performance/browsing_test.rb" + end + + + def create_database_configuration_file(m) + m.template "configs/databases/#{options[:db]}.yml", "config/database.yml", :assigns => { + :app_name => @app_name, + :socket => options[:db] == "mysql" ? mysql_socket_location : nil } + end + + def create_routes_file(m) + m.file "configs/routes.rb", "config/routes.rb" + end + + def create_initializer_files(m) + %w( + backtrace_silencers + inflections + mime_types + new_rails_defaults + ).each do |initializer| + m.file "configs/initializers/#{initializer}.rb", "config/initializers/#{initializer}.rb" + end + + m.template "configs/initializers/session_store.rb", "config/initializers/session_store.rb", + :assigns => { :app_name => @app_name, :app_secret => ActiveSupport::SecureRandom.hex(64) } + end + + def create_locale_file(m) + m.file "configs/locales/en.yml", "config/locales/en.yml" + end + + def create_environment_files(m) + m.template "environments/environment.rb", "config/environment.rb", + :assigns => { :freeze => options[:freeze] } + + m.file "environments/boot.rb", "config/boot.rb" + m.file "environments/production.rb", "config/environments/production.rb" + m.file "environments/development.rb", "config/environments/development.rb" + m.file "environments/test.rb", "config/environments/test.rb" + end + + + def create_dispatch_files(m) + if options[:with_dispatchers] + dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] } + + m.file "dispatches/config.ru", "config.ru" + m.file "dispatches/dispatch.rb", "public/dispatch.rb", dispatcher_options + m.file "dispatches/dispatch.rb", "public/dispatch.cgi", dispatcher_options + m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", dispatcher_options + end + end + + def create_error_files(m) + %w( 404 422 500 ).each do |file| + m.file "html/#{file}.html", "public/#{file}.html" + end + end + + def create_welcome_file(m) + m.file 'html/index.html', 'public/index.html' + end + + def create_browser_convention_files(m) + m.file "html/favicon.ico", "public/favicon.ico" + m.file "html/robots.txt", "public/robots.txt" + end + + def create_rails_image(m) + m.file "html/images/rails.png", "public/images/rails.png" + end + + def create_javascript_files(m) + %w( prototype effects dragdrop controls application ).each do |javascript| + m.file "html/javascripts/#{javascript}.js", "public/javascripts/#{javascript}.js" + end + end + + def mysql_socket_location - MYSQL_SOCKET_LOCATIONS.find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ - end - - - # Installation skeleton. Intermediate directories are automatically - # created so don't sweat their absence here. - BASEDIRS = %w( - app/controllers - app/helpers - app/models - app/views/layouts - config/environments - config/initializers - db - doc - lib - lib/tasks - log - public/images - public/javascripts - public/stylesheets - script/performance - script/process - test/fixtures - test/functional - test/integration - test/performance - test/unit - vendor - vendor/plugins - tmp/sessions - tmp/sockets - tmp/cache - tmp/pids - ) - - MYSQL_SOCKET_LOCATIONS = [ - "/tmp/mysql.sock", # default - "/var/run/mysqld/mysqld.sock", # debian/gentoo - "/var/tmp/mysql.sock", # freebsd - "/var/lib/mysql/mysql.sock", # fedora - "/opt/local/lib/mysql/mysql.sock", # fedora - "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql - "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 - "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 - "/opt/lampp/var/mysql/mysql.sock" # xampp for linux - ] -end + [ + "/tmp/mysql.sock", # default + "/var/run/mysqld/mysqld.sock", # debian/gentoo + "/var/tmp/mysql.sock", # freebsd + "/var/lib/mysql/mysql.sock", # fedora + "/opt/local/lib/mysql/mysql.sock", # fedora + "/opt/local/var/run/mysqld/mysqld.sock", # mac + darwinports + mysql + "/opt/local/var/run/mysql4/mysqld.sock", # mac + darwinports + mysql4 + "/opt/local/var/run/mysql5/mysqld.sock", # mac + darwinports + mysql5 + "/opt/lampp/var/mysql/mysql.sock" # xampp for linux + ].find { |f| File.exist?(f) } unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ + end +end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/scm/git.rb b/railties/lib/rails_generator/generators/applications/app/scm/git.rb new file mode 100644 index 0000000000..445de6ab42 --- /dev/null +++ b/railties/lib/rails_generator/generators/applications/app/scm/git.rb @@ -0,0 +1,16 @@ +module Rails + class Git < Scm + def self.clone(repos, branch=nil) + `git clone #{repos}` + + if branch + `cd #{repos.split('/').last}/` + `git checkout #{branch}` + end + end + + def self.run(command) + `git #{command}` + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/scm/scm.rb b/railties/lib/rails_generator/generators/applications/app/scm/scm.rb new file mode 100644 index 0000000000..f6c08cad39 --- /dev/null +++ b/railties/lib/rails_generator/generators/applications/app/scm/scm.rb @@ -0,0 +1,8 @@ +module Rails + class Scm + private + def self.hash_to_parameters(hash) + hash.collect { |key, value| "--#{key} #{(value.kind_of?(String) ? value : "")}"}.join(" ") + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/scm/svn.rb b/railties/lib/rails_generator/generators/applications/app/scm/svn.rb new file mode 100644 index 0000000000..22b5966d25 --- /dev/null +++ b/railties/lib/rails_generator/generators/applications/app/scm/svn.rb @@ -0,0 +1,7 @@ +module Rails + class Svn < Scm + def self.checkout(repos, branch = nil) + `svn checkout #{repos}/#{branch || "trunk"}` + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/applications/app/template_runner.rb b/railties/lib/rails_generator/generators/applications/app/template_runner.rb new file mode 100644 index 0000000000..0083e0d5a5 --- /dev/null +++ b/railties/lib/rails_generator/generators/applications/app/template_runner.rb @@ -0,0 +1,363 @@ +require File.dirname(__FILE__) + '/scm/scm' +require File.dirname(__FILE__) + '/scm/git' +require File.dirname(__FILE__) + '/scm/svn' + +require 'open-uri' +require 'fileutils' + +module Rails + class TemplateRunner + attr_reader :behavior, :description, :root + + def initialize(root, template) # :nodoc: + @root = Dir.pwd + "/" + root + + puts "applying template: #{template}" + + load_template(template) + + puts "#{template} applied." + end + + def load_template(template) + begin + code = open(template).read + in_root { self.instance_eval(code) } + rescue LoadError + raise "The template [#{template}] could not be loaded." + end + end + + # Create a new file in the Rails project folder. Specify the + # relative path from RAILS_ROOT. Data is the return value of a block + # or a data string. + # + # ==== Examples + # + # file("lib/fun_party.rb") do + # hostname = ask("What is the virtual hostname I should use?") + # "vhost.name = #{hostname}" + # end + # + # file("config/apach.conf", "your apache config") + # + def file(filename, data = nil, &block) + puts "creating file #{filename}" + dir, file = [File.dirname(filename), File.basename(filename)] + + inside(dir) do + File.open(file, "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Install a plugin. You must provide either a Subversion url or Git url. + # + # ==== Examples + # + # plugin 'restful-authentication', :git => 'git://github.com/technoweenie/restful-authentication.git' + # plugin 'restful-authentication', :svn => 'svn://svnhub.com/technoweenie/restful-authentication/trunk' + # + def plugin(name, options) + puts "installing plugin #{name}" + + if options[:git] || options[:svn] + in_root do + `script/plugin install #{options[:svn] || options[:git]}` + end + else + puts "! no git or svn provided for #{name}. skipping..." + end + end + + # Adds an entry into config/environment.rb for the supplied gem : + def gem(name, options = {}) + puts "adding gem #{name}" + + sentinel = 'Rails::Initializer.run do |config|' + gems_code = "config.gem '#{name}'" + + if options.any? + opts = options.inject([]) {|result, h| result << [":#{h[0]} => '#{h[1]}'"] }.join(", ") + gems_code << ", #{opts}" + end + + in_root do + gsub_file 'config/environment.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n #{gems_code}" + end + end + end + + # Run a command in git. + # + # ==== Examples + # + # git :init + # git :add => "this.file that.rb" + # git :add => "onefile.rb", :rm => "badfile.cxx" + # + def git(command = {}) + puts "running git #{command}" + + in_root do + if command.is_a?(Symbol) + Git.run(command.to_s) + else + command.each do |command, options| + Git.run("#{command} #{options}") + end + end + end + end + + # Create a new file in the vendor/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # vendor("sekrit.rb") do + # sekrit_salt = "#{Time.now}--#{3.years.ago}--#{rand}--" + # "salt = '#{sekrit_salt}'" + # end + # + # vendor("foreign.rb", "# Foreign code is fun") + # + def vendor(filename, data = nil, &block) + puts "vendoring file #{filename}" + inside("vendor") do |folder| + File.open("#{folder}/#{filename}", "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Create a new file in the lib/ directory. Code can be specified + # in a block or a data string can be given. + # + # ==== Examples + # + # lib("crypto.rb") do + # "crypted_special_value = '#{rand}--#{Time.now}--#{rand(1337)}--'" + # end + # + # lib("foreign.rb", "# Foreign code is fun") + # + def lib(filename, data = nil) + puts "add lib file #{filename}" + inside("lib") do |folder| + File.open("#{folder}/#{filename}", "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Create a new Rakefile with the provided code (either in a block or a string). + # + # ==== Examples + # + # rakefile("bootstrap.rake") do + # project = ask("What is the UNIX name of your project?") + # + # <<-TASK + # namespace :#{project} do + # task :bootstrap do + # puts "i like boots!" + # end + # end + # TASK + # end + # + # rakefile("seed.rake", "puts 'im plantin ur seedz'") + # + def rakefile(filename, data = nil, &block) + puts "adding rakefile #{filename}" + inside("lib/tasks") do |folder| + File.open("#{folder}/#{filename}", "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Create a new initializer with the provided code (either in a block or a string). + # + # ==== Examples + # + # initializer("globals.rb") do + # data = "" + # + # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do + # data << "#{const} = :entp" + # end + # + # data + # end + # + # initializer("api.rb", "API_KEY = '123456'") + # + def initializer(filename, data = nil, &block) + puts "adding initializer #{filename}" + inside("config/initializers") do |folder| + File.open("#{folder}/#{filename}", "w") do |f| + if block_given? + f.write(block.call) + else + f.write(data) + end + end + end + end + + # Generate something using a generator from Rails or a plugin. + # The second parameter is the argument string that is passed to + # the generator or an Array that is joined. + # + # ==== Example + # + # generate(:authenticated, "user session") + # + def generate(what, args = nil) + puts "generating #{what}" + args = args.join(" ") if args.class == Array + + in_root { `#{root}/script/generate #{what} #{args}` } + end + + # Executes a command + # + # ==== Example + # + # inside('vendor') do + # run('ln -s ~/edge rails) + # end + # + def run(command) + puts "executing #{command} from #{Dir.pwd}" + `#{command}` + end + + # Runs the supplied rake task + # + # ==== Example + # + # rake("db:migrate") + # rake("db:migrate", "production") + # + def rake(command, env = 'development') + puts "running rake task #{command}" + in_root { `rake #{command} RAILS_ENV=#{env}` } + end + + # Just run the capify command in root + # + # ==== Example + # + # capify! + # + def capify! + in_root { `capify .` } + end + + # Add Rails to /vendor/rails + # + # ==== Example + # + # freeze! + # + def freeze!(args = {}) + puts "vendoring rails edge" + in_root { `rake rails:freeze:edge` } + end + + # Make an entry in Rails routing file conifg/routes.rb + # + # === Example + # + # route "map.root :controller => :welcome" + # + def route(routing_code) + sentinel = 'ActionController::Routing::Routes.draw do |map|' + + in_root do + gsub_file 'config/routes.rb', /(#{Regexp.escape(sentinel)})/mi do |match| + "#{match}\n #{routing_code}\n" + end + end + end + + protected + + # Get a user's input + # + # ==== Example + # + # answer = ask("Should I freeze the latest Rails?") + # freeze! if ask("Should I freeze the latest Rails?") == "yes" + # + def ask(string) + puts string + gets.strip + end + + # Do something in the root of the Rails application or + # a provided subfolder; the full path is yielded to the block you provide. + # The path is set back to the previous path when the method exits. + def inside(dir = '', &block) + folder = File.join(root, dir) + FileUtils.mkdir_p(folder) unless File.exist?(folder) + FileUtils.cd(folder) { block.arity == 1 ? yield(folder) : yield } + end + + def in_root + FileUtils.cd(root) { yield } + end + + # Helper to test if the user says yes(y)? + # + # ==== Example + # + # freeze! if yes?("Should I freeze the latest Rails?") + # + def yes?(question) + answer = ask(question).downcase + answer == "y" || answer == "yes" + end + + # Helper to test if the user does NOT say yes(y)? + # + # ==== Example + # + # capify! if no?("Will you be using vlad to deploy your application?") + # + def no?(question) + !yes?(question) + end + + def gsub_file(relative_destination, regexp, *args, &block) + path = destination_path(relative_destination) + content = File.read(path).gsub(regexp, *args, &block) + File.open(path, 'wb') { |file| file.write(content) } + end + + def destination_path(relative_destination) + File.join(root, relative_destination) + end + end +end
\ No newline at end of file diff --git a/railties/lib/rails_generator/generators/components/controller/USAGE b/railties/lib/rails_generator/generators/components/controller/USAGE index d4fae60c81..362872e84a 100644 --- a/railties/lib/rails_generator/generators/components/controller/USAGE +++ b/railties/lib/rails_generator/generators/components/controller/USAGE @@ -6,24 +6,25 @@ Description: path like 'parent_module/controller_name'. This generates a controller class in app/controllers, view templates in - app/views/controller_name, a helper class in app/helpers, and a functional - test suite in test/functional. + app/views/controller_name, a helper class in app/helpers, a functional + test suite in test/functional and a helper test suite in test/unit/helpers. Example: `./script/generate controller CreditCard open debit credit close` Credit card controller with URLs like /credit_card/debit. - Controller: app/controllers/credit_card_controller.rb - Views: app/views/credit_card/debit.html.erb [...] - Helper: app/helpers/credit_card_helper.rb - Test: test/functional/credit_card_controller_test.rb + Controller: app/controllers/credit_card_controller.rb + Functional Test: test/functional/credit_card_controller_test.rb + Views: app/views/credit_card/debit.html.erb [...] + Helper: app/helpers/credit_card_helper.rb + Helper Test: test/unit/helpers/credit_card_helper_test.rb Modules Example: `./script/generate controller 'admin/credit_card' suspend late_fee` Credit card admin controller with URLs /admin/credit_card/suspend. - Controller: app/controllers/admin/credit_card_controller.rb - Views: app/views/admin/credit_card/debit.html.erb [...] - Helper: app/helpers/admin/credit_card_helper.rb - Test: test/functional/admin/credit_card_controller_test.rb - + Controller: app/controllers/admin/credit_card_controller.rb + Functional Test: test/functional/admin/credit_card_controller_test.rb + Views: app/views/admin/credit_card/debit.html.erb [...] + Helper: app/helpers/admin/credit_card_helper.rb + Helper Test: test/unit/helpers/admin/credit_card_helper_test.rb diff --git a/railties/lib/rails_generator/generators/components/controller/controller_generator.rb b/railties/lib/rails_generator/generators/components/controller/controller_generator.rb index 77b2220d57..dc126e8a98 100644 --- a/railties/lib/rails_generator/generators/components/controller/controller_generator.rb +++ b/railties/lib/rails_generator/generators/components/controller/controller_generator.rb @@ -2,13 +2,14 @@ class ControllerGenerator < Rails::Generator::NamedBase def manifest record do |m| # Check for class naming collisions. - m.class_collisions "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper" + m.class_collisions "#{class_name}Controller", "#{class_name}ControllerTest", "#{class_name}Helper", "#{class_name}HelperTest" # Controller, helper, views, and test directories. m.directory File.join('app/controllers', class_path) m.directory File.join('app/helpers', class_path) m.directory File.join('app/views', class_path, file_name) m.directory File.join('test/functional', class_path) + m.directory File.join('test/unit/helpers', class_path) # Controller class, functional test, and helper class. m.template 'controller.rb', @@ -26,6 +27,11 @@ class ControllerGenerator < Rails::Generator::NamedBase class_path, "#{file_name}_helper.rb") + m.template 'helper_test.rb', + File.join('test/unit/helpers', + class_path, + "#{file_name}_helper_test.rb") + # View template for each action. actions.each do |action| path = File.join('app/views', class_path, file_name, "#{action}.html.erb") diff --git a/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb b/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb new file mode 100644 index 0000000000..591e40900e --- /dev/null +++ b/railties/lib/rails_generator/generators/components/controller/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= class_name %>HelperTest < ActionView::TestCase +end diff --git a/railties/lib/rails_generator/generators/components/helper/USAGE b/railties/lib/rails_generator/generators/components/helper/USAGE new file mode 100644 index 0000000000..ef27ca617e --- /dev/null +++ b/railties/lib/rails_generator/generators/components/helper/USAGE @@ -0,0 +1,24 @@ +Description: + Stubs out a new helper. Pass the helper name, either + CamelCased or under_scored. + + To create a helper within a module, specify the helper name as a + path like 'parent_module/helper_name'. + + This generates a helper class in app/helpers and a helper test + suite in test/unit/helpers. + +Example: + `./script/generate helper CreditCard` + + Credit card helper. + Helper: app/helpers/credit_card_helper.rb + Test: test/unit/helpers/credit_card_helper_test.rb + +Modules Example: + `./script/generate helper 'admin/credit_card'` + + Credit card admin helper. + Helper: app/helpers/admin/credit_card_helper.rb + Test: test/unit/helpers/admin/credit_card_helper_test.rb + diff --git a/railties/lib/rails_generator/generators/components/helper/helper_generator.rb b/railties/lib/rails_generator/generators/components/helper/helper_generator.rb new file mode 100644 index 0000000000..f7831f7c7a --- /dev/null +++ b/railties/lib/rails_generator/generators/components/helper/helper_generator.rb @@ -0,0 +1,25 @@ +class HelperGenerator < Rails::Generator::NamedBase + def manifest + record do |m| + # Check for class naming collisions. + m.class_collisions class_path, "#{class_name}Helper", "#{class_name}HelperTest" + + # Helper and helper test directories. + m.directory File.join('app/helpers', class_path) + m.directory File.join('test/unit/helpers', class_path) + + # Helper and helper test class. + + m.template 'helper.rb', + File.join('app/helpers', + class_path, + "#{file_name}_helper.rb") + + m.template 'helper_test.rb', + File.join('test/unit/helpers', + class_path, + "#{file_name}_helper_test.rb") + + end + end +end diff --git a/railties/lib/rails_generator/generators/components/helper/templates/helper.rb b/railties/lib/rails_generator/generators/components/helper/templates/helper.rb new file mode 100644 index 0000000000..3fe2ecdc74 --- /dev/null +++ b/railties/lib/rails_generator/generators/components/helper/templates/helper.rb @@ -0,0 +1,2 @@ +module <%= class_name %>Helper +end diff --git a/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb b/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb new file mode 100644 index 0000000000..591e40900e --- /dev/null +++ b/railties/lib/rails_generator/generators/components/helper/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= class_name %>HelperTest < ActionView::TestCase +end diff --git a/railties/lib/rails_generator/generators/components/resource/USAGE b/railties/lib/rails_generator/generators/components/resource/USAGE index 83cc9d7654..e6043f1de1 100644 --- a/railties/lib/rails_generator/generators/components/resource/USAGE +++ b/railties/lib/rails_generator/generators/components/resource/USAGE @@ -11,8 +11,8 @@ Description: You don't have to think up every attribute up front, but it helps to sketch out a few so you can start working with the resource immediately. - This creates a model, controller, tests and fixtures for both, and the - corresponding map.resources declaration in config/routes.rb + This creates a model, controller, helper, tests and fixtures for all of them, + and the corresponding map.resources declaration in config/routes.rb Unlike the scaffold generator, the resource generator does not create views or add any methods to the generated controller. diff --git a/railties/lib/rails_generator/generators/components/resource/resource_generator.rb b/railties/lib/rails_generator/generators/components/resource/resource_generator.rb index ea6dd65bde..4ee2fbff63 100644 --- a/railties/lib/rails_generator/generators/components/resource/resource_generator.rb +++ b/railties/lib/rails_generator/generators/components/resource/resource_generator.rb @@ -40,6 +40,7 @@ class ResourceGenerator < Rails::Generator::NamedBase m.directory(File.join('app/views', controller_class_path, controller_file_name)) m.directory(File.join('test/functional', controller_class_path)) m.directory(File.join('test/unit', class_path)) + m.directory(File.join('test/unit/helpers', class_path)) m.dependency 'model', [name] + @args, :collision => :skip @@ -49,6 +50,7 @@ class ResourceGenerator < Rails::Generator::NamedBase m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb")) m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb")) + m.template('helper_test.rb', File.join('test/unit/helpers', controller_class_path, "#{controller_file_name}_helper_test.rb")) m.route_resources controller_file_name end diff --git a/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb b/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb new file mode 100644 index 0000000000..061f64a5e3 --- /dev/null +++ b/railties/lib/rails_generator/generators/components/resource/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= controller_class_name %>HelperTest < ActionView::TestCase +end diff --git a/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb b/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb index ff0381da2a..2a5edeedb6 100644 --- a/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb +++ b/railties/lib/rails_generator/generators/components/scaffold/scaffold_generator.rb @@ -47,6 +47,7 @@ class ScaffoldGenerator < Rails::Generator::NamedBase m.directory(File.join('app/views/layouts', controller_class_path)) m.directory(File.join('test/functional', controller_class_path)) m.directory(File.join('test/unit', class_path)) + m.directory(File.join('test/unit/helpers', class_path)) m.directory(File.join('public/stylesheets', class_path)) for action in scaffold_views @@ -66,6 +67,7 @@ class ScaffoldGenerator < Rails::Generator::NamedBase m.template('functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb")) m.template('helper.rb', File.join('app/helpers', controller_class_path, "#{controller_file_name}_helper.rb")) + m.template('helper_test.rb', File.join('test/unit/helpers', controller_class_path, "#{controller_file_name}_helper_test.rb")) m.route_resources controller_file_name diff --git a/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb b/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb new file mode 100644 index 0000000000..061f64a5e3 --- /dev/null +++ b/railties/lib/rails_generator/generators/components/scaffold/templates/helper_test.rb @@ -0,0 +1,4 @@ +require 'test_helper' + +class <%= controller_class_name %>HelperTest < ActionView::TestCase +end diff --git a/railties/lib/rails_generator/secret_key_generator.rb b/railties/lib/rails_generator/secret_key_generator.rb index 553811d35d..7dd495a2f5 100644 --- a/railties/lib/rails_generator/secret_key_generator.rb +++ b/railties/lib/rails_generator/secret_key_generator.rb @@ -1,3 +1,5 @@ +require 'active_support/deprecation' + module Rails # A class for creating random secret keys. This class will do its best to create a # random secret key that's as secure as possible, using whatever methods are diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake index 5cb27f1f10..a90c1d4a77 100644 --- a/railties/lib/tasks/databases.rake +++ b/railties/lib/tasks/databases.rake @@ -1,7 +1,12 @@ namespace :db do + task :load_config => :rails_env do + require 'active_record' + ActiveRecord::Base.configurations = Rails::Configuration.new.database_configuration + end + namespace :create do desc 'Create all the local databases defined in config/database.yml' - task :all => :environment do + task :all => :load_config do ActiveRecord::Base.configurations.each_value do |config| # Skip entries that don't have a database key, such as the first entry here: # @@ -22,7 +27,7 @@ namespace :db do end desc 'Create the database defined in config/database.yml for the current RAILS_ENV' - task :create => :environment do + task :create => :load_config do create_database(ActiveRecord::Base.configurations[RAILS_ENV]) end @@ -76,7 +81,7 @@ namespace :db do namespace :drop do desc 'Drops all the local databases defined in config/database.yml' - task :all => :environment do + task :all => :load_config do ActiveRecord::Base.configurations.each_value do |config| # Skip entries that don't have a database key next unless config['database'] @@ -87,7 +92,7 @@ namespace :db do end desc 'Drops the database for the current RAILS_ENV' - task :drop => :environment do + task :drop => :load_config do config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] begin drop_database(config) @@ -393,6 +398,7 @@ end def drop_database(config) case config['adapter'] when 'mysql' + ActiveRecord::Base.establish_connection(config) ActiveRecord::Base.connection.drop_database config['database'] when /^sqlite/ FileUtils.rm(File.join(RAILS_ROOT, config['database'])) diff --git a/railties/lib/tasks/framework.rake b/railties/lib/tasks/framework.rake index 66ab78c3b2..d639214ffe 100644 --- a/railties/lib/tasks/framework.rake +++ b/railties/lib/tasks/framework.rake @@ -5,7 +5,6 @@ namespace :rails do deps = %w(actionpack activerecord actionmailer activesupport activeresource) require 'rubygems' require 'rubygems/gem_runner' - Gem.manage_gems rails = (version = ENV['VERSION']) ? Gem.cache.find_name('rails', "= #{version}").first : @@ -79,7 +78,7 @@ namespace :rails do end desc "Update both configs, scripts and public/javascripts from Rails" - task :update => [ "update:scripts", "update:javascripts", "update:configs" ] + task :update => [ "update:scripts", "update:javascripts", "update:configs", "update:application_controller" ] namespace :update do desc "Add new scripts to the application script/ directory" @@ -115,5 +114,24 @@ namespace :rails do require 'railties_path' FileUtils.cp(RAILTIES_PATH + '/environments/boot.rb', RAILS_ROOT + '/config/boot.rb') end + + desc "Rename application.rb to application_controller.rb" + task :application_controller do + old_style = RAILS_ROOT + '/app/controllers/application.rb' + new_style = RAILS_ROOT + '/app/controllers/application_controller.rb' + if File.exists?(old_style) && !File.exists?(new_style) + FileUtils.mv(old_style, new_style) + puts "#{old_style} has been renamed to #{new_style}, update your SCM as necessary" + end + end + + desc "Generate dispatcher files in RAILS_ROOT/public" + task :generate_dispatchers do + require 'railties_path' + FileUtils.cp(RAILTIES_PATH + '/dispatches/config.ru', RAILS_ROOT + '/config.ru') + FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.fcgi', RAILS_ROOT + '/public/dispatch.fcgi') + FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.rb') + FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.rb', RAILS_ROOT + '/public/dispatch.cgi') + end end end diff --git a/railties/lib/tasks/middleware.rake b/railties/lib/tasks/middleware.rake new file mode 100644 index 0000000000..e0dcf50307 --- /dev/null +++ b/railties/lib/tasks/middleware.rake @@ -0,0 +1,7 @@ +desc 'Prints out your Rack middleware stack' +task :middleware => :environment do + ActionController::Dispatcher.middleware.each do |middleware| + puts "use #{middleware.inspect}" + end + puts "run ActionController::Dispatcher.new" +end diff --git a/railties/lib/tasks/misc.rake b/railties/lib/tasks/misc.rake index 5c99725203..411750bf40 100644 --- a/railties/lib/tasks/misc.rake +++ b/railties/lib/tasks/misc.rake @@ -3,6 +3,12 @@ task :environment do require(File.join(RAILS_ROOT, 'config', 'environment')) end +task :rails_env do + unless defined? RAILS_ENV + RAILS_ENV = ENV['RAILS_ENV'] ||= 'development' + end +end + desc 'Generate a crytographically secure secret key. This is typically used to generate a secret for cookie sessions.' task :secret do puts ActiveSupport::SecureRandom.hex(64) diff --git a/railties/lib/tasks/statistics.rake b/railties/lib/tasks/statistics.rake index dbd0773194..5ab27a0f62 100644 --- a/railties/lib/tasks/statistics.rake +++ b/railties/lib/tasks/statistics.rake @@ -4,7 +4,6 @@ STATS_DIRECTORIES = [ %w(Models app/models), %w(Libraries lib/), %w(APIs app/apis), - %w(Components components), %w(Integration\ tests test/integration), %w(Functional\ tests test/functional), %w(Unit\ tests test/unit) diff --git a/railties/lib/tasks/testing.rake b/railties/lib/tasks/testing.rake index 328bde7442..4242458672 100644 --- a/railties/lib/tasks/testing.rake +++ b/railties/lib/tasks/testing.rake @@ -7,7 +7,7 @@ def recent_tests(source_pattern, test_path, touched_since = 10.minutes.ago) tests = [] source_dir = File.dirname(path).split("/") source_file = File.basename(path, '.rb') - + # Support subdirs in app/models and app/controllers modified_test_path = source_dir.length > 2 ? "#{test_path}/" << source_dir[1..source_dir.length].join('/') : test_path @@ -18,7 +18,7 @@ def recent_tests(source_pattern, test_path, touched_since = 10.minutes.ago) # For modified files in app, run tests in subdirs too. ex. /test/functional/account/*_test.rb test = "#{modified_test_path}/#{File.basename(path, '.rb').sub("_controller","")}" FileList["#{test}/*_test.rb"].each { |f| tests.push f } if File.exist?(test) - + return tests end @@ -63,7 +63,7 @@ namespace :test do t.test_files = touched.uniq end Rake::Task['test:recent'].comment = "Test recent changes" - + Rake::TestTask.new(:uncommitted => "db:test:prepare") do |t| def t.file_list if File.directory?(".svn") @@ -82,7 +82,7 @@ namespace :test do unit_tests.uniq + functional_tests.uniq end - + t.libs << 'test' t.verbose = true end diff --git a/railties/lib/test_help.rb b/railties/lib/test_help.rb index 3cc61d7932..b5c92c1790 100644 --- a/railties/lib/test_help.rb +++ b/railties/lib/test_help.rb @@ -1,21 +1,30 @@ -require_dependency 'application' - # Make double-sure the RAILS_ENV is set to test, # so fixtures are loaded to the right database silence_warnings { RAILS_ENV = "test" } require 'test/unit' require 'active_support/test_case' -require 'active_record/fixtures' require 'action_controller/test_case' +require 'action_view/test_case' require 'action_controller/integration' require 'action_mailer/test_case' if defined?(ActionMailer) -Test::Unit::TestCase.fixture_path = RAILS_ROOT + "/test/fixtures/" -ActionController::IntegrationTest.fixture_path = Test::Unit::TestCase.fixture_path +if defined?(ActiveRecord) + require 'active_record/test_case' + require 'active_record/fixtures' + + class ActiveSupport::TestCase + include ActiveRecord::TestFixtures + self.fixture_path = "#{RAILS_ROOT}/test/fixtures/" + self.use_instantiated_fixtures = false + self.use_transactional_fixtures = true + end + + ActionController::IntegrationTest.fixture_path = ActiveSupport::TestCase.fixture_path -def create_fixtures(*table_names) - Fixtures.create_fixtures(Test::Unit::TestCase.fixture_path, table_names) + def create_fixtures(*table_names, &block) + Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, {}, &block) + end end begin |