From 4129449594ad3d8ff2f8fb4836104f25406a104f Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Fri, 25 Sep 2009 17:42:53 -0700 Subject: [PATCH] Start Rails::Application object --- .gitignore | 1 + railties/lib/rails.rb | 2 + railties/lib/rails/application.rb | 58 ++++++++++++ railties/lib/rails/commands/server.rb | 40 +-------- railties/test/application/load_test.rb | 98 +++++++++++++++++++++ railties/test/isolation/abstract_unit.rb | 107 +++++++++++++++++++++++ 6 files changed, 268 insertions(+), 38 deletions(-) create mode 100644 railties/lib/rails.rb create mode 100644 railties/lib/rails/application.rb create mode 100644 railties/test/application/load_test.rb create mode 100644 railties/test/isolation/abstract_unit.rb diff --git a/.gitignore b/.gitignore index ae8ebc4607c2a..4bb38907601eb 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,4 @@ actionpack/bin vendor/gems/ */vendor/gems/ bin/ +railties/tmp \ No newline at end of file diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb new file mode 100644 index 0000000000000..329b60fb87c8f --- /dev/null +++ b/railties/lib/rails.rb @@ -0,0 +1,2 @@ +require "rails/application" +require "rails/initializer" \ No newline at end of file diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb new file mode 100644 index 0000000000000..e379504f54128 --- /dev/null +++ b/railties/lib/rails/application.rb @@ -0,0 +1,58 @@ +require 'action_controller' + +module Rails + class Application + # Loads a Rails application from a directory and returns a Rails + # Application object that responds to #call(env) + def self.load(path, options = {}) + require "#{path}/config/environment" + new(path, options) + end + + def initialize(path, options) + @path = path + + ensure_tmp_dirs + + if options[:config] + config = File.join(path, options[:config]) + config = nil unless File.exist?(config) + end + + @app = ::Rack::Builder.new { + use Rails::Rack::LogTailer unless options[:detach] + use Rails::Rack::Debugger if options[:debugger] + if options[:path] + base = options[:path] + ActionController::Base.relative_url_root = base + end + + map base || "/" do + use Rails::Rack::Static + + if config && config =~ /\.ru$/ + instance_eval(File.read(config), config) + elsif config + require config + run Object.const_get(File.basename(config, '.rb').capitalize) + else + run ActionController::Dispatcher.new + end + end + }.to_app + end + + def call(env) + @app.call(env) + end + + private + + def ensure_tmp_dirs + %w(cache pids sessions sockets).each do |dir_to_make| + FileUtils.mkdir_p(File.join(@path, 'tmp', dir_to_make)) + end + end + + end +end \ No newline at end of file diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index 823916b1dc4da..b8ba4a9f91f03 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -1,7 +1,6 @@ -require 'action_controller' - require 'fileutils' require 'optparse' +require 'rails' options = { :Port => 3000, @@ -46,10 +45,6 @@ puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}#{options[:path]}" -%w(cache pids sessions sockets).each do |dir_to_make| - FileUtils.mkdir_p(File.join(RAILS_ROOT, 'tmp', dir_to_make)) -end - if options[:detach] Process.daemon pid = "#{RAILS_ROOT}/tmp/pids/server.pid" @@ -60,38 +55,7 @@ ENV["RAILS_ENV"] = options[:environment] RAILS_ENV.replace(options[:environment]) if defined?(RAILS_ENV) -if File.exist?(options[:config]) - config = options[:config] - if config =~ /\.ru$/ - cfgfile = File.read(config) - if cfgfile[/^#\\(.*)/] - opts.parse!($1.split(/\s+/)) - end - inner_app = eval("Rack::Builder.new {( " + cfgfile + "\n )}.to_app", nil, config) - else - require config - inner_app = Object.const_get(File.basename(config, '.rb').capitalize) - end -else - require RAILS_ROOT + "/config/environment" - inner_app = ActionController::Dispatcher.new -end - -if options[:path].nil? - map_path = "/" -else - ActionController::Base.relative_url_root = options[:path] - map_path = options[:path] -end - -app = Rack::Builder.new { - use Rails::Rack::LogTailer unless options[:detach] - use Rails::Rack::Debugger if options[:debugger] - map map_path do - use Rails::Rack::Static - run inner_app - end -}.to_app +app = Rails::Application.load(RAILS_ROOT, options) puts "=> Call with -d to detach" diff --git a/railties/test/application/load_test.rb b/railties/test/application/load_test.rb new file mode 100644 index 0000000000000..48917f1825a03 --- /dev/null +++ b/railties/test/application/load_test.rb @@ -0,0 +1,98 @@ +require "isolation/abstract_unit" +require "rails" +require "rack" + +module ApplicationTests + class LoadTest < Test::Unit::TestCase + include ActiveSupport::Testing::Isolation + + def setup + build_app + end + + test "rails app is present" do + assert File.exist?(app_path("config")) + end + + test "running Rails::Application.load on the path returns a (vaguely) useful application" do + @app = Rails::Application.load app_path + assert_welcome get("/") + end + + test "setting the map_path of the application" do + controller "says", <<-CONTROLLER + class SaysController < ActionController::Base + def index ; render :text => "MOO!" ; end + end + CONTROLLER + + @app = Rails::Application.load app_path, :path => "/the/cow" + + assert_missing get("/") + assert_welcome get("/the/cow") + assert_body "MOO!", get("/the/cow/says") + end + + test "url generation with a base path" do + controller "generatin", <<-CONTROLLER + class GeneratinController < ActionController::Base + def index ; render :text => url_for(:action => "index", :only_path => true) ; end + end + CONTROLLER + + @app = Rails::Application.load app_path, :path => "/base" + + assert_body "/base/generatin", get("/base/generatin") + end + + test "config.ru is used" do + app_file "config.ru", <<-CONFIG + class MyMiddleware + def initialize(app) + @app = app + end + + def call(env) + status, headers, body = @app.call(env) + headers["Config-Ru-Test"] = "TESTING" + [status, headers, body] + end + end + + use MyMiddleware + run proc {|env| [200, {"Content-Type" => "text/html"}, ["VICTORY"]] } + CONFIG + + @app = Rails::Application.load app_path, :config => "config.ru" + + assert_body "VICTORY", get("/omg") + assert_header "Config-Ru-Test", "TESTING", get("/omg") + end + + test "arbitrary.rb can be used as a config" do + app_file "myapp.rb", <<-CONFIG + Myapp = proc {|env| [200, {"Content-Type" => "text/html"}, ["OMG ROBOTS"]] } + CONFIG + + @app = Rails::Application.load app_path, :config => "myapp.rb" + + assert_body "OMG ROBOTS", get("/omg") + end + + %w(cache pids sessions sockets).each do |dir| + test "creating tmp/#{dir} if it does not exist" do + FileUtils.rm_r(app_path("tmp/#{dir}")) + Rails::Application.load app_path + assert File.exist?(app_path("tmp/#{dir}")) + end + end + + test "LogTailer middleware is present when not detached" do + + end + + test "Debugger middleware is present when using debugger option" do + + end + end +end \ No newline at end of file diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb new file mode 100644 index 0000000000000..18ac745101083 --- /dev/null +++ b/railties/test/isolation/abstract_unit.rb @@ -0,0 +1,107 @@ +# Note: +# It is important to keep this file as light as possible +# the goal for tests that require this is to test booting up +# rails from an empty state, so anything added here could +# hide potential failures +# +# It is also good to know what is the bare minimum to get +# Rails booted up. + +# TODO: Remove rubygems when possible +require 'rubygems' +require 'test/unit' + +# TODO: Remove setting this magic constant +RAILS_FRAMEWORK_ROOT = File.expand_path("#{File.dirname(__FILE__)}/../../..") + +# These load paths usually get set inside of boot.rb +$:.unshift "#{RAILS_FRAMEWORK_ROOT}/railties/lib" +$:.unshift "#{RAILS_FRAMEWORK_ROOT}/actionpack/lib" + +# These files do not require any others and are needed +# to run the tests +require "#{RAILS_FRAMEWORK_ROOT}/activesupport/lib/active_support/testing/isolation" +require "#{RAILS_FRAMEWORK_ROOT}/activesupport/lib/active_support/testing/declarative" + +module TestHelpers + module Paths + module_function + + def tmp_path(*args) + File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. tmp] + args)) + end + + def app_path(*args) + tmp_path(*%w[app] + args) + end + end + + module Rack + def extract_body(response) + "".tap do |body| + response[2].each {|chunk| body << chunk } + end + end + + def get(path) + @app.call(::Rack::MockRequest.env_for(path)) + end + + def assert_welcome(resp) + assert_equal 200, resp[0] + assert resp[1]["Content-Type"] = "text/html" + assert extract_body(resp).match(/Welcome aboard/) + end + + def assert_success(resp) + assert_equal 202, resp[0] + end + + def assert_missing(resp) + assert_equal 404, resp[0] + end + + def assert_header(key, value, resp) + assert_equal value, resp[1][key.to_s] + end + + def assert_body(expected, resp) + assert_equal expected, extract_body(resp) + end + end + + module Generation + def build_app + FileUtils.cp_r(tmp_path('app_template'), app_path) + end + + def app_file(path, contents) + File.open(app_path(path), 'w') do |f| + f.puts contents + end + end + + def controller(name, contents) + app_file("app/controllers/#{name}_controller.rb", contents) + end + end +end + +class Test::Unit::TestCase + include TestHelpers::Paths + include TestHelpers::Rack + include TestHelpers::Generation + extend ActiveSupport::Testing::Declarative +end + +# Create a scope and build a fixture rails app +Module.new do + extend TestHelpers::Paths + # Build a rails app + if File.exist?(tmp_path) + FileUtils.rm_rf(tmp_path) + end + + FileUtils.mkdir(tmp_path) + `#{Gem.ruby} #{RAILS_FRAMEWORK_ROOT}/railties/bin/rails #{tmp_path('app_template')}` +end \ No newline at end of file