From fa007e591d1ebf6df3fc8af4777c06e8a3d84eb3 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 24 Nov 2009 12:04:36 -0600 Subject: Quieter rake test runner --- railties/lib/rails/generators/rails/plugin/templates/Rakefile | 1 - railties/lib/rails/tasks/testing.rake | 9 --------- 2 files changed, 10 deletions(-) diff --git a/railties/lib/rails/generators/rails/plugin/templates/Rakefile b/railties/lib/rails/generators/rails/plugin/templates/Rakefile index 85e8ff1834..23c2245a41 100644 --- a/railties/lib/rails/generators/rails/plugin/templates/Rakefile +++ b/railties/lib/rails/generators/rails/plugin/templates/Rakefile @@ -10,7 +10,6 @@ Rake::TestTask.new(:test) do |t| t.libs << 'lib' t.libs << 'test' t.pattern = 'test/**/*_test.rb' - t.verbose = true end desc 'Generate documentation for the <%= file_name %> plugin.' diff --git a/railties/lib/rails/tasks/testing.rake b/railties/lib/rails/tasks/testing.rake index fd5e52a05b..5bd4aa1596 100644 --- a/railties/lib/rails/tasks/testing.rake +++ b/railties/lib/rails/tasks/testing.rake @@ -59,7 +59,6 @@ namespace :test do recent_tests('app/controllers/**/*.rb', 'test/functional', since) t.libs << 'test' - t.verbose = true t.test_files = touched.uniq end Rake::Task['test:recent'].comment = "Test recent changes" @@ -84,35 +83,30 @@ namespace :test do end t.libs << 'test' - t.verbose = true end Rake::Task['test:uncommitted'].comment = "Test changes since last checkin (only Subversion and Git)" Rake::TestTask.new(:units => "db:test:prepare") do |t| t.libs << "test" t.pattern = 'test/unit/**/*_test.rb' - t.verbose = true end Rake::Task['test:units'].comment = "Run the unit tests in test/unit" Rake::TestTask.new(:functionals => "db:test:prepare") do |t| t.libs << "test" t.pattern = 'test/functional/**/*_test.rb' - t.verbose = true end Rake::Task['test:functionals'].comment = "Run the functional tests in test/functional" Rake::TestTask.new(:integration => "db:test:prepare") do |t| t.libs << "test" t.pattern = 'test/integration/**/*_test.rb' - t.verbose = true end Rake::Task['test:integration'].comment = "Run the integration tests in test/integration" Rake::TestTask.new(:benchmark => 'db:test:prepare') do |t| t.libs << 'test' t.pattern = 'test/performance/**/*_test.rb' - t.verbose = true t.options = '-- --benchmark' end Rake::Task['test:benchmark'].comment = 'Benchmark the performance tests' @@ -120,7 +114,6 @@ namespace :test do Rake::TestTask.new(:profile => 'db:test:prepare') do |t| t.libs << 'test' t.pattern = 'test/performance/**/*_test.rb' - t.verbose = true end Rake::Task['test:profile'].comment = 'Profile the performance tests' @@ -132,8 +125,6 @@ namespace :test do else t.pattern = 'vendor/plugins/*/**/test/**/*_test.rb' end - - t.verbose = true end Rake::Task['test:plugins'].comment = "Run the plugin tests in vendor/plugins/*/**/test (or specify with PLUGIN=name)" end -- cgit v1.2.3 From d8c5ea76bce3bdc810f3d06af7908c6e474b154c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 24 Nov 2009 10:46:16 -0800 Subject: Use Rails.env after loading environment --- railties/lib/rails/commands/console.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb index fc22ad64a9..8807307271 100644 --- a/railties/lib/rails/commands/console.rb +++ b/railties/lib/rails/commands/console.rb @@ -41,10 +41,10 @@ module Rails end if options[:sandbox] - puts "Loading #{ENV['RAILS_ENV']} environment in sandbox (Rails #{Rails.version})" + puts "Loading #{Rails.env} environment in sandbox (Rails #{Rails.version})" puts "Any modifications you make will be rolled back on exit" else - puts "Loading #{ENV['RAILS_ENV']} environment (Rails #{Rails.version})" + puts "Loading #{Rails.env} environment (Rails #{Rails.version})" end IRB.start end -- cgit v1.2.3 From a2cb90c0c246e772e9735ff59f5517e3dee313ff Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Tue, 24 Nov 2009 11:53:14 -0800 Subject: Refactor script/dbconsole into an object --- railties/lib/rails/commands/dbconsole.rb | 169 +++++++++++---------- .../rails/app/templates/script/dbconsole | 1 + 2 files changed, 90 insertions(+), 80 deletions(-) diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb index 4e699acf6b..064373f8f9 100644 --- a/railties/lib/rails/commands/dbconsole.rb +++ b/railties/lib/rails/commands/dbconsole.rb @@ -2,86 +2,95 @@ require 'erb' require 'yaml' require 'optparse' -include_password = false -options = {} - -OptionParser.new do |opt| - opt.banner = "Usage: dbconsole [options] [environment]" - opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v| - include_password = true - end - - opt.on("--mode [MODE]", ['html', 'list', 'line', 'column'], - "Automatically put the sqlite3 database in the specified mode (html, list, line, column).") do |mode| - options['mode'] = mode - end - - opt.on("-h", "--header") do |h| - options['header'] = h - end - - opt.parse!(ARGV) - abort opt.to_s unless (0..1).include?(ARGV.size) -end - -env = ARGV.first || ENV['RAILS_ENV'] || 'development' -unless config = YAML::load(ERB.new(IO.read(Rails.root + "/config/database.yml")).result)[env] - abort "No database is configured for the environment '#{env}'" -end - - -def find_cmd(*commands) - dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR) - commands += commands.map{|cmd| "#{cmd}.exe"} if RUBY_PLATFORM =~ /win32/ +module Rails + class DBConsole + def self.start + new.start + end - full_path_command = nil - found = commands.detect do |cmd| - dir = dirs_on_path.detect do |path| - full_path_command = File.join(path, cmd) - File.executable? full_path_command + def start + include_password = false + options = {} + OptionParser.new do |opt| + opt.banner = "Usage: dbconsole [options] [environment]" + opt.on("-p", "--include-password", "Automatically provide the password from database.yml") do |v| + include_password = true + end + + opt.on("--mode [MODE]", ['html', 'list', 'line', 'column'], + "Automatically put the sqlite3 database in the specified mode (html, list, line, column).") do |mode| + options['mode'] = mode + end + + opt.on("-h", "--header") do |h| + options['header'] = h + end + + opt.parse!(ARGV) + abort opt.to_s unless (0..1).include?(ARGV.size) + end + + env = ARGV.first || ENV['RAILS_ENV'] || 'development' + unless config = YAML::load(ERB.new(IO.read("#{Rails.root}/config/database.yml")).result)[env] + abort "No database is configured for the environment '#{env}'" + end + + + def find_cmd(*commands) + dirs_on_path = ENV['PATH'].to_s.split(File::PATH_SEPARATOR) + commands += commands.map{|cmd| "#{cmd}.exe"} if RUBY_PLATFORM =~ /win32/ + + full_path_command = nil + found = commands.detect do |cmd| + dir = dirs_on_path.detect do |path| + full_path_command = File.join(path, cmd) + File.executable? full_path_command + end + end + found ? full_path_command : abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.") + end + + case config["adapter"] + when "mysql" + args = { + 'host' => '--host', + 'port' => '--port', + 'socket' => '--socket', + 'username' => '--user', + 'encoding' => '--default-character-set' + }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact + + if config['password'] && include_password + args << "--password=#{config['password']}" + elsif config['password'] && !config['password'].to_s.empty? + args << "-p" + end + + args << config['database'] + + exec(find_cmd('mysql', 'mysql5'), *args) + + when "postgresql" + ENV['PGUSER'] = config["username"] if config["username"] + ENV['PGHOST'] = config["host"] if config["host"] + ENV['PGPORT'] = config["port"].to_s if config["port"] + ENV['PGPASSWORD'] = config["password"].to_s if config["password"] && include_password + exec(find_cmd('psql'), config["database"]) + + when "sqlite" + exec(find_cmd('sqlite'), config["database"]) + + when "sqlite3" + args = [] + + args << "-#{options['mode']}" if options['mode'] + args << "-header" if options['header'] + args << config['database'] + + exec(find_cmd('sqlite3'), *args) + else + abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!" + end end end - found ? full_path_command : abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.") -end - -case config["adapter"] -when "mysql" - args = { - 'host' => '--host', - 'port' => '--port', - 'socket' => '--socket', - 'username' => '--user', - 'encoding' => '--default-character-set' - }.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact - - if config['password'] && include_password - args << "--password=#{config['password']}" - elsif config['password'] && !config['password'].to_s.empty? - args << "-p" - end - - args << config['database'] - - exec(find_cmd('mysql', 'mysql5'), *args) - -when "postgresql" - ENV['PGUSER'] = config["username"] if config["username"] - ENV['PGHOST'] = config["host"] if config["host"] - ENV['PGPORT'] = config["port"].to_s if config["port"] - ENV['PGPASSWORD'] = config["password"].to_s if config["password"] && include_password - exec(find_cmd('psql'), config["database"]) - -when "sqlite" - exec(find_cmd('sqlite'), config["database"]) - -when "sqlite3" - args = [] - - args << "-#{options['mode']}" if options['mode'] - args << "-header" if options['header'] - args << config['database'] - - exec(find_cmd('sqlite3'), *args) -else - abort "Unknown command-line client for #{config['database']}. Submit a Rails patch to add support!" -end +end \ No newline at end of file diff --git a/railties/lib/rails/generators/rails/app/templates/script/dbconsole b/railties/lib/rails/generators/rails/app/templates/script/dbconsole index e6a1c59394..904df54ff2 100755 --- a/railties/lib/rails/generators/rails/app/templates/script/dbconsole +++ b/railties/lib/rails/generators/rails/app/templates/script/dbconsole @@ -1,2 +1,3 @@ require File.expand_path('../../config/application', __FILE__) require 'rails/commands/dbconsole' +Rails::DBConsole.start \ No newline at end of file -- cgit v1.2.3 From 530b8ff5ae811e0dd5e1343b478f42eed6fffdbd Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Tue, 24 Nov 2009 13:03:24 -0800 Subject: Have script/* and Rakefile use the application object --- railties/lib/rails/application.rb | 18 +++++++++++++----- railties/lib/rails/commands/console.rb | 10 +++++++--- railties/lib/rails/commands/dbconsole.rb | 10 +++++++--- railties/lib/rails/commands/server.rb | 19 ++++++++++++++++--- .../lib/rails/generators/rails/app/app_generator.rb | 8 ++++++-- .../rails/generators/rails/app/templates/Rakefile | 2 +- .../rails/generators/rails/app/templates/config.ru | 2 +- .../rails/app/templates/config/application.rb | 2 +- .../rails/app/templates/config/environment.rb | 2 +- .../generators/rails/app/templates/script/console | 3 --- .../rails/app/templates/script/console.tt | 3 +++ .../generators/rails/app/templates/script/dbconsole | 3 --- .../rails/app/templates/script/dbconsole.tt | 3 +++ .../generators/rails/app/templates/script/server | 3 --- .../generators/rails/app/templates/script/server.tt | 3 +++ railties/lib/rails/rack/log_tailer.rb | 4 +--- railties/lib/rails/tasks/misc.rake | 4 ---- railties/test/application/load_test.rb | 21 ++++----------------- 18 files changed, 67 insertions(+), 53 deletions(-) delete mode 100755 railties/lib/rails/generators/rails/app/templates/script/console create mode 100755 railties/lib/rails/generators/rails/app/templates/script/console.tt delete mode 100755 railties/lib/rails/generators/rails/app/templates/script/dbconsole create mode 100755 railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt delete mode 100755 railties/lib/rails/generators/rails/app/templates/script/server create mode 100755 railties/lib/rails/generators/rails/app/templates/script/server.tt diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 7c2d8eab67..e49ea8973b 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -3,11 +3,6 @@ module Rails include Initializable class << self - def inherited(klass) - Rails.application ||= klass unless klass.name =~ /Rails/ - super - end - # Stub out App initialize def initialize! new @@ -32,12 +27,21 @@ module Rails config.root end + def load_tasks + require "rails/tasks" + task :environment do + $rails_rake_task = true + initialize! + end + end + def call(env) new.call(env) end end def initialize + Rails.application ||= self run_initializers(self) end @@ -45,6 +49,10 @@ module Rails self.class.config end + def root + config.root + end + alias configuration config def middleware diff --git a/railties/lib/rails/commands/console.rb b/railties/lib/rails/commands/console.rb index 8807307271..37eb6d40ea 100644 --- a/railties/lib/rails/commands/console.rb +++ b/railties/lib/rails/commands/console.rb @@ -6,8 +6,12 @@ module Rails class Console ENVIRONMENTS = %w(production development test) - def self.start - new.start + def self.start(app) + new(app).start + end + + def initialize(app) + @app = app end def start @@ -25,7 +29,7 @@ module Rails ENV['RAILS_ENV'] = ENVIRONMENTS.find { |e| e.index(env) } || env end - require "#{Rails.root}/config/environment" + @app.initialize! require "rails/console_app" require "rails/console_sandbox" if options[:sandbox] require "rails/console_with_helpers" diff --git a/railties/lib/rails/commands/dbconsole.rb b/railties/lib/rails/commands/dbconsole.rb index 064373f8f9..77c3404343 100644 --- a/railties/lib/rails/commands/dbconsole.rb +++ b/railties/lib/rails/commands/dbconsole.rb @@ -4,8 +4,12 @@ require 'optparse' module Rails class DBConsole - def self.start - new.start + def self.start(app) + new(app).start + end + + def initialize(app) + @app = app end def start @@ -31,7 +35,7 @@ module Rails end env = ARGV.first || ENV['RAILS_ENV'] || 'development' - unless config = YAML::load(ERB.new(IO.read("#{Rails.root}/config/database.yml")).result)[env] + unless config = YAML::load(ERB.new(IO.read("#{@app.root}/config/database.yml")).result)[env] abort "No database is configured for the environment '#{env}'" end diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index ff2282a534..57b7c6a49c 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -37,6 +37,15 @@ module Rails Options.new end + def self.start(app) + new(app).start + end + + def initialize(app) + super() # Call Rack::Server#initialize without passing any options to use. + @app = app + end + def start puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" puts "=> Rails #{Rails.version} application starting on http://#{options[:Host]}:#{options[:Port]}" @@ -54,20 +63,24 @@ module Rails def middleware middlewares = [] - middlewares << [Rails::Rack::LogTailer] unless options[:daemonize] + middlewares << [Rails::Rack::LogTailer, log_path] unless options[:daemonize] middlewares << [Rails::Rack::Debugger] if options[:debugger] Hash.new(middlewares) end + def log_path + "#{File.expand_path(@app.root)}/log/#{options[:environment]}.log" + end + def default_options { :Port => 3000, :Host => "0.0.0.0", :environment => (ENV['RAILS_ENV'] || "development").dup, - :rack_file => "#{Rails.root}/config.ru", + :rack_file => "#{@app.root}/config.ru", :daemonize => false, :debugger => false, - :pid => "#{Rails.root}/tmp/pids/server.pid", + :pid => "#{@app.root}/tmp/pids/server.pid", :AccessLog => [] } end diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 2bcea4bc8f..7e21a6d1d9 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -48,9 +48,9 @@ module Rails::Generators end def create_root_files - copy_file "Rakefile" copy_file "README" - copy_file "config.ru" + template "Rakefile" + template "config.ru" template "Gemfile" end @@ -181,6 +181,10 @@ module Rails::Generators @app_name ||= File.basename(destination_root) end + def app_const + @app_const ||= app_name.classify + end + def app_secret ActiveSupport::SecureRandom.hex(64) end diff --git a/railties/lib/rails/generators/rails/app/templates/Rakefile b/railties/lib/rails/generators/rails/app/templates/Rakefile index 6b6d07e8cc..c19ad0e945 100755 --- a/railties/lib/rails/generators/rails/app/templates/Rakefile +++ b/railties/lib/rails/generators/rails/app/templates/Rakefile @@ -7,4 +7,4 @@ require 'rake' require 'rake/testtask' require 'rake/rdoctask' -require 'rails/tasks' +<%= app_const %>.load_tasks diff --git a/railties/lib/rails/generators/rails/app/templates/config.ru b/railties/lib/rails/generators/rails/app/templates/config.ru index 509a0da5b7..f3bf3d6117 100644 --- a/railties/lib/rails/generators/rails/app/templates/config.ru +++ b/railties/lib/rails/generators/rails/app/templates/config.ru @@ -2,4 +2,4 @@ require ::File.expand_path('../config/environment', __FILE__) # Dispatch the request -run Rails.application +run <%= app_const%> diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index 8008c6ba07..2c17de2a23 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -1,6 +1,6 @@ require File.expand_path('../boot', __FILE__) -Rails::Initializer.run do |config| +class <%= app_const %> < Rails::Application # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. diff --git a/railties/lib/rails/generators/rails/app/templates/config/environment.rb b/railties/lib/rails/generators/rails/app/templates/config/environment.rb index 0bb191f205..1684986a59 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environment.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/environment.rb @@ -2,4 +2,4 @@ require File.expand_path('../application', __FILE__) # Initialize the rails application -Rails.initialize! +<%= app_const %>.initialize! diff --git a/railties/lib/rails/generators/rails/app/templates/script/console b/railties/lib/rails/generators/rails/app/templates/script/console deleted file mode 100755 index 6043f3792b..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/script/console +++ /dev/null @@ -1,3 +0,0 @@ -require File.expand_path('../../config/application', __FILE__) -require 'rails/commands/console' -Rails::Console.start diff --git a/railties/lib/rails/generators/rails/app/templates/script/console.tt b/railties/lib/rails/generators/rails/app/templates/script/console.tt new file mode 100755 index 0000000000..4262439e52 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/script/console.tt @@ -0,0 +1,3 @@ +require File.expand_path('../../config/application', __FILE__) +require 'rails/commands/console' +Rails::Console.start(<%= app_const %>) diff --git a/railties/lib/rails/generators/rails/app/templates/script/dbconsole b/railties/lib/rails/generators/rails/app/templates/script/dbconsole deleted file mode 100755 index 904df54ff2..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/script/dbconsole +++ /dev/null @@ -1,3 +0,0 @@ -require File.expand_path('../../config/application', __FILE__) -require 'rails/commands/dbconsole' -Rails::DBConsole.start \ No newline at end of file diff --git a/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt b/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt new file mode 100755 index 0000000000..9dfa24c378 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/script/dbconsole.tt @@ -0,0 +1,3 @@ +require File.expand_path('../../config/application', __FILE__) +require 'rails/commands/dbconsole' +Rails::DBConsole.start(<%= app_const %>) \ No newline at end of file diff --git a/railties/lib/rails/generators/rails/app/templates/script/server b/railties/lib/rails/generators/rails/app/templates/script/server deleted file mode 100755 index 709ca002df..0000000000 --- a/railties/lib/rails/generators/rails/app/templates/script/server +++ /dev/null @@ -1,3 +0,0 @@ -require File.expand_path('../../config/application', __FILE__) -require 'rails/commands/server' -Rails::Server.start diff --git a/railties/lib/rails/generators/rails/app/templates/script/server.tt b/railties/lib/rails/generators/rails/app/templates/script/server.tt new file mode 100755 index 0000000000..d98f677475 --- /dev/null +++ b/railties/lib/rails/generators/rails/app/templates/script/server.tt @@ -0,0 +1,3 @@ +require File.expand_path('../../config/application', __FILE__) +require 'rails/commands/server' +Rails::Server.start(<%= app_const %>) diff --git a/railties/lib/rails/rack/log_tailer.rb b/railties/lib/rails/rack/log_tailer.rb index a237cee6bc..077311be3c 100644 --- a/railties/lib/rails/rack/log_tailer.rb +++ b/railties/lib/rails/rack/log_tailer.rb @@ -1,12 +1,10 @@ module Rails module Rack class LogTailer - EnvironmentLog = "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log" - def initialize(app, log = nil) @app = app - path = Pathname.new(log || EnvironmentLog).cleanpath + path = Pathname.new(log || "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log").cleanpath @cursor = ::File.size(path) @last_checked = Time.now.to_f diff --git a/railties/lib/rails/tasks/misc.rake b/railties/lib/rails/tasks/misc.rake index 7f244ebaed..9433b3556a 100644 --- a/railties/lib/rails/tasks/misc.rake +++ b/railties/lib/rails/tasks/misc.rake @@ -1,8 +1,4 @@ task :default => :test -task :environment do - $rails_rake_task = true - require(File.join(Rails.root, 'config', 'environment')) -end task :rails_env do unless defined? RAILS_ENV diff --git a/railties/test/application/load_test.rb b/railties/test/application/load_test.rb index 3da51c4355..dc5d03b323 100644 --- a/railties/test/application/load_test.rb +++ b/railties/test/application/load_test.rb @@ -7,21 +7,8 @@ module ApplicationTests include ActiveSupport::Testing::Isolation def rackup - config = "#{app_path}/config.ru" - # Copied from ActionDispatch::Utils.parse_config - # ActionDispatch is not necessarily available at this point. - require 'rack' - 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 + require "rack" + Rack::Builder.parse_file("#{app_path}/config.ru") end def setup @@ -40,14 +27,14 @@ module ApplicationTests test "Rails.application is available after config.ru has been racked up" do rackup - assert Rails.application < Rails::Application + assert Rails.application.is_a?(Rails::Application) end # Passenger still uses AC::Dispatcher, so we need to # keep it working for now test "deprecated ActionController::Dispatcher still works" do rackup - assert ActionController::Dispatcher.new < Rails::Application + assert ActionController::Dispatcher.new.is_a?(Rails::Application) end test "the config object is available on the application object" do -- cgit v1.2.3 From 71a6b0465be2ebbc9268423bafb1e764ce044b9d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 24 Nov 2009 17:15:35 -0800 Subject: Revert "macruby: no format_datetime or msg2str" Obviated by fix in macruby trunk. This reverts commit 6d91e7dca163ef55dde319452b21b767e8a0df33. --- activesupport/lib/active_support/core_ext/logger.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb index e4df8fe338..22749229a3 100644 --- a/activesupport/lib/active_support/core_ext/logger.rb +++ b/activesupport/lib/active_support/core_ext/logger.rb @@ -137,10 +137,10 @@ class Logger attr_writer :formatter public :formatter= - alias old_format_datetime format_datetime if method_defined?(:format_datetime) + alias old_format_datetime format_datetime def format_datetime(datetime) datetime end - alias old_msg2str msg2str if method_defined?(:msg2str) + alias old_msg2str msg2str def msg2str(msg) msg end end end -- cgit v1.2.3 From 34eac1cf1cf52784b4c344b44034d98adebdcc28 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Wed, 25 Nov 2009 14:29:28 -0800 Subject: Load application specific rake tasks in the application object --- railties/lib/rails/application.rb | 2 ++ railties/lib/rails/tasks.rb | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index e49ea8973b..7ea79aa9c9 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -29,6 +29,8 @@ module Rails def load_tasks require "rails/tasks" + Dir["#{root}/vendor/plugins/*/**/tasks/**/*.rake"].sort.each { |ext| load ext } + Dir["#{root}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } task :environment do $rails_rake_task = true initialize! diff --git a/railties/lib/rails/tasks.rb b/railties/lib/rails/tasks.rb index 82113a297c..dc886f4a4d 100644 --- a/railties/lib/rails/tasks.rb +++ b/railties/lib/rails/tasks.rb @@ -16,8 +16,3 @@ $VERBOSE = nil ).each do |task| load "rails/tasks/#{task}.rake" end - -# Load any custom rakefile extensions -# TODO: Don't hardcode these paths. -Dir["#{Rails.root}/vendor/plugins/*/**/tasks/**/*.rake"].sort.each { |ext| load ext } -Dir["#{Rails.root}/lib/tasks/**/*.rake"].sort.each { |ext| load ext } -- cgit v1.2.3 From 25b8e996bd29384d37df9939cd651b550e709ee7 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Wed, 25 Nov 2009 14:45:12 -0800 Subject: Fix the rackup test to correctly use Rack::Builder.parse_file --- railties/test/application/load_test.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/railties/test/application/load_test.rb b/railties/test/application/load_test.rb index dc5d03b323..e17f1ebdb0 100644 --- a/railties/test/application/load_test.rb +++ b/railties/test/application/load_test.rb @@ -8,7 +8,8 @@ module ApplicationTests def rackup require "rack" - Rack::Builder.parse_file("#{app_path}/config.ru") + app, options = Rack::Builder.parse_file("#{app_path}/config.ru") + app end def setup -- cgit v1.2.3 From 02c3c9dfbcec05e3b0cecc062da8acd0cf7c53e0 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Wed, 25 Nov 2009 14:52:56 -0800 Subject: Fix the application.rb generator --- railties/lib/rails/generators/actions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 8677bf283b..57abc151d2 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -104,7 +104,7 @@ module Rails # file in config/environments. # def environment(data=nil, options={}, &block) - sentinel = "Rails::Initializer.run do |config|" + sentinel = /class [a-z_:]+ < Rails::Application/i data = block.call if !data && block_given? in_root do -- cgit v1.2.3 From 7e0aa35c20f06fd9ef245155e30e81cfb38bad05 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sat, 28 Nov 2009 13:34:05 +1300 Subject: avoid generating invalid SMTP commands in ruby pre 1.9 Signed-off-by: Michael Koziarski Conflicts: actionmailer/lib/action_mailer/base.rb --- .../lib/action_mailer/delivery_method/smtp.rb | 2 +- actionmailer/test/mail_service_test.rb | 34 ++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/actionmailer/lib/action_mailer/delivery_method/smtp.rb b/actionmailer/lib/action_mailer/delivery_method/smtp.rb index 86b0ae8329..95c117c9e0 100644 --- a/actionmailer/lib/action_mailer/delivery_method/smtp.rb +++ b/actionmailer/lib/action_mailer/delivery_method/smtp.rb @@ -16,7 +16,7 @@ module ActionMailer def perform_delivery(mail) destinations = mail.destinations mail.ready_to_send - sender = (mail['return-path'] && mail['return-path'].spec) || mail['from'] + sender = (mail['return-path'] && mail['return-path'].spec) || Array(mail.from).first smtp = Net::SMTP.new(settings[:address], settings[:port]) smtp.enable_starttls_auto if settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto) diff --git a/actionmailer/test/mail_service_test.rb b/actionmailer/test/mail_service_test.rb index c98f0a7601..697265b8ec 100644 --- a/actionmailer/test/mail_service_test.rb +++ b/actionmailer/test/mail_service_test.rb @@ -31,6 +31,18 @@ class TestMailer < ActionMailer::Base render :text => "Goodbye, Mr. #{recipient}" end + def from_with_name + from "System " + recipients "root@loudthinking.com" + body "Nothing to see here." + end + + def from_without_name + from "system@loudthinking.com" + recipients "root@loudthinking.com" + body "Nothing to see here." + end + def cc_bcc(recipient) recipients recipient subject "testing bcc/cc" @@ -487,6 +499,28 @@ class ActionMailerTest < Test::Unit::TestCase assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded end + def test_from_without_name_for_smtp + ActionMailer::Base.delivery_method = :smtp + TestMailer.deliver_from_without_name + + mail = MockSMTP.deliveries.first + assert_not_nil mail + mail, from, to = mail + + assert_equal 'system@loudthinking.com', from.to_s + end + + def test_from_with_name_for_smtp + ActionMailer::Base.delivery_method = :smtp + TestMailer.deliver_from_with_name + + mail = MockSMTP.deliveries.first + assert_not_nil mail + mail, from, to = mail + + assert_equal 'system@loudthinking.com', from.to_s + end + def test_reply_to expected = new_mail -- cgit v1.2.3 From eeb1afa20003050039eab7420003a775343754c1 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 24 Nov 2009 18:41:30 -0800 Subject: Expand path relative to __FILE__ --- .../generators/rails/generator/templates/%file_name%_generator.rb.tt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt b/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt index 675f00043f..d8757460e4 100644 --- a/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt +++ b/railties/lib/rails/generators/rails/generator/templates/%file_name%_generator.rb.tt @@ -1,5 +1,5 @@ class <%= class_name %>Generator < Rails::Generators::NamedBase def self.source_root - @source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates')) + @source_root ||= File.expand_path('../templates', __FILE__) end end -- cgit v1.2.3 From 6f7fc5824f2033c0f674b002dbee7f1c3f3384ac Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 24 Nov 2009 19:26:13 -0800 Subject: Revert "Create SyncListener. Since they do not rely on Thread, they can be used on Google App Engine." Take a step back on this API direction. This reverts commit 8104f65c3225453d13307c3c2733c2a8f99e491a. --- activesupport/lib/active_support/notifications.rb | 92 +++++++++-------------- activesupport/test/notifications_test.rb | 17 +---- 2 files changed, 35 insertions(+), 74 deletions(-) diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index e2540cd598..7a9f76b26a 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -41,7 +41,7 @@ module ActiveSupport # to subscribers in a thread. You can use any queue implementation you want. # module Notifications - mattr_accessor :queue, :listener + mattr_accessor :queue class << self delegate :instrument, :transaction_id, :transaction, :to => :instrumenter @@ -54,13 +54,8 @@ module ActiveSupport @publisher ||= Publisher.new(queue) end - def subscriber - @subscriber ||= Subscriber.new(queue) - end - - def subscribe(pattern=nil, options={}, &block) - with = options[:with] || listener - subscriber.bind(with, pattern).subscribe(&block) + def subscribe(pattern=nil, &block) + Subscriber.new(queue).bind(pattern).subscribe(&block) end end @@ -109,14 +104,13 @@ module ActiveSupport @queue = queue end - def bind(listener, pattern) - @listener = listener - @pattern = pattern + def bind(pattern) + @pattern = pattern self end def subscribe - @queue.subscribe(@listener, @pattern) do |*args| + @queue.subscribe(@pattern) do |*args| yield(*args) end end @@ -144,48 +138,6 @@ module ActiveSupport end end - class AsyncListener - def initialize(pattern, &block) - @pattern = pattern - @subscriber = block - @queue = Queue.new - Thread.new { consume } - end - - def publish(name, *args) - if !@pattern || @pattern === name.to_s - @queue << args.unshift(name) - end - end - - def consume - while args = @queue.shift - @subscriber.call(*args) - end - end - - def drained? - @queue.size.zero? - end - end - - class SyncListener - def initialize(pattern, &block) - @pattern = pattern - @subscriber = block - end - - def publish(name, *args) - if !@pattern || @pattern === name.to_s - @subscriber.call(*args.unshift(name)) - end - end - - def drained? - true - end - end - # This is a default queue implementation that ships with Notifications. It # consumes events in a thread and publish them to all registered subscribers. # @@ -198,16 +150,40 @@ module ActiveSupport @listeners.each { |l| l.publish(*args) } end - def subscribe(listener, pattern=nil, &block) - @listeners << listener.new(pattern, &block) + def subscribe(pattern=nil, &block) + @listeners << Listener.new(pattern, &block) end def drained? @listeners.all? &:drained? end + + class Listener + def initialize(pattern, &block) + @pattern = pattern + @subscriber = block + @queue = Queue.new + Thread.new { consume } + end + + def publish(name, *args) + if !@pattern || @pattern === name.to_s + @queue << args.unshift(name) + end + end + + def consume + while args = @queue.shift + @subscriber.call(*args) + end + end + + def drained? + @queue.size.zero? + end + end end end - Notifications.queue = Notifications::LittleFanout.new - Notifications.listener = Notifications::AsyncListener + Notifications.queue = Notifications::LittleFanout.new end diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index 35d44367cf..01106e83e9 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -176,21 +176,6 @@ class NotificationsMainTest < Test::Unit::TestCase assert_equal 1, @another.first.result end - def test_subscriber_allows_sync_listeners - @another = [] - ActiveSupport::Notifications.subscribe(/cache/, :with => ActiveSupport::Notifications::SyncListener) do |*args| - @another << ActiveSupport::Notifications::Event.new(*args) - end - - Thread.expects(:new).never - ActiveSupport::Notifications.instrument(:something){ 0 } - ActiveSupport::Notifications.instrument(:cache){ 1 } - - assert_equal 1, @another.size - assert_equal :cache, @another.first.name - assert_equal 1, @another.first.result - end - def test_with_several_consumers_and_several_events @another = [] ActiveSupport::Notifications.subscribe do |*args| @@ -216,6 +201,6 @@ class NotificationsMainTest < Test::Unit::TestCase private def drain - sleep(0.05) until ActiveSupport::Notifications.queue.drained? + sleep(0.1) until ActiveSupport::Notifications.queue.drained? end end -- cgit v1.2.3 From ddf681ce1d3e71aef913dd7f94c60b7622523f8b Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 24 Nov 2009 19:35:01 -0800 Subject: Expose a simple Queue#wait to block until all notifications are drained --- activesupport/lib/active_support/notifications.rb | 9 +++++++-- activesupport/test/notifications_test.rb | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index 7a9f76b26a..09b1aa1713 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -154,10 +154,15 @@ module ActiveSupport @listeners << Listener.new(pattern, &block) end - def drained? - @listeners.all? &:drained? + def wait + sleep 0.05 until drained? end + private + def drained? + @listeners.all? &:drained? + end + class Listener def initialize(pattern, &block) @pattern = pattern diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index 01106e83e9..3df2088ac9 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -201,6 +201,6 @@ class NotificationsMainTest < Test::Unit::TestCase private def drain - sleep(0.1) until ActiveSupport::Notifications.queue.drained? + ActiveSupport::Notifications.queue.wait end end -- cgit v1.2.3 From 02893d17053123cbf02b65c2fe549421c11a2604 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 24 Nov 2009 19:37:10 -0800 Subject: Remark that Listener is an implementation detail --- activesupport/lib/active_support/notifications.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index 09b1aa1713..316d80e064 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -163,7 +163,8 @@ module ActiveSupport @listeners.all? &:drained? end - class Listener + # Used for internal implementation only. + class Listener #:nodoc: def initialize(pattern, &block) @pattern = pattern @subscriber = block -- cgit v1.2.3 From 4f2a04cc085b9117e8af8079a95a063f671d7a3d Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 28 Nov 2009 12:49:07 -0800 Subject: Notifications: extract central Notifier, cordon off the internal Fanout implementation, and segregate instrumentation concerns --- actionpack/test/controller/caching_test.rb | 4 +- activesupport/lib/active_support/notifications.rb | 148 ++--------- .../lib/active_support/notifications/fanout.rb | 84 +++++++ .../active_support/notifications/instrumenter.rb | 47 ++++ activesupport/test/notifications_test.rb | 270 +++++++++------------ railties/test/application/notifications_test.rb | 30 +-- 6 files changed, 268 insertions(+), 315 deletions(-) create mode 100644 activesupport/lib/active_support/notifications/fanout.rb create mode 100644 activesupport/lib/active_support/notifications/instrumenter.rb diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 3ce90b6ccf..54de920740 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -632,13 +632,15 @@ class FragmentCachingTest < ActionController::TestCase def test_fragment_for_logging fragment_computed = false - ActiveSupport::Notifications.queue.expects(:publish).times(2) + events = [] + ActiveSupport::Notifications.subscribe { |*args| events << args } buffer = 'generated till now -> ' @controller.fragment_for(buffer, 'expensive') { fragment_computed = true } assert fragment_computed assert_equal 'generated till now -> ', buffer + assert_equal [:fragment_exist?, :write_fragment], events.map(&:first) end end diff --git a/activesupport/lib/active_support/notifications.rb b/activesupport/lib/active_support/notifications.rb index 316d80e064..d9bfcbfcab 100644 --- a/activesupport/lib/active_support/notifications.rb +++ b/activesupport/lib/active_support/notifications.rb @@ -1,7 +1,4 @@ -require 'thread' require 'active_support/core_ext/module/delegation' -require 'active_support/core_ext/module/attribute_accessors' -require 'active_support/secure_random' module ActiveSupport # Notifications provides an instrumentation API for Ruby. To instrument an @@ -41,155 +38,42 @@ module ActiveSupport # to subscribers in a thread. You can use any queue implementation you want. # module Notifications - mattr_accessor :queue + autoload :Instrumenter, 'active_support/notifications/instrumenter' + autoload :Event, 'active_support/notifications/instrumenter' + autoload :Fanout, 'active_support/notifications/fanout' class << self - delegate :instrument, :transaction_id, :transaction, :to => :instrumenter + attr_writer :notifier + delegate :publish, :subscribe, :instrument, :to => :notifier - def instrumenter - Thread.current[:notifications_instrumeter] ||= Instrumenter.new(publisher) - end - - def publisher - @publisher ||= Publisher.new(queue) - end - - def subscribe(pattern=nil, &block) - Subscriber.new(queue).bind(pattern).subscribe(&block) + def notifier + @notifier ||= Notifier.new end end - class Instrumenter - def initialize(publisher) - @publisher = publisher - @id = random_id - end - - def transaction - @id, old_id = random_id, @id - yield - ensure - @id = old_id - end - - def transaction_id - @id - end - - def instrument(name, payload={}) - time = Time.now - result = yield if block_given? - ensure - @publisher.publish(name, time, Time.now, result, @id, payload) - end - - private - def random_id - SecureRandom.hex(10) - end - end - - class Publisher - def initialize(queue) + class Notifier + def initialize(queue = Fanout.new) @queue = queue end def publish(*args) @queue.publish(*args) end - end - - class Subscriber - def initialize(queue) - @queue = queue - end - def bind(pattern) - @pattern = pattern - self - end - - def subscribe - @queue.subscribe(@pattern) do |*args| - yield(*args) - end - end - end - - class Event - attr_reader :name, :time, :end, :transaction_id, :result, :payload - - def initialize(name, start, ending, result, transaction_id, payload) - @name = name - @payload = payload.dup - @time = start - @transaction_id = transaction_id - @end = ending - @result = result - end - - def duration - @duration ||= 1000.0 * (@end - @time) - end - - def parent_of?(event) - start = (self.time - event.time) * 1000 - start <= 0 && (start + duration >= event.duration) - end - end - - # This is a default queue implementation that ships with Notifications. It - # consumes events in a thread and publish them to all registered subscribers. - # - class LittleFanout - def initialize - @listeners = [] - end - - def publish(*args) - @listeners.each { |l| l.publish(*args) } - end - - def subscribe(pattern=nil, &block) - @listeners << Listener.new(pattern, &block) + def subscribe(pattern = nil, &block) + @queue.bind(pattern).subscribe(&block) end def wait - sleep 0.05 until drained? + @queue.wait end - private - def drained? - @listeners.all? &:drained? - end - - # Used for internal implementation only. - class Listener #:nodoc: - def initialize(pattern, &block) - @pattern = pattern - @subscriber = block - @queue = Queue.new - Thread.new { consume } - end - - def publish(name, *args) - if !@pattern || @pattern === name.to_s - @queue << args.unshift(name) - end - end - - def consume - while args = @queue.shift - @subscriber.call(*args) - end - end + delegate :instrument, :to => :current_instrumenter - def drained? - @queue.size.zero? + private + def current_instrumenter + Thread.current[:"instrumentation_#{object_id}"] ||= Notifications::Instrumenter.new(self) end - end end end - - Notifications.queue = Notifications::LittleFanout.new end diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb new file mode 100644 index 0000000000..412d977b25 --- /dev/null +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -0,0 +1,84 @@ +require 'thread' + +module ActiveSupport + module Notifications + # This is a default queue implementation that ships with Notifications. It + # consumes events in a thread and publish them to all registered subscribers. + # + class Fanout + def initialize + @subscribers = [] + end + + def bind(pattern) + Binding.new(self, pattern) + end + + def subscribe(pattern = nil, &block) + @subscribers << Subscriber.new(pattern, &block) + end + + def publish(*args) + @subscribers.each { |s| s.publish(*args) } + end + + def wait + sleep(0.05) until @subscribers.all?(&:drained?) + end + + # Used for internal implementation only. + class Binding #:nodoc: + def initialize(queue, pattern) + @queue, @pattern = queue, pattern + end + + def subscribe(&block) + @queue.subscribe(@pattern, &block) + end + end + + # Used for internal implementation only. + class Subscriber #:nodoc: + def initialize(pattern, &block) + @pattern = + case pattern + when Regexp, NilClass + pattern + else + /^#{Regexp.escape(pattern.to_s)}/ + end + @block = block + @events = Queue.new + start_consumer + end + + def publish(name, *args) + push(name, args) if matches?(name) + end + + def consume + while args = @events.shift + @block.call(*args) + end + end + + def drained? + @events.size.zero? + end + + private + def start_consumer + Thread.new { consume } + end + + def matches?(name) + !@pattern || @pattern =~ name.to_s + end + + def push(name, args) + @events << args.unshift(name) + end + end + end + end +end diff --git a/activesupport/lib/active_support/notifications/instrumenter.rb b/activesupport/lib/active_support/notifications/instrumenter.rb new file mode 100644 index 0000000000..fb95422af2 --- /dev/null +++ b/activesupport/lib/active_support/notifications/instrumenter.rb @@ -0,0 +1,47 @@ +require 'active_support/secure_random' +require 'active_support/core_ext/module/delegation' + +module ActiveSupport + module Notifications + class Instrumenter + def initialize(notifier) + @id = unique_id + @notifier = notifier + end + + def instrument(name, payload={}) + time = Time.now + result = yield if block_given? + ensure + @notifier.publish(name, time, Time.now, result, @id, payload) + end + + private + def unique_id + SecureRandom.hex(10) + end + end + + class Event + attr_reader :name, :time, :end, :transaction_id, :result, :payload + + def initialize(name, start, ending, result, transaction_id, payload) + @name = name + @payload = payload.dup + @time = start + @transaction_id = transaction_id + @end = ending + @result = result + end + + def duration + @duration ||= 1000.0 * (@end - @time) + end + + def parent_of?(event) + start = (self.time - event.time) * 1000 + start <= 0 && (start + duration >= event.duration) + end + end + end +end diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index 3df2088ac9..93c61b2c83 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -1,206 +1,160 @@ require 'abstract_unit' -# Allow LittleFanout to be cleaned. -class ActiveSupport::Notifications::LittleFanout - def clear - @listeners.clear - end -end +module Notifications + class TestCase < ActiveSupport::TestCase + def setup + Thread.abort_on_exception = true + + @notifier = ActiveSupport::Notifications::Notifier.new + @events = [] + @notifier.subscribe { |*args| @events << event(*args) } + end -class NotificationsEventTest < Test::Unit::TestCase - def test_events_are_initialized_with_details - event = event(:foo, Time.now, Time.now + 1, 1, random_id, :payload => :bar) - assert_equal :foo, event.name - assert_equal Hash[:payload => :bar], event.payload - end + def teardown + Thread.abort_on_exception = false + end - def test_events_consumes_information_given_as_payload - time = Time.now - event = event(:foo, time, time + 0.01, 1, random_id, {}) + private + def event(*args) + ActiveSupport::Notifications::Event.new(*args) + end - assert_equal Hash.new, event.payload - assert_equal time, event.time - assert_equal 1, event.result - assert_equal 10.0, event.duration + def drain + @notifier.wait + end end - def test_event_is_parent_based_on_time_frame - time = Time.utc(2009, 01, 01, 0, 0, 1) + class PubSubTest < TestCase + def test_events_are_published_to_a_listener + @notifier.publish :foo + @notifier.wait + assert_equal [[:foo]], @events + end - parent = event(:foo, Time.utc(2009), Time.utc(2009) + 100, nil, random_id, {}) - child = event(:foo, time, time + 10, nil, random_id, {}) - not_child = event(:foo, time, time + 100, nil, random_id, {}) + def test_subscriber_with_pattern + events = [] + @notifier.subscribe('1') { |*args| events << args } - assert parent.parent_of?(child) - assert !child.parent_of?(parent) - assert !parent.parent_of?(not_child) - assert !not_child.parent_of?(parent) - end + @notifier.publish '1' + @notifier.publish '1.a' + @notifier.publish 'a.1' + @notifier.wait -protected + assert_equal [['1'], ['1.a']], events + end - def random_id - @random_id ||= ActiveSupport::SecureRandom.hex(10) - end + def test_subscriber_with_pattern_as_regexp + events = [] + @notifier.subscribe(/\d/) { |*args| events << args } - def event(*args) - ActiveSupport::Notifications::Event.new(*args) - end -end + @notifier.publish '1' + @notifier.publish 'a.1' + @notifier.publish '1.a' + @notifier.wait -class NotificationsMainTest < Test::Unit::TestCase - def setup - @events = [] - Thread.abort_on_exception = true - ActiveSupport::Notifications.subscribe do |*args| - @events << ActiveSupport::Notifications::Event.new(*args) + assert_equal [['1'], ['a.1'], ['1.a']], events end - end - def teardown - Thread.abort_on_exception = false - ActiveSupport::Notifications.queue.clear - end + def test_multiple_subscribers + @another = [] + @notifier.subscribe { |*args| @another << args } + @notifier.publish :foo + @notifier.wait - def test_notifications_returns_action_result - result = ActiveSupport::Notifications.instrument(:awesome, :payload => "notifications") do - 1 + 1 + assert_equal [[:foo]], @events + assert_equal [[:foo]], @another end - assert_equal 2, result + private + def event(*args) + args + end end - def test_events_are_published_to_a_listener - ActiveSupport::Notifications.instrument(:awesome, :payload => "notifications") do - 1 + 1 + class InstrumentationTest < TestCase + def test_instrument_returns_block_result + assert_equal 2, @notifier.instrument(:awesome) { 1 + 1 } end - drain + def test_nested_events_can_be_instrumented + @notifier.instrument(:awesome, :payload => "notifications") do + @notifier.instrument(:wot, :payload => "child") do + 1 + 1 + end - assert_equal 1, @events.size - assert_equal :awesome, @events.last.name - assert_equal Hash[:payload => "notifications"], @events.last.payload - end + drain - def test_nested_events_can_be_instrumented - ActiveSupport::Notifications.instrument(:awesome, :payload => "notifications") do - ActiveSupport::Notifications.instrument(:wot, :payload => "child") do - 1 + 1 + assert_equal 1, @events.size + assert_equal :wot, @events.first.name + assert_equal Hash[:payload => "child"], @events.first.payload end drain - assert_equal 1, @events.size - assert_equal :wot, @events.first.name - assert_equal Hash[:payload => "child"], @events.first.payload + assert_equal 2, @events.size + assert_equal :awesome, @events.last.name + assert_equal Hash[:payload => "notifications"], @events.last.payload end - drain - - assert_equal 2, @events.size - assert_equal :awesome, @events.last.name - assert_equal Hash[:payload => "notifications"], @events.last.payload - end - - def test_event_is_pushed_even_if_block_fails - ActiveSupport::Notifications.instrument(:awesome, :payload => "notifications") do - raise "OMG" - end rescue RuntimeError - - drain - - assert_equal 1, @events.size - assert_equal :awesome, @events.last.name - assert_equal Hash[:payload => "notifications"], @events.last.payload - end - - def test_event_is_pushed_even_without_block - ActiveSupport::Notifications.instrument(:awesome, :payload => "notifications") - drain - - assert_equal 1, @events.size - assert_equal :awesome, @events.last.name - assert_equal Hash[:payload => "notifications"], @events.last.payload - end - - def test_subscribed_in_a_transaction - @another = [] + def test_instrument_publishes_when_exception_is_raised + begin + @notifier.instrument(:awesome, :payload => "notifications") do + raise "OMG" + end + flunk + rescue + end - ActiveSupport::Notifications.subscribe("cache") do |*args| - @another << ActiveSupport::Notifications::Event.new(*args) - end + drain - ActiveSupport::Notifications.instrument(:cache){ 1 } - ActiveSupport::Notifications.transaction do - ActiveSupport::Notifications.instrument(:cache){ 1 } + assert_equal 1, @events.size + assert_equal :awesome, @events.last.name + assert_equal Hash[:payload => "notifications"], @events.last.payload end - ActiveSupport::Notifications.instrument(:cache){ 1 } - - drain - assert_equal 3, @another.size - before, during, after = @another.map {|e| e.transaction_id } - assert_equal before, after - assert_not_equal before, during - end - - def test_subscriber_with_pattern - @another = [] + def test_event_is_pushed_even_without_block + @notifier.instrument(:awesome, :payload => "notifications") + drain - ActiveSupport::Notifications.subscribe("cache") do |*args| - @another << ActiveSupport::Notifications::Event.new(*args) + assert_equal 1, @events.size + assert_equal :awesome, @events.last.name + assert_equal Hash[:payload => "notifications"], @events.last.payload end - - ActiveSupport::Notifications.instrument(:cache){ 1 } - - drain - - assert_equal 1, @another.size - assert_equal :cache, @another.first.name - assert_equal 1, @another.first.result end - def test_subscriber_with_pattern_as_regexp - @another = [] - ActiveSupport::Notifications.subscribe(/cache/) do |*args| - @another << ActiveSupport::Notifications::Event.new(*args) + class EventTest < TestCase + def test_events_are_initialized_with_details + event = event(:foo, Time.now, Time.now + 1, 1, random_id, :payload => :bar) + assert_equal :foo, event.name + assert_equal Hash[:payload => :bar], event.payload end - ActiveSupport::Notifications.instrument(:something){ 0 } - ActiveSupport::Notifications.instrument(:cache){ 1 } - - drain - - assert_equal 1, @another.size - assert_equal :cache, @another.first.name - assert_equal 1, @another.first.result - end + def test_events_consumes_information_given_as_payload + time = Time.now + event = event(:foo, time, time + 0.01, 1, random_id, {}) - def test_with_several_consumers_and_several_events - @another = [] - ActiveSupport::Notifications.subscribe do |*args| - @another << ActiveSupport::Notifications::Event.new(*args) + assert_equal Hash.new, event.payload + assert_equal time, event.time + assert_equal 1, event.result + assert_equal 10.0, event.duration end - 1.upto(100) do |i| - ActiveSupport::Notifications.instrument(:value){ i } - end + def test_event_is_parent_based_on_time_frame + time = Time.utc(2009, 01, 01, 0, 0, 1) - drain + parent = event(:foo, Time.utc(2009), Time.utc(2009) + 100, nil, random_id, {}) + child = event(:foo, time, time + 10, nil, random_id, {}) + not_child = event(:foo, time, time + 100, nil, random_id, {}) - assert_equal 100, @events.size - assert_equal :value, @events.first.name - assert_equal 1, @events.first.result - assert_equal 100, @events.last.result + assert parent.parent_of?(child) + assert !child.parent_of?(parent) + assert !parent.parent_of?(not_child) + assert !not_child.parent_of?(parent) + end - assert_equal 100, @another.size - assert_equal :value, @another.first.name - assert_equal 1, @another.first.result - assert_equal 100, @another.last.result + protected + def random_id + @random_id ||= ActiveSupport::SecureRandom.hex(10) + end end - - private - def drain - ActiveSupport::Notifications.queue.wait - end end diff --git a/railties/test/application/notifications_test.rb b/railties/test/application/notifications_test.rb index 28dfdfcd83..71e406f2c1 100644 --- a/railties/test/application/notifications_test.rb +++ b/railties/test/application/notifications_test.rb @@ -5,21 +5,8 @@ module ApplicationTests include ActiveSupport::Testing::Isolation class MyQueue - attr_reader :events, :subscribers - - def initialize - @events = [] - @subscribers = [] - @listeners = [] - end - def publish(name, *args) - @events << name - end - - def subscribe(listener, pattern=nil, &block) - @listeners << listener - @subscribers << pattern + raise name end end @@ -28,21 +15,16 @@ module ApplicationTests boot_rails require "rails" require "active_support/notifications" + @events = [] Rails::Initializer.run do |c| - c.notifications.queue = MyQueue.new - c.notifications.subscribe(/listening/) do - puts "Cool" - end + c.notifications.notifier = ActiveSupport::Notifications::Notifier.new(MyQueue.new) end end test "new queue is set" do - ActiveSupport::Notifications.instrument(:foo) - assert_equal :foo, ActiveSupport::Notifications.queue.events.first - end - - test "configuration subscribers are loaded" do - assert_equal 1, ActiveSupport::Notifications.queue.subscribers.count { |s| s == /listening/ } + assert_raise RuntimeError do + ActiveSupport::Notifications.publish('foo') + end end end end -- cgit v1.2.3 From 45d8ff08a449d694f33e42ec2f97515d790e3cf7 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sat, 28 Nov 2009 21:36:58 -0800 Subject: Remove reference to class that doesn't exist (ht: brynary) --- actionpack/lib/action_view.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index e95e84aeb5..f6f7ec0c8c 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -25,7 +25,7 @@ require File.join(File.dirname(__FILE__), "action_pack") module ActionView def self.load_all! - [Context, Base, InlineTemplate, TemplateError] + [Context, Base, TemplateError] end autoload :Base, 'action_view/base' -- cgit v1.2.3 From bb84cab2fceb470cd1e7fa1ceeb5e7c4c764f5de Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Sun, 29 Nov 2009 01:37:50 -0500 Subject: Update reference to deprecated constant to avoid warnings --- actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb index f8f6b424ca..07b4919934 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb @@ -7,7 +7,7 @@ names = traces.collect {|name, trace| name} %> -

RAILS_ROOT: <%= defined?(RAILS_ROOT) ? RAILS_ROOT : "unset" %>

+

Rails.root: <%= defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : "unset" %>

<% names.each do |name| %> -- cgit v1.2.3 From 327545c3ae904d1a9c67de3e280c182ed6418023 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 29 Nov 2009 02:30:35 -0800 Subject: Notifications: synchronous fanout queue pushes events to subscribers rather than having them concurrently pull --- .../lib/active_support/notifications/fanout.rb | 61 ++++++++++++++-------- activesupport/test/notifications_test.rb | 10 ++++ 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index 412d977b25..bb07e4765c 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -6,7 +6,8 @@ module ActiveSupport # consumes events in a thread and publish them to all registered subscribers. # class Fanout - def initialize + def initialize(sync = false) + @subscriber_klass = sync ? Subscriber : AsyncSubscriber @subscribers = [] end @@ -15,7 +16,7 @@ module ActiveSupport end def subscribe(pattern = nil, &block) - @subscribers << Subscriber.new(pattern, &block) + @subscribers << @subscriber_klass.new(pattern, &block) end def publish(*args) @@ -29,7 +30,14 @@ module ActiveSupport # Used for internal implementation only. class Binding #:nodoc: def initialize(queue, pattern) - @queue, @pattern = queue, pattern + @queue = queue + @pattern = + case pattern + when Regexp, NilClass + pattern + else + /^#{Regexp.escape(pattern.to_s)}/ + end end def subscribe(&block) @@ -37,33 +45,40 @@ module ActiveSupport end end - # Used for internal implementation only. class Subscriber #:nodoc: def initialize(pattern, &block) - @pattern = - case pattern - when Regexp, NilClass - pattern - else - /^#{Regexp.escape(pattern.to_s)}/ - end + @pattern = pattern @block = block - @events = Queue.new - start_consumer end - def publish(name, *args) - push(name, args) if matches?(name) + def publish(*args) + push(*args) if matches?(args.first) end - def consume - while args = @events.shift + def drained? + true + end + + private + def matches?(name) + !@pattern || @pattern =~ name.to_s + end + + def push(*args) @block.call(*args) end + end + + # Used for internal implementation only. + class AsyncSubscriber < Subscriber #:nodoc: + def initialize(pattern, &block) + super + @events = Queue.new + start_consumer end def drained? - @events.size.zero? + @events.empty? end private @@ -71,12 +86,14 @@ module ActiveSupport Thread.new { consume } end - def matches?(name) - !@pattern || @pattern =~ name.to_s + def consume + while args = @events.shift + @block.call(*args) + end end - def push(name, args) - @events << args.unshift(name) + def push(*args) + @events << args end end end diff --git a/activesupport/test/notifications_test.rb b/activesupport/test/notifications_test.rb index 93c61b2c83..4f880d0db7 100644 --- a/activesupport/test/notifications_test.rb +++ b/activesupport/test/notifications_test.rb @@ -71,6 +71,16 @@ module Notifications end end + class SyncPubSubTest < PubSubTest + def setup + Thread.abort_on_exception = true + + @notifier = ActiveSupport::Notifications::Notifier.new(ActiveSupport::Notifications::Fanout.new(true)) + @events = [] + @notifier.subscribe { |*args| @events << event(*args) } + end + end + class InstrumentationTest < TestCase def test_instrument_returns_block_result assert_equal 2, @notifier.instrument(:awesome) { 1 + 1 } -- cgit v1.2.3 From 3f025e64083c4a0cde4841254a88ef54780824f9 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 29 Nov 2009 15:23:27 -0600 Subject: Resource collection should be defined before member routes --- actionpack/lib/action_dispatch/routing/mapper.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index cfe7425a61..087d4ab478 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -75,18 +75,18 @@ module ActionDispatch with_scope_level(:resources, :name => singular) do yield if block_given? + collection do + get "", :to => :index, :as => plural + post "", :to => :create + get "new", :to => :new, :as => "new_#{singular}" + end + member do get "", :to => :show, :as => singular put "", :to => :update delete "", :to => :destroy get "edit", :to => :edit, :as => "edit_#{singular}" end - - collection do - get "", :to => :index, :as => plural - post "", :to => :create - get "new", :to => :new, :as => "new_#{singular}" - end end end end -- cgit v1.2.3 From b87c13242c43e03b12b0a8a497942b1778064bcd Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 29 Nov 2009 15:26:55 -0600 Subject: Remove rackmount submodule --- .gitmodules | 3 --- rack-mount | 1 - 2 files changed, 4 deletions(-) delete mode 160000 rack-mount diff --git a/.gitmodules b/.gitmodules index 20ed3ed9c9..fd4fd34d3e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ [submodule "arel"] path = arel url = git://github.com/rails/arel.git -[submodule "rack-mount"] - path = rack-mount - url = git://github.com/rails/rack-mount.git diff --git a/rack-mount b/rack-mount deleted file mode 160000 index 3784e633b4..0000000000 --- a/rack-mount +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3784e633b42f43a4131e02519be60080d179da21 -- cgit v1.2.3 From 8815fefd162bbb8f7c0214d48e9f4f2ee47823a4 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 29 Nov 2009 15:34:46 -0600 Subject: Bump required rack-mount version to 0.2.3 --- actionpack/actionpack.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index ccf6bf1e83..936279ad12 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |s| s.add_dependency('activemodel', '= 3.0.pre') s.add_dependency('rack', '~> 1.0.1') s.add_dependency('rack-test', '~> 0.5.0') - s.add_dependency('rack-mount', '~> 0.2.2') + s.add_dependency('rack-mount', '~> 0.2.3') s.add_dependency('erubis', '~> 2.6.5') s.require_path = 'lib' -- cgit v1.2.3 From 40ae2070d57867bac1c9e7f77c25ac7ac7b5a647 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 29 Nov 2009 16:59:44 -0600 Subject: Extract Resource and SingletonResource helper objects --- actionpack/lib/action_dispatch/routing/mapper.rb | 112 +++++++++++++++++------ 1 file changed, 84 insertions(+), 28 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 087d4ab478..9b00bd3dc0 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -2,6 +2,71 @@ module ActionDispatch module Routing class Mapper module Resources + class Resource #:nodoc: + attr_reader :plural, :singular + attr_reader :path_prefix, :name_prefix + + def initialize(entities, options = {}) + entities = entities.to_s + + @plural = entities.pluralize + @singular = entities.singularize + + @path_prefix = options[:path_prefix] + @name_prefix = options[:name_prefix] + end + + def name + plural + end + + def controller + plural + end + + def member_name + if name_prefix + "#{name_prefix}_#{singular}" + else + singular + end + end + + def collection_name + if name_prefix + "#{name_prefix}_#{plural}" + else + plural + end + end + + def new_name + if name_prefix + "new_#{name_prefix}_#{singular}" + else + "new_#{singular}" + end + end + + def edit_name + if name_prefix + "edit_#{name_prefix}_#{singular}" + else + "edit_#{singular}" + end + end + end + + class SingletonResource < Resource #:nodoc: + def initialize(entity, options = {}) + super(entity) + end + + def name + singular + end + end + def resource(*resources, &block) options = resources.last.is_a?(Hash) ? resources.pop : {} @@ -11,29 +76,27 @@ module ActionDispatch return self end - resource = resources.pop + name_prefix = @scope[:options][:name_prefix] if @scope[:options] + resource = SingletonResource.new(resources.pop, :name_prefix => name_prefix) if @scope[:scope_level] == :resources member do - resource(resource, options, &block) + resource(resource.name, options, &block) end return self end - singular = resource.to_s - plural = singular.pluralize - - controller(plural) do - namespace(resource) do - with_scope_level(:resource) do + controller(resource.controller) do + namespace(resource.name) do + with_scope_level(:resource, :name => resource.singular) do yield if block_given? - get "", :to => :show, :as => "#{singular}" + get "", :to => :show, :as => resource.member_name post "", :to => :create put "", :to => :update delete "", :to => :destroy - get "new", :to => :new, :as => "new_#{singular}" - get "edit", :to => :edit, :as => "edit_#{singular}" + get "new", :to => :new, :as => resource.new_name + get "edit", :to => :edit, :as => resource.edit_name end end end @@ -50,42 +113,35 @@ module ActionDispatch return self end - resource = resources.pop - - plural = resource.to_s - singular = plural.singularize + name_prefix = @scope[:options][:name_prefix] if @scope[:options] + resource = Resource.new(resources.pop, :name_prefix => name_prefix) if @scope[:scope_level] == :resources parent_resource = @scope[:scope_level_options][:name] with_scope_level(:member) do scope(":#{parent_resource}_id", :name_prefix => parent_resource) do - resources(resource, options, &block) + resources(resource.name, options, &block) end end return self end - if @scope[:options] && (prefix = @scope[:options][:name_prefix]) - plural = "#{prefix}_#{plural}" - singular = "#{prefix}_#{singular}" - end - - controller(resource) do - namespace(resource) do - with_scope_level(:resources, :name => singular) do + controller(resource.controller) do + namespace(resource.name) do + with_scope_level(:resources, :name => resource.singular) do yield if block_given? collection do - get "", :to => :index, :as => plural + get "", :to => :index, :as => resource.collection_name post "", :to => :create - get "new", :to => :new, :as => "new_#{singular}" + get "new", :to => :new, :as => resource.new_name end member do - get "", :to => :show, :as => singular + get "", :to => :show, :as => resource.member_name put "", :to => :update delete "", :to => :destroy - get "edit", :to => :edit, :as => "edit_#{singular}" + get "edit", :to => :edit, :as => resource.edit_name end end end -- cgit v1.2.3 From 5da01a92c741b3a9a020a4dec9ddf120c0484e20 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 29 Nov 2009 17:01:14 -0600 Subject: Make use of extract_options! --- actionpack/lib/action_dispatch/routing/mapper.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 9b00bd3dc0..9ec85daba8 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -68,7 +68,7 @@ module ActionDispatch end def resource(*resources, &block) - options = resources.last.is_a?(Hash) ? resources.pop : {} + options = resources.extract_options! if resources.length > 1 raise ArgumentError if block_given? @@ -105,7 +105,7 @@ module ActionDispatch end def resources(*resources, &block) - options = resources.last.is_a?(Hash) ? resources.pop : {} + options = resources.extract_options! if resources.length > 1 raise ArgumentError if block_given? @@ -173,7 +173,7 @@ module ActionDispatch end def match(*args) - options = args.last.is_a?(Hash) ? args.pop : {} + options = args.extract_options! args.push(options) case options.delete(:on) @@ -203,7 +203,7 @@ module ActionDispatch module Scoping def scope(*args) - options = args.last.is_a?(Hash) ? args.pop : {} + options = args.extract_options! constraints = options.delete(:constraints) || {} unless constraints.is_a?(Hash) @@ -300,7 +300,7 @@ module ActionDispatch end def match(*args) - options = args.last.is_a?(Hash) ? args.pop : {} + options = args.extract_options! if args.length > 1 args.each { |path| match(path, options) } @@ -384,7 +384,7 @@ module ActionDispatch private def map_method(method, *args, &block) - options = args.last.is_a?(Hash) ? args.pop : {} + options = args.extract_options! options[:via] = method args.push(options) match(*args, &block) -- cgit v1.2.3 From 312c3bfa247249a1562eb9d04c335f4d38a18b28 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 29 Nov 2009 17:39:37 -0600 Subject: Break down long match routing method --- actionpack/lib/action_dispatch/routing/mapper.rb | 76 +++++++++++++++++------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 9ec85daba8..57bbb55c1c 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -202,6 +202,12 @@ module ActionDispatch end module Scoping + def self.extended(object) + object.instance_eval do + @scope = {} + end + end + def scope(*args) options = args.extract_options! @@ -249,9 +255,24 @@ module ActionDispatch def constraints(constraints = {}) scope(:constraints => constraints) { yield } end + + def match(*args) + options = args.extract_options! + options = (@scope[:options] || {}).merge(options) + args.push(options) + super(*args) + end end class Constraints + def new(app, constraints = []) + if constraints.any? + super(app, constraints) + else + app + end + end + def initialize(app, constraints = []) @app, @constraints = app, constraints end @@ -273,7 +294,6 @@ module ActionDispatch def initialize(set) @set = set - @scope = {} extend Scoping extend Resources @@ -313,7 +333,6 @@ module ActionDispatch path = args.first - options = (@scope[:options] || {}).merge(options) conditions, defaults = {}, {} path = nil if path == "" @@ -345,29 +364,12 @@ module ActionDispatch conditions[:request_method] = Regexp.union(*via) end - defaults[:controller] = @scope[:controller].to_s if @scope[:controller] - - if options[:to].respond_to?(:call) - app = options[:to] - defaults.delete(:controller) - defaults.delete(:action) - elsif options[:to].is_a?(String) - defaults[:controller], defaults[:action] = options[:to].split('#') - elsif options[:to].is_a?(Symbol) - defaults[:action] = options[:to].to_s - end - app ||= Routing::RouteSet::Dispatcher.new(:defaults => defaults) + defaults[:controller] ||= @scope[:controller].to_s if @scope[:controller] - if app.is_a?(Routing::RouteSet::Dispatcher) - unless defaults.include?(:controller) || segment_keys.include?("controller") - raise ArgumentError, "missing :controller" - end - unless defaults.include?(:action) || segment_keys.include?("action") - raise ArgumentError, "missing :action" - end - end + app = initialize_app_endpoint(options, defaults) + validate_defaults!(app, defaults, segment_keys) + app = Constraints.new(app, blocks) - app = Constraints.new(app, blocks) if blocks.any? @set.add_route(app, conditions, requirements, defaults, options[:as]) self @@ -383,6 +385,34 @@ module ActionDispatch end private + def initialize_app_endpoint(options, defaults) + app = nil + + if options[:to].respond_to?(:call) + app = options[:to] + defaults.delete(:controller) + defaults.delete(:action) + elsif options[:to].is_a?(String) + defaults[:controller], defaults[:action] = options[:to].split('#') + elsif options[:to].is_a?(Symbol) + defaults[:action] = options[:to].to_s + end + + app || Routing::RouteSet::Dispatcher.new(:defaults => defaults) + end + + def validate_defaults!(app, defaults, segment_keys) + return unless app.is_a?(Routing::RouteSet::Dispatcher) + + unless defaults.include?(:controller) || segment_keys.include?("controller") + raise ArgumentError, "missing :controller" + end + + unless defaults.include?(:action) || segment_keys.include?("action") + raise ArgumentError, "missing :action" + end + end + def map_method(method, *args, &block) options = args.extract_options! options[:via] = method -- cgit v1.2.3 From f69f9820ee84f32bb53d001efd6ebc79517fb0e1 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 29 Nov 2009 17:45:12 -0600 Subject: Wrap up http related routing helpers --- actionpack/lib/action_dispatch/routing/mapper.rb | 70 +++++++++++++----------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 57bbb55c1c..8dfac30ac0 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -264,6 +264,42 @@ module ActionDispatch end end + module HttpHelpers + def get(*args, &block) + map_method(:get, *args, &block) + end + + def post(*args, &block) + map_method(:post, *args, &block) + end + + def put(*args, &block) + map_method(:put, *args, &block) + end + + def delete(*args, &block) + map_method(:delete, *args, &block) + end + + def redirect(path, options = {}) + status = options[:status] || 301 + lambda { |env| + req = Rack::Request.new(env) + url = req.scheme + '://' + req.host + path + [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] + } + end + + private + def map_method(method, *args, &block) + options = args.extract_options! + options[:via] = method + args.push(options) + match(*args, &block) + self + end + end + class Constraints def new(app, constraints = []) if constraints.any? @@ -295,26 +331,11 @@ module ActionDispatch def initialize(set) @set = set + extend HttpHelpers extend Scoping extend Resources end - def get(*args, &block) - map_method(:get, *args, &block) - end - - def post(*args, &block) - map_method(:post, *args, &block) - end - - def put(*args, &block) - map_method(:put, *args, &block) - end - - def delete(*args, &block) - map_method(:delete, *args, &block) - end - def root(options = {}) match '/', options.merge(:as => :root) end @@ -375,15 +396,6 @@ module ActionDispatch self end - def redirect(path, options = {}) - status = options[:status] || 301 - lambda { |env| - req = Rack::Request.new(env) - url = req.scheme + '://' + req.host + path - [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] - } - end - private def initialize_app_endpoint(options, defaults) app = nil @@ -412,14 +424,6 @@ module ActionDispatch raise ArgumentError, "missing :action" end end - - def map_method(method, *args, &block) - options = args.extract_options! - options[:via] = method - args.push(options) - match(*args, &block) - self - end end end end -- cgit v1.2.3 From 075f50d62cd02c2cc14b145cdb34bc9ee85cc83c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 29 Nov 2009 18:17:14 -0600 Subject: Fix some nested resource generation tests --- actionpack/lib/action_dispatch/routing/mapper.rb | 15 ++++++++++----- actionpack/test/dispatch/routing_test.rb | 8 ++------ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 8dfac30ac0..34d75e55b6 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -80,15 +80,19 @@ module ActionDispatch resource = SingletonResource.new(resources.pop, :name_prefix => name_prefix) if @scope[:scope_level] == :resources - member do - resource(resource.name, options, &block) + parent_resource = @scope[:scope_level_options][:name] + parent_named_prefix = @scope[:scope_level_options][:name_prefix] + with_scope_level(:member) do + scope(":#{parent_resource}_id", :name_prefix => parent_named_prefix) do + resource(resource.name, options, &block) + end end return self end controller(resource.controller) do namespace(resource.name) do - with_scope_level(:resource, :name => resource.singular) do + with_scope_level(:resource, :name => resource.singular, :name_prefix => resource.member_name) do yield if block_given? get "", :to => :show, :as => resource.member_name @@ -118,8 +122,9 @@ module ActionDispatch if @scope[:scope_level] == :resources parent_resource = @scope[:scope_level_options][:name] + parent_named_prefix = @scope[:scope_level_options][:name_prefix] with_scope_level(:member) do - scope(":#{parent_resource}_id", :name_prefix => parent_resource) do + scope(":#{parent_resource}_id", :name_prefix => parent_named_prefix) do resources(resource.name, options, &block) end end @@ -128,7 +133,7 @@ module ActionDispatch controller(resource.controller) do namespace(resource.name) do - with_scope_level(:resources, :name => resource.singular) do + with_scope_level(:resources, :name => resource.singular, :name_prefix => resource.member_name) do yield if block_given? collection do diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 496445fc34..b8bcdc2808 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -251,9 +251,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/projects/1/companies/1/people' assert_equal 'people#index', @response.body - pending do - assert_equal '/projects/1/companies/1/people', project_company_people_path(:project_id => '1', :company_id => '1') - end + assert_equal '/projects/1/companies/1/people', project_company_people_path(:project_id => '1', :company_id => '1') get '/projects/1/companies/1/avatar' assert_equal 'avatars#show', @response.body @@ -345,9 +343,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/projects/1/posts/1/comments' assert_equal 'comments#index', @response.body - pending do - assert_equal '/projects/1/posts/1/comments', project_post_comments_path(:project_id => '1', :post_id => '1') - end + assert_equal '/projects/1/posts/1/comments', project_post_comments_path(:project_id => '1', :post_id => '1') post '/projects/1/posts/1/comments/preview' assert_equal 'comments#preview', @response.body -- cgit v1.2.3 From 6ac32a83283f46b55675ddf4ecab6c91f6f8abde Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 29 Nov 2009 18:28:45 -0600 Subject: Define a welcome controller in mailer tests --- actionmailer/test/url_test.rb | 44 +++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/actionmailer/test/url_test.rb b/actionmailer/test/url_test.rb index 2224f6321c..18eb355e7d 100644 --- a/actionmailer/test/url_test.rb +++ b/actionmailer/test/url_test.rb @@ -1,7 +1,9 @@ require 'abstract_unit' -class TestMailer < ActionMailer::Base +class WelcomeController < ActionController::Base +end +class TestMailer < ActionMailer::Base default_url_options[:host] = 'www.basecamphq.com' def signed_up_with_url(recipient) @@ -52,27 +54,25 @@ class ActionMailerUrlTest < Test::Unit::TestCase end def test_signed_up_with_url - ActionController::Routing.use_controllers! ['welcome'] do - ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' - map.welcome 'welcome', :controller=>"foo", :action=>"bar" - end - - expected = new_mail - expected.to = @recipient - expected.subject = "[Signed up] Welcome #{@recipient}" - expected.body = "Hello there, \n\nMr. #{@recipient}. Please see our greeting at http://example.com/welcome/greeting http://www.basecamphq.com/welcome\n\n\"Somelogo\"" - expected.from = "system@loudthinking.com" - expected.date = Time.local(2004, 12, 12) - - created = nil - assert_nothing_raised { created = TestMailer.create_signed_up_with_url(@recipient) } - assert_not_nil created - assert_equal expected.encoded, created.encoded - - assert_nothing_raised { TestMailer.deliver_signed_up_with_url(@recipient) } - assert_not_nil ActionMailer::Base.deliveries.first - assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded + ActionController::Routing::Routes.draw do |map| + map.connect ':controller/:action/:id' + map.welcome 'welcome', :controller=>"foo", :action=>"bar" end + + expected = new_mail + expected.to = @recipient + expected.subject = "[Signed up] Welcome #{@recipient}" + expected.body = "Hello there, \n\nMr. #{@recipient}. Please see our greeting at http://example.com/welcome/greeting http://www.basecamphq.com/welcome\n\n\"Somelogo\"" + expected.from = "system@loudthinking.com" + expected.date = Time.local(2004, 12, 12) + + created = nil + assert_nothing_raised { created = TestMailer.create_signed_up_with_url(@recipient) } + assert_not_nil created + assert_equal expected.encoded, created.encoded + + assert_nothing_raised { TestMailer.deliver_signed_up_with_url(@recipient) } + assert_not_nil ActionMailer::Base.deliveries.first + assert_equal expected.encoded, ActionMailer::Base.deliveries.first.encoded end end -- cgit v1.2.3 From 6e30361260205cb7029fbc78b4a98b66a884ce45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 1 Dec 2009 13:11:24 -0200 Subject: Allow ActionController::Responder to have a common entry point for all formats. Signed-off-by: Yehuda Katz --- actionpack/lib/action_controller/metal/responder.rb | 11 ++++++++--- actionpack/test/controller/mime_responds_test.rb | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index e8e88e7479..6c76c57839 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -102,9 +102,14 @@ module ActionController #:nodoc: # not defined, call to_format. # def self.call(*args) - responder = new(*args) - method = :"to_#{responder.format}" - responder.respond_to?(method) ? responder.send(method) : responder.to_format + new(*args).respond + end + + # Main entry point for responder responsible to dispatch to the proper format. + # + def respond + method = :"to_#{format}" + respond_to?(method) ? send(method) : to_format end # HTML format does not render the resource, it always attempt to render a diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index fee9cf46f9..c1fa74b8c8 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -507,6 +507,13 @@ class RespondWithController < ActionController::Base end end + def using_responder_with_respond + responder = Class.new(ActionController::Responder) do + def respond; @controller.render :text => "respond #{format}"; end + end + respond_with(Customer.new("david", 13), :responder => responder) + end + protected def _render_js(js, options) @@ -735,6 +742,16 @@ class RespondWithControllerTest < ActionController::TestCase assert_equal "foo - #{[:html].to_s}", @controller.response_body end + def test_respond_as_responder_entry_point + @request.accept = "text/html" + get :using_responder_with_respond + assert_equal "respond html", @response.body + + @request.accept = "application/xml" + get :using_responder_with_respond + assert_equal "respond xml", @response.body + end + def test_clear_respond_to @controller = InheritedRespondWithController.new @request.accept = "text/html" -- cgit v1.2.3 From 0c4990b5f4b8c99ea656170eff9bb9c3490de642 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 1 Dec 2009 12:19:42 -0800 Subject: Fix caching test to ensure notifications are all delivered --- actionpack/test/controller/caching_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 54de920740..682a8f3995 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -640,6 +640,7 @@ class FragmentCachingTest < ActionController::TestCase assert fragment_computed assert_equal 'generated till now -> ', buffer + ActiveSupport::Notifications.notifier.wait assert_equal [:fragment_exist?, :write_fragment], events.map(&:first) end -- cgit v1.2.3 From 61a31f3d3dae55b3ed2a49fafcbfe45b77ea3be2 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 1 Dec 2009 14:52:19 -0600 Subject: Fix generating params with optional defaults [#3404 state:resolved] --- actionpack/lib/action_dispatch/routing/deprecated_mapper.rb | 2 +- actionpack/test/controller/routing_test.rb | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb index dd76391870..87dfaba6c7 100644 --- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb +++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb @@ -175,7 +175,7 @@ module ActionDispatch optional = false elsif segment =~ /^:(\w+)$/ if defaults.has_key?($1.to_sym) - defaults.delete($1.to_sym) + defaults.delete($1.to_sym) if defaults[$1.to_sym].nil? else optional = false end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index b5effeda40..97fbd95e73 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1639,9 +1639,7 @@ class RouteSetTest < ActiveSupport::TestCase map.connect ':controller/:action/:id' end - pending do - assert_equal '/ibocorp', set.generate({:controller => 'ibocorp', :page => 1}) - end + assert_equal '/ibocorp', set.generate({:controller => 'ibocorp', :page => 1}) end def test_generate_with_optional_params_recalls_last_request -- cgit v1.2.3 From fe319103dbb5f883b4320410a8b8e5bb64bfaebc Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 1 Dec 2009 13:18:14 -0800 Subject: Fix rails:update:scripts --- railties/lib/rails/tasks/framework.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index 1611d1d94d..2b8f3378fc 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -92,7 +92,7 @@ namespace :rails do namespace :update do def invoke_from_app_generator(method) - require 'generators' + require 'rails/generators' require 'rails/generators/rails/app/app_generator' generator = Rails::Generators::AppGenerator.new ["rails"], { :with_dispatchers => true }, -- cgit v1.2.3 From df9f5e771b0b3d5f2b95d3124514a1fd7db572ab Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 1 Dec 2009 13:27:59 -0800 Subject: Reinstate rails:update:configs task removed in e1fdc8b --- railties/lib/rails/tasks/framework.rake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index 2b8f3378fc..975c180f86 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -100,6 +100,11 @@ namespace :rails do generator.invoke(method) end + desc "Update config/boot.rb from your current rails install" + task :configs do + invoke_from_app_generator :create_boot_file + end + desc "Update Prototype javascripts from your current rails install" task :javascripts do invoke_from_app_generator :create_prototype_files -- cgit v1.2.3 From 544de096316b81c7cd890884537a513f2d22f6ba Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 1 Dec 2009 13:37:12 -0800 Subject: rails:update:configs updates boot.rb and the rest of config/* --- railties/lib/rails/tasks/framework.rake | 1 + 1 file changed, 1 insertion(+) diff --git a/railties/lib/rails/tasks/framework.rake b/railties/lib/rails/tasks/framework.rake index 975c180f86..f7b53885c8 100644 --- a/railties/lib/rails/tasks/framework.rake +++ b/railties/lib/rails/tasks/framework.rake @@ -103,6 +103,7 @@ namespace :rails do desc "Update config/boot.rb from your current rails install" task :configs do invoke_from_app_generator :create_boot_file + invoke_from_app_generator :create_config_files end desc "Update Prototype javascripts from your current rails install" -- cgit v1.2.3 From 74be70039f0718a2f8102302c9ea1ea5b2db681a Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 1 Dec 2009 15:07:41 -0800 Subject: Avoid using unrequired core extensions --- railties/lib/rails/tasks/testing.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/tasks/testing.rake b/railties/lib/rails/tasks/testing.rake index 5bd4aa1596..57857fb911 100644 --- a/railties/lib/rails/tasks/testing.rake +++ b/railties/lib/rails/tasks/testing.rake @@ -48,7 +48,7 @@ task :test do task end end.compact - abort "Errors running #{errors.to_sentence(:locale => :en)}!" if errors.any? + abort "Errors running #{errors * ', '}!" if errors.any? end namespace :test do -- cgit v1.2.3 From 28657e4f418c84dad08ae1f541d24df2d91d93aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 1 Dec 2009 23:42:07 -0200 Subject: Update vendored thor and ensure that content is completely modified before checking file collisions. Signed-off-by: Jeremy Kemper --- railties/lib/rails/generators.rb | 2 +- .../rails/generators/rails/app/app_generator.rb | 6 +- .../lib/rails/vendor/thor-0.12.0/CHANGELOG.rdoc | 82 ---- railties/lib/rails/vendor/thor-0.12.0/LICENSE | 20 - railties/lib/rails/vendor/thor-0.12.0/README.rdoc | 234 ---------- railties/lib/rails/vendor/thor-0.12.0/Thorfile | 63 --- railties/lib/rails/vendor/thor-0.12.0/lib/thor.rb | 242 ---------- .../rails/vendor/thor-0.12.0/lib/thor/actions.rb | 273 ----------- .../thor-0.12.0/lib/thor/actions/create_file.rb | 103 ---- .../thor-0.12.0/lib/thor/actions/directory.rb | 93 ---- .../lib/thor/actions/empty_directory.rb | 134 ------ .../lib/thor/actions/file_manipulation.rb | 219 --------- .../lib/thor/actions/inject_into_file.rb | 101 ---- .../lib/rails/vendor/thor-0.12.0/lib/thor/base.rb | 517 --------------------- .../thor/core_ext/hash_with_indifferent_access.rb | 75 --- .../thor-0.12.0/lib/thor/core_ext/ordered_hash.rb | 100 ---- .../lib/rails/vendor/thor-0.12.0/lib/thor/error.rb | 27 -- .../lib/rails/vendor/thor-0.12.0/lib/thor/group.rb | 263 ----------- .../vendor/thor-0.12.0/lib/thor/invocation.rb | 178 ------- .../rails/vendor/thor-0.12.0/lib/thor/parser.rb | 4 - .../vendor/thor-0.12.0/lib/thor/parser/argument.rb | 67 --- .../thor-0.12.0/lib/thor/parser/arguments.rb | 145 ------ .../vendor/thor-0.12.0/lib/thor/parser/option.rb | 132 ------ .../vendor/thor-0.12.0/lib/thor/parser/options.rb | 142 ------ .../vendor/thor-0.12.0/lib/thor/rake_compat.rb | 66 --- .../rails/vendor/thor-0.12.0/lib/thor/runner.rb | 299 ------------ .../lib/rails/vendor/thor-0.12.0/lib/thor/shell.rb | 78 ---- .../vendor/thor-0.12.0/lib/thor/shell/basic.rb | 219 --------- .../vendor/thor-0.12.0/lib/thor/shell/color.rb | 108 ----- .../lib/rails/vendor/thor-0.12.0/lib/thor/task.rb | 122 ----- .../lib/rails/vendor/thor-0.12.0/lib/thor/util.rb | 251 ---------- .../rails/vendor/thor-0.12.0/lib/thor/version.rb | 3 - .../lib/rails/vendor/thor-0.12.1/CHANGELOG.rdoc | 82 ++++ railties/lib/rails/vendor/thor-0.12.1/LICENSE | 20 + railties/lib/rails/vendor/thor-0.12.1/README.rdoc | 234 ++++++++++ railties/lib/rails/vendor/thor-0.12.1/Thorfile | 63 +++ railties/lib/rails/vendor/thor-0.12.1/lib/thor.rb | 242 ++++++++++ .../rails/vendor/thor-0.12.1/lib/thor/actions.rb | 273 +++++++++++ .../thor-0.12.1/lib/thor/actions/create_file.rb | 103 ++++ .../thor-0.12.1/lib/thor/actions/directory.rb | 91 ++++ .../lib/thor/actions/empty_directory.rb | 134 ++++++ .../lib/thor/actions/file_manipulation.rb | 223 +++++++++ .../lib/thor/actions/inject_into_file.rb | 101 ++++ .../lib/rails/vendor/thor-0.12.1/lib/thor/base.rb | 517 +++++++++++++++++++++ .../thor/core_ext/hash_with_indifferent_access.rb | 75 +++ .../thor-0.12.1/lib/thor/core_ext/ordered_hash.rb | 100 ++++ .../lib/rails/vendor/thor-0.12.1/lib/thor/error.rb | 27 ++ .../lib/rails/vendor/thor-0.12.1/lib/thor/group.rb | 263 +++++++++++ .../vendor/thor-0.12.1/lib/thor/invocation.rb | 178 +++++++ .../rails/vendor/thor-0.12.1/lib/thor/parser.rb | 4 + .../vendor/thor-0.12.1/lib/thor/parser/argument.rb | 67 +++ .../thor-0.12.1/lib/thor/parser/arguments.rb | 145 ++++++ .../vendor/thor-0.12.1/lib/thor/parser/option.rb | 132 ++++++ .../vendor/thor-0.12.1/lib/thor/parser/options.rb | 142 ++++++ .../vendor/thor-0.12.1/lib/thor/rake_compat.rb | 66 +++ .../rails/vendor/thor-0.12.1/lib/thor/runner.rb | 299 ++++++++++++ .../lib/rails/vendor/thor-0.12.1/lib/thor/shell.rb | 78 ++++ .../vendor/thor-0.12.1/lib/thor/shell/basic.rb | 219 +++++++++ .../vendor/thor-0.12.1/lib/thor/shell/color.rb | 108 +++++ .../lib/rails/vendor/thor-0.12.1/lib/thor/task.rb | 122 +++++ .../lib/rails/vendor/thor-0.12.1/lib/thor/util.rb | 251 ++++++++++ .../rails/vendor/thor-0.12.1/lib/thor/version.rb | 3 + 62 files changed, 4366 insertions(+), 4364 deletions(-) delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/CHANGELOG.rdoc delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/LICENSE delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/README.rdoc delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/Thorfile delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/create_file.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/directory.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/empty_directory.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/file_manipulation.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/inject_into_file.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/base.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/hash_with_indifferent_access.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/ordered_hash.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/error.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/group.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/invocation.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/argument.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/arguments.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/option.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/options.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/rake_compat.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/runner.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell/basic.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell/color.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/task.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/util.rb delete mode 100644 railties/lib/rails/vendor/thor-0.12.0/lib/thor/version.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/CHANGELOG.rdoc create mode 100644 railties/lib/rails/vendor/thor-0.12.1/LICENSE create mode 100644 railties/lib/rails/vendor/thor-0.12.1/README.rdoc create mode 100644 railties/lib/rails/vendor/thor-0.12.1/Thorfile create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/create_file.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/directory.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/empty_directory.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/file_manipulation.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/inject_into_file.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/base.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/hash_with_indifferent_access.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/ordered_hash.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/error.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/group.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/invocation.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/argument.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/arguments.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/option.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/options.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/rake_compat.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/runner.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/basic.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/color.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/task.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/util.rb create mode 100644 railties/lib/rails/vendor/thor-0.12.1/lib/thor/version.rb diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 85c2fd52a4..ffb9cfe1cd 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -9,7 +9,7 @@ require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/string/inflections' # TODO: Do not always push on vendored thor -$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/vendor/thor-0.12.0/lib") +$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/vendor/thor-0.12.1/lib") require 'rails/generators/base' require 'rails/generators/named_base' diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb index 7e21a6d1d9..ae18fa843b 100644 --- a/railties/lib/rails/generators/rails/app/app_generator.rb +++ b/railties/lib/rails/generators/rails/app/app_generator.rb @@ -123,10 +123,10 @@ module Rails::Generators end def create_script_files - directory "script" do |file| - prepend_file file, "#{shebang}\n", :verbose => false - chmod file, 0755, :verbose => false + directory "script" do |content| + "#{shebang}\n" + content end + chmod "script", 0755, :verbose => false end def create_test_files diff --git a/railties/lib/rails/vendor/thor-0.12.0/CHANGELOG.rdoc b/railties/lib/rails/vendor/thor-0.12.0/CHANGELOG.rdoc deleted file mode 100644 index adedfeca9d..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/CHANGELOG.rdoc +++ /dev/null @@ -1,82 +0,0 @@ -== TODO - -* Improve spec coverage for Thor::Runner - -== 0.12, released 2009-11-06 - -* [#7] Do not force white color on status -* [#8] Yield a block with the filename on directory - -== 0.11, released 2009-07-01 - -* Added a rake compatibility layer. It allows you to use spec and rdoc tasks on - Thor classes. - -* BACKWARDS INCOMPATIBLE: aliases are not generated automatically anymore - since it wrong behavior to the invocation system. - -* thor help now show information about any class/task. All those calls are - possible: - - thor help describe - thor help describe:amazing - - Or even with default namespaces: - - thor help :spec - -* Thor::Runner now invokes the default task if none is supplied: - - thor describe # invokes the default task, usually help - -* Thor::Runner now works with mappings: - - thor describe -h - -* Added some documentation and code refactoring. - -== 0.9.8, released 2008-10-20 - -* Fixed some tiny issues that were introduced lately. - -== 0.9.7, released 2008-10-13 - -* Setting global method options on the initialize method works as expected: - All other tasks will accept these global options in addition to their own. -* Added 'group' notion to Thor task sets (class Thor); by default all tasks - are in the 'standard' group. Running 'thor -T' will only show the standard - tasks - adding --all will show all tasks. You can also filter on a specific - group using the --group option: thor -T --group advanced - -== 0.9.6, released 2008-09-13 - -* Generic improvements - -== 0.9.5, released 2008-08-27 - -* Improve Windows compatibility -* Update (incorrect) README and task.thor sample file -* Options hash is now frozen (once returned) -* Allow magic predicates on options object. For instance: `options.force?` -* Add support for :numeric type -* BACKWARDS INCOMPATIBLE: Refactor Thor::Options. You cannot access shorthand forms in options hash anymore (for instance, options[:f]) -* Allow specifying optional args with default values: method_options(:user => "mislav") -* Don't write options for nil or false values. This allows, for example, turning color off when running specs. -* Exit with the status of the spec command to help CI stuff out some. - -== 0.9.4, released 2008-08-13 - -* Try to add Windows compatibility. -* BACKWARDS INCOMPATIBLE: options hash is now accessed as a property in your class and is not passed as last argument anymore -* Allow options at the beginning of the argument list as well as the end. -* Make options available with symbol keys in addition to string keys. -* Allow true to be passed to Thor#method_options to denote a boolean option. -* If loading a thor file fails, don't give up, just print a warning and keep going. -* Make sure that we re-raise errors if they happened further down the pipe than we care about. -* Only delete the old file on updating when the installation of the new one is a success -* Make it Ruby 1.8.5 compatible. -* Don't raise an error if a boolean switch is defined multiple times. -* Thor::Options now doesn't parse through things that look like options but aren't. -* Add URI detection to install task, and make sure we don't append ".thor" to URIs -* Add rake2thor to the gem binfiles. -* Make sure local Thorfiles override system-wide ones. diff --git a/railties/lib/rails/vendor/thor-0.12.0/LICENSE b/railties/lib/rails/vendor/thor-0.12.0/LICENSE deleted file mode 100644 index 98722da459..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2008 Yehuda Katz - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/railties/lib/rails/vendor/thor-0.12.0/README.rdoc b/railties/lib/rails/vendor/thor-0.12.0/README.rdoc deleted file mode 100644 index f1106f02b6..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/README.rdoc +++ /dev/null @@ -1,234 +0,0 @@ -= thor - -Map options to a class. Simply create a class with the appropriate annotations -and have options automatically map to functions and parameters. - -Example: - - class App < Thor # [1] - map "-L" => :list # [2] - - desc "install APP_NAME", "install one of the available apps" # [3] - method_options :force => :boolean, :alias => :string # [4] - def install(name) - user_alias = options[:alias] - if options.force? - # do something - end - # other code - end - - desc "list [SEARCH]", "list all of the available apps, limited by SEARCH" - def list(search="") - # list everything - end - end - -Thor automatically maps commands as such: - - thor app:install myname --force - -That gets converted to: - - App.new.install("myname") - # with {'force' => true} as options hash - -1. Inherit from Thor to turn a class into an option mapper -2. Map additional non-valid identifiers to specific methods. In this case, convert -L to :list -3. Describe the method immediately below. The first parameter is the usage information, and the second parameter is the description -4. Provide any additional options that will be available the instance method options. - -== Types for method_options - -* :boolean - is parsed as --option or --option=true -* :string - is parsed as --option=VALUE -* :numeric - is parsed as --option=N -* :array - is parsed as --option=one two three -* :hash - is parsed as --option=name:string age:integer - -Besides, method_option allows a default value to be given, examples: - - method_options :force => false - #=> Creates a boolean option with default value false - - method_options :alias => "bar" - #=> Creates a string option with default value "bar" - - method_options :threshold => 3.0 - #=> Creates a numeric option with default value 3.0 - -You can also supply :option => :required to mark an option as required. The -type is assumed to be string. If you want a required hash with default values -as option, you can use method_option which uses a more declarative style: - - method_option :attributes, :type => :hash, :default => {}, :required => true - -All arguments can be set to nil (except required arguments), by suppling a no or -skip variant. For example: - - thor app name --no-attributes - -In previous versions, aliases for options were created automatically, but now -they should be explicit. You can supply aliases in both short and declarative -styles: - - method_options %w( force -f ) => :boolean - -Or: - - method_option :force, :type => :boolean, :aliases => "-f" - -You can supply as many aliases as you want. - -NOTE: Type :optional available in Thor 0.9.0 was deprecated. Use :string or :boolean instead. - -== Namespaces - -By default, your Thor tasks are invoked using Ruby namespace. In the example -above, tasks are invoked as: - - thor app:install name --force - -However, you could namespace your class as: - - module Sinatra - class App < Thor - # tasks - end - end - -And then you should invoke your tasks as: - - thor sinatra:app:install name --force - -If desired, you can change the namespace: - - module Sinatra - class App < Thor - namespace :myapp - # tasks - end - end - -And then your tasks hould be invoked as: - - thor myapp:install name --force - -== Invocations - -Thor comes with a invocation-dependency system as well which allows a task to be -invoked only once. For example: - - class Counter < Thor - desc "one", "Prints 1, 2, 3" - def one - puts 1 - invoke :two - invoke :three - end - - desc "two", "Prints 2, 3" - def two - puts 2 - invoke :three - end - - desc "three", "Prints 3" - def three - puts 3 - end - end - -When invoking the task one: - - thor counter:one - -The output is "1 2 3", which means that the three task was invoked only once. -You can even invoke tasks from another class, so be sure to check the -documentation. - -== Thor::Group - -Thor has a special class called Thor::Group. The main difference to Thor class -is that it invokes all tasks at once. The example above could be rewritten in -Thor::Group as this: - - class Counter < Thor::Group - desc "Prints 1, 2, 3" - - def one - puts 1 - end - - def two - puts 2 - end - - def three - puts 3 - end - end - -When invoked: - - thor counter - -It prints "1 2 3" as well. Notice you should describe (using the method desc) -only the class and not each task anymore. Thor::Group is a great tool to create -generators, since you can define several steps which are invoked in the order they -are defined (Thor::Group is the tool use in generators in Rails 3.0). - -Besides, Thor::Group can parse arguments and options as Thor tasks: - - class Counter < Thor::Group - # number will be available as attr_accessor - argument :number, :type => :numeric, :desc => "The number to start counting" - desc "Prints the 'number' given upto 'number+2'" - - def one - puts number + 0 - end - - def two - puts number + 1 - end - - def three - puts number + 2 - end - end - -The counter above expects one parameter and has the folling outputs: - - thor counter 5 - # Prints "5 6 7" - - thor counter 11 - # Prints "11 12 13" - -You can also give options to Thor::Group, but instead of using method_option -and method_options, you should use class_option and class_options. -Both argument and class_options methods are available to Thor class as well. - -== Actions - -Thor comes with several actions which helps with script and generator tasks. You -might be familiar with them since some came from Rails Templates. They are: -say, ask, yes?, no?, add_file, -remove_file, copy_file, template, directory, -inside, run, inject_into_file and a couple more. - -To use them, you just need to include Thor::Actions in your Thor classes: - - class App < Thor - include Thor::Actions - # tasks - end - -Some actions like copy file requires that a class method called source_root is -defined in your class. This is the directory where your templates should be -placed. Be sure to check the documentation. - -== License - -See MIT LICENSE. diff --git a/railties/lib/rails/vendor/thor-0.12.0/Thorfile b/railties/lib/rails/vendor/thor-0.12.0/Thorfile deleted file mode 100644 index f71a1e57e2..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/Thorfile +++ /dev/null @@ -1,63 +0,0 @@ -# enconding: utf-8 - -require File.join(File.dirname(__FILE__), "lib", "thor", "version") -require 'thor/rake_compat' -require 'spec/rake/spectask' -require 'rdoc/task' - -GEM_NAME = 'thor' -EXTRA_RDOC_FILES = ["README.rdoc", "LICENSE", "CHANGELOG.rdoc", "VERSION", "Thorfile"] - -class Default < Thor - include Thor::RakeCompat - - Spec::Rake::SpecTask.new(:spec) do |t| - t.libs << 'lib' - t.spec_opts = ['--options', "spec/spec.opts"] - t.spec_files = FileList['spec/**/*_spec.rb'] - end - - Spec::Rake::SpecTask.new(:rcov) do |t| - t.libs << 'lib' - t.spec_opts = ['--options', "spec/spec.opts"] - t.spec_files = FileList['spec/**/*_spec.rb'] - t.rcov = true - t.rcov_dir = "rcov" - end - - RDoc::Task.new do |rdoc| - rdoc.main = "README.rdoc" - rdoc.rdoc_dir = "rdoc" - rdoc.title = GEM_NAME - rdoc.rdoc_files.include(*EXTRA_RDOC_FILES) - rdoc.rdoc_files.include('lib/**/*.rb') - rdoc.options << '--line-numbers' << '--inline-source' - end - - begin - require 'jeweler' - Jeweler::Tasks.new do |s| - s.name = GEM_NAME - s.version = Thor::VERSION - s.rubyforge_project = "textmate" - s.platform = Gem::Platform::RUBY - s.summary = "A scripting framework that replaces rake, sake and rubigen" - s.email = "ruby-thor@googlegroups.com" - s.homepage = "http://yehudakatz.com" - s.description = "A scripting framework that replaces rake, sake and rubigen" - s.authors = ['Yehuda Katz', 'José Valim'] - s.has_rdoc = true - s.extra_rdoc_files = EXTRA_RDOC_FILES - s.require_path = 'lib' - s.bindir = "bin" - s.executables = %w( thor rake2thor ) - s.files = s.extra_rdoc_files + Dir.glob("{bin,lib}/**/*") - s.files.exclude 'spec/sandbox/**/*' - s.test_files.exclude 'spec/sandbox/**/*' - end - - Jeweler::RubyforgeTasks.new - rescue LoadError - puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor.rb deleted file mode 100644 index 68944f140d..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor.rb +++ /dev/null @@ -1,242 +0,0 @@ -require 'thor/base' -require 'thor/group' -require 'thor/actions' - -class Thor - class << self - # Sets the default task when thor is executed without an explicit task to be called. - # - # ==== Parameters - # meth:: name of the defaut task - # - def default_task(meth=nil) - case meth - when :none - @default_task = 'help' - when nil - @default_task ||= from_superclass(:default_task, 'help') - else - @default_task = meth.to_s - end - end - - # Defines the usage and the description of the next task. - # - # ==== Parameters - # usage - # description - # - def desc(usage, description, options={}) - if options[:for] - task = find_and_refresh_task(options[:for]) - task.usage = usage if usage - task.description = description if description - else - @usage, @desc = usage, description - end - end - - # Maps an input to a task. If you define: - # - # map "-T" => "list" - # - # Running: - # - # thor -T - # - # Will invoke the list task. - # - # ==== Parameters - # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given task. - # - def map(mappings=nil) - @map ||= from_superclass(:map, {}) - - if mappings - mappings.each do |key, value| - if key.respond_to?(:each) - key.each {|subkey| @map[subkey] = value} - else - @map[key] = value - end - end - end - - @map - end - - # Declares the options for the next task to be declared. - # - # ==== Parameters - # Hash[Symbol => Object]:: The hash key is the name of the option and the value - # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric - # or :required (string). If you give a value, the type of the value is used. - # - def method_options(options=nil) - @method_options ||= {} - build_options(options, @method_options) if options - @method_options - end - - # Adds an option to the set of class options. If :for is given as option, - # it allows you to change the options from a previous defined task. - # - # def previous_task - # # magic - # end - # - # method_options :foo => :bar, :for => :previous_task - # - # def next_task - # # magic - # end - # - # ==== Parameters - # name:: The name of the argument. - # options:: Described below. - # - # ==== Options - # :desc - Description for the argument. - # :required - If the argument is required or not. - # :default - Default value for this argument. It cannot be required and have default values. - # :aliases - Aliases for this option. - # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean. - # :group - The group for this options. Use by class options to output options in different levels. - # :banner - String to show on usage notes. - # - def method_option(name, options={}) - scope = if options[:for] - find_and_refresh_task(options[:for]).options - else - method_options - end - - build_option(name, options, scope) - end - - # Parses the task and options from the given args, instantiate the class - # and invoke the task. This method is used when the arguments must be parsed - # from an array. If you are inside Ruby and want to use a Thor class, you - # can simply initialize it: - # - # script = MyScript.new(args, options, config) - # script.invoke(:task, first_arg, second_arg, third_arg) - # - def start(given_args=ARGV, config={}) - super do - meth = normalize_task_name(given_args.shift) - task = all_tasks[meth] - - if task - args, opts = Thor::Options.split(given_args) - config.merge!(:task_options => task.options) - else - args, opts = given_args, {} - end - - task ||= Thor::Task::Dynamic.new(meth) - trailing = args[Range.new(arguments.size, -1)] - new(args, opts, config).invoke(task, trailing || []) - end - end - - # Prints help information. If a task name is given, it shows information - # only about the specific task. - # - # ==== Parameters - # meth:: An optional task name to print usage information about. - # - # ==== Options - # namespace:: When true, shows the namespace in the output before the usage. - # skip_inherited:: When true, does not show tasks from superclass. - # - def help(shell, meth=nil, options={}) - meth, options = nil, meth if meth.is_a?(Hash) - - if meth - task = all_tasks[meth] - raise UndefinedTaskError, "task '#{meth}' could not be found in namespace '#{self.namespace}'" unless task - - shell.say "Usage:" - shell.say " #{banner(task, options[:namespace], false)}" - shell.say - class_options_help(shell, "Class", :Method => task.options.map { |_, o| o }) - shell.say task.description - else - list = (options[:short] ? tasks : all_tasks).map do |_, task| - item = [ banner(task, options[:namespace]) ] - item << "# #{task.short_description}" if task.short_description - item << " " - end - - options[:ident] ||= 2 - if options[:short] - shell.print_list(list, :ident => options[:ident]) - else - shell.say "Tasks:" - shell.print_list(list, :ident => options[:ident]) - end - - Thor::Util.thor_classes_in(self).each do |subclass| - namespace = options[:namespace] == true || subclass.namespace.gsub(/^#{self.namespace}:/, '') - subclass.help(shell, options.merge(:short => true, :namespace => namespace)) - end - - class_options_help(shell, "Class") unless options[:short] - end - end - - protected - - # The banner for this class. You can customize it if you are invoking the - # thor class by another ways which is not the Thor::Runner. It receives - # the task that is going to be invoked and a boolean which indicates if - # the namespace should be displayed as arguments. - # - def banner(task, namespace=true, show_options=true) - task.formatted_usage(self, namespace, show_options) - end - - def baseclass #:nodoc: - Thor - end - - def create_task(meth) #:nodoc: - if @usage && @desc - tasks[meth.to_s] = Thor::Task.new(meth, @desc, @usage, method_options) - @usage, @desc, @method_options = nil - true - elsif self.all_tasks[meth.to_s] || meth.to_sym == :method_missing - true - else - puts "[WARNING] Attempted to create task #{meth.inspect} without usage or description. " << - "Call desc if you want this method to be available as task or declare it inside a " << - "no_tasks{} block. Invoked from #{caller[1].inspect}." - false - end - end - - def initialize_added #:nodoc: - class_options.merge!(method_options) - @method_options = nil - end - - # Receives a task name (can be nil), and try to get a map from it. - # If a map can't be found use the sent name or the default task. - # - def normalize_task_name(meth) #:nodoc: - mapping = map[meth.to_s] - meth = mapping || meth || default_task - meth.to_s.gsub('-','_') # treat foo-bar > foo_bar - end - end - - include Thor::Base - - map HELP_MAPPINGS => :help - - desc "help [TASK]", "Describe available tasks or one specific task" - def help(task=nil) - self.class.help(shell, task, :namespace => task && task.include?(?:)) - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions.rb deleted file mode 100644 index d561ccb2aa..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions.rb +++ /dev/null @@ -1,273 +0,0 @@ -require 'fileutils' - -Dir[File.join(File.dirname(__FILE__), "actions", "*.rb")].each do |action| - require action -end - -class Thor - module Actions - attr_accessor :behavior - - def self.included(base) #:nodoc: - base.extend ClassMethods - end - - module ClassMethods - # Hold source paths for one Thor instance. source_paths_for_search is the - # method responsible to gather source_paths from this current class, - # inherited paths and the source root. - # - def source_paths - @source_paths ||= [] - end - - # Returns the source paths in the following order: - # - # 1) This class source paths - # 2) Source root - # 3) Parents source paths - # - def source_paths_for_search - paths = [] - paths += self.source_paths - paths << self.source_root if self.respond_to?(:source_root) - paths += from_superclass(:source_paths, []) - paths - end - - # Add runtime options that help actions execution. - # - def add_runtime_options! - class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime, - :desc => "Run but do not make any changes" - - class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime, - :desc => "Overwrite files that already exist" - - class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime, - :desc => "Skip files that already exist" - - class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime, - :desc => "Supress status output" - end - end - - # Extends initializer to add more configuration options. - # - # ==== Configuration - # behavior:: The actions default behavior. Can be :invoke or :revoke. - # It also accepts :force, :skip and :pretend to set the behavior - # and the respective option. - # - # destination_root:: The root directory needed for some actions. - # - def initialize(args=[], options={}, config={}) - self.behavior = case config[:behavior].to_s - when "force", "skip" - _cleanup_options_and_set(options, config[:behavior]) - :invoke - when "revoke" - :revoke - else - :invoke - end - - super - self.destination_root = config[:destination_root] - end - - # Wraps an action object and call it accordingly to the thor class behavior. - # - def action(instance) #:nodoc: - if behavior == :revoke - instance.revoke! - else - instance.invoke! - end - end - - # Returns the root for this thor class (also aliased as destination root). - # - def destination_root - @destination_stack.last - end - - # Sets the root for this thor class. Relatives path are added to the - # directory where the script was invoked and expanded. - # - def destination_root=(root) - @destination_stack ||= [] - @destination_stack[0] = File.expand_path(root || '') - end - - # Returns the given path relative to the absolute root (ie, root where - # the script started). - # - def relative_to_original_destination_root(path, remove_dot=true) - path = path.gsub(@destination_stack[0], '.') - remove_dot ? (path[2..-1] || '') : path - end - - # Holds source paths in instance so they can be manipulated. - # - def source_paths - @source_paths ||= self.class.source_paths_for_search - end - - # Receives a file or directory and search for it in the source paths. - # - def find_in_source_paths(file) - relative_root = relative_to_original_destination_root(destination_root, false) - - source_paths.each do |source| - source_file = File.expand_path(file, File.join(source, relative_root)) - return source_file if File.exists?(source_file) - end - - if source_paths.empty? - raise Error, "You don't have any source path defined for class #{self.class.name}. To fix this, " << - "you can define a source_root in your class." - else - raise Error, "Could not find #{file.inspect} in source paths." - end - end - - # Do something in the root or on a provided subfolder. If a relative path - # is given it's referenced from the current root. The full path is yielded - # to the block you provide. The path is set back to the previous path when - # the method exits. - # - # ==== Parameters - # dir:: the directory to move to. - # config:: give :verbose => true to log and use padding. - # - def inside(dir='', config={}, &block) - verbose = config.fetch(:verbose, false) - - say_status :inside, dir, verbose - shell.padding += 1 if verbose - @destination_stack.push File.expand_path(dir, destination_root) - - FileUtils.mkdir_p(destination_root) unless File.exist?(destination_root) - FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield } - - @destination_stack.pop - shell.padding -= 1 if verbose - end - - # Goes to the root and execute the given block. - # - def in_root - inside(@destination_stack.first) { yield } - end - - # Loads an external file and execute it in the instance binding. - # - # ==== Parameters - # path:: The path to the file to execute. Can be a web address or - # a relative path from the source root. - # - # ==== Examples - # - # apply "http://gist.github.com/103208" - # - # apply "recipes/jquery.rb" - # - def apply(path, config={}) - verbose = config.fetch(:verbose, true) - path = find_in_source_paths(path) unless path =~ /^http\:\/\// - - say_status :apply, path, verbose - shell.padding += 1 if verbose - instance_eval(open(path).read) - shell.padding -= 1 if verbose - end - - # Executes a command. - # - # ==== Parameters - # command:: the command to be executed. - # config:: give :verbose => false to not log the status. Specify :with - # to append an executable to command executation. - # - # ==== Example - # - # inside('vendor') do - # run('ln -s ~/edge rails') - # end - # - def run(command, config={}) - return unless behavior == :invoke - - destination = relative_to_original_destination_root(destination_root, false) - desc = "#{command} from #{destination.inspect}" - - if config[:with] - desc = "#{File.basename(config[:with].to_s)} #{desc}" - command = "#{config[:with]} #{command}" - end - - say_status :run, desc, config.fetch(:verbose, true) - system(command) unless options[:pretend] - end - - # Executes a ruby script (taking into account WIN32 platform quirks). - # - # ==== Parameters - # command:: the command to be executed. - # config:: give :verbose => false to not log the status. - # - def run_ruby_script(command, config={}) - return unless behavior == :invoke - run "#{command}", config.merge(:with => Thor::Util.ruby_command) - end - - # Run a thor command. A hash of options can be given and it's converted to - # switches. - # - # ==== Parameters - # task:: the task to be invoked - # args:: arguments to the task - # config:: give :verbose => false to not log the status. Other options - # are given as parameter to Thor. - # - # ==== Examples - # - # thor :install, "http://gist.github.com/103208" - # #=> thor install http://gist.github.com/103208 - # - # thor :list, :all => true, :substring => 'rails' - # #=> thor list --all --substring=rails - # - def thor(task, *args) - config = args.last.is_a?(Hash) ? args.pop : {} - verbose = config.key?(:verbose) ? config.delete(:verbose) : true - - args.unshift task - args.push Thor::Options.to_switches(config) - command = args.join(' ').strip - - run command, :with => :thor, :verbose => verbose - end - - protected - - # Allow current root to be shared between invocations. - # - def _shared_configuration #:nodoc: - super.merge!(:destination_root => self.destination_root) - end - - def _cleanup_options_and_set(options, key) #:nodoc: - case options - when Array - %w(--force -f --skip -s).each { |i| options.delete(i) } - options << "--#{key}" - when Hash - [:force, :skip, "force", "skip"].each { |i| options.delete(i) } - options.merge!(key => true) - end - end - - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/create_file.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/create_file.rb deleted file mode 100644 index a3d9296823..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/create_file.rb +++ /dev/null @@ -1,103 +0,0 @@ -require 'thor/actions/empty_directory' - -class Thor - module Actions - - # Create a new file relative to the destination root with the given data, - # which is the return value of a block or a data string. - # - # ==== Parameters - # destination:: the relative path to the destination root. - # data:: the data to append to the file. - # config:: give :verbose => false to not log the status. - # - # ==== Examples - # - # create_file "lib/fun_party.rb" do - # hostname = ask("What is the virtual hostname I should use?") - # "vhost.name = #{hostname}" - # end - # - # create_file "config/apach.conf", "your apache config" - # - def create_file(destination, data=nil, config={}, &block) - action CreateFile.new(self, destination, block || data.to_s, config) - end - alias :add_file :create_file - - # AddFile is a subset of Template, which instead of rendering a file with - # ERB, it gets the content from the user. - # - class CreateFile < EmptyDirectory #:nodoc: - attr_reader :data - - def initialize(base, destination, data, config={}) - @data = data - super(base, destination, config) - end - - # Checks if the content of the file at the destination is identical to the rendered result. - # - # ==== Returns - # Boolean:: true if it is identical, false otherwise. - # - def identical? - exists? && File.read(destination) == render - end - - # Holds the content to be added to the file. - # - def render - @render ||= if data.is_a?(Proc) - data.call - else - data - end - end - - def invoke! - invoke_with_conflict_check do - FileUtils.mkdir_p(File.dirname(destination)) - File.open(destination, 'w'){ |f| f.write render } - end - given_destination - end - - protected - - # Now on conflict we check if the file is identical or not. - # - def on_conflict_behavior(&block) - if identical? - say_status :identical, :blue - else - options = base.options.merge(config) - force_or_skip_or_conflict(options[:force], options[:skip], &block) - end - end - - # If force is true, run the action, otherwise check if it's not being - # skipped. If both are false, show the file_collision menu, if the menu - # returns true, force it, otherwise skip. - # - def force_or_skip_or_conflict(force, skip, &block) - if force - say_status :force, :yellow - block.call unless pretend? - elsif skip - say_status :skip, :yellow - else - say_status :conflict, :red - force_or_skip_or_conflict(force_on_collision?, true, &block) - end - end - - # Shows the file collision menu to the user and gets the result. - # - def force_on_collision? - base.shell.file_collision(destination){ render } - end - - end - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/directory.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/directory.rb deleted file mode 100644 index 467e63732a..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/directory.rb +++ /dev/null @@ -1,93 +0,0 @@ -require 'thor/actions/empty_directory' - -class Thor - module Actions - - # Copies recursively the files from source directory to root directory. - # If any of the files finishes with .tt, it's considered to be a template - # and is placed in the destination without the extension .tt. If any - # empty directory is found, it's copied and all .empty_directory files are - # ignored. Remember that file paths can also be encoded, let's suppose a doc - # directory with the following files: - # - # doc/ - # components/.empty_directory - # README - # rdoc.rb.tt - # %app_name%.rb - # - # When invoked as: - # - # directory "doc" - # - # It will create a doc directory in the destination with the following - # files (assuming that the app_name is "blog"): - # - # doc/ - # components/ - # README - # rdoc.rb - # blog.rb - # - # ==== Parameters - # source:: the relative path to the source root. - # destination:: the relative path to the destination root. - # config:: give :verbose => false to not log the status. - # If :recursive => false, does not look for paths recursively. - # - # ==== Examples - # - # directory "doc" - # directory "doc", "docs", :recursive => false - # - def directory(source, destination=nil, config={}, &block) - action Directory.new(self, source, destination || source, config, &block) - end - - class Directory < EmptyDirectory #:nodoc: - attr_reader :source - - def initialize(base, source, destination=nil, config={}, &block) - @source = File.expand_path(base.find_in_source_paths(source.to_s)) - @block = block - super(base, destination, { :recursive => true }.merge(config)) - end - - def invoke! - base.empty_directory given_destination, config - execute! - end - - def revoke! - execute! - end - - protected - - def execute! - lookup = config[:recursive] ? File.join(source, '**') : source - lookup = File.join(lookup, '{*,.[a-z]*}') - - Dir[lookup].each do |file_source| - next if File.directory?(file_source) - file_destination = File.join(given_destination, file_source.gsub(source, '.')) - file_destination.gsub!('/./', '/') - - case file_source - when /\.empty_directory$/ - dirname = File.dirname(file_destination).gsub(/\/\.$/, '') - next if dirname == given_destination - base.empty_directory(dirname, config) - when /\.tt$/ - destination = base.template(file_source, file_destination[0..-4], config) - @block.call(destination) if @block - else - destination = base.copy_file(file_source, file_destination, config) - @block.call(destination) if @block - end - end - end - - end - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/empty_directory.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/empty_directory.rb deleted file mode 100644 index 484cb820f8..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/empty_directory.rb +++ /dev/null @@ -1,134 +0,0 @@ -class Thor - module Actions - - # Creates an empty directory. - # - # ==== Parameters - # destination:: the relative path to the destination root. - # config:: give :verbose => false to not log the status. - # - # ==== Examples - # - # empty_directory "doc" - # - def empty_directory(destination, config={}) - action EmptyDirectory.new(self, destination, config) - end - - # Class which holds create directory logic. This is the base class for - # other actions like create_file and directory. - # - # This implementation is based in Templater actions, created by Jonas Nicklas - # and Michael S. Klishin under MIT LICENSE. - # - class EmptyDirectory #:nodoc: - attr_reader :base, :destination, :given_destination, :relative_destination, :config - - # Initializes given the source and destination. - # - # ==== Parameters - # base:: A Thor::Base instance - # source:: Relative path to the source of this file - # destination:: Relative path to the destination of this file - # config:: give :verbose => false to not log the status. - # - def initialize(base, destination, config={}) - @base, @config = base, { :verbose => true }.merge(config) - self.destination = destination - end - - # Checks if the destination file already exists. - # - # ==== Returns - # Boolean:: true if the file exists, false otherwise. - # - def exists? - ::File.exists?(destination) - end - - def invoke! - invoke_with_conflict_check do - ::FileUtils.mkdir_p(destination) - end - end - - def revoke! - say_status :remove, :red - ::FileUtils.rm_rf(destination) if !pretend? && exists? - given_destination - end - - protected - - # Shortcut for pretend. - # - def pretend? - base.options[:pretend] - end - - # Sets the absolute destination value from a relative destination value. - # It also stores the given and relative destination. Let's suppose our - # script is being executed on "dest", it sets the destination root to - # "dest". The destination, given_destination and relative_destination - # are related in the following way: - # - # inside "bar" do - # empty_directory "baz" - # end - # - # destination #=> dest/bar/baz - # relative_destination #=> bar/baz - # given_destination #=> baz - # - def destination=(destination) - if destination - @given_destination = convert_encoded_instructions(destination.to_s) - @destination = ::File.expand_path(@given_destination, base.destination_root) - @relative_destination = base.relative_to_original_destination_root(@destination) - end - end - - # Filenames in the encoded form are converted. If you have a file: - # - # %class_name%.rb - # - # It gets the class name from the base and replace it: - # - # user.rb - # - def convert_encoded_instructions(filename) - filename.gsub(/%(.*?)%/) do |string| - instruction = $1.strip - base.respond_to?(instruction) ? base.send(instruction) : string - end - end - - # Receives a hash of options and just execute the block if some - # conditions are met. - # - def invoke_with_conflict_check(&block) - if exists? - on_conflict_behavior(&block) - else - say_status :create, :green - block.call unless pretend? - end - - destination - end - - # What to do when the destination file already exists. - # - def on_conflict_behavior(&block) - say_status :exist, :blue - end - - # Shortcut to say_status shell method. - # - def say_status(status, color) - base.shell.say_status status, relative_destination, color if config[:verbose] - end - - end - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/file_manipulation.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/file_manipulation.rb deleted file mode 100644 index d77d90d448..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/file_manipulation.rb +++ /dev/null @@ -1,219 +0,0 @@ -require 'erb' -require 'open-uri' - -class Thor - module Actions - - # Copies the file from the relative source to the relative destination. If - # the destination is not given it's assumed to be equal to the source. - # - # ==== Parameters - # source:: the relative path to the source root. - # destination:: the relative path to the destination root. - # config:: give :verbose => false to not log the status. - # - # ==== Examples - # - # copy_file "README", "doc/README" - # - # copy_file "doc/README" - # - def copy_file(source, destination=nil, config={}) - destination ||= source - source = File.expand_path(find_in_source_paths(source.to_s)) - - create_file destination, nil, config do - File.read(source) - end - end - - # Gets the content at the given address and places it at the given relative - # destination. If a block is given instead of destination, the content of - # the url is yielded and used as location. - # - # ==== Parameters - # source:: the address of the given content. - # destination:: the relative path to the destination root. - # config:: give :verbose => false to not log the status. - # - # ==== Examples - # - # get "http://gist.github.com/103208", "doc/README" - # - # get "http://gist.github.com/103208" do |content| - # content.split("\n").first - # end - # - def get(source, destination=nil, config={}, &block) - source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^http\:\/\// - render = open(source).read - - destination ||= if block_given? - block.arity == 1 ? block.call(render) : block.call - else - File.basename(source) - end - - create_file destination, render, config - end - - # Gets an ERB template at the relative source, executes it and makes a copy - # at the relative destination. If the destination is not given it's assumed - # to be equal to the source removing .tt from the filename. - # - # ==== Parameters - # source:: the relative path to the source root. - # destination:: the relative path to the destination root. - # config:: give :verbose => false to not log the status. - # - # ==== Examples - # - # template "README", "doc/README" - # - # template "doc/README" - # - def template(source, destination=nil, config={}) - destination ||= source - source = File.expand_path(find_in_source_paths(source.to_s)) - context = instance_eval('binding') - - create_file destination, nil, config do - ERB.new(::File.read(source), nil, '-').result(context) - end - end - - # Changes the mode of the given file or directory. - # - # ==== Parameters - # mode:: the file mode - # path:: the name of the file to change mode - # config:: give :verbose => false to not log the status. - # - # ==== Example - # - # chmod "script/*", 0755 - # - def chmod(path, mode, config={}) - return unless behavior == :invoke - path = File.expand_path(path, destination_root) - say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true) - FileUtils.chmod_R(mode, path) unless options[:pretend] - end - - # Prepend text to a file. Since it depends on inject_into_file, it's reversible. - # - # ==== Parameters - # path:: path of the file to be changed - # data:: the data to prepend to the file, can be also given as a block. - # config:: give :verbose => false to not log the status. - # - # ==== Example - # - # prepend_file 'config/environments/test.rb', 'config.gem "rspec"' - # - # prepend_file 'config/environments/test.rb' do - # 'config.gem "rspec"' - # end - # - def prepend_file(path, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - config.merge!(:after => /\A/) - inject_into_file(path, *(args << config), &block) - end - - # Append text to a file. Since it depends on inject_into_file, it's reversible. - # - # ==== Parameters - # path:: path of the file to be changed - # data:: the data to append to the file, can be also given as a block. - # config:: give :verbose => false to not log the status. - # - # ==== Example - # - # append_file 'config/environments/test.rb', 'config.gem "rspec"' - # - # append_file 'config/environments/test.rb' do - # 'config.gem "rspec"' - # end - # - def append_file(path, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - config.merge!(:before => /\z/) - inject_into_file(path, *(args << config), &block) - end - - # Injects text right after the class definition. Since it depends on - # inject_into_file, it's reversible. - # - # ==== Parameters - # path:: path of the file to be changed - # klass:: the class to be manipulated - # data:: the data to append to the class, can be also given as a block. - # config:: give :verbose => false to not log the status. - # - # ==== Examples - # - # inject_into_class "app/controllers/application_controller.rb", " filter_parameter :password\n" - # - # inject_into_class "app/controllers/application_controller.rb", ApplicationController do - # " filter_parameter :password\n" - # end - # - def inject_into_class(path, klass, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - config.merge!(:after => /class #{klass}\n|class #{klass} .*\n/) - inject_into_file(path, *(args << config), &block) - end - - # Run a regular expression replacement on a file. - # - # ==== Parameters - # path:: path of the file to be changed - # flag:: the regexp or string to be replaced - # replacement:: the replacement, can be also given as a block - # config:: give :verbose => false to not log the status. - # - # ==== Example - # - # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' - # - # gsub_file 'README', /rake/, :green do |match| - # match << " no more. Use thor!" - # end - # - def gsub_file(path, flag, *args, &block) - return unless behavior == :invoke - config = args.last.is_a?(Hash) ? args.pop : {} - - path = File.expand_path(path, destination_root) - say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true) - - unless options[:pretend] - content = File.read(path) - content.gsub!(flag, *args, &block) - File.open(path, 'wb') { |file| file.write(content) } - end - end - - # Removes a file at the given location. - # - # ==== Parameters - # path:: path of the file to be changed - # config:: give :verbose => false to not log the status. - # - # ==== Example - # - # remove_file 'README' - # remove_file 'app/controllers/application_controller.rb' - # - def remove_file(path, config={}) - return unless behavior == :invoke - path = File.expand_path(path, destination_root) - - say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true) - ::FileUtils.rm_rf(path) if !options[:pretend] && File.exists?(path) - end - alias :remove_dir :remove_file - - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/inject_into_file.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/inject_into_file.rb deleted file mode 100644 index 0636ec6591..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/actions/inject_into_file.rb +++ /dev/null @@ -1,101 +0,0 @@ -require 'thor/actions/empty_directory' - -class Thor - module Actions - - # Injects the given content into a file. Different from gsub_file, this - # method is reversible. - # - # ==== Parameters - # destination:: Relative path to the destination root - # data:: Data to add to the file. Can be given as a block. - # config:: give :verbose => false to not log the status and the flag - # for injection (:after or :before). - # - # ==== Examples - # - # inject_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" - # - # inject_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do - # gems = ask "Which gems would you like to add?" - # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") - # end - # - def inject_into_file(destination, *args, &block) - if block_given? - data, config = block, args.shift - else - data, config = args.shift, args.shift - end - action InjectIntoFile.new(self, destination, data, config) - end - - class InjectIntoFile < EmptyDirectory #:nodoc: - attr_reader :replacement, :flag, :behavior - - def initialize(base, destination, data, config) - super(base, destination, { :verbose => true }.merge(config)) - - @behavior, @flag = if @config.key?(:after) - [:after, @config.delete(:after)] - else - [:before, @config.delete(:before)] - end - - @replacement = data.is_a?(Proc) ? data.call : data - @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp) - end - - def invoke! - say_status :invoke - - content = if @behavior == :after - '\0' + replacement - else - replacement + '\0' - end - - replace!(/#{flag}/, content) - end - - def revoke! - say_status :revoke - - regexp = if @behavior == :after - content = '\1\2' - /(#{flag})(.*)(#{Regexp.escape(replacement)})/m - else - content = '\2\3' - /(#{Regexp.escape(replacement)})(.*)(#{flag})/m - end - - replace!(regexp, content) - end - - protected - - def say_status(behavior) - status = if flag == /\A/ - behavior == :invoke ? :prepend : :unprepend - elsif flag == /\z/ - behavior == :invoke ? :append : :unappend - else - behavior == :invoke ? :inject : :deinject - end - - super(status, config[:verbose]) - end - - # Adds the content to the file. - # - def replace!(regexp, string) - unless base.options[:pretend] - content = File.read(destination) - content.gsub!(regexp, string) - File.open(destination, 'wb') { |file| file.write(content) } - end - end - - end - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/base.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/base.rb deleted file mode 100644 index 700d794123..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/base.rb +++ /dev/null @@ -1,517 +0,0 @@ -require 'thor/core_ext/hash_with_indifferent_access' -require 'thor/core_ext/ordered_hash' -require 'thor/error' -require 'thor/shell' -require 'thor/invocation' -require 'thor/parser' -require 'thor/task' -require 'thor/util' - -class Thor - # Shortcuts for help. - HELP_MAPPINGS = %w(-h -? --help -D) - - # Thor methods that should not be overwritten by the user. - THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root - action add_file create_file in_root inside run run_ruby_script) - - module Base - attr_accessor :options - - # It receives arguments in an Array and two hashes, one for options and - # other for configuration. - # - # Notice that it does not check if all required arguments were supplied. - # It should be done by the parser. - # - # ==== Parameters - # args:: An array of objects. The objects are applied to their - # respective accessors declared with argument. - # - # options:: An options hash that will be available as self.options. - # The hash given is converted to a hash with indifferent - # access, magic predicates (options.skip?) and then frozen. - # - # config:: Configuration for this Thor class. - # - def initialize(args=[], options={}, config={}) - Thor::Arguments.parse(self.class.arguments, args).each do |key, value| - send("#{key}=", value) - end - - parse_options = self.class.class_options - - if options.is_a?(Array) - task_options = config.delete(:task_options) # hook for start - parse_options = parse_options.merge(task_options) if task_options - array_options, hash_options = options, {} - else - array_options, hash_options = [], options - end - - options = Thor::Options.parse(parse_options, array_options) - self.options = Thor::CoreExt::HashWithIndifferentAccess.new(options).merge!(hash_options) - self.options.freeze - end - - class << self - def included(base) #:nodoc: - base.send :extend, ClassMethods - base.send :include, Invocation - base.send :include, Shell - end - - # Returns the classes that inherits from Thor or Thor::Group. - # - # ==== Returns - # Array[Class] - # - def subclasses - @subclasses ||= [] - end - - # Returns the files where the subclasses are kept. - # - # ==== Returns - # Hash[path => Class] - # - def subclass_files - @subclass_files ||= Hash.new{ |h,k| h[k] = [] } - end - - # Whenever a class inherits from Thor or Thor::Group, we should track the - # class and the file on Thor::Base. This is the method responsable for it. - # - def register_klass_file(klass) #:nodoc: - file = caller[1].match(/(.*):\d+/)[1] - Thor::Base.subclasses << klass unless Thor::Base.subclasses.include?(klass) - - file_subclasses = Thor::Base.subclass_files[File.expand_path(file)] - file_subclasses << klass unless file_subclasses.include?(klass) - end - end - - module ClassMethods - # Adds an argument to the class and creates an attr_accessor for it. - # - # Arguments are different from options in several aspects. The first one - # is how they are parsed from the command line, arguments are retrieved - # from position: - # - # thor task NAME - # - # Instead of: - # - # thor task --name=NAME - # - # Besides, arguments are used inside your code as an accessor (self.argument), - # while options are all kept in a hash (self.options). - # - # Finally, arguments cannot have type :default or :boolean but can be - # optional (supplying :optional => :true or :required => false), although - # you cannot have a required argument after a non-required argument. If you - # try it, an error is raised. - # - # ==== Parameters - # name:: The name of the argument. - # options:: Described below. - # - # ==== Options - # :desc - Description for the argument. - # :required - If the argument is required or not. - # :optional - If the argument is optional or not. - # :type - The type of the argument, can be :string, :hash, :array, :numeric. - # :default - Default value for this argument. It cannot be required and have default values. - # :banner - String to show on usage notes. - # - # ==== Errors - # ArgumentError:: Raised if you supply a required argument after a non required one. - # - def argument(name, options={}) - is_thor_reserved_word?(name, :argument) - no_tasks { attr_accessor name } - - required = if options.key?(:optional) - !options[:optional] - elsif options.key?(:required) - options[:required] - else - options[:default].nil? - end - - remove_argument name - - arguments.each do |argument| - next if argument.required? - raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " << - "the non-required argument #{argument.human_name.inspect}." - end if required - - arguments << Thor::Argument.new(name, options[:desc], required, options[:type], - options[:default], options[:banner]) - end - - # Returns this class arguments, looking up in the ancestors chain. - # - # ==== Returns - # Array[Thor::Argument] - # - def arguments - @arguments ||= from_superclass(:arguments, []) - end - - # Adds a bunch of options to the set of class options. - # - # class_options :foo => false, :bar => :required, :baz => :string - # - # If you prefer more detailed declaration, check class_option. - # - # ==== Parameters - # Hash[Symbol => Object] - # - def class_options(options=nil) - @class_options ||= from_superclass(:class_options, {}) - build_options(options, @class_options) if options - @class_options - end - - # Adds an option to the set of class options - # - # ==== Parameters - # name:: The name of the argument. - # options:: Described below. - # - # ==== Options - # :desc - Description for the argument. - # :required - If the argument is required or not. - # :default - Default value for this argument. - # :group - The group for this options. Use by class options to output options in different levels. - # :aliases - Aliases for this option. - # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean. - # :banner - String to show on usage notes. - # - def class_option(name, options={}) - build_option(name, options, class_options) - end - - # Removes a previous defined argument. If :undefine is given, undefine - # accessors as well. - # - # ==== Paremeters - # names:: Arguments to be removed - # - # ==== Examples - # - # remove_argument :foo - # remove_argument :foo, :bar, :baz, :undefine => true - # - def remove_argument(*names) - options = names.last.is_a?(Hash) ? names.pop : {} - - names.each do |name| - arguments.delete_if { |a| a.name == name.to_s } - undef_method name, "#{name}=" if options[:undefine] - end - end - - # Removes a previous defined class option. - # - # ==== Paremeters - # names:: Class options to be removed - # - # ==== Examples - # - # remove_class_option :foo - # remove_class_option :foo, :bar, :baz - # - def remove_class_option(*names) - names.each do |name| - class_options.delete(name) - end - end - - # Defines the group. This is used when thor list is invoked so you can specify - # that only tasks from a pre-defined group will be shown. Defaults to standard. - # - # ==== Parameters - # name - # - def group(name=nil) - case name - when nil - @group ||= from_superclass(:group, 'standard') - else - @group = name.to_s - end - end - - # Returns the tasks for this Thor class. - # - # ==== Returns - # OrderedHash:: An ordered hash with tasks names as keys and Thor::Task - # objects as values. - # - def tasks - @tasks ||= Thor::CoreExt::OrderedHash.new - end - - # Returns the tasks for this Thor class and all subclasses. - # - # ==== Returns - # OrderedHash:: An ordered hash with tasks names as keys and Thor::Task - # objects as values. - # - def all_tasks - @all_tasks ||= from_superclass(:all_tasks, Thor::CoreExt::OrderedHash.new) - @all_tasks.merge(tasks) - end - - # Removes a given task from this Thor class. This is usually done if you - # are inheriting from another class and don't want it to be available - # anymore. - # - # By default it only remove the mapping to the task. But you can supply - # :undefine => true to undefine the method from the class as well. - # - # ==== Parameters - # name:: The name of the task to be removed - # options:: You can give :undefine => true if you want tasks the method - # to be undefined from the class as well. - # - def remove_task(*names) - options = names.last.is_a?(Hash) ? names.pop : {} - - names.each do |name| - tasks.delete(name.to_s) - all_tasks.delete(name.to_s) - undef_method name if options[:undefine] - end - end - - # All methods defined inside the given block are not added as tasks. - # - # So you can do: - # - # class MyScript < Thor - # no_tasks do - # def this_is_not_a_task - # end - # end - # end - # - # You can also add the method and remove it from the task list: - # - # class MyScript < Thor - # def this_is_not_a_task - # end - # remove_task :this_is_not_a_task - # end - # - def no_tasks - @no_tasks = true - yield - @no_tasks = false - end - - # Sets the namespace for the Thor or Thor::Group class. By default the - # namespace is retrieved from the class name. If your Thor class is named - # Scripts::MyScript, the help method, for example, will be called as: - # - # thor scripts:my_script -h - # - # If you change the namespace: - # - # namespace :my_scripts - # - # You change how your tasks are invoked: - # - # thor my_scripts -h - # - # Finally, if you change your namespace to default: - # - # namespace :default - # - # Your tasks can be invoked with a shortcut. Instead of: - # - # thor :my_task - # - def namespace(name=nil) - case name - when nil - @namespace ||= Thor::Util.namespace_from_thor_class(self, false) - else - @namespace = name.to_s - end - end - - # Default way to start generators from the command line. - # - def start(given_args=ARGV, config={}) - config[:shell] ||= Thor::Base.shell.new - yield - rescue Thor::Error => e - if given_args.include?("--debug") - raise e - else - config[:shell].error e.message - end - exit(1) if exit_on_failure? - end - - protected - - # Prints the class options per group. If an option does not belong to - # any group, it uses the ungrouped name value. This method provide to - # hooks to add extra options, one of them if the third argument called - # extra_group that should be a hash in the format :group => Array[Options]. - # - # The second is by returning a lambda used to print values. The lambda - # requires two options: the group name and the array of options. - # - def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc: - groups = {} - - class_options.each do |_, value| - groups[value.group] ||= [] - groups[value.group] << value - end - - printer = proc do |group_name, options| - list = [] - padding = options.collect{ |o| o.aliases.size }.max.to_i * 4 - - options.each do |option| - item = [ option.usage(padding) ] - item.push(option.description ? "# #{option.description}" : "") - - list << item - list << [ "", "# Default: #{option.default}" ] if option.show_default? - end - - unless list.empty? - shell.say(group_name ? "#{group_name} options:" : "Options:") - shell.print_table(list, :ident => 2) - shell.say "" - end - end - - # Deal with default group - global_options = groups.delete(nil) || [] - printer.call(ungrouped_name, global_options) if global_options - - # Print all others - groups = extra_group.merge(groups) if extra_group - groups.each(&printer) - printer - end - - # Raises an error if the word given is a Thor reserved word. - # - def is_thor_reserved_word?(word, type) #:nodoc: - return false unless THOR_RESERVED_WORDS.include?(word.to_s) - raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}" - end - - # Build an option and adds it to the given scope. - # - # ==== Parameters - # name:: The name of the argument. - # options:: Described in both class_option and method_option. - # - def build_option(name, options, scope) #:nodoc: - scope[name] = Thor::Option.new(name, options[:desc], options[:required], - options[:type], options[:default], options[:banner], - options[:group], options[:aliases]) - end - - # Receives a hash of options, parse them and add to the scope. This is a - # fast way to set a bunch of options: - # - # build_options :foo => true, :bar => :required, :baz => :string - # - # ==== Parameters - # Hash[Symbol => Object] - # - def build_options(options, scope) #:nodoc: - options.each do |key, value| - scope[key] = Thor::Option.parse(key, value) - end - end - - # Finds a task with the given name. If the task belongs to the current - # class, just return it, otherwise dup it and add the fresh copy to the - # current task hash. - # - def find_and_refresh_task(name) #:nodoc: - task = if task = tasks[name.to_s] - task - elsif task = all_tasks[name.to_s] - tasks[name.to_s] = task.clone - else - raise ArgumentError, "You supplied :for => #{name.inspect}, but the task #{name.inspect} could not be found." - end - end - - # Everytime someone inherits from a Thor class, register the klass - # and file into baseclass. - # - def inherited(klass) - Thor::Base.register_klass_file(klass) - end - - # Fire this callback whenever a method is added. Added methods are - # tracked as tasks by invoking the create_task method. - # - def method_added(meth) - meth = meth.to_s - - if meth == "initialize" - initialize_added - return - end - - # Return if it's not a public instance method - return unless public_instance_methods.include?(meth) || - public_instance_methods.include?(meth.to_sym) - - return if @no_tasks || !create_task(meth) - - is_thor_reserved_word?(meth, :task) - Thor::Base.register_klass_file(self) - end - - # Retrieves a value from superclass. If it reaches the baseclass, - # returns default. - # - def from_superclass(method, default=nil) - if self == baseclass || !superclass.respond_to?(method, true) - default - else - value = superclass.send(method) - value.dup if value - end - end - - # A flag that makes the process exit with status 1 if any error happens. - # - def exit_on_failure? - false - end - - # SIGNATURE: Sets the baseclass. This is where the superclass lookup - # finishes. - def baseclass #:nodoc: - end - - # SIGNATURE: Creates a new task if valid_task? is true. This method is - # called when a new method is added to the class. - def create_task(meth) #:nodoc: - end - - # SIGNATURE: Defines behavior when the initialize method is added to the - # class. - def initialize_added #:nodoc: - end - end - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/hash_with_indifferent_access.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/hash_with_indifferent_access.rb deleted file mode 100644 index 78bc5cf4bf..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/hash_with_indifferent_access.rb +++ /dev/null @@ -1,75 +0,0 @@ -class Thor - module CoreExt #:nodoc: - - # A hash with indifferent access and magic predicates. - # - # hash = Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true - # - # hash[:foo] #=> 'bar' - # hash['foo'] #=> 'bar' - # hash.foo? #=> true - # - class HashWithIndifferentAccess < ::Hash #:nodoc: - - def initialize(hash={}) - super() - hash.each do |key, value| - self[convert_key(key)] = value - end - end - - def [](key) - super(convert_key(key)) - end - - def []=(key, value) - super(convert_key(key), value) - end - - def delete(key) - super(convert_key(key)) - end - - def values_at(*indices) - indices.collect { |key| self[convert_key(key)] } - end - - def merge(other) - dup.merge!(other) - end - - def merge!(other) - other.each do |key, value| - self[convert_key(key)] = value - end - self - end - - protected - - def convert_key(key) - key.is_a?(Symbol) ? key.to_s : key - end - - # Magic predicates. For instance: - # - # options.force? # => !!options['force'] - # options.shebang # => "/usr/lib/local/ruby" - # options.test_framework?(:rspec) # => options[:test_framework] == :rspec - # - def method_missing(method, *args, &block) - method = method.to_s - if method =~ /^(\w+)\?$/ - if args.empty? - !!self[$1] - else - self[$1] == args.first - end - else - self[method] - end - end - - end - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/ordered_hash.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/ordered_hash.rb deleted file mode 100644 index 27fea5bb35..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/core_ext/ordered_hash.rb +++ /dev/null @@ -1,100 +0,0 @@ -class Thor - module CoreExt #:nodoc: - - if RUBY_VERSION >= '1.9' - class OrderedHash < ::Hash - end - else - # This class is based on the Ruby 1.9 ordered hashes. - # - # It keeps the semantics and most of the efficiency of normal hashes - # while also keeping track of the order in which elements were set. - # - class OrderedHash #:nodoc: - include Enumerable - - Node = Struct.new(:key, :value, :next, :prev) - - def initialize - @hash = {} - end - - def [](key) - @hash[key] && @hash[key].value - end - - def []=(key, value) - if node = @hash[key] - node.value = value - else - node = Node.new(key, value) - - if @first.nil? - @first = @last = node - else - node.prev = @last - @last.next = node - @last = node - end - end - - @hash[key] = node - value - end - - def delete(key) - if node = @hash[key] - prev_node = node.prev - next_node = node.next - - next_node.prev = prev_node if next_node - prev_node.next = next_node if prev_node - - @first = next_node if @first == node - @last = prev_node if @last == node - - value = node.value - end - - @hash.delete(key) - value - end - - def keys - self.map { |k, v| k } - end - - def values - self.map { |k, v| v } - end - - def each - return unless @first - yield [@first.key, @first.value] - node = @first - yield [node.key, node.value] while node = node.next - self - end - - def merge(other) - hash = self.class.new - - self.each do |key, value| - hash[key] = value - end - - other.each do |key, value| - hash[key] = value - end - - hash - end - - def empty? - @hash.empty? - end - end - end - - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/error.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/error.rb deleted file mode 100644 index f9b31a35d1..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/error.rb +++ /dev/null @@ -1,27 +0,0 @@ -class Thor - # Thor::Error is raised when it's caused by wrong usage of thor classes. Those - # errors have their backtrace supressed and are nicely shown to the user. - # - # Errors that are caused by the developer, like declaring a method which - # overwrites a thor keyword, it SHOULD NOT raise a Thor::Error. This way, we - # ensure that developer errors are shown with full backtrace. - # - class Error < StandardError - end - - # Raised when a task was not found. - # - class UndefinedTaskError < Error - end - - # Raised when a task was found, but not invoked properly. - # - class InvocationError < Error - end - - class RequiredArgumentMissingError < InvocationError - end - - class MalformattedArgumentError < InvocationError - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/group.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/group.rb deleted file mode 100644 index 0964a9667a..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/group.rb +++ /dev/null @@ -1,263 +0,0 @@ -# Thor has a special class called Thor::Group. The main difference to Thor class -# is that it invokes all tasks at once. It also include some methods that allows -# invocations to be done at the class method, which are not available to Thor -# tasks. -# -class Thor::Group - class << self - # The descrition for this Thor::Group. If none is provided, but a source root - # exists, tries to find the USAGE one folder above it, otherwise searches - # in the superclass. - # - # ==== Parameters - # description:: The description for this Thor::Group. - # - def desc(description=nil) - case description - when nil - @desc ||= from_superclass(:desc, nil) - else - @desc = description - end - end - - # Start works differently in Thor::Group, it simply invokes all tasks - # inside the class. - # - def start(given_args=ARGV, config={}) - super do - if Thor::HELP_MAPPINGS.include?(given_args.first) - help(config[:shell]) - return - end - - args, opts = Thor::Options.split(given_args) - new(args, opts, config).invoke - end - end - - # Prints help information. - # - # ==== Options - # short:: When true, shows only usage. - # - def help(shell, options={}) - if options[:short] - shell.say banner - else - shell.say "Usage:" - shell.say " #{banner}" - shell.say - class_options_help(shell) - shell.say self.desc if self.desc - end - end - - # Stores invocations for this class merging with superclass values. - # - def invocations #:nodoc: - @invocations ||= from_superclass(:invocations, {}) - end - - # Stores invocation blocks used on invoke_from_option. - # - def invocation_blocks #:nodoc: - @invocation_blocks ||= from_superclass(:invocation_blocks, {}) - end - - # Invoke the given namespace or class given. It adds an instance - # method that will invoke the klass and task. You can give a block to - # configure how it will be invoked. - # - # The namespace/class given will have its options showed on the help - # usage. Check invoke_from_option for more information. - # - def invoke(*names, &block) - options = names.last.is_a?(Hash) ? names.pop : {} - verbose = options.fetch(:verbose, true) - - names.each do |name| - invocations[name] = false - invocation_blocks[name] = block if block_given? - - class_eval <<-METHOD, __FILE__, __LINE__ - def _invoke_#{name.to_s.gsub(/\W/, '_')} - klass, task = self.class.prepare_for_invocation(nil, #{name.inspect}) - - if klass - say_status :invoke, #{name.inspect}, #{verbose.inspect} - block = self.class.invocation_blocks[#{name.inspect}] - _invoke_for_class_method klass, task, &block - else - say_status :error, %(#{name.inspect} [not found]), :red - end - end - METHOD - end - end - - # Invoke a thor class based on the value supplied by the user to the - # given option named "name". A class option must be created before this - # method is invoked for each name given. - # - # ==== Examples - # - # class GemGenerator < Thor::Group - # class_option :test_framework, :type => :string - # invoke_from_option :test_framework - # end - # - # ==== Boolean options - # - # In some cases, you want to invoke a thor class if some option is true or - # false. This is automatically handled by invoke_from_option. Then the - # option name is used to invoke the generator. - # - # ==== Preparing for invocation - # - # In some cases you want to customize how a specified hook is going to be - # invoked. You can do that by overwriting the class method - # prepare_for_invocation. The class method must necessarily return a klass - # and an optional task. - # - # ==== Custom invocations - # - # You can also supply a block to customize how the option is giong to be - # invoked. The block receives two parameters, an instance of the current - # class and the klass to be invoked. - # - def invoke_from_option(*names, &block) - options = names.last.is_a?(Hash) ? names.pop : {} - verbose = options.fetch(:verbose, :white) - - names.each do |name| - unless class_options.key?(name) - raise ArgumentError, "You have to define the option #{name.inspect} " << - "before setting invoke_from_option." - end - - invocations[name] = true - invocation_blocks[name] = block if block_given? - - class_eval <<-METHOD, __FILE__, __LINE__ - def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')} - return unless options[#{name.inspect}] - - value = options[#{name.inspect}] - value = #{name.inspect} if TrueClass === value - klass, task = self.class.prepare_for_invocation(#{name.inspect}, value) - - if klass - say_status :invoke, value, #{verbose.inspect} - block = self.class.invocation_blocks[#{name.inspect}] - _invoke_for_class_method klass, task, &block - else - say_status :error, %(\#{value} [not found]), :red - end - end - METHOD - end - end - - # Remove a previously added invocation. - # - # ==== Examples - # - # remove_invocation :test_framework - # - def remove_invocation(*names) - names.each do |name| - remove_task(name) - remove_class_option(name) - invocations.delete(name) - invocation_blocks.delete(name) - end - end - - # Overwrite class options help to allow invoked generators options to be - # shown recursively when invoking a generator. - # - def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc: - group_options = {} - - get_options_from_invocations(group_options, class_options) do |klass| - klass.send(:get_options_from_invocations, group_options, class_options) - end - - group_options.merge!(extra_group) if extra_group - super(shell, ungrouped_name, group_options) - end - - # Get invocations array and merge options from invocations. Those - # options are added to group_options hash. Options that already exists - # in base_options are not added twice. - # - def get_options_from_invocations(group_options, base_options) #:nodoc: - invocations.each do |name, from_option| - value = if from_option - option = class_options[name] - option.type == :boolean ? name : option.default - else - name - end - next unless value - - klass, task = prepare_for_invocation(name, value) - next unless klass && klass.respond_to?(:class_options) - - value = value.to_s - human_name = value.respond_to?(:classify) ? value.classify : value - - group_options[human_name] ||= [] - group_options[human_name] += klass.class_options.values.select do |option| - base_options[option.name.to_sym].nil? && option.group.nil? && - !group_options.values.flatten.any? { |i| i.name == option.name } - end - - yield klass if block_given? - end - end - - protected - - # The banner for this class. You can customize it if you are invoking the - # thor class by another ways which is not the Thor::Runner. - # - def banner - "#{self.namespace} #{self.arguments.map {|a| a.usage }.join(' ')}" - end - - def baseclass #:nodoc: - Thor::Group - end - - def create_task(meth) #:nodoc: - tasks[meth.to_s] = Thor::Task.new(meth, nil, nil, nil) - true - end - end - - include Thor::Base - - protected - - # Shortcut to invoke with padding and block handling. Use internally by - # invoke and invoke_from_option class methods. - # - def _invoke_for_class_method(klass, task=nil, *args, &block) #:nodoc: - shell.padding += 1 - - result = if block_given? - if block.arity == 2 - block.call(self, klass) - else - block.call(self, klass, task) - end - else - invoke klass, task, *args - end - - shell.padding -= 1 - result - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/invocation.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/invocation.rb deleted file mode 100644 index 32e6a72454..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/invocation.rb +++ /dev/null @@ -1,178 +0,0 @@ -class Thor - module Invocation - def self.included(base) #:nodoc: - base.extend ClassMethods - end - - module ClassMethods - # Prepare for class methods invocations. This method must return a klass to - # have the invoked class options showed in help messages in generators. - # - def prepare_for_invocation(key, name) #:nodoc: - case name - when Symbol, String - Thor::Util.namespace_to_thor_class_and_task(name.to_s, false) - else - name - end - end - end - - # Make initializer aware of invocations and the initializer proc. - # - def initialize(args=[], options={}, config={}, &block) #:nodoc: - @_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] } - @_initializer = [ args, options, config ] - super - end - - # Receives a name and invokes it. The name can be a string (either "task" or - # "namespace:task"), a Thor::Task, a Class or a Thor instance. If the task - # cannot be guessed by name, it can also be supplied as second argument. - # - # You can also supply the arguments, options and configuration values for - # the task to be invoked, if none is given, the same values used to - # initialize the invoker are used to initialize the invoked. - # - # ==== Examples - # - # class A < Thor - # def foo - # invoke :bar - # invoke "b:hello", ["José"] - # end - # - # def bar - # invoke "b:hello", ["José"] - # end - # end - # - # class B < Thor - # def hello(name) - # puts "hello #{name}" - # end - # end - # - # You can notice that the method "foo" above invokes two tasks: "bar", - # which belongs to the same class and "hello" which belongs to the class B. - # - # By using an invocation system you ensure that a task is invoked only once. - # In the example above, invoking "foo" will invoke "b:hello" just once, even - # if it's invoked later by "bar" method. - # - # When class A invokes class B, all arguments used on A initialization are - # supplied to B. This allows lazy parse of options. Let's suppose you have - # some rspec tasks: - # - # class Rspec < Thor::Group - # class_option :mock_framework, :type => :string, :default => :rr - # - # def invoke_mock_framework - # invoke "rspec:#{options[:mock_framework]}" - # end - # end - # - # As you noticed, it invokes the given mock framework, which might have its - # own options: - # - # class Rspec::RR < Thor::Group - # class_option :style, :type => :string, :default => :mock - # end - # - # Since it's not rspec concern to parse mock framework options, when RR - # is invoked all options are parsed again, so RR can extract only the options - # that it's going to use. - # - # If you want Rspec::RR to be initialized with its own set of options, you - # have to do that explicitely: - # - # invoke "rspec:rr", [], :style => :foo - # - # Besides giving an instance, you can also give a class to invoke: - # - # invoke Rspec::RR, [], :style => :foo - # - def invoke(name=nil, task=nil, args=nil, opts=nil, config=nil) - task, args, opts, config = nil, task, args, opts if task.nil? || task.is_a?(Array) - args, opts, config = nil, args, opts if args.is_a?(Hash) - - object, task = _prepare_for_invocation(name, task) - klass, instance = _initialize_klass_with_initializer(object, args, opts, config) - - method_args = [] - current = @_invocations[klass] - - iterator = proc do |_, task| - unless current.include?(task.name) - current << task.name - task.run(instance, method_args) - end - end - - if task - args ||= [] - method_args = args[Range.new(klass.arguments.size, -1)] || [] - iterator.call(nil, task) - else - klass.all_tasks.map(&iterator) - end - end - - protected - - # Configuration values that are shared between invocations. - # - def _shared_configuration #:nodoc: - { :invocations => @_invocations } - end - - # Prepare for invocation in the instance level. In this case, we have to - # take into account that a just a task name from the current class was - # given or even a Thor::Task object. - # - def _prepare_for_invocation(name, sent_task=nil) #:nodoc: - if name.is_a?(Thor::Task) - task = name - elsif task = self.class.all_tasks[name.to_s] - object = self - else - object, task = self.class.prepare_for_invocation(nil, name) - task ||= sent_task - end - - # If the object was not set, use self and use the name as task. - object, task = self, name unless object - return object, _validate_task(object, task) - end - - # Check if the object given is a Thor class object and get a task object - # for it. - # - def _validate_task(object, task) #:nodoc: - klass = object.is_a?(Class) ? object : object.class - raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base - - task ||= klass.default_task if klass <= Thor - task = klass.all_tasks[task.to_s] || Thor::Task::Dynamic.new(task) if task && !task.is_a?(Thor::Task) - task - end - - # Initialize klass using values stored in the @_initializer. - # - def _initialize_klass_with_initializer(object, args, opts, config) #:nodoc: - if object.is_a?(Class) - klass = object - - stored_args, stored_opts, stored_config = @_initializer - args ||= stored_args.dup - opts ||= stored_opts.dup - - config ||= {} - config = stored_config.merge(_shared_configuration).merge!(config) - [ klass, klass.new(args, opts, config) ] - else - [ object.class, object ] - end - end - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser.rb deleted file mode 100644 index 57a3f6e1a5..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'thor/parser/argument' -require 'thor/parser/arguments' -require 'thor/parser/option' -require 'thor/parser/options' diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/argument.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/argument.rb deleted file mode 100644 index aa8ace4719..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/argument.rb +++ /dev/null @@ -1,67 +0,0 @@ -class Thor - class Argument #:nodoc: - VALID_TYPES = [ :numeric, :hash, :array, :string ] - - attr_reader :name, :description, :required, :type, :default, :banner - alias :human_name :name - - def initialize(name, description=nil, required=true, type=:string, default=nil, banner=nil) - class_name = self.class.name.split("::").last - - raise ArgumentError, "#{class_name} name can't be nil." if name.nil? - raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type) - - @name = name.to_s - @description = description - @required = required || false - @type = (type || :string).to_sym - @default = default - @banner = banner || default_banner - - validate! # Trigger specific validations - end - - def usage - required? ? banner : "[#{banner}]" - end - - def required? - required - end - - def show_default? - case default - when Array, String, Hash - !default.empty? - else - default - end - end - - protected - - def validate! - raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil? - end - - def valid_type?(type) - VALID_TYPES.include?(type.to_sym) - end - - def default_banner - case type - when :boolean - nil - when :string, :default - human_name.upcase - when :numeric - "N" - when :hash - "key:value" - when :array - "one two three" - end - end - - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/arguments.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/arguments.rb deleted file mode 100644 index fb5d965e06..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/arguments.rb +++ /dev/null @@ -1,145 +0,0 @@ -class Thor - class Arguments #:nodoc: - NUMERIC = /(\d*\.\d+|\d+)/ - - # Receives an array of args and returns two arrays, one with arguments - # and one with switches. - # - def self.split(args) - arguments = [] - - args.each do |item| - break if item =~ /^-/ - arguments << item - end - - return arguments, args[Range.new(arguments.size, -1)] - end - - def self.parse(base, args) - new(base).parse(args) - end - - # Takes an array of Thor::Argument objects. - # - def initialize(arguments=[]) - @assigns, @non_assigned_required = {}, [] - @switches = arguments - - arguments.each do |argument| - if argument.default - @assigns[argument.human_name] = argument.default - elsif argument.required? - @non_assigned_required << argument - end - end - end - - def parse(args) - @pile = args.dup - - @switches.each do |argument| - break unless peek - @non_assigned_required.delete(argument) - @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name) - end - - check_requirement! - @assigns - end - - private - - def peek - @pile.first - end - - def shift - @pile.shift - end - - def unshift(arg) - unless arg.kind_of?(Array) - @pile.unshift(arg) - else - @pile = arg + @pile - end - end - - def current_is_value? - peek && peek.to_s !~ /^-/ - end - - # Runs through the argument array getting strings that contains ":" and - # mark it as a hash: - # - # [ "name:string", "age:integer" ] - # - # Becomes: - # - # { "name" => "string", "age" => "integer" } - # - def parse_hash(name) - return shift if peek.is_a?(Hash) - hash = {} - - while current_is_value? && peek.include?(?:) - key, value = shift.split(':') - hash[key] = value - end - hash - end - - # Runs through the argument array getting all strings until no string is - # found or a switch is found. - # - # ["a", "b", "c"] - # - # And returns it as an array: - # - # ["a", "b", "c"] - # - def parse_array(name) - return shift if peek.is_a?(Array) - array = [] - - while current_is_value? - array << shift - end - array - end - - # Check if the peel is numeric ofrmat and return a Float or Integer. - # Otherwise raises an error. - # - def parse_numeric(name) - return shift if peek.is_a?(Numeric) - - unless peek =~ NUMERIC && $& == peek - raise MalformattedArgumentError, "expected numeric value for '#{name}'; got #{peek.inspect}" - end - - $&.index('.') ? shift.to_f : shift.to_i - end - - # Parse string, i.e., just return the current value in the pile. - # - def parse_string(name) - shift - end - - # Raises an error if @non_assigned_required array is not empty. - # - def check_requirement! - unless @non_assigned_required.empty? - names = @non_assigned_required.map do |o| - o.respond_to?(:switch_name) ? o.switch_name : o.human_name - end.join("', '") - - class_name = self.class.name.split('::').last.downcase - raise RequiredArgumentMissingError, "no value provided for required #{class_name} '#{names}'" - end - end - - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/option.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/option.rb deleted file mode 100644 index 9e40ec73fa..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/option.rb +++ /dev/null @@ -1,132 +0,0 @@ -class Thor - class Option < Argument #:nodoc: - attr_reader :aliases, :group - - VALID_TYPES = [:boolean, :numeric, :hash, :array, :string] - - def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, group=nil, aliases=nil) - super(name, description, required, type, default, banner) - @aliases = [*aliases].compact - @group = group.to_s.capitalize if group - end - - # This parse quick options given as method_options. It makes several - # assumptions, but you can be more specific using the option method. - # - # parse :foo => "bar" - # #=> Option foo with default value bar - # - # parse [:foo, :baz] => "bar" - # #=> Option foo with default value bar and alias :baz - # - # parse :foo => :required - # #=> Required option foo without default value - # - # parse :foo => 2 - # #=> Option foo with default value 2 and type numeric - # - # parse :foo => :numeric - # #=> Option foo without default value and type numeric - # - # parse :foo => true - # #=> Option foo with default value true and type boolean - # - # The valid types are :boolean, :numeric, :hash, :array and :string. If none - # is given a default type is assumed. This default type accepts arguments as - # string (--foo=value) or booleans (just --foo). - # - # By default all options are optional, unless :required is given. - # - def self.parse(key, value) - if key.is_a?(Array) - name, *aliases = key - else - name, aliases = key, [] - end - - name = name.to_s - default = value - - type = case value - when Symbol - default = nil - - if VALID_TYPES.include?(value) - value - elsif required = (value == :required) - :string - elsif value == :optional - # TODO Remove this warning in the future. - warn "Optional type is deprecated. Choose :boolean or :string instead. Assumed to be :boolean." - :boolean - end - when TrueClass, FalseClass - :boolean - when Numeric - :numeric - when Hash, Array, String - value.class.name.downcase.to_sym - end - - self.new(name.to_s, nil, required, type, default, nil, nil, aliases) - end - - def switch_name - @switch_name ||= dasherized? ? name : dasherize(name) - end - - def human_name - @human_name ||= dasherized? ? undasherize(name) : name - end - - def usage(padding=0) - sample = if banner && !banner.to_s.empty? - "#{switch_name}=#{banner}" - else - switch_name - end - - sample = "[#{sample}]" unless required? - - if aliases.empty? - (" " * padding) << sample - else - "#{aliases.join(', ')}, #{sample}" - end - end - - # Allow some type predicates as: boolean?, string? and etc. - # - def method_missing(method, *args, &block) - given = method.to_s.sub(/\?$/, '').to_sym - if valid_type?(given) - self.type == given - else - super - end - end - - protected - - def validate! - raise ArgumentError, "An option cannot be boolean and required." if boolean? && required? - end - - def valid_type?(type) - VALID_TYPES.include?(type.to_sym) - end - - def dasherized? - name.index('-') == 0 - end - - def undasherize(str) - str.sub(/^-{1,2}/, '') - end - - def dasherize(str) - (str.length > 1 ? "--" : "-") + str.gsub('_', '-') - end - - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/options.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/options.rb deleted file mode 100644 index 75092308b5..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/parser/options.rb +++ /dev/null @@ -1,142 +0,0 @@ -class Thor - # This is a modified version of Daniel Berger's Getopt::Long class, licensed - # under Ruby's license. - # - class Options < Arguments #:nodoc: - LONG_RE = /^(--\w+[-\w+]*)$/ - SHORT_RE = /^(-[a-z])$/i - EQ_RE = /^(--\w+[-\w+]*|-[a-z])=(.*)$/i - SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args - SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i - - # Receives a hash and makes it switches. - # - def self.to_switches(options) - options.map do |key, value| - case value - when true - "--#{key}" - when Array - "--#{key} #{value.map{ |v| v.inspect }.join(' ')}" - when Hash - "--#{key} #{value.map{ |k,v| "#{k}:#{v}" }.join(' ')}" - when nil, false - "" - else - "--#{key} #{value.inspect}" - end - end.join(" ") - end - - # Takes a hash of Thor::Option objects. - # - def initialize(options={}) - options = options.values - super(options) - @shorts, @switches = {}, {} - - options.each do |option| - @switches[option.switch_name] = option - - option.aliases.each do |short| - @shorts[short.to_s] ||= option.switch_name - end - end - end - - def parse(args) - @pile = args.dup - - while peek - if current_is_switch? - case shift - when SHORT_SQ_RE - unshift($1.split('').map { |f| "-#{f}" }) - next - when EQ_RE, SHORT_NUM - unshift($2) - switch = $1 - when LONG_RE, SHORT_RE - switch = $1 - end - - switch = normalize_switch(switch) - next unless option = switch_option(switch) - - @assigns[option.human_name] = parse_peek(switch, option) - else - shift - end - end - - check_requirement! - @assigns - end - - protected - - # Returns true if the current value in peek is a registered switch. - # - def current_is_switch? - case peek - when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM - switch?($1) - when SHORT_SQ_RE - $1.split('').any? { |f| switch?("-#{f}") } - end - end - - def switch?(arg) - switch_option(arg) || @shorts.key?(arg) - end - - def switch_option(arg) - if match = no_or_skip?(arg) - @switches[arg] || @switches["--#{match}"] - else - @switches[arg] - end - end - - def no_or_skip?(arg) - arg =~ /^--(no|skip)-([-\w]+)$/ - $2 - end - - # Check if the given argument is actually a shortcut. - # - def normalize_switch(arg) - @shorts.key?(arg) ? @shorts[arg] : arg - end - - # Parse boolean values which can be given as --foo=true, --foo or --no-foo. - # - def parse_boolean(switch) - if current_is_value? - ["true", "TRUE", "t", "T", true].include?(shift) - else - @switches.key?(switch) || !no_or_skip?(switch) - end - end - - # Parse the value at the peek analyzing if it requires an input or not. - # - def parse_peek(switch, option) - unless current_is_value? - if option.boolean? - # No problem for boolean types - elsif no_or_skip?(switch) - return nil # User set value to nil - elsif option.string? && !option.required? - return option.human_name # Return the option name - else - raise MalformattedArgumentError, "no value provided for option '#{switch}'" - end - end - - @non_assigned_required.delete(option) - send(:"parse_#{option.type}", switch) - end - - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/rake_compat.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/rake_compat.rb deleted file mode 100644 index 0d0757fdda..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/rake_compat.rb +++ /dev/null @@ -1,66 +0,0 @@ -require 'rake' - -class Thor - # Adds a compatibility layer to your Thor classes which allows you to use - # rake package tasks. For example, to use rspec rake tasks, one can do: - # - # require 'thor/rake_compat' - # - # class Default < Thor - # include Thor::RakeCompat - # - # Spec::Rake::SpecTask.new(:spec) do |t| - # t.spec_opts = ['--options', "spec/spec.opts"] - # t.spec_files = FileList['spec/**/*_spec.rb'] - # end - # end - # - module RakeCompat - def self.rake_classes - @rake_classes ||= [] - end - - def self.included(base) - # Hack. Make rakefile point to invoker, so rdoc task is generated properly. - rakefile = File.basename(caller[0].match(/(.*):\d+/)[1]) - Rake.application.instance_variable_set(:@rakefile, rakefile) - self.rake_classes << base - end - end -end - -class Object #:nodoc: - alias :rake_task :task - alias :rake_namespace :namespace - - def task(*args, &block) - task = rake_task(*args, &block) - - if klass = Thor::RakeCompat.rake_classes.last - non_namespaced_name = task.name.split(':').last - - description = non_namespaced_name - description << task.arg_names.map{ |n| n.to_s.upcase }.join(' ') - description.strip! - - klass.desc description, task.comment || non_namespaced_name - klass.send :define_method, non_namespaced_name do |*args| - Rake::Task[task.name.to_sym].invoke(*args) - end - end - - task - end - - def namespace(name, &block) - if klass = Thor::RakeCompat.rake_classes.last - const_name = Thor::Util.camel_case(name.to_s).to_sym - klass.const_set(const_name, Class.new(Thor)) - new_klass = klass.const_get(const_name) - Thor::RakeCompat.rake_classes << new_klass - end - - rake_namespace(name, &block) - Thor::RakeCompat.rake_classes.pop - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/runner.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/runner.rb deleted file mode 100644 index 9dc70ea069..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/runner.rb +++ /dev/null @@ -1,299 +0,0 @@ -require 'fileutils' -require 'open-uri' -require 'yaml' -require 'digest/md5' -require 'pathname' - -class Thor::Runner < Thor #:nodoc: - map "-T" => :list, "-i" => :install, "-u" => :update - - # Override Thor#help so it can give information about any class and any method. - # - def help(meth=nil) - if meth && !self.respond_to?(meth) - initialize_thorfiles(meth) - klass, task = Thor::Util.namespace_to_thor_class_and_task(meth) - # Send mapping -h because it works with Thor::Group too - klass.start(["-h", task].compact, :shell => self.shell) - else - super - end - end - - # If a task is not found on Thor::Runner, method missing is invoked and - # Thor::Runner is then responsable for finding the task in all classes. - # - def method_missing(meth, *args) - meth = meth.to_s - initialize_thorfiles(meth) - klass, task = Thor::Util.namespace_to_thor_class_and_task(meth) - args.unshift(task) if task - klass.start(args, :shell => shell) - end - - desc "install NAME", "Install an optionally named Thor file into your system tasks" - method_options :as => :string, :relative => :boolean - def install(name) - initialize_thorfiles - - # If a directory name is provided as the argument, look for a 'main.thor' - # task in said directory. - begin - if File.directory?(File.expand_path(name)) - base, package = File.join(name, "main.thor"), :directory - contents = open(base).read - else - base, package = name, :file - contents = open(name).read - end - rescue OpenURI::HTTPError - raise Error, "Error opening URI '#{name}'" - rescue Errno::ENOENT - raise Error, "Error opening file '#{name}'" - end - - say "Your Thorfile contains:" - say contents - - return false if no?("Do you wish to continue [y/N]?") - - as = options["as"] || begin - first_line = contents.split("\n")[0] - (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil - end - - unless as - basename = File.basename(name) - as = ask("Please specify a name for #{name} in the system repository [#{basename}]:") - as = basename if as.empty? - end - - location = if options[:relative] || name =~ /^http:\/\// - name - else - File.expand_path(name) - end - - thor_yaml[as] = { - :filename => Digest::MD5.hexdigest(name + as), - :location => location, - :namespaces => Thor::Util.namespaces_in_content(contents, base) - } - - save_yaml(thor_yaml) - say "Storing thor file in your system repository" - destination = File.join(thor_root, thor_yaml[as][:filename]) - - if package == :file - File.open(destination, "w") { |f| f.puts contents } - else - FileUtils.cp_r(name, destination) - end - - thor_yaml[as][:filename] # Indicate success - end - - desc "uninstall NAME", "Uninstall a named Thor module" - def uninstall(name) - raise Error, "Can't find module '#{name}'" unless thor_yaml[name] - say "Uninstalling #{name}." - FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}")) - - thor_yaml.delete(name) - save_yaml(thor_yaml) - - puts "Done." - end - - desc "update NAME", "Update a Thor file from its original location" - def update(name) - raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location] - - say "Updating '#{name}' from #{thor_yaml[name][:location]}" - - old_filename = thor_yaml[name][:filename] - self.options = self.options.merge("as" => name) - filename = install(thor_yaml[name][:location]) - - unless filename == old_filename - File.delete(File.join(thor_root, old_filename)) - end - end - - desc "installed", "List the installed Thor modules and tasks" - method_options :internal => :boolean - def installed - initialize_thorfiles(nil, true) - - klasses = Thor::Base.subclasses - klasses -= [Thor, Thor::Runner] unless options["internal"] - - display_klasses(true, klasses) - end - - desc "list [SEARCH]", "List the available thor tasks (--substring means .*SEARCH)" - method_options :substring => :boolean, :group => :string, :all => :boolean - def list(search="") - initialize_thorfiles - - search = ".*#{search}" if options["substring"] - search = /^#{search}.*/i - group = options[:group] || "standard" - - klasses = Thor::Base.subclasses.select do |k| - (options[:all] || k.group == group) && k.namespace =~ search - end - - display_klasses(false, klasses) - end - - private - - def thor_root - Thor::Util.thor_root - end - - def thor_yaml - @thor_yaml ||= begin - yaml_file = File.join(thor_root, "thor.yml") - yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file) - yaml || {} - end - end - - # Save the yaml file. If none exists in thor root, creates one. - # - def save_yaml(yaml) - yaml_file = File.join(thor_root, "thor.yml") - - unless File.exists?(yaml_file) - FileUtils.mkdir_p(thor_root) - yaml_file = File.join(thor_root, "thor.yml") - FileUtils.touch(yaml_file) - end - - File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml } - end - - def self.exit_on_failure? - true - end - - # Load the thorfiles. If relevant_to is supplied, looks for specific files - # in the thor_root instead of loading them all. - # - # By default, it also traverses the current path until find Thor files, as - # described in thorfiles. This look up can be skipped by suppliying - # skip_lookup true. - # - def initialize_thorfiles(relevant_to=nil, skip_lookup=false) - thorfiles(relevant_to, skip_lookup).each do |f| - Thor::Util.load_thorfile(f) unless Thor::Base.subclass_files.keys.include?(File.expand_path(f)) - end - end - - # Finds Thorfiles by traversing from your current directory down to the root - # directory of your system. If at any time we find a Thor file, we stop. - # - # We also ensure that system-wide Thorfiles are loaded first, so local - # Thorfiles can override them. - # - # ==== Example - # - # If we start at /Users/wycats/dev/thor ... - # - # 1. /Users/wycats/dev/thor - # 2. /Users/wycats/dev - # 3. /Users/wycats <-- we find a Thorfile here, so we stop - # - # Suppose we start at c:\Documents and Settings\james\dev\thor ... - # - # 1. c:\Documents and Settings\james\dev\thor - # 2. c:\Documents and Settings\james\dev - # 3. c:\Documents and Settings\james - # 4. c:\Documents and Settings - # 5. c:\ <-- no Thorfiles found! - # - def thorfiles(relevant_to=nil, skip_lookup=false) - # TODO Remove this dealing with deprecated thor when :namespaces: is available as constants - save_yaml(thor_yaml) if Thor::Util.convert_constants_to_namespaces(thor_yaml) - - thorfiles = [] - - unless skip_lookup - Pathname.pwd.ascend do |path| - thorfiles = Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten - break unless thorfiles.empty? - end - end - - files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Thor::Util.thor_root_glob) - files += thorfiles - files -= ["#{thor_root}/thor.yml"] - - files.map! do |file| - File.directory?(file) ? File.join(file, "main.thor") : file - end - end - - # Load thorfiles relevant to the given method. If you provide "foo:bar" it - # will load all thor files in the thor.yaml that has "foo" e "foo:bar" - # namespaces registered. - # - def thorfiles_relevant_to(meth) - lookup = [ meth, meth.split(":")[0...-1].join(":") ] - - files = thor_yaml.select do |k, v| - v[:namespaces] && !(v[:namespaces] & lookup).empty? - end - - files.map { |k, v| File.join(thor_root, "#{v[:filename]}") } - end - - # Display information about the given klasses. If with_module is given, - # it shows a table with information extracted from the yaml file. - # - def display_klasses(with_modules=false, klasses=Thor.subclasses) - klasses -= [Thor, Thor::Runner] unless with_modules - raise Error, "No Thor tasks available" if klasses.empty? - - if with_modules && !thor_yaml.empty? - info = [] - labels = ["Modules", "Namespaces"] - - info << labels - info << [ "-" * labels[0].size, "-" * labels[1].size ] - - thor_yaml.each do |name, hash| - info << [ name, hash[:namespaces].join(", ") ] - end - - print_table info - say "" - end - - unless klasses.empty? - klasses.dup.each do |klass| - klasses -= Thor::Util.thor_classes_in(klass) - end - - klasses.each { |k| display_tasks(k) } - else - say "\033[1;34mNo Thor tasks available\033[0m" - end - end - - # Display tasks from the given Thor class. - # - def display_tasks(klass) - unless klass.tasks.empty? - base = klass.namespace - - color = base == "default" ? :magenta : :blue - say shell.set_color(base, color, true) - say "-" * base.length - - klass.help(shell, :short => true, :ident => 0, :namespace => true) - end - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell.rb deleted file mode 100644 index 1dc8f0e5b4..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell.rb +++ /dev/null @@ -1,78 +0,0 @@ -require 'rbconfig' -require 'thor/shell/color' - -class Thor - module Base - # Returns the shell used in all Thor classes. If you are in a Unix platform - # it will use a colored log, otherwise it will use a basic one without color. - # - def self.shell - @shell ||= if Config::CONFIG['host_os'] =~ /mswin|mingw/ - Thor::Shell::Basic - else - Thor::Shell::Color - end - end - - # Sets the shell used in all Thor classes. - # - def self.shell=(klass) - @shell = klass - end - end - - module Shell - SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_list, :print_table] - - # Add shell to initialize config values. - # - # ==== Configuration - # shell:: An instance of the shell to be used. - # - # ==== Examples - # - # class MyScript < Thor - # argument :first, :type => :numeric - # end - # - # MyScript.new [1.0], { :foo => :bar }, :shell => Thor::Shell::Basic.new - # - def initialize(args=[], options={}, config={}) - super - self.shell = config[:shell] - self.shell.base ||= self if self.shell.respond_to?(:base) - end - - # Holds the shell for the given Thor instance. If no shell is given, - # it gets a default shell from Thor::Base.shell. - # - def shell - @shell ||= Thor::Base.shell.new - end - - # Sets the shell for this thor class. - # - def shell=(shell) - @shell = shell - end - - # Common methods that are delegated to the shell. - # - SHELL_DELEGATED_METHODS.each do |method| - module_eval <<-METHOD, __FILE__, __LINE__ - def #{method}(*args) - shell.#{method}(*args) - end - METHOD - end - - protected - - # Allow shell to be shared between invocations. - # - def _shared_configuration #:nodoc: - super.merge!(:shell => self.shell) - end - - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell/basic.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell/basic.rb deleted file mode 100644 index ea9665380b..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/shell/basic.rb +++ /dev/null @@ -1,219 +0,0 @@ -require 'tempfile' - -class Thor - module Shell - class Basic - attr_accessor :base, :padding - - # Initialize base and padding to nil. - # - def initialize #:nodoc: - @base, @padding = nil, 0 - end - - # Sets the output padding, not allowing less than zero values. - # - def padding=(value) - @padding = [0, value].max - end - - # Ask something to the user and receives a response. - # - # ==== Example - # ask("What is your name?") - # - def ask(statement, color=nil) - say("#{statement} ", color) - $stdin.gets.strip - end - - # Say (print) something to the user. If the sentence ends with a whitespace - # or tab character, a new line is not appended (print + flush). Otherwise - # are passed straight to puts (behavior got from Highline). - # - # ==== Example - # say("I know you knew that.") - # - def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)$/)) - message = message.to_s - message = set_color(message, color) if color - - if force_new_line - $stdout.puts(message) - else - $stdout.print(message) - $stdout.flush - end - end - - # Say a status with the given color and appends the message. Since this - # method is used frequently by actions, it allows nil or false to be given - # in log_status, avoiding the message from being shown. If a Symbol is - # given in log_status, it's used as the color. - # - def say_status(status, message, log_status=true) - return if quiet? || log_status == false - spaces = " " * (padding + 1) - color = log_status.is_a?(Symbol) ? log_status : :green - - status = status.to_s.rjust(12) - status = set_color status, color, true if color - say "#{status}#{spaces}#{message}", nil, true - end - - # Make a question the to user and returns true if the user replies "y" or - # "yes". - # - def yes?(statement, color=nil) - ask(statement, color) =~ is?(:yes) - end - - # Make a question the to user and returns true if the user replies "n" or - # "no". - # - def no?(statement, color=nil) - !yes?(statement, color) - end - - # Prints a list of items. - # - # ==== Parameters - # list - # - # ==== Options - # mode:: Can be :rows or :inline. Defaults to :rows. - # ident:: Ident each item with the value given. - # - def print_list(list, options={}) - return if list.empty? - - ident = " " * (options[:ident] || 0) - content = case options[:mode] - when :inline - last = list.pop - "#{list.join(", ")}, and #{last}" - else # rows - ident + list.join("\n#{ident}") - end - - $stdout.puts content - end - - # Prints a table. - # - # ==== Parameters - # Array[Array[String, String, ...]] - # - # ==== Options - # ident:: Ident the first column by ident value. - # - def print_table(table, options={}) - return if table.empty? - - formats = [] - 0.upto(table.first.length - 2) do |i| - maxima = table.max{ |a,b| a[i].size <=> b[i].size }[i].size - formats << "%-#{maxima + 2}s" - end - - formats[0] = formats[0].insert(0, " " * options[:ident]) if options[:ident] - formats << "%s" - - table.each do |row| - row.each_with_index do |column, i| - $stdout.print formats[i] % column.to_s - end - $stdout.puts - end - end - - # Deals with file collision and returns true if the file should be - # overwriten and false otherwise. If a block is given, it uses the block - # response as the content for the diff. - # - # ==== Parameters - # destination:: the destination file to solve conflicts - # block:: an optional block that returns the value to be used in diff - # - def file_collision(destination) - return true if @always_force - options = block_given? ? "[Ynaqdh]" : "[Ynaqh]" - - while true - answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}] - - case answer - when is?(:yes), is?(:force) - return true - when is?(:no), is?(:skip) - return false - when is?(:always) - return @always_force = true - when is?(:quit) - say 'Aborting...' - raise SystemExit - when is?(:diff) - show_diff(destination, yield) if block_given? - say 'Retrying...' - else - say file_collision_help - end - end - end - - # Called if something goes wrong during the execution. This is used by Thor - # internally and should not be used inside your scripts. If someone went - # wrong, you can always raise an exception. If you raise a Thor::Error, it - # will be rescued and wrapped in the method below. - # - def error(statement) - $stderr.puts statement - end - - # Apply color to the given string with optional bold. Disabled in the - # Thor::Shell::Basic class. - # - def set_color(string, color, bold=false) #:nodoc: - string - end - - protected - - def is?(value) #:nodoc: - value = value.to_s - - if value.size == 1 - /\A#{value}\z/i - else - /\A(#{value}|#{value[0,1]})\z/i - end - end - - def file_collision_help #:nodoc: -< e - parse_argument_error(instance, e, caller) - rescue NoMethodError => e - parse_no_method_error(instance, e) - end - - # Returns the formatted usage. If a class is given, the class arguments are - # injected in the usage. - # - def formatted_usage(klass=nil, namespace=false, show_options=true) - formatted = if namespace.is_a?(String) - "#{namespace}:" - elsif klass && namespace - "#{klass.namespace.gsub(/^default/,'')}:" - else - "" - end - - formatted << formatted_arguments(klass) - formatted << " #{formatted_options}" if show_options - formatted.strip! - formatted - end - - # Injects the class arguments into the task usage. - # - def formatted_arguments(klass) - if klass && !klass.arguments.empty? - usage.to_s.gsub(/^#{name}/) do |match| - match << " " << klass.arguments.map{ |a| a.usage }.join(' ') - end - else - usage.to_s - end - end - - # Returns the options usage for this task. - # - def formatted_options - @formatted_options ||= options.map{ |_, o| o.usage }.sort.join(" ") - end - - protected - - # Given a target, checks if this class name is not a private/protected method. - # - def public_method?(instance) #:nodoc: - collection = instance.private_methods + instance.protected_methods - (collection & [name.to_s, name.to_sym]).empty? - end - - # Clean everything that comes from the Thor gempath and remove the caller. - # - def sans_backtrace(backtrace, caller) #:nodoc: - dirname = /^#{Regexp.escape(File.dirname(__FILE__))}/ - saned = backtrace.reject { |frame| frame =~ dirname } - saned -= caller - end - - def parse_argument_error(instance, e, caller) #:nodoc: - backtrace = sans_backtrace(e.backtrace, caller) - - if backtrace.empty? && e.message =~ /wrong number of arguments/ - if instance.is_a?(Thor::Group) - raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?" - else - raise InvocationError, "'#{name}' was called incorrectly. Call as " << - "'#{formatted_usage(instance.class, true)}'" - end - else - raise e - end - end - - def parse_no_method_error(instance, e) #:nodoc: - if e.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/ - raise UndefinedTaskError, "The #{instance.class.namespace} namespace " << - "doesn't have a '#{name}' task" - else - raise e - end - end - - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/util.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/util.rb deleted file mode 100644 index ebae0a3193..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/util.rb +++ /dev/null @@ -1,251 +0,0 @@ -require 'rbconfig' - -class Thor - module Sandbox #:nodoc: - end - - # This module holds several utilities: - # - # 1) Methods to convert thor namespaces to constants and vice-versa. - # - # Thor::Utils.namespace_from_thor_class(Foo::Bar::Baz) #=> "foo:bar:baz" - # - # 2) Loading thor files and sandboxing: - # - # Thor::Utils.load_thorfile("~/.thor/foo") - # - module Util - - # Receives a namespace and search for it in the Thor::Base subclasses. - # - # ==== Parameters - # namespace:: The namespace to search for. - # - def self.find_by_namespace(namespace) - namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/ - - Thor::Base.subclasses.find do |klass| - klass.namespace == namespace - end - end - - # Receives a constant and converts it to a Thor namespace. Since Thor tasks - # can be added to a sandbox, this method is also responsable for removing - # the sandbox namespace. - # - # This method should not be used in general because it's used to deal with - # older versions of Thor. On current versions, if you need to get the - # namespace from a class, just call namespace on it. - # - # ==== Parameters - # constant:: The constant to be converted to the thor path. - # - # ==== Returns - # String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz" - # - def self.namespace_from_thor_class(constant, remove_default=true) - constant = constant.to_s.gsub(/^Thor::Sandbox::/, "") - constant = snake_case(constant).squeeze(":") - constant.gsub!(/^default/, '') if remove_default - constant - end - - # Given the contents, evaluate it inside the sandbox and returns the - # namespaces defined in the sandbox. - # - # ==== Parameters - # contents - # - # ==== Returns - # Array[Object] - # - def self.namespaces_in_content(contents, file=__FILE__) - old_constants = Thor::Base.subclasses.dup - Thor::Base.subclasses.clear - - load_thorfile(file, contents) - - new_constants = Thor::Base.subclasses.dup - Thor::Base.subclasses.replace(old_constants) - - new_constants.map!{ |c| c.namespace } - new_constants.compact! - new_constants - end - - # Returns the thor classes declared inside the given class. - # - def self.thor_classes_in(klass) - Thor::Base.subclasses.select do |subclass| - klass.constants.include?(subclass.name.gsub("#{klass.name}::", '')) - end - end - - # Receives a string and convert it to snake case. SnakeCase returns snake_case. - # - # ==== Parameters - # String - # - # ==== Returns - # String - # - def self.snake_case(str) - return str.downcase if str =~ /^[A-Z_]+$/ - str.gsub(/\B[A-Z]/, '_\&').squeeze('_') =~ /_*(.*)/ - return $+.downcase - end - - # Receives a string and convert it to camel case. camel_case returns CamelCase. - # - # ==== Parameters - # String - # - # ==== Returns - # String - # - def self.camel_case(str) - return str if str !~ /_/ && str =~ /[A-Z]+.*/ - str.split('_').map { |i| i.capitalize }.join - end - - # Receives a namespace and tries to retrieve a Thor or Thor::Group class - # from it. It first searches for a class using the all the given namespace, - # if it's not found, removes the highest entry and searches for the class - # again. If found, returns the highest entry as the class name. - # - # ==== Examples - # - # class Foo::Bar < Thor - # def baz - # end - # end - # - # class Baz::Foo < Thor::Group - # end - # - # Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default task - # Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil - # Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz" - # - # ==== Parameters - # namespace - # - # ==== Errors - # Thor::Error:: raised if the namespace cannot be found. - # - # Thor::Error:: raised if the namespace evals to a class which does not - # inherit from Thor or Thor::Group. - # - def self.namespace_to_thor_class_and_task(namespace, raise_if_nil=true) - if namespace.include?(?:) - pieces = namespace.split(":") - task = pieces.pop - klass = Thor::Util.find_by_namespace(pieces.join(":")) - end - - unless klass - klass, task = Thor::Util.find_by_namespace(namespace), nil - end - - raise Error, "could not find Thor class or task '#{namespace}'" if raise_if_nil && klass.nil? - return klass, task - end - - # Receives a path and load the thor file in the path. The file is evaluated - # inside the sandbox to avoid namespacing conflicts. - # - def self.load_thorfile(path, content=nil) - content ||= File.read(path) - - begin - Thor::Sandbox.class_eval(content, path) - rescue Exception => e - $stderr.puts "WARNING: unable to load thorfile #{path.inspect}: #{e.message}" - end - end - - # Receives a yaml (hash) and updates all constants entries to namespace. - # This was added to deal with deprecated versions of Thor. - # - # TODO Deprecate this method in the future. - # - # ==== Returns - # TrueClass|FalseClass:: Returns true if any change to the yaml file was made. - # - def self.convert_constants_to_namespaces(yaml) - yaml_changed = false - - yaml.each do |k, v| - next unless v[:constants] && v[:namespaces].nil? - yaml_changed = true - yaml[k][:namespaces] = v[:constants].map{|c| Thor::Util.namespace_from_thor_class(c)} - end - - yaml_changed - end - - def self.user_home - @@user_home ||= if ENV["HOME"] - ENV["HOME"] - elsif ENV["USERPROFILE"] - ENV["USERPROFILE"] - elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"] - File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"]) - elsif ENV["APPDATA"] - ENV["APPDATA"] - else - begin - File.expand_path("~") - rescue - if File::ALT_SEPARATOR - "C:/" - else - "/" - end - end - end - end - - # Returns the root where thor files are located, dependending on the OS. - # - def self.thor_root - File.join(user_home, ".thor").gsub(/\\/, '/') - end - - # Returns the files in the thor root. On Windows thor_root will be something - # like this: - # - # C:\Documents and Settings\james\.thor - # - # If we don't #gsub the \ character, Dir.glob will fail. - # - def self.thor_root_glob - files = Dir["#{thor_root}/*"] - - files.map! do |file| - File.directory?(file) ? File.join(file, "main.thor") : file - end - end - - # Where to look for Thor files. - # - def self.globs_for(path) - ["#{path}/Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"] - end - - # Return the path to the ruby interpreter taking into account multiple - # installations and windows extensions. - # - def self.ruby_command - @ruby_command ||= begin - ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) - ruby << Config::CONFIG['EXEEXT'] - - # escape string in case path to ruby executable contain spaces. - ruby.sub!(/.*\s.*/m, '"\&"') - ruby - end - end - - end -end diff --git a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/version.rb b/railties/lib/rails/vendor/thor-0.12.0/lib/thor/version.rb deleted file mode 100644 index 885230fac4..0000000000 --- a/railties/lib/rails/vendor/thor-0.12.0/lib/thor/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Thor - VERSION = "0.11.8".freeze -end diff --git a/railties/lib/rails/vendor/thor-0.12.1/CHANGELOG.rdoc b/railties/lib/rails/vendor/thor-0.12.1/CHANGELOG.rdoc new file mode 100644 index 0000000000..606a0cdb52 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/CHANGELOG.rdoc @@ -0,0 +1,82 @@ +== TODO + +* Improve spec coverage for Thor::Runner + +== 0.12, released 2009-11-06 + +* [#7] Do not force white color on status +* [#8] Yield a block with the filename on directory + +== 0.11, released 2009-07-01 + +* Added a rake compatibility layer. It allows you to use spec and rdoc tasks on + Thor classes. + +* BACKWARDS INCOMPATIBLE: aliases are not generated automatically anymore + since it wrong behavior to the invocation system. + +* thor help now show information about any class/task. All those calls are + possible: + + thor help describe + thor help describe:amazing + + Or even with default namespaces: + + thor help :spec + +* Thor::Runner now invokes the default task if none is supplied: + + thor describe # invokes the default task, usually help + +* Thor::Runner now works with mappings: + + thor describe -h + +* Added some documentation and code refactoring. + +== 0.9.8, released 2008-10-20 + +* Fixed some tiny issues that were introduced lately. + +== 0.9.7, released 2008-10-13 + +* Setting global method options on the initialize method works as expected: + All other tasks will accept these global options in addition to their own. +* Added 'group' notion to Thor task sets (class Thor); by default all tasks + are in the 'standard' group. Running 'thor -T' will only show the standard + tasks - adding --all will show all tasks. You can also filter on a specific + group using the --group option: thor -T --group advanced + +== 0.9.6, released 2008-09-13 + +* Generic improvements + +== 0.9.5, released 2008-08-27 + +* Improve Windows compatibility +* Update (incorrect) README and task.thor sample file +* Options hash is now frozen (once returned) +* Allow magic predicates on options object. For instance: `options.force?` +* Add support for :numeric type +* BACKWARDS INCOMPATIBLE: Refactor Thor::Options. You cannot access shorthand forms in options hash anymore (for instance, options[:f]) +* Allow specifying optional args with default values: method_options(:user => "mislav") +* Don't write options for nil or false values. This allows, for example, turning color off when running specs. +* Exit with the status of the spec command to help CI stuff out some. + +== 0.9.4, released 2008-08-13 + +* Try to add Windows compatibility. +* BACKWARDS INCOMPATIBLE: options hash is now accessed as a property in your class and is not passed as last argument anymore +* Allow options at the beginning of the argument list as well as the end. +* Make options available with symbol keys in addition to string keys. +* Allow true to be passed to Thor#method_options to denote a boolean option. +* If loading a thor file fails, don't give up, just print a warning and keep going. +* Make sure that we re-raise errors if they happened further down the pipe than we care about. +* Only delete the old file on updating when the installation of the new one is a success +* Make it Ruby 1.8.5 compatible. +* Don't raise an error if a boolean switch is defined multiple times. +* Thor::Options now doesn't parse through things that look like options but aren't. +* Add URI detection to install task, and make sure we don't append ".thor" to URIs +* Add rake2thor to the gem binfiles. +* Make sure local Thorfiles override system-wide ones. diff --git a/railties/lib/rails/vendor/thor-0.12.1/LICENSE b/railties/lib/rails/vendor/thor-0.12.1/LICENSE new file mode 100644 index 0000000000..98722da459 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008 Yehuda Katz + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/railties/lib/rails/vendor/thor-0.12.1/README.rdoc b/railties/lib/rails/vendor/thor-0.12.1/README.rdoc new file mode 100644 index 0000000000..ee545f3d97 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/README.rdoc @@ -0,0 +1,234 @@ += thor + +Map options to a class. Simply create a class with the appropriate annotations +and have options automatically map to functions and parameters. + +Example: + + class App < Thor # [1] + map "-L" => :list # [2] + + desc "install APP_NAME", "install one of the available apps" # [3] + method_options :force => :boolean, :alias => :string # [4] + def install(name) + user_alias = options[:alias] + if options.force? + # do something + end + # other code + end + + desc "list [SEARCH]", "list all of the available apps, limited by SEARCH" + def list(search="") + # list everything + end + end + +Thor automatically maps commands as such: + + thor app:install myname --force + +That gets converted to: + + App.new.install("myname") + # with {'force' => true} as options hash + +1. Inherit from Thor to turn a class into an option mapper +2. Map additional non-valid identifiers to specific methods. In this case, convert -L to :list +3. Describe the method immediately below. The first parameter is the usage information, and the second parameter is the description +4. Provide any additional options that will be available the instance method options. + +== Types for method_options + +* :boolean - is parsed as --option or --option=true +* :string - is parsed as --option=VALUE +* :numeric - is parsed as --option=N +* :array - is parsed as --option=one two three +* :hash - is parsed as --option=name:string age:integer + +Besides, method_option allows a default value to be given, examples: + + method_options :force => false + #=> Creates a boolean option with default value false + + method_options :alias => "bar" + #=> Creates a string option with default value "bar" + + method_options :threshold => 3.0 + #=> Creates a numeric option with default value 3.0 + +You can also supply :option => :required to mark an option as required. The +type is assumed to be string. If you want a required hash with default values +as option, you can use method_option which uses a more declarative style: + + method_option :attributes, :type => :hash, :default => {}, :required => true + +All arguments can be set to nil (except required arguments), by suppling a no or +skip variant. For example: + + thor app name --no-attributes + +In previous versions, aliases for options were created automatically, but now +they should be explicit. You can supply aliases in both short and declarative +styles: + + method_options %w( force -f ) => :boolean + +Or: + + method_option :force, :type => :boolean, :aliases => "-f" + +You can supply as many aliases as you want. + +NOTE: Type :optional available in Thor 0.9.0 was deprecated. Use :string or :boolean instead. + +== Namespaces + +By default, your Thor tasks are invoked using Ruby namespace. In the example +above, tasks are invoked as: + + thor app:install name --force + +However, you could namespace your class as: + + module Sinatra + class App < Thor + # tasks + end + end + +And then you should invoke your tasks as: + + thor sinatra:app:install name --force + +If desired, you can change the namespace: + + module Sinatra + class App < Thor + namespace :myapp + # tasks + end + end + +And then your tasks hould be invoked as: + + thor myapp:install name --force + +== Invocations + +Thor comes with a invocation-dependency system as well which allows a task to be +invoked only once. For example: + + class Counter < Thor + desc "one", "Prints 1, 2, 3" + def one + puts 1 + invoke :two + invoke :three + end + + desc "two", "Prints 2, 3" + def two + puts 2 + invoke :three + end + + desc "three", "Prints 3" + def three + puts 3 + end + end + +When invoking the task one: + + thor counter:one + +The output is "1 2 3", which means that the three task was invoked only once. +You can even invoke tasks from another class, so be sure to check the +documentation. + +== Thor::Group + +Thor has a special class called Thor::Group. The main difference to Thor class +is that it invokes all tasks at once. The example above could be rewritten in +Thor::Group as this: + + class Counter < Thor::Group + desc "Prints 1, 2, 3" + + def one + puts 1 + end + + def two + puts 2 + end + + def three + puts 3 + end + end + +When invoked: + + thor counter + +It prints "1 2 3" as well. Notice you should describe (using the method desc) +only the class and not each task anymore. Thor::Group is a great tool to create +generators, since you can define several steps which are invoked in the order they +are defined (Thor::Group is the tool use in generators in Rails 3.0). + +Besides, Thor::Group can parse arguments and options as Thor tasks: + + class Counter < Thor::Group + # number will be available as attr_accessor + argument :number, :type => :numeric, :desc => "The number to start counting" + desc "Prints the 'number' given upto 'number+2'" + + def one + puts number + 0 + end + + def two + puts number + 1 + end + + def three + puts number + 2 + end + end + +The counter above expects one parameter and has the folling outputs: + + thor counter 5 + # Prints "5 6 7" + + thor counter 11 + # Prints "11 12 13" + +You can also give options to Thor::Group, but instead of using method_option +and method_options, you should use class_option and class_options. +Both argument and class_options methods are available to Thor class as well. + +== Actions + +Thor comes with several actions which helps with script and generator tasks. You +might be familiar with them since some came from Rails Templates. They are: +say, ask, yes?, no?, add_file, +remove_file, copy_file, template, directory, +inside, run, inject_into_file and a couple more. + +To use them, you just need to include Thor::Actions in your Thor classes: + + class App < Thor + include Thor::Actions + # tasks + end + +Some actions like copy file requires that a class method called source_root is +defined in your class. This is the directory where your templates should be +placed. Be sure to check the documentation. + +== License + +See MIT LICENSE. diff --git a/railties/lib/rails/vendor/thor-0.12.1/Thorfile b/railties/lib/rails/vendor/thor-0.12.1/Thorfile new file mode 100644 index 0000000000..ff1cb4498a --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/Thorfile @@ -0,0 +1,63 @@ +# enconding: utf-8 + +require File.join(File.dirname(__FILE__), "lib", "thor", "version") +require 'thor/rake_compat' +require 'spec/rake/spectask' +require 'rdoc/task' + +GEM_NAME = 'thor' +EXTRA_RDOC_FILES = ["README.rdoc", "LICENSE", "CHANGELOG.rdoc", "VERSION", "Thorfile"] + +class Default < Thor + include Thor::RakeCompat + + Spec::Rake::SpecTask.new(:spec) do |t| + t.libs << 'lib' + t.spec_opts = ['--options', "spec/spec.opts"] + t.spec_files = FileList['spec/**/*_spec.rb'] + end + + Spec::Rake::SpecTask.new(:rcov) do |t| + t.libs << 'lib' + t.spec_opts = ['--options', "spec/spec.opts"] + t.spec_files = FileList['spec/**/*_spec.rb'] + t.rcov = true + t.rcov_dir = "rcov" + end + + RDoc::Task.new do |rdoc| + rdoc.main = "README.rdoc" + rdoc.rdoc_dir = "rdoc" + rdoc.title = GEM_NAME + rdoc.rdoc_files.include(*EXTRA_RDOC_FILES) + rdoc.rdoc_files.include('lib/**/*.rb') + rdoc.options << '--line-numbers' << '--inline-source' + end + + begin + require 'jeweler' + Jeweler::Tasks.new do |s| + s.name = GEM_NAME + s.version = Thor::VERSION + s.rubyforge_project = "textmate" + s.platform = Gem::Platform::RUBY + s.summary = "A scripting framework that replaces rake, sake and rubigen" + s.email = "ruby-thor@googlegroups.com" + s.homepage = "http://yehudakatz.com" + s.description = "A scripting framework that replaces rake, sake and rubigen" + s.authors = ['Yehuda Katz', 'José Valim'] + s.has_rdoc = true + s.extra_rdoc_files = EXTRA_RDOC_FILES + s.require_path = 'lib' + s.bindir = "bin" + s.executables = %w( thor rake2thor ) + s.files = s.extra_rdoc_files + Dir.glob("{bin,lib}/**/*") + s.files.exclude 'spec/sandbox/**/*' + s.test_files.exclude 'spec/sandbox/**/*' + end + + Jeweler::GemcutterTasks.new + rescue LoadError + puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor.rb new file mode 100644 index 0000000000..68944f140d --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor.rb @@ -0,0 +1,242 @@ +require 'thor/base' +require 'thor/group' +require 'thor/actions' + +class Thor + class << self + # Sets the default task when thor is executed without an explicit task to be called. + # + # ==== Parameters + # meth:: name of the defaut task + # + def default_task(meth=nil) + case meth + when :none + @default_task = 'help' + when nil + @default_task ||= from_superclass(:default_task, 'help') + else + @default_task = meth.to_s + end + end + + # Defines the usage and the description of the next task. + # + # ==== Parameters + # usage + # description + # + def desc(usage, description, options={}) + if options[:for] + task = find_and_refresh_task(options[:for]) + task.usage = usage if usage + task.description = description if description + else + @usage, @desc = usage, description + end + end + + # Maps an input to a task. If you define: + # + # map "-T" => "list" + # + # Running: + # + # thor -T + # + # Will invoke the list task. + # + # ==== Parameters + # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given task. + # + def map(mappings=nil) + @map ||= from_superclass(:map, {}) + + if mappings + mappings.each do |key, value| + if key.respond_to?(:each) + key.each {|subkey| @map[subkey] = value} + else + @map[key] = value + end + end + end + + @map + end + + # Declares the options for the next task to be declared. + # + # ==== Parameters + # Hash[Symbol => Object]:: The hash key is the name of the option and the value + # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric + # or :required (string). If you give a value, the type of the value is used. + # + def method_options(options=nil) + @method_options ||= {} + build_options(options, @method_options) if options + @method_options + end + + # Adds an option to the set of class options. If :for is given as option, + # it allows you to change the options from a previous defined task. + # + # def previous_task + # # magic + # end + # + # method_options :foo => :bar, :for => :previous_task + # + # def next_task + # # magic + # end + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :default - Default value for this argument. It cannot be required and have default values. + # :aliases - Aliases for this option. + # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean. + # :group - The group for this options. Use by class options to output options in different levels. + # :banner - String to show on usage notes. + # + def method_option(name, options={}) + scope = if options[:for] + find_and_refresh_task(options[:for]).options + else + method_options + end + + build_option(name, options, scope) + end + + # Parses the task and options from the given args, instantiate the class + # and invoke the task. This method is used when the arguments must be parsed + # from an array. If you are inside Ruby and want to use a Thor class, you + # can simply initialize it: + # + # script = MyScript.new(args, options, config) + # script.invoke(:task, first_arg, second_arg, third_arg) + # + def start(given_args=ARGV, config={}) + super do + meth = normalize_task_name(given_args.shift) + task = all_tasks[meth] + + if task + args, opts = Thor::Options.split(given_args) + config.merge!(:task_options => task.options) + else + args, opts = given_args, {} + end + + task ||= Thor::Task::Dynamic.new(meth) + trailing = args[Range.new(arguments.size, -1)] + new(args, opts, config).invoke(task, trailing || []) + end + end + + # Prints help information. If a task name is given, it shows information + # only about the specific task. + # + # ==== Parameters + # meth:: An optional task name to print usage information about. + # + # ==== Options + # namespace:: When true, shows the namespace in the output before the usage. + # skip_inherited:: When true, does not show tasks from superclass. + # + def help(shell, meth=nil, options={}) + meth, options = nil, meth if meth.is_a?(Hash) + + if meth + task = all_tasks[meth] + raise UndefinedTaskError, "task '#{meth}' could not be found in namespace '#{self.namespace}'" unless task + + shell.say "Usage:" + shell.say " #{banner(task, options[:namespace], false)}" + shell.say + class_options_help(shell, "Class", :Method => task.options.map { |_, o| o }) + shell.say task.description + else + list = (options[:short] ? tasks : all_tasks).map do |_, task| + item = [ banner(task, options[:namespace]) ] + item << "# #{task.short_description}" if task.short_description + item << " " + end + + options[:ident] ||= 2 + if options[:short] + shell.print_list(list, :ident => options[:ident]) + else + shell.say "Tasks:" + shell.print_list(list, :ident => options[:ident]) + end + + Thor::Util.thor_classes_in(self).each do |subclass| + namespace = options[:namespace] == true || subclass.namespace.gsub(/^#{self.namespace}:/, '') + subclass.help(shell, options.merge(:short => true, :namespace => namespace)) + end + + class_options_help(shell, "Class") unless options[:short] + end + end + + protected + + # The banner for this class. You can customize it if you are invoking the + # thor class by another ways which is not the Thor::Runner. It receives + # the task that is going to be invoked and a boolean which indicates if + # the namespace should be displayed as arguments. + # + def banner(task, namespace=true, show_options=true) + task.formatted_usage(self, namespace, show_options) + end + + def baseclass #:nodoc: + Thor + end + + def create_task(meth) #:nodoc: + if @usage && @desc + tasks[meth.to_s] = Thor::Task.new(meth, @desc, @usage, method_options) + @usage, @desc, @method_options = nil + true + elsif self.all_tasks[meth.to_s] || meth.to_sym == :method_missing + true + else + puts "[WARNING] Attempted to create task #{meth.inspect} without usage or description. " << + "Call desc if you want this method to be available as task or declare it inside a " << + "no_tasks{} block. Invoked from #{caller[1].inspect}." + false + end + end + + def initialize_added #:nodoc: + class_options.merge!(method_options) + @method_options = nil + end + + # Receives a task name (can be nil), and try to get a map from it. + # If a map can't be found use the sent name or the default task. + # + def normalize_task_name(meth) #:nodoc: + mapping = map[meth.to_s] + meth = mapping || meth || default_task + meth.to_s.gsub('-','_') # treat foo-bar > foo_bar + end + end + + include Thor::Base + + map HELP_MAPPINGS => :help + + desc "help [TASK]", "Describe available tasks or one specific task" + def help(task=nil) + self.class.help(shell, task, :namespace => task && task.include?(?:)) + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions.rb new file mode 100644 index 0000000000..4bfb7c2870 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions.rb @@ -0,0 +1,273 @@ +require 'fileutils' + +Dir[File.join(File.dirname(__FILE__), "actions", "*.rb")].each do |action| + require action +end + +class Thor + module Actions + attr_accessor :behavior + + def self.included(base) #:nodoc: + base.extend ClassMethods + end + + module ClassMethods + # Hold source paths for one Thor instance. source_paths_for_search is the + # method responsible to gather source_paths from this current class, + # inherited paths and the source root. + # + def source_paths + @source_paths ||= [] + end + + # Returns the source paths in the following order: + # + # 1) This class source paths + # 2) Source root + # 3) Parents source paths + # + def source_paths_for_search + paths = [] + paths += self.source_paths + paths << self.source_root if self.respond_to?(:source_root) + paths += from_superclass(:source_paths, []) + paths + end + + # Add runtime options that help actions execution. + # + def add_runtime_options! + class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime, + :desc => "Run but do not make any changes" + + class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime, + :desc => "Overwrite files that already exist" + + class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime, + :desc => "Skip files that already exist" + + class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime, + :desc => "Supress status output" + end + end + + # Extends initializer to add more configuration options. + # + # ==== Configuration + # behavior:: The actions default behavior. Can be :invoke or :revoke. + # It also accepts :force, :skip and :pretend to set the behavior + # and the respective option. + # + # destination_root:: The root directory needed for some actions. + # + def initialize(args=[], options={}, config={}) + self.behavior = case config[:behavior].to_s + when "force", "skip" + _cleanup_options_and_set(options, config[:behavior]) + :invoke + when "revoke" + :revoke + else + :invoke + end + + super + self.destination_root = config[:destination_root] + end + + # Wraps an action object and call it accordingly to the thor class behavior. + # + def action(instance) #:nodoc: + if behavior == :revoke + instance.revoke! + else + instance.invoke! + end + end + + # Returns the root for this thor class (also aliased as destination root). + # + def destination_root + @destination_stack.last + end + + # Sets the root for this thor class. Relatives path are added to the + # directory where the script was invoked and expanded. + # + def destination_root=(root) + @destination_stack ||= [] + @destination_stack[0] = File.expand_path(root || '') + end + + # Returns the given path relative to the absolute root (ie, root where + # the script started). + # + def relative_to_original_destination_root(path, remove_dot=true) + path = path.gsub(@destination_stack[0], '.') + remove_dot ? (path[2..-1] || '') : path + end + + # Holds source paths in instance so they can be manipulated. + # + def source_paths + @source_paths ||= self.class.source_paths_for_search + end + + # Receives a file or directory and search for it in the source paths. + # + def find_in_source_paths(file) + relative_root = relative_to_original_destination_root(destination_root, false) + + source_paths.each do |source| + source_file = File.expand_path(file, File.join(source, relative_root)) + return source_file if File.exists?(source_file) + end + + if source_paths.empty? + raise Error, "You don't have any source path defined for class #{self.class.name}. To fix this, " << + "you can define a source_root in your class." + else + raise Error, "Could not find #{file.inspect} in source paths." + end + end + + # Do something in the root or on a provided subfolder. If a relative path + # is given it's referenced from the current root. The full path is yielded + # to the block you provide. The path is set back to the previous path when + # the method exits. + # + # ==== Parameters + # dir:: the directory to move to. + # config:: give :verbose => true to log and use padding. + # + def inside(dir='', config={}, &block) + verbose = config.fetch(:verbose, false) + + say_status :inside, dir, verbose + shell.padding += 1 if verbose + @destination_stack.push File.expand_path(dir, destination_root) + + FileUtils.mkdir_p(destination_root) unless File.exist?(destination_root) + FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield } + + @destination_stack.pop + shell.padding -= 1 if verbose + end + + # Goes to the root and execute the given block. + # + def in_root + inside(@destination_stack.first) { yield } + end + + # Loads an external file and execute it in the instance binding. + # + # ==== Parameters + # path:: The path to the file to execute. Can be a web address or + # a relative path from the source root. + # + # ==== Examples + # + # apply "http://gist.github.com/103208" + # + # apply "recipes/jquery.rb" + # + def apply(path, config={}) + verbose = config.fetch(:verbose, true) + path = find_in_source_paths(path) unless path =~ /^http\:\/\// + + say_status :apply, path, verbose + shell.padding += 1 if verbose + instance_eval(open(path).read) + shell.padding -= 1 if verbose + end + + # Executes a command. + # + # ==== Parameters + # command:: the command to be executed. + # config:: give :verbose => false to not log the status. Specify :with + # to append an executable to command executation. + # + # ==== Example + # + # inside('vendor') do + # run('ln -s ~/edge rails') + # end + # + def run(command, config={}) + return unless behavior == :invoke + + destination = relative_to_original_destination_root(destination_root, false) + desc = "#{command} from #{destination.inspect}" + + if config[:with] + desc = "#{File.basename(config[:with].to_s)} #{desc}" + command = "#{config[:with]} #{command}" + end + + say_status :run, desc, config.fetch(:verbose, true) + system(command) unless options[:pretend] + end + + # Executes a ruby script (taking into account WIN32 platform quirks). + # + # ==== Parameters + # command:: the command to be executed. + # config:: give :verbose => false to not log the status. + # + def run_ruby_script(command, config={}) + return unless behavior == :invoke + run "#{command}", config.merge(:with => Thor::Util.ruby_command) + end + + # Run a thor command. A hash of options can be given and it's converted to + # switches. + # + # ==== Parameters + # task:: the task to be invoked + # args:: arguments to the task + # config:: give :verbose => false to not log the status. Other options + # are given as parameter to Thor. + # + # ==== Examples + # + # thor :install, "http://gist.github.com/103208" + # #=> thor install http://gist.github.com/103208 + # + # thor :list, :all => true, :substring => 'rails' + # #=> thor list --all --substring=rails + # + def thor(task, *args) + config = args.last.is_a?(Hash) ? args.pop : {} + verbose = config.key?(:verbose) ? config.delete(:verbose) : true + + args.unshift task + args.push Thor::Options.to_switches(config) + command = args.join(' ').strip + + run command, :with => :thor, :verbose => verbose + end + + protected + + # Allow current root to be shared between invocations. + # + def _shared_configuration #:nodoc: + super.merge!(:destination_root => self.destination_root) + end + + def _cleanup_options_and_set(options, key) #:nodoc: + case options + when Array + %w(--force -f --skip -s).each { |i| options.delete(i) } + options << "--#{key}" + when Hash + [:force, :skip, "force", "skip"].each { |i| options.delete(i) } + options.merge!(key => true) + end + end + + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/create_file.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/create_file.rb new file mode 100644 index 0000000000..a3d9296823 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/create_file.rb @@ -0,0 +1,103 @@ +require 'thor/actions/empty_directory' + +class Thor + module Actions + + # Create a new file relative to the destination root with the given data, + # which is the return value of a block or a data string. + # + # ==== Parameters + # destination:: the relative path to the destination root. + # data:: the data to append to the file. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # create_file "lib/fun_party.rb" do + # hostname = ask("What is the virtual hostname I should use?") + # "vhost.name = #{hostname}" + # end + # + # create_file "config/apach.conf", "your apache config" + # + def create_file(destination, data=nil, config={}, &block) + action CreateFile.new(self, destination, block || data.to_s, config) + end + alias :add_file :create_file + + # AddFile is a subset of Template, which instead of rendering a file with + # ERB, it gets the content from the user. + # + class CreateFile < EmptyDirectory #:nodoc: + attr_reader :data + + def initialize(base, destination, data, config={}) + @data = data + super(base, destination, config) + end + + # Checks if the content of the file at the destination is identical to the rendered result. + # + # ==== Returns + # Boolean:: true if it is identical, false otherwise. + # + def identical? + exists? && File.read(destination) == render + end + + # Holds the content to be added to the file. + # + def render + @render ||= if data.is_a?(Proc) + data.call + else + data + end + end + + def invoke! + invoke_with_conflict_check do + FileUtils.mkdir_p(File.dirname(destination)) + File.open(destination, 'w'){ |f| f.write render } + end + given_destination + end + + protected + + # Now on conflict we check if the file is identical or not. + # + def on_conflict_behavior(&block) + if identical? + say_status :identical, :blue + else + options = base.options.merge(config) + force_or_skip_or_conflict(options[:force], options[:skip], &block) + end + end + + # If force is true, run the action, otherwise check if it's not being + # skipped. If both are false, show the file_collision menu, if the menu + # returns true, force it, otherwise skip. + # + def force_or_skip_or_conflict(force, skip, &block) + if force + say_status :force, :yellow + block.call unless pretend? + elsif skip + say_status :skip, :yellow + else + say_status :conflict, :red + force_or_skip_or_conflict(force_on_collision?, true, &block) + end + end + + # Shows the file collision menu to the user and gets the result. + # + def force_on_collision? + base.shell.file_collision(destination){ render } + end + + end + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/directory.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/directory.rb new file mode 100644 index 0000000000..2e0b459fa3 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/directory.rb @@ -0,0 +1,91 @@ +require 'thor/actions/empty_directory' + +class Thor + module Actions + + # Copies recursively the files from source directory to root directory. + # If any of the files finishes with .tt, it's considered to be a template + # and is placed in the destination without the extension .tt. If any + # empty directory is found, it's copied and all .empty_directory files are + # ignored. Remember that file paths can also be encoded, let's suppose a doc + # directory with the following files: + # + # doc/ + # components/.empty_directory + # README + # rdoc.rb.tt + # %app_name%.rb + # + # When invoked as: + # + # directory "doc" + # + # It will create a doc directory in the destination with the following + # files (assuming that the app_name is "blog"): + # + # doc/ + # components/ + # README + # rdoc.rb + # blog.rb + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # If :recursive => false, does not look for paths recursively. + # + # ==== Examples + # + # directory "doc" + # directory "doc", "docs", :recursive => false + # + def directory(source, destination=nil, config={}, &block) + action Directory.new(self, source, destination || source, config, &block) + end + + class Directory < EmptyDirectory #:nodoc: + attr_reader :source + + def initialize(base, source, destination=nil, config={}, &block) + @source = File.expand_path(base.find_in_source_paths(source.to_s)) + @block = block + super(base, destination, { :recursive => true }.merge(config)) + end + + def invoke! + base.empty_directory given_destination, config + execute! + end + + def revoke! + execute! + end + + protected + + def execute! + lookup = config[:recursive] ? File.join(source, '**') : source + lookup = File.join(lookup, '{*,.[a-z]*}') + + Dir[lookup].each do |file_source| + next if File.directory?(file_source) + file_destination = File.join(given_destination, file_source.gsub(source, '.')) + file_destination.gsub!('/./', '/') + + case file_source + when /\.empty_directory$/ + dirname = File.dirname(file_destination).gsub(/\/\.$/, '') + next if dirname == given_destination + base.empty_directory(dirname, config) + when /\.tt$/ + destination = base.template(file_source, file_destination[0..-4], config, &@block) + else + destination = base.copy_file(file_source, file_destination, config, &@block) + end + end + end + + end + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/empty_directory.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/empty_directory.rb new file mode 100644 index 0000000000..484cb820f8 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/empty_directory.rb @@ -0,0 +1,134 @@ +class Thor + module Actions + + # Creates an empty directory. + # + # ==== Parameters + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # empty_directory "doc" + # + def empty_directory(destination, config={}) + action EmptyDirectory.new(self, destination, config) + end + + # Class which holds create directory logic. This is the base class for + # other actions like create_file and directory. + # + # This implementation is based in Templater actions, created by Jonas Nicklas + # and Michael S. Klishin under MIT LICENSE. + # + class EmptyDirectory #:nodoc: + attr_reader :base, :destination, :given_destination, :relative_destination, :config + + # Initializes given the source and destination. + # + # ==== Parameters + # base:: A Thor::Base instance + # source:: Relative path to the source of this file + # destination:: Relative path to the destination of this file + # config:: give :verbose => false to not log the status. + # + def initialize(base, destination, config={}) + @base, @config = base, { :verbose => true }.merge(config) + self.destination = destination + end + + # Checks if the destination file already exists. + # + # ==== Returns + # Boolean:: true if the file exists, false otherwise. + # + def exists? + ::File.exists?(destination) + end + + def invoke! + invoke_with_conflict_check do + ::FileUtils.mkdir_p(destination) + end + end + + def revoke! + say_status :remove, :red + ::FileUtils.rm_rf(destination) if !pretend? && exists? + given_destination + end + + protected + + # Shortcut for pretend. + # + def pretend? + base.options[:pretend] + end + + # Sets the absolute destination value from a relative destination value. + # It also stores the given and relative destination. Let's suppose our + # script is being executed on "dest", it sets the destination root to + # "dest". The destination, given_destination and relative_destination + # are related in the following way: + # + # inside "bar" do + # empty_directory "baz" + # end + # + # destination #=> dest/bar/baz + # relative_destination #=> bar/baz + # given_destination #=> baz + # + def destination=(destination) + if destination + @given_destination = convert_encoded_instructions(destination.to_s) + @destination = ::File.expand_path(@given_destination, base.destination_root) + @relative_destination = base.relative_to_original_destination_root(@destination) + end + end + + # Filenames in the encoded form are converted. If you have a file: + # + # %class_name%.rb + # + # It gets the class name from the base and replace it: + # + # user.rb + # + def convert_encoded_instructions(filename) + filename.gsub(/%(.*?)%/) do |string| + instruction = $1.strip + base.respond_to?(instruction) ? base.send(instruction) : string + end + end + + # Receives a hash of options and just execute the block if some + # conditions are met. + # + def invoke_with_conflict_check(&block) + if exists? + on_conflict_behavior(&block) + else + say_status :create, :green + block.call unless pretend? + end + + destination + end + + # What to do when the destination file already exists. + # + def on_conflict_behavior(&block) + say_status :exist, :blue + end + + # Shortcut to say_status shell method. + # + def say_status(status, color) + base.shell.say_status status, relative_destination, color if config[:verbose] + end + + end + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/file_manipulation.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/file_manipulation.rb new file mode 100644 index 0000000000..8a45c83f25 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/file_manipulation.rb @@ -0,0 +1,223 @@ +require 'erb' +require 'open-uri' + +class Thor + module Actions + + # Copies the file from the relative source to the relative destination. If + # the destination is not given it's assumed to be equal to the source. + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # copy_file "README", "doc/README" + # + # copy_file "doc/README" + # + def copy_file(source, destination=nil, config={}, &block) + destination ||= source + source = File.expand_path(find_in_source_paths(source.to_s)) + + create_file destination, nil, config do + content = File.read(source) + content = block.call(content) if block + content + end + end + + # Gets the content at the given address and places it at the given relative + # destination. If a block is given instead of destination, the content of + # the url is yielded and used as location. + # + # ==== Parameters + # source:: the address of the given content. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # get "http://gist.github.com/103208", "doc/README" + # + # get "http://gist.github.com/103208" do |content| + # content.split("\n").first + # end + # + def get(source, destination=nil, config={}, &block) + source = File.expand_path(find_in_source_paths(source.to_s)) unless source =~ /^http\:\/\// + render = open(source).read + + destination ||= if block_given? + block.arity == 1 ? block.call(render) : block.call + else + File.basename(source) + end + + create_file destination, render, config + end + + # Gets an ERB template at the relative source, executes it and makes a copy + # at the relative destination. If the destination is not given it's assumed + # to be equal to the source removing .tt from the filename. + # + # ==== Parameters + # source:: the relative path to the source root. + # destination:: the relative path to the destination root. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # template "README", "doc/README" + # + # template "doc/README" + # + def template(source, destination=nil, config={}, &block) + destination ||= source + source = File.expand_path(find_in_source_paths(source.to_s)) + context = instance_eval('binding') + + create_file destination, nil, config do + content = ERB.new(::File.read(source), nil, '-').result(context) + content = block.call(content) if block + content + end + end + + # Changes the mode of the given file or directory. + # + # ==== Parameters + # mode:: the file mode + # path:: the name of the file to change mode + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # chmod "script/*", 0755 + # + def chmod(path, mode, config={}) + return unless behavior == :invoke + path = File.expand_path(path, destination_root) + say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true) + FileUtils.chmod_R(mode, path) unless options[:pretend] + end + + # Prepend text to a file. Since it depends on inject_into_file, it's reversible. + # + # ==== Parameters + # path:: path of the file to be changed + # data:: the data to prepend to the file, can be also given as a block. + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # prepend_file 'config/environments/test.rb', 'config.gem "rspec"' + # + # prepend_file 'config/environments/test.rb' do + # 'config.gem "rspec"' + # end + # + def prepend_file(path, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config.merge!(:after => /\A/) + inject_into_file(path, *(args << config), &block) + end + + # Append text to a file. Since it depends on inject_into_file, it's reversible. + # + # ==== Parameters + # path:: path of the file to be changed + # data:: the data to append to the file, can be also given as a block. + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # append_file 'config/environments/test.rb', 'config.gem "rspec"' + # + # append_file 'config/environments/test.rb' do + # 'config.gem "rspec"' + # end + # + def append_file(path, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config.merge!(:before => /\z/) + inject_into_file(path, *(args << config), &block) + end + + # Injects text right after the class definition. Since it depends on + # inject_into_file, it's reversible. + # + # ==== Parameters + # path:: path of the file to be changed + # klass:: the class to be manipulated + # data:: the data to append to the class, can be also given as a block. + # config:: give :verbose => false to not log the status. + # + # ==== Examples + # + # inject_into_class "app/controllers/application_controller.rb", " filter_parameter :password\n" + # + # inject_into_class "app/controllers/application_controller.rb", ApplicationController do + # " filter_parameter :password\n" + # end + # + def inject_into_class(path, klass, *args, &block) + config = args.last.is_a?(Hash) ? args.pop : {} + config.merge!(:after => /class #{klass}\n|class #{klass} .*\n/) + inject_into_file(path, *(args << config), &block) + end + + # Run a regular expression replacement on a file. + # + # ==== Parameters + # path:: path of the file to be changed + # flag:: the regexp or string to be replaced + # replacement:: the replacement, can be also given as a block + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' + # + # gsub_file 'README', /rake/, :green do |match| + # match << " no more. Use thor!" + # end + # + def gsub_file(path, flag, *args, &block) + return unless behavior == :invoke + config = args.last.is_a?(Hash) ? args.pop : {} + + path = File.expand_path(path, destination_root) + say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true) + + unless options[:pretend] + content = File.read(path) + content.gsub!(flag, *args, &block) + File.open(path, 'wb') { |file| file.write(content) } + end + end + + # Removes a file at the given location. + # + # ==== Parameters + # path:: path of the file to be changed + # config:: give :verbose => false to not log the status. + # + # ==== Example + # + # remove_file 'README' + # remove_file 'app/controllers/application_controller.rb' + # + def remove_file(path, config={}) + return unless behavior == :invoke + path = File.expand_path(path, destination_root) + + say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true) + ::FileUtils.rm_rf(path) if !options[:pretend] && File.exists?(path) + end + alias :remove_dir :remove_file + + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/inject_into_file.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/inject_into_file.rb new file mode 100644 index 0000000000..6b0b42ea02 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/actions/inject_into_file.rb @@ -0,0 +1,101 @@ +require 'thor/actions/empty_directory' + +class Thor + module Actions + + # Injects the given content into a file. Different from gsub_file, this + # method is reversible. + # + # ==== Parameters + # destination:: Relative path to the destination root + # data:: Data to add to the file. Can be given as a block. + # config:: give :verbose => false to not log the status and the flag + # for injection (:after or :before). + # + # ==== Examples + # + # inject_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" + # + # inject_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do + # gems = ask "Which gems would you like to add?" + # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") + # end + # + def inject_into_file(destination, *args, &block) + if block_given? + data, config = block, args.shift + else + data, config = args.shift, args.shift + end + action InjectIntoFile.new(self, destination, data, config) + end + + class InjectIntoFile < EmptyDirectory #:nodoc: + attr_reader :replacement, :flag, :behavior + + def initialize(base, destination, data, config) + super(base, destination, { :verbose => true }.merge(config)) + + @behavior, @flag = if @config.key?(:after) + [:after, @config.delete(:after)] + else + [:before, @config.delete(:before)] + end + + @replacement = data.is_a?(Proc) ? data.call : data + @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp) + end + + def invoke! + say_status :invoke + + content = if @behavior == :after + '\0' + replacement + else + replacement + '\0' + end + + replace!(/#{flag}/, content) + end + + def revoke! + say_status :revoke + + regexp = if @behavior == :after + content = '\1\2' + /(#{flag})(.*)(#{Regexp.escape(replacement)})/m + else + content = '\2\3' + /(#{Regexp.escape(replacement)})(.*)(#{flag})/m + end + + replace!(regexp, content) + end + + protected + + def say_status(behavior) + status = if flag == /\A/ + behavior == :invoke ? :prepend : :unprepend + elsif flag == /\z/ + behavior == :invoke ? :append : :unappend + else + behavior == :invoke ? :inject : :deinject + end + + super(status, config[:verbose]) + end + + # Adds the content to the file. + # + def replace!(regexp, string) + unless base.options[:pretend] + content = File.read(destination) + content.gsub!(regexp, string) + File.open(destination, 'wb') { |file| file.write(content) } + end + end + + end + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/base.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/base.rb new file mode 100644 index 0000000000..700d794123 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/base.rb @@ -0,0 +1,517 @@ +require 'thor/core_ext/hash_with_indifferent_access' +require 'thor/core_ext/ordered_hash' +require 'thor/error' +require 'thor/shell' +require 'thor/invocation' +require 'thor/parser' +require 'thor/task' +require 'thor/util' + +class Thor + # Shortcuts for help. + HELP_MAPPINGS = %w(-h -? --help -D) + + # Thor methods that should not be overwritten by the user. + THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root + action add_file create_file in_root inside run run_ruby_script) + + module Base + attr_accessor :options + + # It receives arguments in an Array and two hashes, one for options and + # other for configuration. + # + # Notice that it does not check if all required arguments were supplied. + # It should be done by the parser. + # + # ==== Parameters + # args:: An array of objects. The objects are applied to their + # respective accessors declared with argument. + # + # options:: An options hash that will be available as self.options. + # The hash given is converted to a hash with indifferent + # access, magic predicates (options.skip?) and then frozen. + # + # config:: Configuration for this Thor class. + # + def initialize(args=[], options={}, config={}) + Thor::Arguments.parse(self.class.arguments, args).each do |key, value| + send("#{key}=", value) + end + + parse_options = self.class.class_options + + if options.is_a?(Array) + task_options = config.delete(:task_options) # hook for start + parse_options = parse_options.merge(task_options) if task_options + array_options, hash_options = options, {} + else + array_options, hash_options = [], options + end + + options = Thor::Options.parse(parse_options, array_options) + self.options = Thor::CoreExt::HashWithIndifferentAccess.new(options).merge!(hash_options) + self.options.freeze + end + + class << self + def included(base) #:nodoc: + base.send :extend, ClassMethods + base.send :include, Invocation + base.send :include, Shell + end + + # Returns the classes that inherits from Thor or Thor::Group. + # + # ==== Returns + # Array[Class] + # + def subclasses + @subclasses ||= [] + end + + # Returns the files where the subclasses are kept. + # + # ==== Returns + # Hash[path => Class] + # + def subclass_files + @subclass_files ||= Hash.new{ |h,k| h[k] = [] } + end + + # Whenever a class inherits from Thor or Thor::Group, we should track the + # class and the file on Thor::Base. This is the method responsable for it. + # + def register_klass_file(klass) #:nodoc: + file = caller[1].match(/(.*):\d+/)[1] + Thor::Base.subclasses << klass unless Thor::Base.subclasses.include?(klass) + + file_subclasses = Thor::Base.subclass_files[File.expand_path(file)] + file_subclasses << klass unless file_subclasses.include?(klass) + end + end + + module ClassMethods + # Adds an argument to the class and creates an attr_accessor for it. + # + # Arguments are different from options in several aspects. The first one + # is how they are parsed from the command line, arguments are retrieved + # from position: + # + # thor task NAME + # + # Instead of: + # + # thor task --name=NAME + # + # Besides, arguments are used inside your code as an accessor (self.argument), + # while options are all kept in a hash (self.options). + # + # Finally, arguments cannot have type :default or :boolean but can be + # optional (supplying :optional => :true or :required => false), although + # you cannot have a required argument after a non-required argument. If you + # try it, an error is raised. + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :optional - If the argument is optional or not. + # :type - The type of the argument, can be :string, :hash, :array, :numeric. + # :default - Default value for this argument. It cannot be required and have default values. + # :banner - String to show on usage notes. + # + # ==== Errors + # ArgumentError:: Raised if you supply a required argument after a non required one. + # + def argument(name, options={}) + is_thor_reserved_word?(name, :argument) + no_tasks { attr_accessor name } + + required = if options.key?(:optional) + !options[:optional] + elsif options.key?(:required) + options[:required] + else + options[:default].nil? + end + + remove_argument name + + arguments.each do |argument| + next if argument.required? + raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " << + "the non-required argument #{argument.human_name.inspect}." + end if required + + arguments << Thor::Argument.new(name, options[:desc], required, options[:type], + options[:default], options[:banner]) + end + + # Returns this class arguments, looking up in the ancestors chain. + # + # ==== Returns + # Array[Thor::Argument] + # + def arguments + @arguments ||= from_superclass(:arguments, []) + end + + # Adds a bunch of options to the set of class options. + # + # class_options :foo => false, :bar => :required, :baz => :string + # + # If you prefer more detailed declaration, check class_option. + # + # ==== Parameters + # Hash[Symbol => Object] + # + def class_options(options=nil) + @class_options ||= from_superclass(:class_options, {}) + build_options(options, @class_options) if options + @class_options + end + + # Adds an option to the set of class options + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described below. + # + # ==== Options + # :desc - Description for the argument. + # :required - If the argument is required or not. + # :default - Default value for this argument. + # :group - The group for this options. Use by class options to output options in different levels. + # :aliases - Aliases for this option. + # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean. + # :banner - String to show on usage notes. + # + def class_option(name, options={}) + build_option(name, options, class_options) + end + + # Removes a previous defined argument. If :undefine is given, undefine + # accessors as well. + # + # ==== Paremeters + # names:: Arguments to be removed + # + # ==== Examples + # + # remove_argument :foo + # remove_argument :foo, :bar, :baz, :undefine => true + # + def remove_argument(*names) + options = names.last.is_a?(Hash) ? names.pop : {} + + names.each do |name| + arguments.delete_if { |a| a.name == name.to_s } + undef_method name, "#{name}=" if options[:undefine] + end + end + + # Removes a previous defined class option. + # + # ==== Paremeters + # names:: Class options to be removed + # + # ==== Examples + # + # remove_class_option :foo + # remove_class_option :foo, :bar, :baz + # + def remove_class_option(*names) + names.each do |name| + class_options.delete(name) + end + end + + # Defines the group. This is used when thor list is invoked so you can specify + # that only tasks from a pre-defined group will be shown. Defaults to standard. + # + # ==== Parameters + # name + # + def group(name=nil) + case name + when nil + @group ||= from_superclass(:group, 'standard') + else + @group = name.to_s + end + end + + # Returns the tasks for this Thor class. + # + # ==== Returns + # OrderedHash:: An ordered hash with tasks names as keys and Thor::Task + # objects as values. + # + def tasks + @tasks ||= Thor::CoreExt::OrderedHash.new + end + + # Returns the tasks for this Thor class and all subclasses. + # + # ==== Returns + # OrderedHash:: An ordered hash with tasks names as keys and Thor::Task + # objects as values. + # + def all_tasks + @all_tasks ||= from_superclass(:all_tasks, Thor::CoreExt::OrderedHash.new) + @all_tasks.merge(tasks) + end + + # Removes a given task from this Thor class. This is usually done if you + # are inheriting from another class and don't want it to be available + # anymore. + # + # By default it only remove the mapping to the task. But you can supply + # :undefine => true to undefine the method from the class as well. + # + # ==== Parameters + # name:: The name of the task to be removed + # options:: You can give :undefine => true if you want tasks the method + # to be undefined from the class as well. + # + def remove_task(*names) + options = names.last.is_a?(Hash) ? names.pop : {} + + names.each do |name| + tasks.delete(name.to_s) + all_tasks.delete(name.to_s) + undef_method name if options[:undefine] + end + end + + # All methods defined inside the given block are not added as tasks. + # + # So you can do: + # + # class MyScript < Thor + # no_tasks do + # def this_is_not_a_task + # end + # end + # end + # + # You can also add the method and remove it from the task list: + # + # class MyScript < Thor + # def this_is_not_a_task + # end + # remove_task :this_is_not_a_task + # end + # + def no_tasks + @no_tasks = true + yield + @no_tasks = false + end + + # Sets the namespace for the Thor or Thor::Group class. By default the + # namespace is retrieved from the class name. If your Thor class is named + # Scripts::MyScript, the help method, for example, will be called as: + # + # thor scripts:my_script -h + # + # If you change the namespace: + # + # namespace :my_scripts + # + # You change how your tasks are invoked: + # + # thor my_scripts -h + # + # Finally, if you change your namespace to default: + # + # namespace :default + # + # Your tasks can be invoked with a shortcut. Instead of: + # + # thor :my_task + # + def namespace(name=nil) + case name + when nil + @namespace ||= Thor::Util.namespace_from_thor_class(self, false) + else + @namespace = name.to_s + end + end + + # Default way to start generators from the command line. + # + def start(given_args=ARGV, config={}) + config[:shell] ||= Thor::Base.shell.new + yield + rescue Thor::Error => e + if given_args.include?("--debug") + raise e + else + config[:shell].error e.message + end + exit(1) if exit_on_failure? + end + + protected + + # Prints the class options per group. If an option does not belong to + # any group, it uses the ungrouped name value. This method provide to + # hooks to add extra options, one of them if the third argument called + # extra_group that should be a hash in the format :group => Array[Options]. + # + # The second is by returning a lambda used to print values. The lambda + # requires two options: the group name and the array of options. + # + def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc: + groups = {} + + class_options.each do |_, value| + groups[value.group] ||= [] + groups[value.group] << value + end + + printer = proc do |group_name, options| + list = [] + padding = options.collect{ |o| o.aliases.size }.max.to_i * 4 + + options.each do |option| + item = [ option.usage(padding) ] + item.push(option.description ? "# #{option.description}" : "") + + list << item + list << [ "", "# Default: #{option.default}" ] if option.show_default? + end + + unless list.empty? + shell.say(group_name ? "#{group_name} options:" : "Options:") + shell.print_table(list, :ident => 2) + shell.say "" + end + end + + # Deal with default group + global_options = groups.delete(nil) || [] + printer.call(ungrouped_name, global_options) if global_options + + # Print all others + groups = extra_group.merge(groups) if extra_group + groups.each(&printer) + printer + end + + # Raises an error if the word given is a Thor reserved word. + # + def is_thor_reserved_word?(word, type) #:nodoc: + return false unless THOR_RESERVED_WORDS.include?(word.to_s) + raise "#{word.inspect} is a Thor reserved word and cannot be defined as #{type}" + end + + # Build an option and adds it to the given scope. + # + # ==== Parameters + # name:: The name of the argument. + # options:: Described in both class_option and method_option. + # + def build_option(name, options, scope) #:nodoc: + scope[name] = Thor::Option.new(name, options[:desc], options[:required], + options[:type], options[:default], options[:banner], + options[:group], options[:aliases]) + end + + # Receives a hash of options, parse them and add to the scope. This is a + # fast way to set a bunch of options: + # + # build_options :foo => true, :bar => :required, :baz => :string + # + # ==== Parameters + # Hash[Symbol => Object] + # + def build_options(options, scope) #:nodoc: + options.each do |key, value| + scope[key] = Thor::Option.parse(key, value) + end + end + + # Finds a task with the given name. If the task belongs to the current + # class, just return it, otherwise dup it and add the fresh copy to the + # current task hash. + # + def find_and_refresh_task(name) #:nodoc: + task = if task = tasks[name.to_s] + task + elsif task = all_tasks[name.to_s] + tasks[name.to_s] = task.clone + else + raise ArgumentError, "You supplied :for => #{name.inspect}, but the task #{name.inspect} could not be found." + end + end + + # Everytime someone inherits from a Thor class, register the klass + # and file into baseclass. + # + def inherited(klass) + Thor::Base.register_klass_file(klass) + end + + # Fire this callback whenever a method is added. Added methods are + # tracked as tasks by invoking the create_task method. + # + def method_added(meth) + meth = meth.to_s + + if meth == "initialize" + initialize_added + return + end + + # Return if it's not a public instance method + return unless public_instance_methods.include?(meth) || + public_instance_methods.include?(meth.to_sym) + + return if @no_tasks || !create_task(meth) + + is_thor_reserved_word?(meth, :task) + Thor::Base.register_klass_file(self) + end + + # Retrieves a value from superclass. If it reaches the baseclass, + # returns default. + # + def from_superclass(method, default=nil) + if self == baseclass || !superclass.respond_to?(method, true) + default + else + value = superclass.send(method) + value.dup if value + end + end + + # A flag that makes the process exit with status 1 if any error happens. + # + def exit_on_failure? + false + end + + # SIGNATURE: Sets the baseclass. This is where the superclass lookup + # finishes. + def baseclass #:nodoc: + end + + # SIGNATURE: Creates a new task if valid_task? is true. This method is + # called when a new method is added to the class. + def create_task(meth) #:nodoc: + end + + # SIGNATURE: Defines behavior when the initialize method is added to the + # class. + def initialize_added #:nodoc: + end + end + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/hash_with_indifferent_access.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/hash_with_indifferent_access.rb new file mode 100644 index 0000000000..40d201d9e4 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/hash_with_indifferent_access.rb @@ -0,0 +1,75 @@ +class Thor + module CoreExt #:nodoc: + + # A hash with indifferent access and magic predicates. + # + # hash = Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true + # + # hash[:foo] #=> 'bar' + # hash['foo'] #=> 'bar' + # hash.foo? #=> true + # + class HashWithIndifferentAccess < ::Hash #:nodoc: + + def initialize(hash={}) + super() + hash.each do |key, value| + self[convert_key(key)] = value + end + end + + def [](key) + super(convert_key(key)) + end + + def []=(key, value) + super(convert_key(key), value) + end + + def delete(key) + super(convert_key(key)) + end + + def values_at(*indices) + indices.collect { |key| self[convert_key(key)] } + end + + def merge(other) + dup.merge!(other) + end + + def merge!(other) + other.each do |key, value| + self[convert_key(key)] = value + end + self + end + + protected + + def convert_key(key) + key.is_a?(Symbol) ? key.to_s : key + end + + # Magic predicates. For instance: + # + # options.force? # => !!options['force'] + # options.shebang # => "/usr/lib/local/ruby" + # options.test_framework?(:rspec) # => options[:test_framework] == :rspec + # + def method_missing(method, *args, &block) + method = method.to_s + if method =~ /^(\w+)\?$/ + if args.empty? + !!self[$1] + else + self[$1] == args.first + end + else + self[method] + end + end + + end + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/ordered_hash.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/ordered_hash.rb new file mode 100644 index 0000000000..27fea5bb35 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/core_ext/ordered_hash.rb @@ -0,0 +1,100 @@ +class Thor + module CoreExt #:nodoc: + + if RUBY_VERSION >= '1.9' + class OrderedHash < ::Hash + end + else + # This class is based on the Ruby 1.9 ordered hashes. + # + # It keeps the semantics and most of the efficiency of normal hashes + # while also keeping track of the order in which elements were set. + # + class OrderedHash #:nodoc: + include Enumerable + + Node = Struct.new(:key, :value, :next, :prev) + + def initialize + @hash = {} + end + + def [](key) + @hash[key] && @hash[key].value + end + + def []=(key, value) + if node = @hash[key] + node.value = value + else + node = Node.new(key, value) + + if @first.nil? + @first = @last = node + else + node.prev = @last + @last.next = node + @last = node + end + end + + @hash[key] = node + value + end + + def delete(key) + if node = @hash[key] + prev_node = node.prev + next_node = node.next + + next_node.prev = prev_node if next_node + prev_node.next = next_node if prev_node + + @first = next_node if @first == node + @last = prev_node if @last == node + + value = node.value + end + + @hash.delete(key) + value + end + + def keys + self.map { |k, v| k } + end + + def values + self.map { |k, v| v } + end + + def each + return unless @first + yield [@first.key, @first.value] + node = @first + yield [node.key, node.value] while node = node.next + self + end + + def merge(other) + hash = self.class.new + + self.each do |key, value| + hash[key] = value + end + + other.each do |key, value| + hash[key] = value + end + + hash + end + + def empty? + @hash.empty? + end + end + end + + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/error.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/error.rb new file mode 100644 index 0000000000..f9b31a35d1 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/error.rb @@ -0,0 +1,27 @@ +class Thor + # Thor::Error is raised when it's caused by wrong usage of thor classes. Those + # errors have their backtrace supressed and are nicely shown to the user. + # + # Errors that are caused by the developer, like declaring a method which + # overwrites a thor keyword, it SHOULD NOT raise a Thor::Error. This way, we + # ensure that developer errors are shown with full backtrace. + # + class Error < StandardError + end + + # Raised when a task was not found. + # + class UndefinedTaskError < Error + end + + # Raised when a task was found, but not invoked properly. + # + class InvocationError < Error + end + + class RequiredArgumentMissingError < InvocationError + end + + class MalformattedArgumentError < InvocationError + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/group.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/group.rb new file mode 100644 index 0000000000..021a067a3e --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/group.rb @@ -0,0 +1,263 @@ +# Thor has a special class called Thor::Group. The main difference to Thor class +# is that it invokes all tasks at once. It also include some methods that allows +# invocations to be done at the class method, which are not available to Thor +# tasks. +# +class Thor::Group + class << self + # The descrition for this Thor::Group. If none is provided, but a source root + # exists, tries to find the USAGE one folder above it, otherwise searches + # in the superclass. + # + # ==== Parameters + # description:: The description for this Thor::Group. + # + def desc(description=nil) + case description + when nil + @desc ||= from_superclass(:desc, nil) + else + @desc = description + end + end + + # Start works differently in Thor::Group, it simply invokes all tasks + # inside the class. + # + def start(given_args=ARGV, config={}) + super do + if Thor::HELP_MAPPINGS.include?(given_args.first) + help(config[:shell]) + return + end + + args, opts = Thor::Options.split(given_args) + new(args, opts, config).invoke + end + end + + # Prints help information. + # + # ==== Options + # short:: When true, shows only usage. + # + def help(shell, options={}) + if options[:short] + shell.say banner + else + shell.say "Usage:" + shell.say " #{banner}" + shell.say + class_options_help(shell) + shell.say self.desc if self.desc + end + end + + # Stores invocations for this class merging with superclass values. + # + def invocations #:nodoc: + @invocations ||= from_superclass(:invocations, {}) + end + + # Stores invocation blocks used on invoke_from_option. + # + def invocation_blocks #:nodoc: + @invocation_blocks ||= from_superclass(:invocation_blocks, {}) + end + + # Invoke the given namespace or class given. It adds an instance + # method that will invoke the klass and task. You can give a block to + # configure how it will be invoked. + # + # The namespace/class given will have its options showed on the help + # usage. Check invoke_from_option for more information. + # + def invoke(*names, &block) + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, true) + + names.each do |name| + invocations[name] = false + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_#{name.to_s.gsub(/\W/, '_')} + klass, task = self.class.prepare_for_invocation(nil, #{name.inspect}) + + if klass + say_status :invoke, #{name.inspect}, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, task, &block + else + say_status :error, %(#{name.inspect} [not found]), :red + end + end + METHOD + end + end + + # Invoke a thor class based on the value supplied by the user to the + # given option named "name". A class option must be created before this + # method is invoked for each name given. + # + # ==== Examples + # + # class GemGenerator < Thor::Group + # class_option :test_framework, :type => :string + # invoke_from_option :test_framework + # end + # + # ==== Boolean options + # + # In some cases, you want to invoke a thor class if some option is true or + # false. This is automatically handled by invoke_from_option. Then the + # option name is used to invoke the generator. + # + # ==== Preparing for invocation + # + # In some cases you want to customize how a specified hook is going to be + # invoked. You can do that by overwriting the class method + # prepare_for_invocation. The class method must necessarily return a klass + # and an optional task. + # + # ==== Custom invocations + # + # You can also supply a block to customize how the option is giong to be + # invoked. The block receives two parameters, an instance of the current + # class and the klass to be invoked. + # + def invoke_from_option(*names, &block) + options = names.last.is_a?(Hash) ? names.pop : {} + verbose = options.fetch(:verbose, :white) + + names.each do |name| + unless class_options.key?(name) + raise ArgumentError, "You have to define the option #{name.inspect} " << + "before setting invoke_from_option." + end + + invocations[name] = true + invocation_blocks[name] = block if block_given? + + class_eval <<-METHOD, __FILE__, __LINE__ + def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')} + return unless options[#{name.inspect}] + + value = options[#{name.inspect}] + value = #{name.inspect} if TrueClass === value + klass, task = self.class.prepare_for_invocation(#{name.inspect}, value) + + if klass + say_status :invoke, value, #{verbose.inspect} + block = self.class.invocation_blocks[#{name.inspect}] + _invoke_for_class_method klass, task, &block + else + say_status :error, %(\#{value} [not found]), :red + end + end + METHOD + end + end + + # Remove a previously added invocation. + # + # ==== Examples + # + # remove_invocation :test_framework + # + def remove_invocation(*names) + names.each do |name| + remove_task(name) + remove_class_option(name) + invocations.delete(name) + invocation_blocks.delete(name) + end + end + + # Overwrite class options help to allow invoked generators options to be + # shown recursively when invoking a generator. + # + def class_options_help(shell, ungrouped_name=nil, extra_group=nil) #:nodoc: + group_options = {} + + get_options_from_invocations(group_options, class_options) do |klass| + klass.send(:get_options_from_invocations, group_options, class_options) + end + + group_options.merge!(extra_group) if extra_group + super(shell, ungrouped_name, group_options) + end + + # Get invocations array and merge options from invocations. Those + # options are added to group_options hash. Options that already exists + # in base_options are not added twice. + # + def get_options_from_invocations(group_options, base_options) #:nodoc: + invocations.each do |name, from_option| + value = if from_option + option = class_options[name] + option.type == :boolean ? name : option.default + else + name + end + next unless value + + klass, task = prepare_for_invocation(name, value) + next unless klass && klass.respond_to?(:class_options) + + value = value.to_s + human_name = value.respond_to?(:classify) ? value.classify : value + + group_options[human_name] ||= [] + group_options[human_name] += klass.class_options.values.select do |option| + base_options[option.name.to_sym].nil? && option.group.nil? && + !group_options.values.flatten.any? { |i| i.name == option.name } + end + + yield klass if block_given? + end + end + + protected + + # The banner for this class. You can customize it if you are invoking the + # thor class by another ways which is not the Thor::Runner. + # + def banner + "#{self.namespace} #{self.arguments.map {|a| a.usage }.join(' ')}" + end + + def baseclass #:nodoc: + Thor::Group + end + + def create_task(meth) #:nodoc: + tasks[meth.to_s] = Thor::Task.new(meth, nil, nil, nil) + true + end + end + + include Thor::Base + + protected + + # Shortcut to invoke with padding and block handling. Use internally by + # invoke and invoke_from_option class methods. + # + def _invoke_for_class_method(klass, task=nil, *args, &block) #:nodoc: + shell.padding += 1 + + result = if block_given? + if block.arity == 2 + block.call(self, klass) + else + block.call(self, klass, task) + end + else + invoke klass, task, *args + end + + shell.padding -= 1 + result + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/invocation.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/invocation.rb new file mode 100644 index 0000000000..32e6a72454 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/invocation.rb @@ -0,0 +1,178 @@ +class Thor + module Invocation + def self.included(base) #:nodoc: + base.extend ClassMethods + end + + module ClassMethods + # Prepare for class methods invocations. This method must return a klass to + # have the invoked class options showed in help messages in generators. + # + def prepare_for_invocation(key, name) #:nodoc: + case name + when Symbol, String + Thor::Util.namespace_to_thor_class_and_task(name.to_s, false) + else + name + end + end + end + + # Make initializer aware of invocations and the initializer proc. + # + def initialize(args=[], options={}, config={}, &block) #:nodoc: + @_invocations = config[:invocations] || Hash.new { |h,k| h[k] = [] } + @_initializer = [ args, options, config ] + super + end + + # Receives a name and invokes it. The name can be a string (either "task" or + # "namespace:task"), a Thor::Task, a Class or a Thor instance. If the task + # cannot be guessed by name, it can also be supplied as second argument. + # + # You can also supply the arguments, options and configuration values for + # the task to be invoked, if none is given, the same values used to + # initialize the invoker are used to initialize the invoked. + # + # ==== Examples + # + # class A < Thor + # def foo + # invoke :bar + # invoke "b:hello", ["José"] + # end + # + # def bar + # invoke "b:hello", ["José"] + # end + # end + # + # class B < Thor + # def hello(name) + # puts "hello #{name}" + # end + # end + # + # You can notice that the method "foo" above invokes two tasks: "bar", + # which belongs to the same class and "hello" which belongs to the class B. + # + # By using an invocation system you ensure that a task is invoked only once. + # In the example above, invoking "foo" will invoke "b:hello" just once, even + # if it's invoked later by "bar" method. + # + # When class A invokes class B, all arguments used on A initialization are + # supplied to B. This allows lazy parse of options. Let's suppose you have + # some rspec tasks: + # + # class Rspec < Thor::Group + # class_option :mock_framework, :type => :string, :default => :rr + # + # def invoke_mock_framework + # invoke "rspec:#{options[:mock_framework]}" + # end + # end + # + # As you noticed, it invokes the given mock framework, which might have its + # own options: + # + # class Rspec::RR < Thor::Group + # class_option :style, :type => :string, :default => :mock + # end + # + # Since it's not rspec concern to parse mock framework options, when RR + # is invoked all options are parsed again, so RR can extract only the options + # that it's going to use. + # + # If you want Rspec::RR to be initialized with its own set of options, you + # have to do that explicitely: + # + # invoke "rspec:rr", [], :style => :foo + # + # Besides giving an instance, you can also give a class to invoke: + # + # invoke Rspec::RR, [], :style => :foo + # + def invoke(name=nil, task=nil, args=nil, opts=nil, config=nil) + task, args, opts, config = nil, task, args, opts if task.nil? || task.is_a?(Array) + args, opts, config = nil, args, opts if args.is_a?(Hash) + + object, task = _prepare_for_invocation(name, task) + klass, instance = _initialize_klass_with_initializer(object, args, opts, config) + + method_args = [] + current = @_invocations[klass] + + iterator = proc do |_, task| + unless current.include?(task.name) + current << task.name + task.run(instance, method_args) + end + end + + if task + args ||= [] + method_args = args[Range.new(klass.arguments.size, -1)] || [] + iterator.call(nil, task) + else + klass.all_tasks.map(&iterator) + end + end + + protected + + # Configuration values that are shared between invocations. + # + def _shared_configuration #:nodoc: + { :invocations => @_invocations } + end + + # Prepare for invocation in the instance level. In this case, we have to + # take into account that a just a task name from the current class was + # given or even a Thor::Task object. + # + def _prepare_for_invocation(name, sent_task=nil) #:nodoc: + if name.is_a?(Thor::Task) + task = name + elsif task = self.class.all_tasks[name.to_s] + object = self + else + object, task = self.class.prepare_for_invocation(nil, name) + task ||= sent_task + end + + # If the object was not set, use self and use the name as task. + object, task = self, name unless object + return object, _validate_task(object, task) + end + + # Check if the object given is a Thor class object and get a task object + # for it. + # + def _validate_task(object, task) #:nodoc: + klass = object.is_a?(Class) ? object : object.class + raise "Expected Thor class, got #{klass}" unless klass <= Thor::Base + + task ||= klass.default_task if klass <= Thor + task = klass.all_tasks[task.to_s] || Thor::Task::Dynamic.new(task) if task && !task.is_a?(Thor::Task) + task + end + + # Initialize klass using values stored in the @_initializer. + # + def _initialize_klass_with_initializer(object, args, opts, config) #:nodoc: + if object.is_a?(Class) + klass = object + + stored_args, stored_opts, stored_config = @_initializer + args ||= stored_args.dup + opts ||= stored_opts.dup + + config ||= {} + config = stored_config.merge(_shared_configuration).merge!(config) + [ klass, klass.new(args, opts, config) ] + else + [ object.class, object ] + end + end + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser.rb new file mode 100644 index 0000000000..57a3f6e1a5 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser.rb @@ -0,0 +1,4 @@ +require 'thor/parser/argument' +require 'thor/parser/arguments' +require 'thor/parser/option' +require 'thor/parser/options' diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/argument.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/argument.rb new file mode 100644 index 0000000000..aa8ace4719 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/argument.rb @@ -0,0 +1,67 @@ +class Thor + class Argument #:nodoc: + VALID_TYPES = [ :numeric, :hash, :array, :string ] + + attr_reader :name, :description, :required, :type, :default, :banner + alias :human_name :name + + def initialize(name, description=nil, required=true, type=:string, default=nil, banner=nil) + class_name = self.class.name.split("::").last + + raise ArgumentError, "#{class_name} name can't be nil." if name.nil? + raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type) + + @name = name.to_s + @description = description + @required = required || false + @type = (type || :string).to_sym + @default = default + @banner = banner || default_banner + + validate! # Trigger specific validations + end + + def usage + required? ? banner : "[#{banner}]" + end + + def required? + required + end + + def show_default? + case default + when Array, String, Hash + !default.empty? + else + default + end + end + + protected + + def validate! + raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil? + end + + def valid_type?(type) + VALID_TYPES.include?(type.to_sym) + end + + def default_banner + case type + when :boolean + nil + when :string, :default + human_name.upcase + when :numeric + "N" + when :hash + "key:value" + when :array + "one two three" + end + end + + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/arguments.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/arguments.rb new file mode 100644 index 0000000000..fb5d965e06 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/arguments.rb @@ -0,0 +1,145 @@ +class Thor + class Arguments #:nodoc: + NUMERIC = /(\d*\.\d+|\d+)/ + + # Receives an array of args and returns two arrays, one with arguments + # and one with switches. + # + def self.split(args) + arguments = [] + + args.each do |item| + break if item =~ /^-/ + arguments << item + end + + return arguments, args[Range.new(arguments.size, -1)] + end + + def self.parse(base, args) + new(base).parse(args) + end + + # Takes an array of Thor::Argument objects. + # + def initialize(arguments=[]) + @assigns, @non_assigned_required = {}, [] + @switches = arguments + + arguments.each do |argument| + if argument.default + @assigns[argument.human_name] = argument.default + elsif argument.required? + @non_assigned_required << argument + end + end + end + + def parse(args) + @pile = args.dup + + @switches.each do |argument| + break unless peek + @non_assigned_required.delete(argument) + @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name) + end + + check_requirement! + @assigns + end + + private + + def peek + @pile.first + end + + def shift + @pile.shift + end + + def unshift(arg) + unless arg.kind_of?(Array) + @pile.unshift(arg) + else + @pile = arg + @pile + end + end + + def current_is_value? + peek && peek.to_s !~ /^-/ + end + + # Runs through the argument array getting strings that contains ":" and + # mark it as a hash: + # + # [ "name:string", "age:integer" ] + # + # Becomes: + # + # { "name" => "string", "age" => "integer" } + # + def parse_hash(name) + return shift if peek.is_a?(Hash) + hash = {} + + while current_is_value? && peek.include?(?:) + key, value = shift.split(':') + hash[key] = value + end + hash + end + + # Runs through the argument array getting all strings until no string is + # found or a switch is found. + # + # ["a", "b", "c"] + # + # And returns it as an array: + # + # ["a", "b", "c"] + # + def parse_array(name) + return shift if peek.is_a?(Array) + array = [] + + while current_is_value? + array << shift + end + array + end + + # Check if the peel is numeric ofrmat and return a Float or Integer. + # Otherwise raises an error. + # + def parse_numeric(name) + return shift if peek.is_a?(Numeric) + + unless peek =~ NUMERIC && $& == peek + raise MalformattedArgumentError, "expected numeric value for '#{name}'; got #{peek.inspect}" + end + + $&.index('.') ? shift.to_f : shift.to_i + end + + # Parse string, i.e., just return the current value in the pile. + # + def parse_string(name) + shift + end + + # Raises an error if @non_assigned_required array is not empty. + # + def check_requirement! + unless @non_assigned_required.empty? + names = @non_assigned_required.map do |o| + o.respond_to?(:switch_name) ? o.switch_name : o.human_name + end.join("', '") + + class_name = self.class.name.split('::').last.downcase + raise RequiredArgumentMissingError, "no value provided for required #{class_name} '#{names}'" + end + end + + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/option.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/option.rb new file mode 100644 index 0000000000..e09b4901e2 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/option.rb @@ -0,0 +1,132 @@ +class Thor + class Option < Argument #:nodoc: + attr_reader :aliases, :group + + VALID_TYPES = [:boolean, :numeric, :hash, :array, :string] + + def initialize(name, description=nil, required=nil, type=nil, default=nil, banner=nil, group=nil, aliases=nil) + super(name, description, required, type, default, banner) + @aliases = [*aliases].compact + @group = group.to_s.capitalize if group + end + + # This parse quick options given as method_options. It makes several + # assumptions, but you can be more specific using the option method. + # + # parse :foo => "bar" + # #=> Option foo with default value bar + # + # parse [:foo, :baz] => "bar" + # #=> Option foo with default value bar and alias :baz + # + # parse :foo => :required + # #=> Required option foo without default value + # + # parse :foo => 2 + # #=> Option foo with default value 2 and type numeric + # + # parse :foo => :numeric + # #=> Option foo without default value and type numeric + # + # parse :foo => true + # #=> Option foo with default value true and type boolean + # + # The valid types are :boolean, :numeric, :hash, :array and :string. If none + # is given a default type is assumed. This default type accepts arguments as + # string (--foo=value) or booleans (just --foo). + # + # By default all options are optional, unless :required is given. + # + def self.parse(key, value) + if key.is_a?(Array) + name, *aliases = key + else + name, aliases = key, [] + end + + name = name.to_s + default = value + + type = case value + when Symbol + default = nil + + if VALID_TYPES.include?(value) + value + elsif required = (value == :required) + :string + elsif value == :optional + # TODO Remove this warning in the future. + warn "Optional type is deprecated. Choose :boolean or :string instead. Assumed to be :boolean." + :boolean + end + when TrueClass, FalseClass + :boolean + when Numeric + :numeric + when Hash, Array, String + value.class.name.downcase.to_sym + end + + self.new(name.to_s, nil, required, type, default, nil, nil, aliases) + end + + def switch_name + @switch_name ||= dasherized? ? name : dasherize(name) + end + + def human_name + @human_name ||= dasherized? ? undasherize(name) : name + end + + def usage(padding=0) + sample = if banner && !banner.to_s.empty? + "#{switch_name}=#{banner}" + else + switch_name + end + + sample = "[#{sample}]" unless required? + + if aliases.empty? + (" " * padding) << sample + else + "#{aliases.join(', ')}, #{sample}" + end + end + + # Allow some type predicates as: boolean?, string? and etc. + # + def method_missing(method, *args, &block) + given = method.to_s.sub(/\?$/, '').to_sym + if valid_type?(given) + self.type == given + else + super + end + end + + protected + + def validate! + raise ArgumentError, "An option cannot be boolean and required." if boolean? && required? + end + + def valid_type?(type) + VALID_TYPES.include?(type.to_sym) + end + + def dasherized? + name.index('-') == 0 + end + + def undasherize(str) + str.sub(/^-{1,2}/, '') + end + + def dasherize(str) + (str.length > 1 ? "--" : "-") + str.gsub('_', '-') + end + + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/options.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/options.rb new file mode 100644 index 0000000000..75092308b5 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/parser/options.rb @@ -0,0 +1,142 @@ +class Thor + # This is a modified version of Daniel Berger's Getopt::Long class, licensed + # under Ruby's license. + # + class Options < Arguments #:nodoc: + LONG_RE = /^(--\w+[-\w+]*)$/ + SHORT_RE = /^(-[a-z])$/i + EQ_RE = /^(--\w+[-\w+]*|-[a-z])=(.*)$/i + SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args + SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i + + # Receives a hash and makes it switches. + # + def self.to_switches(options) + options.map do |key, value| + case value + when true + "--#{key}" + when Array + "--#{key} #{value.map{ |v| v.inspect }.join(' ')}" + when Hash + "--#{key} #{value.map{ |k,v| "#{k}:#{v}" }.join(' ')}" + when nil, false + "" + else + "--#{key} #{value.inspect}" + end + end.join(" ") + end + + # Takes a hash of Thor::Option objects. + # + def initialize(options={}) + options = options.values + super(options) + @shorts, @switches = {}, {} + + options.each do |option| + @switches[option.switch_name] = option + + option.aliases.each do |short| + @shorts[short.to_s] ||= option.switch_name + end + end + end + + def parse(args) + @pile = args.dup + + while peek + if current_is_switch? + case shift + when SHORT_SQ_RE + unshift($1.split('').map { |f| "-#{f}" }) + next + when EQ_RE, SHORT_NUM + unshift($2) + switch = $1 + when LONG_RE, SHORT_RE + switch = $1 + end + + switch = normalize_switch(switch) + next unless option = switch_option(switch) + + @assigns[option.human_name] = parse_peek(switch, option) + else + shift + end + end + + check_requirement! + @assigns + end + + protected + + # Returns true if the current value in peek is a registered switch. + # + def current_is_switch? + case peek + when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM + switch?($1) + when SHORT_SQ_RE + $1.split('').any? { |f| switch?("-#{f}") } + end + end + + def switch?(arg) + switch_option(arg) || @shorts.key?(arg) + end + + def switch_option(arg) + if match = no_or_skip?(arg) + @switches[arg] || @switches["--#{match}"] + else + @switches[arg] + end + end + + def no_or_skip?(arg) + arg =~ /^--(no|skip)-([-\w]+)$/ + $2 + end + + # Check if the given argument is actually a shortcut. + # + def normalize_switch(arg) + @shorts.key?(arg) ? @shorts[arg] : arg + end + + # Parse boolean values which can be given as --foo=true, --foo or --no-foo. + # + def parse_boolean(switch) + if current_is_value? + ["true", "TRUE", "t", "T", true].include?(shift) + else + @switches.key?(switch) || !no_or_skip?(switch) + end + end + + # Parse the value at the peek analyzing if it requires an input or not. + # + def parse_peek(switch, option) + unless current_is_value? + if option.boolean? + # No problem for boolean types + elsif no_or_skip?(switch) + return nil # User set value to nil + elsif option.string? && !option.required? + return option.human_name # Return the option name + else + raise MalformattedArgumentError, "no value provided for option '#{switch}'" + end + end + + @non_assigned_required.delete(option) + send(:"parse_#{option.type}", switch) + end + + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/rake_compat.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/rake_compat.rb new file mode 100644 index 0000000000..0d0757fdda --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/rake_compat.rb @@ -0,0 +1,66 @@ +require 'rake' + +class Thor + # Adds a compatibility layer to your Thor classes which allows you to use + # rake package tasks. For example, to use rspec rake tasks, one can do: + # + # require 'thor/rake_compat' + # + # class Default < Thor + # include Thor::RakeCompat + # + # Spec::Rake::SpecTask.new(:spec) do |t| + # t.spec_opts = ['--options', "spec/spec.opts"] + # t.spec_files = FileList['spec/**/*_spec.rb'] + # end + # end + # + module RakeCompat + def self.rake_classes + @rake_classes ||= [] + end + + def self.included(base) + # Hack. Make rakefile point to invoker, so rdoc task is generated properly. + rakefile = File.basename(caller[0].match(/(.*):\d+/)[1]) + Rake.application.instance_variable_set(:@rakefile, rakefile) + self.rake_classes << base + end + end +end + +class Object #:nodoc: + alias :rake_task :task + alias :rake_namespace :namespace + + def task(*args, &block) + task = rake_task(*args, &block) + + if klass = Thor::RakeCompat.rake_classes.last + non_namespaced_name = task.name.split(':').last + + description = non_namespaced_name + description << task.arg_names.map{ |n| n.to_s.upcase }.join(' ') + description.strip! + + klass.desc description, task.comment || non_namespaced_name + klass.send :define_method, non_namespaced_name do |*args| + Rake::Task[task.name.to_sym].invoke(*args) + end + end + + task + end + + def namespace(name, &block) + if klass = Thor::RakeCompat.rake_classes.last + const_name = Thor::Util.camel_case(name.to_s).to_sym + klass.const_set(const_name, Class.new(Thor)) + new_klass = klass.const_get(const_name) + Thor::RakeCompat.rake_classes << new_klass + end + + rake_namespace(name, &block) + Thor::RakeCompat.rake_classes.pop + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/runner.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/runner.rb new file mode 100644 index 0000000000..079f9e0c65 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/runner.rb @@ -0,0 +1,299 @@ +require 'fileutils' +require 'open-uri' +require 'yaml' +require 'digest/md5' +require 'pathname' + +class Thor::Runner < Thor #:nodoc: + map "-T" => :list, "-i" => :install, "-u" => :update + + # Override Thor#help so it can give information about any class and any method. + # + def help(meth=nil) + if meth && !self.respond_to?(meth) + initialize_thorfiles(meth) + klass, task = Thor::Util.namespace_to_thor_class_and_task(meth) + # Send mapping -h because it works with Thor::Group too + klass.start(["-h", task].compact, :shell => self.shell) + else + super + end + end + + # If a task is not found on Thor::Runner, method missing is invoked and + # Thor::Runner is then responsable for finding the task in all classes. + # + def method_missing(meth, *args) + meth = meth.to_s + initialize_thorfiles(meth) + klass, task = Thor::Util.namespace_to_thor_class_and_task(meth) + args.unshift(task) if task + klass.start(args, :shell => shell) + end + + desc "install NAME", "Install an optionally named Thor file into your system tasks" + method_options :as => :string, :relative => :boolean + def install(name) + initialize_thorfiles + + # If a directory name is provided as the argument, look for a 'main.thor' + # task in said directory. + begin + if File.directory?(File.expand_path(name)) + base, package = File.join(name, "main.thor"), :directory + contents = open(base).read + else + base, package = name, :file + contents = open(name).read + end + rescue OpenURI::HTTPError + raise Error, "Error opening URI '#{name}'" + rescue Errno::ENOENT + raise Error, "Error opening file '#{name}'" + end + + say "Your Thorfile contains:" + say contents + + return false if no?("Do you wish to continue [y/N]?") + + as = options["as"] || begin + first_line = contents.split("\n")[0] + (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil + end + + unless as + basename = File.basename(name) + as = ask("Please specify a name for #{name} in the system repository [#{basename}]:") + as = basename if as.empty? + end + + location = if options[:relative] || name =~ /^http:\/\// + name + else + File.expand_path(name) + end + + thor_yaml[as] = { + :filename => Digest::MD5.hexdigest(name + as), + :location => location, + :namespaces => Thor::Util.namespaces_in_content(contents, base) + } + + save_yaml(thor_yaml) + say "Storing thor file in your system repository" + destination = File.join(thor_root, thor_yaml[as][:filename]) + + if package == :file + File.open(destination, "w") { |f| f.puts contents } + else + FileUtils.cp_r(name, destination) + end + + thor_yaml[as][:filename] # Indicate success + end + + desc "uninstall NAME", "Uninstall a named Thor module" + def uninstall(name) + raise Error, "Can't find module '#{name}'" unless thor_yaml[name] + say "Uninstalling #{name}." + FileUtils.rm_rf(File.join(thor_root, "#{thor_yaml[name][:filename]}")) + + thor_yaml.delete(name) + save_yaml(thor_yaml) + + puts "Done." + end + + desc "update NAME", "Update a Thor file from its original location" + def update(name) + raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location] + + say "Updating '#{name}' from #{thor_yaml[name][:location]}" + + old_filename = thor_yaml[name][:filename] + self.options = self.options.merge("as" => name) + filename = install(thor_yaml[name][:location]) + + unless filename == old_filename + File.delete(File.join(thor_root, old_filename)) + end + end + + desc "installed", "List the installed Thor modules and tasks" + method_options :internal => :boolean + def installed + initialize_thorfiles(nil, true) + + klasses = Thor::Base.subclasses + klasses -= [Thor, Thor::Runner] unless options["internal"] + + display_klasses(true, klasses) + end + + desc "list [SEARCH]", "List the available thor tasks (--substring means .*SEARCH)" + method_options :substring => :boolean, :group => :string, :all => :boolean + def list(search="") + initialize_thorfiles + + search = ".*#{search}" if options["substring"] + search = /^#{search}.*/i + group = options[:group] || "standard" + + klasses = Thor::Base.subclasses.select do |k| + (options[:all] || k.group == group) && k.namespace =~ search + end + + display_klasses(false, klasses) + end + + private + + def thor_root + Thor::Util.thor_root + end + + def thor_yaml + @thor_yaml ||= begin + yaml_file = File.join(thor_root, "thor.yml") + yaml = YAML.load_file(yaml_file) if File.exists?(yaml_file) + yaml || {} + end + end + + # Save the yaml file. If none exists in thor root, creates one. + # + def save_yaml(yaml) + yaml_file = File.join(thor_root, "thor.yml") + + unless File.exists?(yaml_file) + FileUtils.mkdir_p(thor_root) + yaml_file = File.join(thor_root, "thor.yml") + FileUtils.touch(yaml_file) + end + + File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml } + end + + def self.exit_on_failure? + true + end + + # Load the thorfiles. If relevant_to is supplied, looks for specific files + # in the thor_root instead of loading them all. + # + # By default, it also traverses the current path until find Thor files, as + # described in thorfiles. This look up can be skipped by suppliying + # skip_lookup true. + # + def initialize_thorfiles(relevant_to=nil, skip_lookup=false) + thorfiles(relevant_to, skip_lookup).each do |f| + Thor::Util.load_thorfile(f) unless Thor::Base.subclass_files.keys.include?(File.expand_path(f)) + end + end + + # Finds Thorfiles by traversing from your current directory down to the root + # directory of your system. If at any time we find a Thor file, we stop. + # + # We also ensure that system-wide Thorfiles are loaded first, so local + # Thorfiles can override them. + # + # ==== Example + # + # If we start at /Users/wycats/dev/thor ... + # + # 1. /Users/wycats/dev/thor + # 2. /Users/wycats/dev + # 3. /Users/wycats <-- we find a Thorfile here, so we stop + # + # Suppose we start at c:\Documents and Settings\james\dev\thor ... + # + # 1. c:\Documents and Settings\james\dev\thor + # 2. c:\Documents and Settings\james\dev + # 3. c:\Documents and Settings\james + # 4. c:\Documents and Settings + # 5. c:\ <-- no Thorfiles found! + # + def thorfiles(relevant_to=nil, skip_lookup=false) + # TODO Remove this dealing with deprecated thor when :namespaces: is available as constants + save_yaml(thor_yaml) if Thor::Util.convert_constants_to_namespaces(thor_yaml) + + thorfiles = [] + + unless skip_lookup + Pathname.pwd.ascend do |path| + thorfiles = Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten + break unless thorfiles.empty? + end + end + + files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Thor::Util.thor_root_glob) + files += thorfiles + files -= ["#{thor_root}/thor.yml"] + + files.map! do |file| + File.directory?(file) ? File.join(file, "main.thor") : file + end + end + + # Load thorfiles relevant to the given method. If you provide "foo:bar" it + # will load all thor files in the thor.yaml that has "foo" e "foo:bar" + # namespaces registered. + # + def thorfiles_relevant_to(meth) + lookup = [ meth, meth.split(":")[0...-1].join(":") ] + + files = thor_yaml.select do |k, v| + v[:namespaces] && !(v[:namespaces] & lookup).empty? + end + + files.map { |k, v| File.join(thor_root, "#{v[:filename]}") } + end + + # Display information about the given klasses. If with_module is given, + # it shows a table with information extracted from the yaml file. + # + def display_klasses(with_modules=false, klasses=Thor.subclasses) + klasses -= [Thor, Thor::Runner] unless with_modules + raise Error, "No Thor tasks available" if klasses.empty? + + if with_modules && !thor_yaml.empty? + info = [] + labels = ["Modules", "Namespaces"] + + info << labels + info << [ "-" * labels[0].size, "-" * labels[1].size ] + + thor_yaml.each do |name, hash| + info << [ name, hash[:namespaces].join(", ") ] + end + + print_table info + say "" + end + + unless klasses.empty? + klasses.dup.each do |klass| + klasses -= Thor::Util.thor_classes_in(klass) + end + + klasses.each { |k| display_tasks(k) } + else + say "\033[1;34mNo Thor tasks available\033[0m" + end + end + + # Display tasks from the given Thor class. + # + def display_tasks(klass) + unless klass.tasks.empty? + base = klass.namespace + + color = base == "default" ? :magenta : :blue + say shell.set_color(base, color, true) + say "-" * base.length + + klass.help(shell, :short => true, :ident => 0, :namespace => true) + end + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell.rb new file mode 100644 index 0000000000..1dc8f0e5b4 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell.rb @@ -0,0 +1,78 @@ +require 'rbconfig' +require 'thor/shell/color' + +class Thor + module Base + # Returns the shell used in all Thor classes. If you are in a Unix platform + # it will use a colored log, otherwise it will use a basic one without color. + # + def self.shell + @shell ||= if Config::CONFIG['host_os'] =~ /mswin|mingw/ + Thor::Shell::Basic + else + Thor::Shell::Color + end + end + + # Sets the shell used in all Thor classes. + # + def self.shell=(klass) + @shell = klass + end + end + + module Shell + SHELL_DELEGATED_METHODS = [:ask, :yes?, :no?, :say, :say_status, :print_list, :print_table] + + # Add shell to initialize config values. + # + # ==== Configuration + # shell:: An instance of the shell to be used. + # + # ==== Examples + # + # class MyScript < Thor + # argument :first, :type => :numeric + # end + # + # MyScript.new [1.0], { :foo => :bar }, :shell => Thor::Shell::Basic.new + # + def initialize(args=[], options={}, config={}) + super + self.shell = config[:shell] + self.shell.base ||= self if self.shell.respond_to?(:base) + end + + # Holds the shell for the given Thor instance. If no shell is given, + # it gets a default shell from Thor::Base.shell. + # + def shell + @shell ||= Thor::Base.shell.new + end + + # Sets the shell for this thor class. + # + def shell=(shell) + @shell = shell + end + + # Common methods that are delegated to the shell. + # + SHELL_DELEGATED_METHODS.each do |method| + module_eval <<-METHOD, __FILE__, __LINE__ + def #{method}(*args) + shell.#{method}(*args) + end + METHOD + end + + protected + + # Allow shell to be shared between invocations. + # + def _shared_configuration #:nodoc: + super.merge!(:shell => self.shell) + end + + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/basic.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/basic.rb new file mode 100644 index 0000000000..f6be3575ca --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/shell/basic.rb @@ -0,0 +1,219 @@ +require 'tempfile' + +class Thor + module Shell + class Basic + attr_accessor :base, :padding + + # Initialize base and padding to nil. + # + def initialize #:nodoc: + @base, @padding = nil, 0 + end + + # Sets the output padding, not allowing less than zero values. + # + def padding=(value) + @padding = [0, value].max + end + + # Ask something to the user and receives a response. + # + # ==== Example + # ask("What is your name?") + # + def ask(statement, color=nil) + say("#{statement} ", color) + $stdin.gets.strip + end + + # Say (print) something to the user. If the sentence ends with a whitespace + # or tab character, a new line is not appended (print + flush). Otherwise + # are passed straight to puts (behavior got from Highline). + # + # ==== Example + # say("I know you knew that.") + # + def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)$/)) + message = message.to_s + message = set_color(message, color) if color + + if force_new_line + $stdout.puts(message) + else + $stdout.print(message) + $stdout.flush + end + end + + # Say a status with the given color and appends the message. Since this + # method is used frequently by actions, it allows nil or false to be given + # in log_status, avoiding the message from being shown. If a Symbol is + # given in log_status, it's used as the color. + # + def say_status(status, message, log_status=true) + return if quiet? || log_status == false + spaces = " " * (padding + 1) + color = log_status.is_a?(Symbol) ? log_status : :green + + status = status.to_s.rjust(12) + status = set_color status, color, true if color + say "#{status}#{spaces}#{message}", nil, true + end + + # Make a question the to user and returns true if the user replies "y" or + # "yes". + # + def yes?(statement, color=nil) + ask(statement, color) =~ is?(:yes) + end + + # Make a question the to user and returns true if the user replies "n" or + # "no". + # + def no?(statement, color=nil) + !yes?(statement, color) + end + + # Prints a list of items. + # + # ==== Parameters + # list + # + # ==== Options + # mode:: Can be :rows or :inline. Defaults to :rows. + # ident:: Ident each item with the value given. + # + def print_list(list, options={}) + return if list.empty? + + ident = " " * (options[:ident] || 0) + content = case options[:mode] + when :inline + last = list.pop + "#{list.join(", ")}, and #{last}" + else # rows + ident + list.join("\n#{ident}") + end + + $stdout.puts content + end + + # Prints a table. + # + # ==== Parameters + # Array[Array[String, String, ...]] + # + # ==== Options + # ident:: Ident the first column by ident value. + # + def print_table(table, options={}) + return if table.empty? + + formats = [] + 0.upto(table.first.length - 2) do |i| + maxima = table.max{ |a,b| a[i].size <=> b[i].size }[i].size + formats << "%-#{maxima + 2}s" + end + + formats[0] = formats[0].insert(0, " " * options[:ident]) if options[:ident] + formats << "%s" + + table.each do |row| + row.each_with_index do |column, i| + $stdout.print formats[i] % column.to_s + end + $stdout.puts + end + end + + # Deals with file collision and returns true if the file should be + # overwriten and false otherwise. If a block is given, it uses the block + # response as the content for the diff. + # + # ==== Parameters + # destination:: the destination file to solve conflicts + # block:: an optional block that returns the value to be used in diff + # + def file_collision(destination) + return true if @always_force + options = block_given? ? "[Ynaqdh]" : "[Ynaqh]" + + while true + answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}] + + case answer + when is?(:yes), is?(:force), "" + return true + when is?(:no), is?(:skip) + return false + when is?(:always) + return @always_force = true + when is?(:quit) + say 'Aborting...' + raise SystemExit + when is?(:diff) + show_diff(destination, yield) if block_given? + say 'Retrying...' + else + say file_collision_help + end + end + end + + # Called if something goes wrong during the execution. This is used by Thor + # internally and should not be used inside your scripts. If someone went + # wrong, you can always raise an exception. If you raise a Thor::Error, it + # will be rescued and wrapped in the method below. + # + def error(statement) + $stderr.puts statement + end + + # Apply color to the given string with optional bold. Disabled in the + # Thor::Shell::Basic class. + # + def set_color(string, color, bold=false) #:nodoc: + string + end + + protected + + def is?(value) #:nodoc: + value = value.to_s + + if value.size == 1 + /\A#{value}\z/i + else + /\A(#{value}|#{value[0,1]})\z/i + end + end + + def file_collision_help #:nodoc: +< e + parse_argument_error(instance, e, caller) + rescue NoMethodError => e + parse_no_method_error(instance, e) + end + + # Returns the formatted usage. If a class is given, the class arguments are + # injected in the usage. + # + def formatted_usage(klass=nil, namespace=false, show_options=true) + formatted = if namespace.is_a?(String) + "#{namespace}:" + elsif klass && namespace + "#{klass.namespace.gsub(/^default/,'')}:" + else + "" + end + + formatted << formatted_arguments(klass) + formatted << " #{formatted_options}" if show_options + formatted.strip! + formatted + end + + # Injects the class arguments into the task usage. + # + def formatted_arguments(klass) + if klass && !klass.arguments.empty? + usage.to_s.gsub(/^#{name}/) do |match| + match << " " << klass.arguments.map{ |a| a.usage }.join(' ') + end + else + usage.to_s + end + end + + # Returns the options usage for this task. + # + def formatted_options + @formatted_options ||= options.map{ |_, o| o.usage }.sort.join(" ") + end + + protected + + # Given a target, checks if this class name is not a private/protected method. + # + def public_method?(instance) #:nodoc: + collection = instance.private_methods + instance.protected_methods + (collection & [name.to_s, name.to_sym]).empty? + end + + # Clean everything that comes from the Thor gempath and remove the caller. + # + def sans_backtrace(backtrace, caller) #:nodoc: + dirname = /^#{Regexp.escape(File.dirname(__FILE__))}/ + saned = backtrace.reject { |frame| frame =~ dirname } + saned -= caller + end + + def parse_argument_error(instance, e, caller) #:nodoc: + backtrace = sans_backtrace(e.backtrace, caller) + + if backtrace.empty? && e.message =~ /wrong number of arguments/ + if instance.is_a?(Thor::Group) + raise e, "'#{name}' was called incorrectly. Are you sure it has arity equals to 0?" + else + raise InvocationError, "'#{name}' was called incorrectly. Call as " << + "'#{formatted_usage(instance.class, true)}'" + end + else + raise e + end + end + + def parse_no_method_error(instance, e) #:nodoc: + if e.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/ + raise UndefinedTaskError, "The #{instance.class.namespace} namespace " << + "doesn't have a '#{name}' task" + else + raise e + end + end + + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/util.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/util.rb new file mode 100644 index 0000000000..ebae0a3193 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/util.rb @@ -0,0 +1,251 @@ +require 'rbconfig' + +class Thor + module Sandbox #:nodoc: + end + + # This module holds several utilities: + # + # 1) Methods to convert thor namespaces to constants and vice-versa. + # + # Thor::Utils.namespace_from_thor_class(Foo::Bar::Baz) #=> "foo:bar:baz" + # + # 2) Loading thor files and sandboxing: + # + # Thor::Utils.load_thorfile("~/.thor/foo") + # + module Util + + # Receives a namespace and search for it in the Thor::Base subclasses. + # + # ==== Parameters + # namespace:: The namespace to search for. + # + def self.find_by_namespace(namespace) + namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/ + + Thor::Base.subclasses.find do |klass| + klass.namespace == namespace + end + end + + # Receives a constant and converts it to a Thor namespace. Since Thor tasks + # can be added to a sandbox, this method is also responsable for removing + # the sandbox namespace. + # + # This method should not be used in general because it's used to deal with + # older versions of Thor. On current versions, if you need to get the + # namespace from a class, just call namespace on it. + # + # ==== Parameters + # constant:: The constant to be converted to the thor path. + # + # ==== Returns + # String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz" + # + def self.namespace_from_thor_class(constant, remove_default=true) + constant = constant.to_s.gsub(/^Thor::Sandbox::/, "") + constant = snake_case(constant).squeeze(":") + constant.gsub!(/^default/, '') if remove_default + constant + end + + # Given the contents, evaluate it inside the sandbox and returns the + # namespaces defined in the sandbox. + # + # ==== Parameters + # contents + # + # ==== Returns + # Array[Object] + # + def self.namespaces_in_content(contents, file=__FILE__) + old_constants = Thor::Base.subclasses.dup + Thor::Base.subclasses.clear + + load_thorfile(file, contents) + + new_constants = Thor::Base.subclasses.dup + Thor::Base.subclasses.replace(old_constants) + + new_constants.map!{ |c| c.namespace } + new_constants.compact! + new_constants + end + + # Returns the thor classes declared inside the given class. + # + def self.thor_classes_in(klass) + Thor::Base.subclasses.select do |subclass| + klass.constants.include?(subclass.name.gsub("#{klass.name}::", '')) + end + end + + # Receives a string and convert it to snake case. SnakeCase returns snake_case. + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def self.snake_case(str) + return str.downcase if str =~ /^[A-Z_]+$/ + str.gsub(/\B[A-Z]/, '_\&').squeeze('_') =~ /_*(.*)/ + return $+.downcase + end + + # Receives a string and convert it to camel case. camel_case returns CamelCase. + # + # ==== Parameters + # String + # + # ==== Returns + # String + # + def self.camel_case(str) + return str if str !~ /_/ && str =~ /[A-Z]+.*/ + str.split('_').map { |i| i.capitalize }.join + end + + # Receives a namespace and tries to retrieve a Thor or Thor::Group class + # from it. It first searches for a class using the all the given namespace, + # if it's not found, removes the highest entry and searches for the class + # again. If found, returns the highest entry as the class name. + # + # ==== Examples + # + # class Foo::Bar < Thor + # def baz + # end + # end + # + # class Baz::Foo < Thor::Group + # end + # + # Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default task + # Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil + # Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz" + # + # ==== Parameters + # namespace + # + # ==== Errors + # Thor::Error:: raised if the namespace cannot be found. + # + # Thor::Error:: raised if the namespace evals to a class which does not + # inherit from Thor or Thor::Group. + # + def self.namespace_to_thor_class_and_task(namespace, raise_if_nil=true) + if namespace.include?(?:) + pieces = namespace.split(":") + task = pieces.pop + klass = Thor::Util.find_by_namespace(pieces.join(":")) + end + + unless klass + klass, task = Thor::Util.find_by_namespace(namespace), nil + end + + raise Error, "could not find Thor class or task '#{namespace}'" if raise_if_nil && klass.nil? + return klass, task + end + + # Receives a path and load the thor file in the path. The file is evaluated + # inside the sandbox to avoid namespacing conflicts. + # + def self.load_thorfile(path, content=nil) + content ||= File.read(path) + + begin + Thor::Sandbox.class_eval(content, path) + rescue Exception => e + $stderr.puts "WARNING: unable to load thorfile #{path.inspect}: #{e.message}" + end + end + + # Receives a yaml (hash) and updates all constants entries to namespace. + # This was added to deal with deprecated versions of Thor. + # + # TODO Deprecate this method in the future. + # + # ==== Returns + # TrueClass|FalseClass:: Returns true if any change to the yaml file was made. + # + def self.convert_constants_to_namespaces(yaml) + yaml_changed = false + + yaml.each do |k, v| + next unless v[:constants] && v[:namespaces].nil? + yaml_changed = true + yaml[k][:namespaces] = v[:constants].map{|c| Thor::Util.namespace_from_thor_class(c)} + end + + yaml_changed + end + + def self.user_home + @@user_home ||= if ENV["HOME"] + ENV["HOME"] + elsif ENV["USERPROFILE"] + ENV["USERPROFILE"] + elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"] + File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"]) + elsif ENV["APPDATA"] + ENV["APPDATA"] + else + begin + File.expand_path("~") + rescue + if File::ALT_SEPARATOR + "C:/" + else + "/" + end + end + end + end + + # Returns the root where thor files are located, dependending on the OS. + # + def self.thor_root + File.join(user_home, ".thor").gsub(/\\/, '/') + end + + # Returns the files in the thor root. On Windows thor_root will be something + # like this: + # + # C:\Documents and Settings\james\.thor + # + # If we don't #gsub the \ character, Dir.glob will fail. + # + def self.thor_root_glob + files = Dir["#{thor_root}/*"] + + files.map! do |file| + File.directory?(file) ? File.join(file, "main.thor") : file + end + end + + # Where to look for Thor files. + # + def self.globs_for(path) + ["#{path}/Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"] + end + + # Return the path to the ruby interpreter taking into account multiple + # installations and windows extensions. + # + def self.ruby_command + @ruby_command ||= begin + ruby = File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name']) + ruby << Config::CONFIG['EXEEXT'] + + # escape string in case path to ruby executable contain spaces. + ruby.sub!(/.*\s.*/m, '"\&"') + ruby + end + end + + end +end diff --git a/railties/lib/rails/vendor/thor-0.12.1/lib/thor/version.rb b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/version.rb new file mode 100644 index 0000000000..650253d648 --- /dev/null +++ b/railties/lib/rails/vendor/thor-0.12.1/lib/thor/version.rb @@ -0,0 +1,3 @@ +class Thor + VERSION = "0.12.1".freeze +end -- cgit v1.2.3 From 30ae3538a3e281be3d6e8ca337a0790c71f8a9b2 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 1 Dec 2009 21:19:20 -0600 Subject: Some basic route discovery tests --- railties/test/application/routing_test.rb | 85 +++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 railties/test/application/routing_test.rb diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb new file mode 100644 index 0000000000..1bfec3805b --- /dev/null +++ b/railties/test/application/routing_test.rb @@ -0,0 +1,85 @@ +require 'isolation/abstract_unit' +require 'rack/test' + +module ApplicationTests + class RoutingTest < Test::Unit::TestCase + include ActiveSupport::Testing::Isolation + include Rack::Test::Methods + + def setup + build_app + end + + def app + @app ||= begin + boot_rails + require "#{app_path}/config/environment" + + Rails.application + end + end + + test "simple controller" do + controller :foo, <<-RUBY + class FooController < ActionController::Base + def index + render :text => "foo" + end + end + RUBY + + get '/foo' + assert_equal 'foo', last_response.body + end + + test "multiple controllers" do + controller :foo, <<-RUBY + class FooController < ActionController::Base + def index + render :text => "foo" + end + end + RUBY + + controller :bar, <<-RUBY + class BarController < ActionController::Base + def index + render :text => "bar" + end + end + RUBY + + get '/foo' + assert_equal 'foo', last_response.body + + get '/bar' + assert_equal 'bar', last_response.body + end + + test "nested controller" do + controller 'foo', <<-RUBY + class FooController < ActionController::Base + def index + render :text => "foo" + end + end + RUBY + + controller 'admin/foo', <<-RUBY + module Admin + class FooController < ActionController::Base + def index + render :text => "admin::foo" + end + end + end + RUBY + + get '/foo' + assert_equal 'foo', last_response.body + + get '/admin/foo' + assert_equal 'admin::foo', last_response.body + end + end +end -- cgit v1.2.3 From 7fe19d415ab80727d685c163d7a0413ca6bfe585 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 1 Dec 2009 22:22:48 -0600 Subject: Make recognize try to constantize controller to see if it exists --- actionpack/lib/action_dispatch/routing/route_set.rb | 3 +++ actionpack/test/controller/routing_test.rb | 2 -- actionpack/test/lib/controller/fake_controllers.rb | 13 ++++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 79e15edeaa..18e18c5820 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -28,6 +28,7 @@ module ActionDispatch end if env['action_controller.recognize'] + controller(params) [200, {}, params] else controller = controller(params) @@ -41,6 +42,8 @@ module ActionDispatch controller = "#{params[:controller].camelize}Controller" ActiveSupport::Inflector.constantize(controller) end + rescue NameError => e + raise ActionController::RoutingError, e.message end def merge_default_action!(params) diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 97fbd95e73..84564f4e43 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1851,11 +1851,9 @@ class RackMountIntegrationTests < ActiveSupport::TestCase assert_equal({:controller => 'posts', :action => 'show_date', :year => '2009'}, @routes.recognize_path('/blog/2009', :method => :get)) assert_equal({:controller => 'posts', :action => 'show_date', :year => '2009', :month => '01'}, @routes.recognize_path('/blog/2009/01', :method => :get)) assert_equal({:controller => 'posts', :action => 'show_date', :year => '2009', :month => '01', :day => '01'}, @routes.recognize_path('/blog/2009/01/01', :method => :get)) - assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/blog/123456789', :method => :get) } assert_equal({:controller => 'archive', :action => 'index', :year => '2010'}, @routes.recognize_path('/archive/2010')) assert_equal({:controller => 'archive', :action => 'index'}, @routes.recognize_path('/archive')) - assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/archive/january') } assert_equal({:controller => 'people', :action => 'index'}, @routes.recognize_path('/people', :method => :get)) assert_equal({:controller => 'people', :action => 'index', :format => 'xml'}, @routes.recognize_path('/people.xml', :method => :get)) diff --git a/actionpack/test/lib/controller/fake_controllers.rb b/actionpack/test/lib/controller/fake_controllers.rb index 250327e6dc..09692f77b5 100644 --- a/actionpack/test/lib/controller/fake_controllers.rb +++ b/actionpack/test/lib/controller/fake_controllers.rb @@ -1,37 +1,48 @@ class << Object; alias_method :const_available?, :const_defined?; end class ContentController < ActionController::Base; end -class NotAController; end module Admin class << self; alias_method :const_available?, :const_defined?; end + class AccountsController < ActionController::Base; end class NewsFeedController < ActionController::Base; end class PostsController < ActionController::Base; end class StuffController < ActionController::Base; end class UserController < ActionController::Base; end + class UsersController < ActionController::Base; end end module Api + class UsersController < ActionController::Base; end class ProductsController < ActionController::Base; end end # TODO: Reduce the number of test controllers we use +class AccountController < ActionController::Base; end class AddressesController < ActionController::Base; end +class ArchiveController < ActionController::Base; end class ArticlesController < ActionController::Base; end class BarController < ActionController::Base; end +class BlogController < ActionController::Base; end class BooksController < ActionController::Base; end class BraveController < ActionController::Base; end +class CarsController < ActionController::Base; end +class CcController < ActionController::Base; end class CController < ActionController::Base; end class ElsewhereController < ActionController::Base; end class FooController < ActionController::Base; end +class GeocodeController < ActionController::Base; end class HiController < ActionController::Base; end class ImageController < ActionController::Base; end +class NewsController < ActionController::Base; end class NotesController < ActionController::Base; end class PeopleController < ActionController::Base; end class PostsController < ActionController::Base; end class SessionsController < ActionController::Base; end class StuffController < ActionController::Base; end class SubpathBooksController < ActionController::Base; end +class SymbolsController < ActionController::Base; end +class UserController < ActionController::Base; end class WeblogController < ActionController::Base; end # For speed test -- cgit v1.2.3 From 2fbd6f46fd00a334119f1a25394046963831ce3e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 1 Dec 2009 22:48:42 -0600 Subject: Simply track controller namespaces instead of a complete list of possible controllers to route to --- actionpack/lib/action_dispatch/routing.rb | 59 ++++++---------------- .../lib/action_dispatch/routing/route_set.rb | 10 ++-- 2 files changed, 22 insertions(+), 47 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 9b977800b4..9159bb6395 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -265,6 +265,7 @@ module ActionDispatch SEPARATORS = %w( / . ? ) HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] + CONTROLLER_REGEXP = /[_a-zA-Z0-9]+/ # The root paths which may contain controller files mattr_accessor :controller_paths @@ -277,7 +278,11 @@ module ActionDispatch class << self def controller_constraints - @controller_constraints ||= Regexp.union(*possible_controllers.collect { |n| Regexp.escape(n) }) + @controller_constraints ||= begin + source = controller_namespaces.map { |ns| "#{Regexp.escape(ns)}/#{CONTROLLER_REGEXP.source}" } + source << CONTROLLER_REGEXP.source + Regexp.compile(source.sort.reverse.join('|')) + end end def clear_controller_cache! @@ -285,57 +290,25 @@ module ActionDispatch end private - # Returns the array of controller names currently available to ActionController::Routing. - def possible_controllers - possible_controllers = [] + def controller_namespaces + namespaces = Set.new - # Find any controller classes already in memory + # Find any nested controllers already in memory ActionController::Base.subclasses.each do |klass| controller_name = klass.underscore - controller_name.gsub!(/_controller\Z/, '') - possible_controllers << controller_name + namespaces << controller_name.split('/')[0...-1].join('/') end - # Find controllers in controllers/ directory - paths = controller_paths.select { |path| File.directory?(path) && path != "." } - seen_paths = Hash.new {|h, k| h[k] = true; false} - normalize_paths(paths).each do |load_path| + # Find namespaces in controllers/ directory + controller_paths.each do |load_path| + load_path = File.expand_path(load_path) Dir["#{load_path}/**/*_controller.rb"].collect do |path| - next if seen_paths[path.gsub(%r{^\.[/\\]}, "")] - - controller_name = path[(load_path.length + 1)..-1] - - controller_name.gsub!(/_controller\.rb\Z/, '') - possible_controllers << controller_name + namespaces << File.dirname(path).sub(/#{load_path}\/?/, '') end end - # remove duplicates - possible_controllers.uniq! - - possible_controllers - end - - # Returns an array of paths, cleaned of double-slashes and relative path references. - # * "\\\" and "//" become "\\" or "/". - # * "/foo/bar/../config" becomes "/foo/config". - # The returned array is sorted by length, descending. - def normalize_paths(paths) - # do the hokey-pokey of path normalization... - paths = paths.collect do |path| - path = path. - gsub("//", "/"). # replace double / chars with a single - gsub("\\\\", "\\"). # replace double \ chars with a single - gsub(%r{(.)[\\/]$}, '\1') # drop final / or \ if path ends with it - - # eliminate .. paths where possible - re = %r{[^/\\]+[/\\]\.\.[/\\]} - path.gsub!(re, "") while path.match(re) - path - end - - # start with longest path, first - paths = paths.uniq.sort_by { |path| - path.length } + namespaces.delete('') + namespaces end end end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 18e18c5820..c2f6531a74 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -27,11 +27,13 @@ module ActionDispatch end end + unless controller = controller(params) + return [417, {}, []] + end + if env['action_controller.recognize'] - controller(params) [200, {}, params] else - controller = controller(params) controller.action(params[:action]).call(env) end end @@ -42,8 +44,8 @@ module ActionDispatch controller = "#{params[:controller].camelize}Controller" ActiveSupport::Inflector.constantize(controller) end - rescue NameError => e - raise ActionController::RoutingError, e.message + rescue NameError + nil end def merge_default_action!(params) -- cgit v1.2.3 From 97be8537ebf33aa0c47fe1c6164e94c24acc3c19 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 1 Dec 2009 22:58:56 -0600 Subject: Fix @renderer warning --- actionpack/lib/action_view/render/partials.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index 2eb88ae3e5..aeaf1ee4ff 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -181,20 +181,20 @@ module ActionView def initialize(view_context, options, block) @view = view_context @partial_names = PARTIAL_NAMES[@view.controller.class] - + key = Thread.current[:format_locale_key] @templates = TEMPLATES[key] if key - + setup(options, block) end - + def setup(options, block) partial = options[:partial] - + @options = options @locals = options[:locals] || {} @block = block - + if String === partial @object = options[:object] @path = partial @@ -240,7 +240,7 @@ module ActionView segments << template.render(@view, locals) end - + @template = template segments end @@ -294,7 +294,7 @@ module ActionView path && @templates[path] ||= _find_template(path) end end - + def _find_template(path) if controller = @view.controller prefix = controller.controller_path unless path.include?(?/) @@ -319,7 +319,7 @@ module ActionView _evaluate_assigns_and_ivars details = options[:_details] - + # Is this needed self.formats = details[:formats] if details renderer = PartialRenderer.new(self, options, nil) @@ -329,12 +329,12 @@ module ActionView end def _render_partial(options, &block) #:nodoc: - if @renderer + if defined? @renderer @renderer.setup(options, block) else @renderer = PartialRenderer.new(self, options, block) end - + @renderer.render end -- cgit v1.2.3 From 5d9af09563897bd36b4baaf9bb366cf100be90a9 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 1 Dec 2009 23:01:21 -0600 Subject: Silence known internal AM deprecation calls --- actionmailer/lib/action_mailer/base.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index bef9bb8e79..7b952c462f 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -515,11 +515,15 @@ module ActionMailer #:nodoc: @headers ||= {} @sent_on ||= Time.now - super # Run deprecation hooks + ActiveSupport::Deprecation.silence do + super # Run deprecation hooks + end end def create_parts - super # Run deprecation hooks + ActiveSupport::Deprecation.silence do + super # Run deprecation hooks + end if String === response_body @parts.unshift Part.new( @@ -592,7 +596,7 @@ module ActionMailer #:nodoc: headers.each { |k, v| m[k] = v } real_content_type, ctype_attrs = parse_content_type - + if @parts.empty? m.set_content_type(real_content_type, nil, ctype_attrs) m.body = normalize_new_lines(body) -- cgit v1.2.3 From 75ae5bb0228ec8d8a144030573db2fd998299042 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 1 Dec 2009 23:14:03 -0600 Subject: cache_store and page_cache_directory are already defined in caching and pages --- actionpack/lib/action_controller/metal/compatibility.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb index c251d79f4e..0c264fcd09 100644 --- a/actionpack/lib/action_controller/metal/compatibility.rb +++ b/actionpack/lib/action_controller/metal/compatibility.rb @@ -46,11 +46,8 @@ module ActionController cattr_accessor :use_accept_header self.use_accept_header = true - cattr_accessor :page_cache_directory self.page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : "" - cattr_reader :cache_store - cattr_accessor :consider_all_requests_local self.consider_all_requests_local = true @@ -116,7 +113,7 @@ module ActionController details[:prefix] = nil if name =~ /\blayouts/ super end - + # Move this into a "don't run in production" module def _default_layout(details, require_layout = false) super -- cgit v1.2.3 From 70d0b7c87a492aef141859127d1b9fec6b4b09f3 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 1 Dec 2009 23:19:41 -0600 Subject: Fix parens warning in ajax test --- actionpack/test/template/ajax_test.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/actionpack/test/template/ajax_test.rb b/actionpack/test/template/ajax_test.rb index 670ba92697..aeb7c09b09 100644 --- a/actionpack/test/template/ajax_test.rb +++ b/actionpack/test/template/ajax_test.rb @@ -6,7 +6,7 @@ class AjaxTestCase < ActiveSupport::TestCase def assert_html(html, matches) matches.each do |match| - assert_match Regexp.new(Regexp.escape(match)), html + assert_match(Regexp.new(Regexp.escape(match)), html) end end @@ -52,18 +52,18 @@ class LinkToRemoteTest < AjaxTestCase test "with a hash for :update" do link = link(:update => {:success => "#posts", :failure => "#error"}) - assert_match /data-update-success="#posts"/, link - assert_match /data-update-failure="#error"/, link + assert_match(/data-update-success="#posts"/, link) + assert_match(/data-update-failure="#error"/, link) end test "with positional parameters" do link = link(:position => :top, :update => "#posts") - assert_match /data\-update\-position="top"/, link + assert_match(/data\-update\-position="top"/, link) end test "with an optional method" do link = link(:method => "delete") - assert_match /data-method="delete"/, link + assert_match(/data-method="delete"/, link) end class LegacyLinkToRemoteTest < AjaxTestCase @@ -99,7 +99,7 @@ class ButtonToRemoteTest < AjaxTestCase button = button({:url => {:action => "whatnot"}}, {:class => "fine"}) [/input/, /class="fine"/, /type="button"/, /value="Remote outpost"/, /data-url="\/whatnot"/].each do |match| - assert_match match, button + assert_match(match, button) end end end -- cgit v1.2.3 From e12380e7877abb3dafeb7875fb74c272e5cbeee0 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 1 Dec 2009 23:25:24 -0600 Subject: Remove concat before overriding it --- .../lib/active_support/core_ext/string/output_safety.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 2cca4763f4..a2a88eb7df 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -7,11 +7,11 @@ class String @_rails_html_safe = true self end - + def html_safe dup.html_safe! end - + alias original_plus + def +(other) result = original_plus(other) @@ -21,7 +21,7 @@ class String result end end - + alias original_concat << def <<(other) result = original_concat(other) @@ -30,14 +30,15 @@ class String end result end - + + remove_method :concat def concat(other) self << other end - + private def also_html_safe?(other) other.respond_to?(:html_safe?) && other.html_safe? end - + end \ No newline at end of file -- cgit v1.2.3 From f22db809c9da89adac0be60c5b70f37f75dc8376 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 1 Dec 2009 23:27:40 -0600 Subject: Response#cache_control is defined later --- actionpack/lib/action_dispatch/http/response.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index b3ed7c9d1a..c651f21f68 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -33,7 +33,6 @@ module ActionDispatch # :nodoc: # end class Response < Rack::Response attr_accessor :request, :blank - attr_reader :cache_control attr_writer :header, :sending_file alias_method :headers=, :header= -- cgit v1.2.3 From ad26f066fe94bad4219f235a0d65190e4d8d15c8 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 1 Dec 2009 23:29:28 -0600 Subject: Response#write is defined twice (this is why -w is good) --- actionpack/lib/action_dispatch/http/response.rb | 6 ------ 1 file changed, 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index c651f21f68..32f9a0031d 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -56,12 +56,6 @@ module ActionDispatch # :nodoc: @cache_control ||= {} end - def write(str) - s = str.to_s - @writer.call s - str - end - def status=(status) @status = status.to_i end -- cgit v1.2.3 From de40bc033a64c7074e01a8e8c585225f4cdaf81e Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 2 Dec 2009 03:23:00 -0800 Subject: Ensure Cache-Control max-age is an integer --- actionpack/lib/action_dispatch/http/response.rb | 2 +- actionpack/test/controller/render_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 32f9a0031d..4f35a00247 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -270,7 +270,7 @@ module ActionDispatch # :nodoc: max_age = control[:max_age] options = [] - options << "max-age=#{max_age}" if max_age + options << "max-age=#{max_age.to_i}" if max_age options << (control[:public] ? "public" : "private") options << "must-revalidate" if control[:must_revalidate] options.concat(extras) if extras diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index b32325fa20..cffa970011 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -54,7 +54,7 @@ class TestController < ActionController::Base end def conditional_hello_with_expires_in - expires_in 1.minute + expires_in 60.1.seconds render :action => 'hello_world' end -- cgit v1.2.3 From 84be6cfb6452a23d5617cd8a8b200d8fb0431d5a Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 2 Dec 2009 12:33:33 -0600 Subject: Fork rack build nested query to support to_param --- .../lib/action_dispatch/routing/route_set.rb | 45 +++++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index c2f6531a74..f029b634d6 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -379,7 +379,8 @@ module ActionDispatch end recall[:action] = options.delete(:action) if options[:action] == 'index' - parameterize = lambda { |name, value| + opts = {} + opts[:parameterize] = lambda { |name, value| if name == :controller value elsif value.is_a?(Array) @@ -389,7 +390,22 @@ module ActionDispatch end } - path = @set.url(named_route, options, recall, :parameterize => parameterize) + unless result = @set.generate(:path_info, named_route, options, recall, opts) + raise ActionController::RoutingError, "No route matches #{options.inspect}" + end + + uri, params = result + params.each do |k, v| + if v + params[k] = v + else + params.delete(k) + end + end + + uri << "?#{build_nested_query(params)}" if uri && params.any? + path = uri + if path && method == :generate_extras uri = URI(path) extras = uri.query ? @@ -456,6 +472,31 @@ module ActionDispatch def extract_request_environment(request) { :method => request.method } end + + private + def build_nested_query(value, prefix = nil) + case value + when Array + value.map { |v| + build_nested_query(v, "#{prefix}[]") + }.join("&") + when Hash + value.map { |k, v| + build_nested_query(v, prefix ? "#{prefix}[#{k}]" : k) + }.join("&") + when String + raise ArgumentError, "value must be a Hash" if prefix.nil? + "#{Rack::Utils.escape(prefix)}=#{Rack::Utils.escape(value)}" + when NilClass + Rack::Utils.escape(prefix) + else + if value.respond_to?(:to_param) + build_nested_query(value.to_param.to_s, prefix) + else + Rack::Utils.escape(prefix) + end + end + end end end end -- cgit v1.2.3 From c0949cc8f6c73111075e2c5b41f22f4b99a8ab26 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 2 Dec 2009 12:38:25 -0600 Subject: Rackmount 0.3.0 --- actionpack/actionpack.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 936279ad12..e191a12997 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |s| s.add_dependency('activemodel', '= 3.0.pre') s.add_dependency('rack', '~> 1.0.1') s.add_dependency('rack-test', '~> 0.5.0') - s.add_dependency('rack-mount', '~> 0.2.3') + s.add_dependency('rack-mount', '~> 0.3.0') s.add_dependency('erubis', '~> 2.6.5') s.require_path = 'lib' -- cgit v1.2.3 From 4dee277a9bc05083de6c831cf9aae0846849ecda Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 2 Dec 2009 12:46:14 -0600 Subject: Stop escaping "[]" in query string --- actionpack/lib/action_dispatch/routing/route_set.rb | 4 +--- actionpack/test/controller/routing_test.rb | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index f029b634d6..5e9c36bbaf 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -486,9 +486,7 @@ module ActionDispatch }.join("&") when String raise ArgumentError, "value must be a Hash" if prefix.nil? - "#{Rack::Utils.escape(prefix)}=#{Rack::Utils.escape(value)}" - when NilClass - Rack::Utils.escape(prefix) + "#{prefix}=#{Rack::Utils.escape(value)}" else if value.respond_to?(:to_param) build_nested_query(value.to_param.to_s, prefix) diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 84564f4e43..b83c5792ba 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1621,7 +1621,7 @@ class RouteSetTest < ActiveSupport::TestCase end def test_expand_array_build_query_string - assert_uri_equal '/foo?x%5B%5D=1&x%5B%5D=2', default_route_set.generate({:controller => 'foo', :x => [1, 2]}) + assert_uri_equal '/foo?x[]=1&x[]=2', default_route_set.generate({:controller => 'foo', :x => [1, 2]}) end def test_escape_spaces_build_query_string_selected_keys @@ -2012,9 +2012,9 @@ class RackMountIntegrationTests < ActiveSupport::TestCase assert_equal '/posts', @routes.generate({:controller => 'posts'}, {:controller => 'posts', :action => 'index'}) assert_equal '/posts/create', @routes.generate({:action => 'create'}, {:controller => 'posts'}) assert_equal '/posts?foo=bar', @routes.generate(:controller => 'posts', :foo => 'bar') - assert_equal '/posts?foo%5B%5D=bar&foo%5B%5D=baz', @routes.generate(:controller => 'posts', :foo => ['bar', 'baz']) + assert_equal '/posts?foo[]=bar&foo[]=baz', @routes.generate(:controller => 'posts', :foo => ['bar', 'baz']) assert_equal '/posts?page=2', @routes.generate(:controller => 'posts', :page => 2) - assert_equal '/posts?q%5Bfoo%5D%5Ba%5D=b', @routes.generate(:controller => 'posts', :q => { :foo => { :a => 'b'}}) + assert_equal '/posts?q[foo][a]=b', @routes.generate(:controller => 'posts', :q => { :foo => { :a => 'b'}}) assert_equal '/', @routes.generate(:controller => 'news', :action => 'index') assert_equal '/', @routes.generate(:controller => 'news', :action => 'index', :format => nil) -- cgit v1.2.3 From abfc4dad3ebb17eccd32d1f1eb8a3e9c0f6e6e4e Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Mon, 30 Nov 2009 15:58:38 -0800 Subject: Remove global Rails initializers --- railties/lib/rails.rb | 17 +++++++++++++- railties/lib/rails/application.rb | 4 ---- railties/lib/rails/initializable.rb | 22 ------------------ railties/lib/rails/initializer.rb | 2 -- railties/test/application/generators_test.rb | 26 ++++++++++++++-------- railties/test/application/initializer_test.rb | 16 ------------- .../test/initializer/check_ruby_version_test.rb | 7 ++---- 7 files changed, 35 insertions(+), 59 deletions(-) diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index c23b67e321..aeac85f4b3 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -10,4 +10,19 @@ require 'rails/core' require 'rails/configuration' require 'rails/deprecation' require 'rails/initializer' -require 'rails/plugin' \ No newline at end of file +require 'rails/plugin' +require 'rails/ruby_version_check' + +# For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the +# multibyte safe operations. Plugin authors supporting other encodings +# should override this behaviour and set the relevant +default_charset+ +# on ActionController::Base. +# +# For Ruby 1.9, UTF-8 is the default internal and external encoding. +if RUBY_VERSION < '1.9' + $KCODE='u' +else + Encoding.default_external = Encoding::UTF_8 +end + +RAILS_ENV = (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development").dup unless defined?(RAILS_ENV) \ No newline at end of file diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 7ea79aa9c9..41ae3f9687 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -82,10 +82,6 @@ module Rails @app.call(env) end - initializer :initialize_rails do - Rails.run_initializers - end - # Set the $LOAD_PATH based on the value of # Configuration#load_paths. Duplicates are removed. initializer :set_load_path do diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index 3866b856b2..96234739cf 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -106,26 +106,4 @@ module Rails end end end - - include Initializable - - # Check for valid Ruby version (1.8.2 or 1.8.4 or higher). This is done in an - # external file, so we can use it from the `rails` program as well without duplication. - initializer :check_ruby_version, :global => true do - require 'rails/ruby_version_check' - end - - # For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the - # multibyte safe operations. Plugin authors supporting other encodings - # should override this behaviour and set the relevant +default_charset+ - # on ActionController::Base. - # - # For Ruby 1.9, UTF-8 is the default internal and external encoding. - initializer :initialize_encoding, :global => true do - if RUBY_VERSION < '1.9' - $KCODE='u' - else - Encoding.default_external = Encoding::UTF_8 - end - end end \ No newline at end of file diff --git a/railties/lib/rails/initializer.rb b/railties/lib/rails/initializer.rb index 44101dcc94..95478428ec 100644 --- a/railties/lib/rails/initializer.rb +++ b/railties/lib/rails/initializer.rb @@ -1,7 +1,5 @@ require "rails" # In case people require this file directly -RAILS_ENV = (ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development').dup unless defined?(RAILS_ENV) - module Rails class Initializer class Error < StandardError ; end diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb index ccbcd84176..2ed49d1057 100644 --- a/railties/test/application/generators_test.rb +++ b/railties/test/application/generators_test.rb @@ -11,8 +11,16 @@ module ApplicationTests require "rails/generators" end + def app_const + @app_const ||= Class.new(Rails::Application) + end + + def with_config + yield app_const.config + end + test "generators default values" do - Rails::Initializer.run do |c| + with_config do |c| assert_equal(true, c.generators.colorize_logging) assert_equal({}, c.generators.aliases) assert_equal({}, c.generators.options) @@ -20,7 +28,7 @@ module ApplicationTests end test "generators set rails options" do - Rails::Initializer.run do |c| + with_config do |c| c.generators.orm = :datamapper c.generators.test_framework = :rspec c.generators.helper = false @@ -30,7 +38,7 @@ module ApplicationTests end test "generators set rails aliases" do - Rails::Initializer.run do |c| + with_config do |c| c.generators.aliases = { :rails => { :test_framework => "-w" } } expected = { :rails => { :test_framework => "-w" } } assert_equal expected, c.generators.aliases @@ -38,14 +46,14 @@ module ApplicationTests end test "generators aliases and options on initialization" do - Rails::Initializer.run do |c| + application = with_config do |c| c.frameworks = [] c.generators.rails :aliases => { :test_framework => "-w" } c.generators.orm :datamapper c.generators.test_framework :rspec end # Initialize the application - Rails.initialize! + app_const.initialize! Rails::Generators.configure! assert_equal :rspec, Rails::Generators.options[:rails][:test_framework] @@ -53,19 +61,19 @@ module ApplicationTests end test "generators no color on initialization" do - Rails::Initializer.run do |c| + with_config do |c| c.frameworks = [] c.generators.colorize_logging = false end # Initialize the application - Rails.initialize! + app_const.initialize! Rails::Generators.configure! assert_equal Thor::Base.shell, Thor::Shell::Basic end test "generators with hashes for options and aliases" do - Rails::Initializer.run do |c| + with_config do |c| c.generators do |g| g.orm :datamapper, :migration => false g.plugin :aliases => { :generator => "-g" }, @@ -84,7 +92,7 @@ module ApplicationTests end test "generators with hashes are deep merged" do - Rails::Initializer.run do |c| + with_config do |c| c.generators do |g| g.orm :datamapper, :migration => false g.plugin :aliases => { :generator => "-g" }, diff --git a/railties/test/application/initializer_test.rb b/railties/test/application/initializer_test.rb index 719520bf68..1b319e8a6d 100644 --- a/railties/test/application/initializer_test.rb +++ b/railties/test/application/initializer_test.rb @@ -10,22 +10,6 @@ module ApplicationTests require "rails" end - test "initializing an application initializes rails" do - Rails::Initializer.run do |config| - config.root = app_path - end - - if RUBY_VERSION < '1.9' - $KCODE = '' - Rails.initialize! - assert_equal 'UTF8', $KCODE - else - Encoding.default_external = Encoding::US_ASCII - Rails.initialize! - assert_equal Encoding::UTF_8, Encoding.default_external - end - end - test "initializing an application adds the application paths to the load path" do Rails::Initializer.run do |config| config.root = app_path diff --git a/railties/test/initializer/check_ruby_version_test.rb b/railties/test/initializer/check_ruby_version_test.rb index 97d884e1be..0691caad9d 100644 --- a/railties/test/initializer/check_ruby_version_test.rb +++ b/railties/test/initializer/check_ruby_version_test.rb @@ -7,7 +7,6 @@ module InitializerTests def setup build_app boot_rails - require "rails" end test "rails does not initialize with ruby version 1.8.1" do @@ -50,8 +49,7 @@ module InitializerTests def assert_rails_boots(version = nil) set_ruby_version(version) if version assert_nothing_raised "It appears that rails does not boot" do - Rails::Initializer.run { |c| c.frameworks = [] } - Rails.initialize! + require "rails" end end @@ -59,8 +57,7 @@ module InitializerTests set_ruby_version(version) $stderr = File.open("/dev/null", "w") assert_raises(SystemExit) do - Rails::Initializer.run { |c| c.frameworks = [] } - Rails.initialize! + require "rails" end end end -- cgit v1.2.3 From 8ff310aef4e7ed424429059d10e3af0f169323fb Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Mon, 30 Nov 2009 16:46:09 -0800 Subject: Break up rails and railties: * rails is now a metagem (gem install rails works as it does now) * railties includes the glue layer, so you can easily declare dependencies on railties, actionpack, and actionmailer and not get ActiveRecord's rake tasks, initializers, etc. --- .gitmodules | 3 +++ Rakefile | 15 +++++++++++++-- rack | 1 + rails.gemspec | 25 +++++++++++++++++++++++++ railties/Rakefile | 4 ++-- railties/rails.gemspec | 31 ------------------------------- railties/railties.gemspec | 28 ++++++++++++++++++++++++++++ 7 files changed, 72 insertions(+), 35 deletions(-) create mode 160000 rack create mode 100644 rails.gemspec delete mode 100644 railties/rails.gemspec create mode 100644 railties/railties.gemspec diff --git a/.gitmodules b/.gitmodules index fd4fd34d3e..d0be7ff194 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "arel"] path = arel url = git://github.com/rails/arel.git +[submodule "rack"] + path = rack + url = git://github.com/rails/rack.git diff --git a/Rakefile b/Rakefile index 6aa8f61028..cda01d7d57 100644 --- a/Rakefile +++ b/Rakefile @@ -1,9 +1,10 @@ require 'rake' require 'rake/rdoctask' +require 'rake/gempackagetask' env = %(PKG_BUILD="#{ENV['PKG_BUILD']}") if ENV['PKG_BUILD'] -PROJECTS = %w(activesupport actionpack actionmailer activeresource activerecord activemodel railties) +PROJECTS = %w(activesupport activemodel actionpack actionmailer activeresource activerecord railties) Dir["#{File.dirname(__FILE__)}/*/lib/*/version.rb"].each do |version_path| require version_path @@ -23,11 +24,21 @@ task :default => %w(test test:isolated) end end +spec = eval(File.read('rails.gemspec')) + +Rake::GemPackageTask.new(spec) do |pkg| + pkg.gem_spec = spec +end + task :install => :gem do + system %(cd arel && gem build arel.gemspec && gem install arel-0.2.pre.gem --no-ri --no-rdoc --ignore-dependencies) + system %(cd rack && rake gem VERSION=1.0.2.pre && gem install rack-1.0.2.pre.gem --no-ri --no-rdoc --ignore-dependencies) (PROJECTS - ["railties"]).each do |project| + puts "INSTALLING #{project}" system("gem install #{project}/pkg/#{project}-#{ActionPack::VERSION::STRING}.gem --no-ri --no-rdoc") end - system("gem install railties/pkg/rails-#{ActionPack::VERSION::STRING}.gem --no-ri --no-rdoc") + system("gem install railties/pkg/railties-#{ActionPack::VERSION::STRING}.gem --no-ri --no-rdoc") + system("gem install pkg/rails-#{ActionPack::VERSION::STRING}.gem --no-ri --no-rdoc") end desc "Generate documentation for the Rails framework" diff --git a/rack b/rack new file mode 160000 index 0000000000..adf996587a --- /dev/null +++ b/rack @@ -0,0 +1 @@ +Subproject commit adf996587aecdd604eff441b8b69e4c47a8c2617 diff --git a/rails.gemspec b/rails.gemspec new file mode 100644 index 0000000000..878a342903 --- /dev/null +++ b/rails.gemspec @@ -0,0 +1,25 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'rails' + s.version = '3.0.pre' + s.summary = "Web-application framework with template engine, control-flow layer, and ORM." + s.description = <<-EOF + Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick + on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates. + EOF + + s.add_dependency('activesupport', '= 3.0.pre') + s.add_dependency('actionpack', '= 3.0.pre') + s.add_dependency('activerecord', '= 3.0.pre') + s.add_dependency('activeresource', '= 3.0.pre') + s.add_dependency('actionmailer', '= 3.0.pre') + s.add_dependency('railties', '= 3.0.pre') + + s.rdoc_options << '--exclude' << '.' + s.has_rdoc = false + + s.author = "David Heinemeier Hansson" + s.email = "david@loudthinking.com" + s.homepage = "http://www.rubyonrails.org" + s.rubyforge_project = "rails" +end diff --git a/railties/Rakefile b/railties/Rakefile index e6f698fc74..cb482c90bf 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -10,7 +10,7 @@ $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/lib" require 'rails/version' PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : '' -PKG_NAME = ENV['PKG_NAME'] || 'rails' +PKG_NAME = ENV['PKG_NAME'] || 'railties' PKG_VERSION = Rails::VERSION::STRING + PKG_BUILD PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}" PKG_DESTINATION = ENV["RAILS_PKG_DESTINATION"] || "../#{PKG_NAME}" @@ -141,7 +141,7 @@ Rake::RDocTask.new { |rdoc| # Generate GEM ---------------------------------------------------------------------------- -spec = eval(File.read('rails.gemspec')) +spec = eval(File.read('railties.gemspec')) Rake::GemPackageTask.new(spec) do |pkg| pkg.gem_spec = spec diff --git a/railties/rails.gemspec b/railties/rails.gemspec deleted file mode 100644 index dc66e1efea..0000000000 --- a/railties/rails.gemspec +++ /dev/null @@ -1,31 +0,0 @@ -Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = 'rails' - s.version = '3.0.pre' - s.summary = "Web-application framework with template engine, control-flow layer, and ORM." - s.description = <<-EOF - Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick - on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates. - EOF - - s.add_dependency('rake', '>= 0.8.3') - s.add_dependency('activesupport', '= 3.0.pre') - s.add_dependency('activerecord', '= 3.0.pre') - s.add_dependency('actionpack', '= 3.0.pre') - s.add_dependency('actionmailer', '= 3.0.pre') - s.add_dependency('activeresource', '= 3.0.pre') - - s.rdoc_options << '--exclude' << '.' - s.has_rdoc = false - - s.files = Dir['CHANGELOG', 'README', 'bin/**/*', 'builtin/**/*', 'guides/**/*', 'lib/**/{*,.[a-z]*}'] - s.require_path = 'lib' - s.bindir = "bin" - s.executables = ["rails"] - s.default_executable = "rails" - - s.author = "David Heinemeier Hansson" - s.email = "david@loudthinking.com" - s.homepage = "http://www.rubyonrails.org" - s.rubyforge_project = "rails" -end diff --git a/railties/railties.gemspec b/railties/railties.gemspec new file mode 100644 index 0000000000..a060c3c301 --- /dev/null +++ b/railties/railties.gemspec @@ -0,0 +1,28 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'railties' + s.version = '3.0.pre' + s.summary = "Controls boot-up, rake tasks and generators for the Rails framework." + s.description = <<-EOF + Rails is a framework for building web-application using CGI, FCGI, mod_ruby, or WEBrick + on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates. + EOF + + s.add_dependency('rake', '>= 0.8.3') + s.add_dependency('activesupport', '= 3.0.pre') + s.add_dependency('actionpack', '= 3.0.pre') + + s.rdoc_options << '--exclude' << '.' + s.has_rdoc = false + + s.files = Dir['CHANGELOG', 'README', 'bin/**/*', 'builtin/**/*', 'guides/**/*', 'lib/**/{*,.[a-z]*}'] + s.require_path = 'lib' + s.bindir = "bin" + s.executables = ["rails"] + s.default_executable = "rails" + + s.author = "David Heinemeier Hansson" + s.email = "david@loudthinking.com" + s.homepage = "http://www.rubyonrails.org" + s.rubyforge_project = "rails" +end -- cgit v1.2.3 From 39034997d1bd1fbaf33ddf1d6e3996b3c298a409 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 2 Dec 2009 10:14:02 -0800 Subject: Add support for bare ActiveSupport via config.active_support.bare --- railties/lib/rails.rb | 4 ++++ railties/lib/rails/application.rb | 17 ++--------------- railties/test/application/configuration_test.rb | 9 +++++++++ railties/test/application/initializer_test.rb | 4 +++- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index aeac85f4b3..b7cae9a9ac 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -1,5 +1,9 @@ require "pathname" +require 'active_support' +require 'active_support/core_ext/kernel/reporting' +require 'active_support/core_ext/logger' + require 'rails/initializable' require 'rails/application' require 'rails/railties_path' diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 41ae3f9687..be71469752 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -93,18 +93,8 @@ module Rails # list. By default, all frameworks (Active Record, Active Support, # Action Pack, Action Mailer, and Active Resource) are loaded. initializer :require_frameworks do - begin - require 'active_support' - require 'active_support/core_ext/kernel/reporting' - require 'active_support/core_ext/logger' - - # TODO: This is here to make Sam Ruby's tests pass. Needs discussion. - require 'active_support/core_ext/numeric/bytes' - config.frameworks.each { |framework| require(framework.to_s) } - rescue LoadError => e - # Re-raise as RuntimeError because Mongrel would swallow LoadError. - raise e.to_s - end + require 'active_support/all' unless config.active_support.bare + config.frameworks.each { |framework| require(framework.to_s) } end # Set the paths from which Rails will automatically load source files, and @@ -308,9 +298,6 @@ module Rails base_class.send("#{setting}=", value) end end - config.active_support.each do |setting, value| - ActiveSupport.send("#{setting}=", value) - end end # Sets +ActionController::Base#view_paths+ and +ActionMailer::Base#template_root+ diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index a3e1916494..fc21d4e5fd 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -44,5 +44,14 @@ module ApplicationTests assert_equal Pathname.new("#{app_path}/app"), Rails.application.root end end + + test "config.active_support.bare does not require all of ActiveSupport" do + add_to_config "config.frameworks = []; config.active_support.bare = true" + + Dir.chdir("#{app_path}/app") do + require "#{app_path}/config/environment" + assert_raises(NoMethodError, /day/) { 1.day } + end + end end end \ No newline at end of file diff --git a/railties/test/application/initializer_test.rb b/railties/test/application/initializer_test.rb index 1b319e8a6d..56582c5772 100644 --- a/railties/test/application/initializer_test.rb +++ b/railties/test/application/initializer_test.rb @@ -25,7 +25,9 @@ module ApplicationTests config.frameworks << :action_foo end - assert_raises RuntimeError do + require "active_support/core_ext/load_error" + + assert_raises MissingSourceFile do Rails.initialize! end end -- cgit v1.2.3 From fe41c7030b0a196600378418df4c59588ca1b4f8 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 2 Dec 2009 11:27:02 -0800 Subject: Stop evalling the environment file in favor of require + setting a Kernel#config. This will fix the bug where reopening classes caused them to be overwritten. --- railties/lib/rails/application.rb | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index be71469752..13232783d1 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -82,6 +82,23 @@ module Rails @app.call(env) end + + # Loads the environment specified by Configuration#environment_path, which + # is typically one of development, test, or production. + initializer :load_environment do + next unless File.file?(config.environment_path) + + config = self.config + + Kernel.class_eval do + meth = instance_method(:config) if Object.respond_to?(:config) + define_method(:config) { config } + require config.environment_path + remove_method :config + define_method(:config, &meth) if meth + end + end + # Set the $LOAD_PATH based on the value of # Configuration#load_paths. Duplicates are removed. initializer :set_load_path do @@ -123,24 +140,6 @@ module Rails end end - # Loads the environment specified by Configuration#environment_path, which - # is typically one of development, test, or production. - initializer :load_environment do - silence_warnings do - next if @environment_loaded - next unless File.file?(config.environment_path) - - @environment_loaded = true - constants = self.class.constants - - eval(IO.read(config.environment_path), binding, config.environment_path) - - (self.class.constants - constants).each do |const| - Object.const_set(const, self.class.const_get(const)) - end - end - 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. -- cgit v1.2.3 From 6680f9c4d77537807c3e43c7de85e94f75472d5c Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 2 Dec 2009 11:27:42 -0800 Subject: Fix an ivar name conflict in Rails::Server --- railties/lib/rails/commands/server.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index 57b7c6a49c..3687b4460e 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -41,9 +41,9 @@ module Rails new(app).start end - def initialize(app) + def initialize(app_const) super() # Call Rack::Server#initialize without passing any options to use. - @app = app + @app_const = app_const end def start @@ -69,7 +69,7 @@ module Rails end def log_path - "#{File.expand_path(@app.root)}/log/#{options[:environment]}.log" + "#{File.expand_path(@app_const.root)}/log/#{options[:environment]}.log" end def default_options @@ -77,10 +77,10 @@ module Rails :Port => 3000, :Host => "0.0.0.0", :environment => (ENV['RAILS_ENV'] || "development").dup, - :rack_file => "#{@app.root}/config.ru", + :rack_file => "#{@app_const.root}/config.ru", :daemonize => false, :debugger => false, - :pid => "#{@app.root}/tmp/pids/server.pid", + :pid => "#{@app_const.root}/tmp/pids/server.pid", :AccessLog => [] } end -- cgit v1.2.3 From c446b1b12b6b54e61136410c6dfe69de3678eae9 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 2 Dec 2009 11:27:56 -0800 Subject: Ignore all pkg files --- .gitignore | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 70b7c0057a..9a65e4996f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,16 +7,9 @@ activerecord/doc actionpack/doc actionmailer/doc activesupport/doc -activemodel/pkg -activeresource/pkg -activerecord/pkg -actionpack/pkg activemodel/test/fixtures/fixture_database.sqlite3 -actionmailer/pkg -activesupport/pkg actionpack/test/tmp activesupport/test/fixtures/isolation_test -railties/pkg railties/test/500.html railties/test/fixtures/tmp railties/test/initializer/root/log @@ -31,3 +24,4 @@ railties/guides/output bin vendor/gems/ railties/tmp +pkg -- cgit v1.2.3 From 8db038227ca4cbcba01a86ef5fb94cb13c780463 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 2 Dec 2009 14:10:22 -0600 Subject: Move controller namespace tracking into route set so it gets reloaded in dev mode --- actionpack/lib/action_dispatch/routing.rb | 41 ---------------------- .../action_dispatch/routing/deprecated_mapper.rb | 2 +- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- .../lib/action_dispatch/routing/route_set.rb | 36 +++++++++++++++++-- railties/lib/rails/application.rb | 2 +- 5 files changed, 37 insertions(+), 46 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 9159bb6395..e99f979197 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -265,51 +265,10 @@ module ActionDispatch SEPARATORS = %w( / . ? ) HTTP_METHODS = [:get, :head, :post, :put, :delete, :options] - CONTROLLER_REGEXP = /[_a-zA-Z0-9]+/ - - # The root paths which may contain controller files - mattr_accessor :controller_paths - self.controller_paths = [] # A helper module to hold URL related helpers. module Helpers include ActionController::PolymorphicRoutes end - - class << self - def controller_constraints - @controller_constraints ||= begin - source = controller_namespaces.map { |ns| "#{Regexp.escape(ns)}/#{CONTROLLER_REGEXP.source}" } - source << CONTROLLER_REGEXP.source - Regexp.compile(source.sort.reverse.join('|')) - end - end - - def clear_controller_cache! - @controller_constraints = nil - end - - private - def controller_namespaces - namespaces = Set.new - - # Find any nested controllers already in memory - ActionController::Base.subclasses.each do |klass| - controller_name = klass.underscore - namespaces << controller_name.split('/')[0...-1].join('/') - end - - # Find namespaces in controllers/ directory - controller_paths.each do |load_path| - load_path = File.expand_path(load_path) - Dir["#{load_path}/**/*_controller.rb"].collect do |path| - namespaces << File.dirname(path).sub(/#{load_path}\/?/, '') - end - end - - namespaces.delete('') - namespaces - end - end end end diff --git a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb index 87dfaba6c7..8ce6b2f6d5 100644 --- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb +++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb @@ -113,7 +113,7 @@ module ActionDispatch end end - requirements[:controller] ||= Routing.controller_constraints + requirements[:controller] ||= @set.controller_constraints if defaults[:controller] defaults[:action] ||= 'index' diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 34d75e55b6..400039353c 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -383,7 +383,7 @@ module ActionDispatch constraints.reject! { |k, v| segment_keys.include?(k.to_s) } conditions.merge!(constraints) - requirements[:controller] ||= Routing.controller_constraints + requirements[:controller] ||= @set.controller_constraints if via = options[:via] via = Array(via).map { |m| m.to_s.upcase } diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 5e9c36bbaf..201cf462e4 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -202,10 +202,11 @@ module ActionDispatch end end - attr_accessor :routes, :named_routes, :configuration_files + attr_accessor :routes, :named_routes, :configuration_files, :controller_paths def initialize self.configuration_files = [] + self.controller_paths = [] self.routes = [] self.named_routes = NamedRouteCollection.new @@ -252,7 +253,7 @@ module ActionDispatch def load! # Clear the controller cache so we may discover new ones - Routing.clear_controller_cache! + @controller_constraints = nil load_routes! end @@ -297,6 +298,37 @@ module ActionDispatch routes_changed_at end + CONTROLLER_REGEXP = /[_a-zA-Z0-9]+/ + + def controller_constraints + @controller_constraints ||= begin + source = controller_namespaces.map { |ns| "#{Regexp.escape(ns)}/#{CONTROLLER_REGEXP.source}" } + source << CONTROLLER_REGEXP.source + Regexp.compile(source.sort.reverse.join('|')) + end + end + + def controller_namespaces + namespaces = Set.new + + # Find any nested controllers already in memory + ActionController::Base.subclasses.each do |klass| + controller_name = klass.underscore + namespaces << controller_name.split('/')[0...-1].join('/') + end + + # Find namespaces in controllers/ directory + controller_paths.each do |load_path| + load_path = File.expand_path(load_path) + Dir["#{load_path}/**/*_controller.rb"].collect do |path| + namespaces << File.dirname(path).sub(/#{load_path}\/?/, '') + end + end + + namespaces.delete('') + namespaces + end + def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil) route = Route.new(app, conditions, requirements, defaults, name) @set.add_route(*route) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 7ea79aa9c9..73198d68ca 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -387,7 +387,7 @@ module Rails initializer :initialize_routing do next unless configuration.frameworks.include?(:action_controller) - ActionController::Routing.controller_paths += configuration.controller_paths + ActionController::Routing::Routes.controller_paths += configuration.controller_paths ActionController::Routing::Routes.add_configuration_file(configuration.routes_configuration_file) ActionController::Routing::Routes.reload! end -- cgit v1.2.3 From 50c28e78c7aa40dc329facbe6131d657d5629bd4 Mon Sep 17 00:00:00 2001 From: Mike Breen Date: Wed, 20 May 2009 10:31:12 -0400 Subject: Implement ActiveRecord#reset_counter_cache [#1211 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/CHANGELOG | 2 ++ activerecord/lib/active_record/base.rb | 18 ++++++++++++++++++ activerecord/test/cases/base_test.rb | 10 ++++++++++ 3 files changed, 30 insertions(+) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index d8bfb1916d..d67b61dc07 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Reset your Active Record counter caches with the reset_counter_cache class method. #1211 [Mike Breen] + * Remove support for SQLite 2. Please upgrade to SQLite 3+ or install the plugin from git://github.com/rails/sqlite2_adapter.git [Pratik Naik] * PostgreSQL: XML datatype support. #1874 [Leonardo Borges] diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 056f29f029..e04684dc43 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -967,6 +967,24 @@ module ActiveRecord #:nodoc: connection.select_value(sql, "#{name} Count").to_i end + # Reset a counter cache for all records. + # + # ==== Parameters + # + # * +association_name+ - The name of of the association counter cache to reset + # + # ==== Examples + # # For all Post records reset the comments_count + # Post.reset_counter_cache(:comments) + def reset_counter_cache(association) + child_class = reflect_on_association(association).klass + counter_name = child_class.reflect_on_association(self.name.downcase.to_sym).counter_cache_column + + find_each do |object| + connection.update("UPDATE #{quoted_table_name} SET #{connection.quote_column_name(counter_name)} = #{object.send(association).count} WHERE #{connection.quote_column_name(primary_key)} = #{quote_value(object.id)}", "#{name} UPDATE") + end + end + # A generic "counter updater" implementation, intended primarily to be # used by increment_counter and decrement_counter, but which may also # be useful on its own. It simply does a direct SQL update for the record diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 5c2911eca1..737ca01d46 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -680,6 +680,16 @@ class BasicsTest < ActiveRecord::TestCase assert_equal -2, Topic.find(2).replies_count end + def test_reset_counter_cache + assert_equal 1, Topic.find(1).replies_count + + Topic.increment_counter("replies_count", 1) + assert_equal 2, Topic.find(1).replies_count + + Topic.reset_counter_cache(:replies) + assert_equal 1, Topic.find(1).replies_count + end + def test_update_counter category = categories(:general) assert_nil category.categorizations_count -- cgit v1.2.3 From 49e943c4f0ac3459bd53023167aaa08fc8e46733 Mon Sep 17 00:00:00 2001 From: Mat Brown Date: Thu, 22 Oct 2009 10:20:44 -0400 Subject: Fix instance_eval calls to association proxies In the current stable, ActiveRecord::Associations::AssociationProxy#method_missing calls yield() if a block is given, causing the block to always be evaluated in its calling context. However, in the case of instance_eval, correct behavior requires that the block be passed directly to the @target, rather than being evaluated inside a different block. Incidentally, this also simplifies the code slightly. [#3412 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/lib/active_record/associations/association_proxy.rb | 8 ++------ .../test/cases/associations/has_many_associations_test.rb | 8 ++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 7d8f4670fa..6ad1e06300 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -200,18 +200,14 @@ module ActiveRecord private # Forwards any missing method call to the \target. - def method_missing(method, *args) + def method_missing(method, *args, &block) if load_target unless @target.respond_to?(method) message = "undefined method `#{method.to_s}' for \"#{@target}\":#{@target.class.to_s}" raise NoMethodError, message end - if block_given? - @target.send(method, *args) { |*block_args| yield(*block_args) } - else - @target.send(method, *args) - end + @target.send(method, *args, &block) end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 86d14c9c81..3c490c1eeb 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1178,5 +1178,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase client = firm.clients_using_primary_key.create!(:name => 'test') assert_equal firm.name, client.firm_name end + + def test_normal_method_call_in_association_proxy + assert_equal 'Welcome to the weblog', Comment.all.map { |comment| comment.post }.first.title + end + + def test_instance_eval_in_association_proxy + assert_equal 'Welcome to the weblog', Comment.all.map { |comment| comment.post }.first.instance_eval{title} + end end -- cgit v1.2.3 From 1ee9b40b18a0bed5bb10a0785f7e2730bac983f6 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 2 Dec 2009 15:07:01 -0600 Subject: Failing tests for to_param/to_query not escaping "[]" --- activesupport/lib/active_support/core_ext/object/to_param.rb | 2 +- activesupport/lib/active_support/core_ext/object/to_query.rb | 2 +- activesupport/test/core_ext/object/to_query_test.rb | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb index a5e2260791..7ca763cbad 100644 --- a/activesupport/lib/active_support/core_ext/object/to_param.rb +++ b/activesupport/lib/active_support/core_ext/object/to_param.rb @@ -38,7 +38,7 @@ class Hash # ==== Examples # { :name => 'David', :nationality => 'Danish' }.to_query # => "name=David&nationality=Danish" # - # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish" + # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user[name]=David&user[nationality]=Danish" def to_param(namespace = nil) collect do |key, value| value.to_query(namespace ? "#{namespace}[#{key}]" : key) diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb index 3f1540f685..1c6a02c9ad 100644 --- a/activesupport/lib/active_support/core_ext/object/to_query.rb +++ b/activesupport/lib/active_support/core_ext/object/to_query.rb @@ -15,7 +15,7 @@ class Array # Converts an array into a string suitable for use as a URL query string, # using the given +key+ as the param name. # - # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding" + # ['Rails', 'coding'].to_query('hobbies') # => "hobbies[]=Rails&hobbies[]=coding" def to_query(key) prefix = "#{key}[]" collect { |value| value.to_query(prefix) }.join '&' diff --git a/activesupport/test/core_ext/object/to_query_test.rb b/activesupport/test/core_ext/object/to_query_test.rb index 0fb15be654..4d655913cc 100644 --- a/activesupport/test/core_ext/object/to_query_test.rb +++ b/activesupport/test/core_ext/object/to_query_test.rb @@ -17,22 +17,22 @@ class ToQueryTest < Test::Unit::TestCase end def test_nested_conversion - assert_query_equal 'person%5Blogin%5D=seckar&person%5Bname%5D=Nicholas', + assert_query_equal 'person[login]=seckar&person[name]=Nicholas', :person => {:name => 'Nicholas', :login => 'seckar'} end def test_multiple_nested - assert_query_equal 'account%5Bperson%5D%5Bid%5D=20&person%5Bid%5D=10', + assert_query_equal 'account[person][id]=20&person[id]=10', :person => {:id => 10}, :account => {:person => {:id => 20}} end def test_array_values - assert_query_equal 'person%5Bid%5D%5B%5D=10&person%5Bid%5D%5B%5D=20', + assert_query_equal 'person[id][]=10&person[id][]=20', :person => {:id => [10, 20]} end def test_array_values_are_not_sorted - assert_query_equal 'person%5Bid%5D%5B%5D=20&person%5Bid%5D%5B%5D=10', + assert_query_equal 'person[id][]=20&person[id][]=10', :person => {:id => [20, 10]} end -- cgit v1.2.3 From 856d2fd874d72dd9f83204affff4edfef3308361 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 2 Dec 2009 15:17:08 -0600 Subject: Quick fix for not escaping []s (not ideal) --- activesupport/lib/active_support/core_ext/object/to_query.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb index 1c6a02c9ad..c9981895b4 100644 --- a/activesupport/lib/active_support/core_ext/object/to_query.rb +++ b/activesupport/lib/active_support/core_ext/object/to_query.rb @@ -7,7 +7,7 @@ class Object # Note: This method is defined as a default implementation for all Objects for Hash#to_query to work. def to_query(key) require 'cgi' unless defined?(CGI) && defined?(CGI::escape) - "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}" + "#{CGI.escape(key.to_s).gsub(/%(5B|5D)/n) { [$1].pack('H*') }}=#{CGI.escape(to_param.to_s)}" end end -- cgit v1.2.3 From 399909b11c094ab32542d300c72940b1b263b8e6 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 2 Dec 2009 15:23:26 -0600 Subject: Use to_query in route query string generation --- .../lib/action_dispatch/routing/route_set.rb | 25 +--------------------- 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 201cf462e4..a8073c2105 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -435,7 +435,7 @@ module ActionDispatch end end - uri << "?#{build_nested_query(params)}" if uri && params.any? + uri << "?#{params.to_query}" if uri && params.any? path = uri if path && method == :generate_extras @@ -504,29 +504,6 @@ module ActionDispatch def extract_request_environment(request) { :method => request.method } end - - private - def build_nested_query(value, prefix = nil) - case value - when Array - value.map { |v| - build_nested_query(v, "#{prefix}[]") - }.join("&") - when Hash - value.map { |k, v| - build_nested_query(v, prefix ? "#{prefix}[#{k}]" : k) - }.join("&") - when String - raise ArgumentError, "value must be a Hash" if prefix.nil? - "#{prefix}=#{Rack::Utils.escape(value)}" - else - if value.respond_to?(:to_param) - build_nested_query(value.to_param.to_s, prefix) - else - Rack::Utils.escape(prefix) - end - end - end end end end -- cgit v1.2.3 From c1304098cca8a9247a9ad1461a1a343354650843 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 2 Dec 2009 20:01:01 -0800 Subject: Reorganize autoloads: * A new module (ActiveSupport::Autoload) is provide that extends autoloading with new behavior. * All autoloads in modules that have extended ActiveSupport::Autoload will be eagerly required in threadsafe environments * Autoloads can optionally leave off the path if the path is the same as full_constant_name.underscore * It is possible to specify that a group of autoloads live under an additional path. For instance, all of ActionDispatch's middlewares are ActionDispatch::MiddlewareName, but they live under "action_dispatch/middlewares/middleware_name" * It is possible to specify that a group of autoloads are all found at the same path. For instance, a number of exceptions might all be declared there. * One consequence of this is that testing-related constants are not autoloaded. To get the testing helpers for a given component, require "component_name/test_case". For instance, "action_controller/test_case". * test_help.rb, which is automatically required by a Rails application's test helper, requires the test_case.rb for all active components, so this change will not be disruptive in existing or new applications. --- actionmailer/lib/action_mailer.rb | 34 +- actionmailer/lib/action_mailer/base.rb | 2 +- .../lib/action_mailer/delivery_method/file.rb | 2 +- actionmailer/lib/action_mailer/mail_helper.rb | 30 +- actionpack/lib/abstract_controller.rb | 25 +- .../abstract_controller/rendering_controller.rb | 2 +- actionpack/lib/action_controller.rb | 116 ++-- actionpack/lib/action_controller/base.rb | 2 +- actionpack/lib/action_controller/caching.rb | 7 +- .../deprecated/integration_test.rb | 2 + .../lib/action_controller/metal/compatibility.rb | 2 +- actionpack/lib/action_controller/metal/helpers.rb | 2 +- .../lib/action_controller/metal/rescuable.rb | 13 - actionpack/lib/action_controller/metal/rescue.rb | 13 + actionpack/lib/action_controller/test_case.rb | 348 +++++++++++ .../lib/action_controller/testing/test_case.rb | 345 ----------- .../lib/action_controller/vendor/html-scanner.rb | 2 + actionpack/lib/action_dispatch.rb | 41 +- .../action_dispatch/middleware/show_exceptions.rb | 4 +- actionpack/lib/action_dispatch/test_case.rb | 6 + actionpack/lib/action_view.rb | 26 +- actionpack/lib/action_view/base.rb | 2 +- actionpack/lib/action_view/paths.rb | 2 +- actionpack/lib/action_view/template.rb | 138 +++++ actionpack/lib/action_view/template/error.rb | 152 ++--- actionpack/lib/action_view/template/handler.rb | 47 +- actionpack/lib/action_view/template/handlers.rb | 96 +-- .../lib/action_view/template/handlers/builder.rb | 4 +- .../lib/action_view/template/handlers/erb.rb | 4 +- .../lib/action_view/template/handlers/rjs.rb | 4 +- actionpack/lib/action_view/template/resolver.rb | 6 +- actionpack/lib/action_view/template/template.rb | 131 ---- actionpack/lib/action_view/template/text.rb | 70 +-- actionpack/lib/action_view/test_case.rb | 2 +- actionpack/test/abstract/layouts_test.rb | 26 +- actionpack/test/abstract/render_test.rb | 2 +- actionpack/test/controller/helper_test.rb | 2 +- actionpack/test/controller/layout_test.rb | 2 +- actionpack/test/template/render_test.rb | 8 +- activemodel/lib/active_model.rb | 43 +- activemodel/test/cases/helper.rb | 1 + activemodel/test/cases/tests_database.rb | 1 + activerecord/lib/active_record.rb | 100 +-- activeresource/lib/active_resource.rb | 16 +- activeresource/test/abstract_unit.rb | 1 + activeresource/test/cases/base_test.rb | 6 +- activesupport/lib/active_support/autoload.rb | 55 +- .../lib/active_support/dependencies/autoload.rb | 37 ++ .../lib/active_support/testing/performance.rb | 678 +++++++++++---------- activesupport/test/autoload.rb | 80 +++ .../test/fixtures/autoload/another_class.rb | 2 + activesupport/test/fixtures/autoload/some_class.rb | 2 + railties/lib/rails/console_app.rb | 1 + railties/lib/rails/test_help.rb | 6 + railties/test/rails_info_controller_test.rb | 1 + 55 files changed, 1474 insertions(+), 1278 deletions(-) delete mode 100644 actionpack/lib/action_controller/metal/rescuable.rb create mode 100644 actionpack/lib/action_controller/metal/rescue.rb create mode 100644 actionpack/lib/action_controller/test_case.rb delete mode 100644 actionpack/lib/action_controller/testing/test_case.rb create mode 100644 actionpack/lib/action_dispatch/test_case.rb create mode 100644 actionpack/lib/action_view/template.rb delete mode 100644 actionpack/lib/action_view/template/template.rb create mode 100644 activesupport/lib/active_support/dependencies/autoload.rb create mode 100644 activesupport/test/autoload.rb create mode 100644 activesupport/test/fixtures/autoload/another_class.rb create mode 100644 activesupport/test/fixtures/autoload/some_class.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 23f04a11ba..12e781b6c3 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -25,32 +25,34 @@ actionpack_path = "#{File.dirname(__FILE__)}/../../actionpack/lib" $:.unshift(actionpack_path) if File.directory?(actionpack_path) require 'action_controller' require 'action_view' +require 'active_support/autoload' module ActionMailer - def self.load_all! - [Base, Part, ::Text::Format, ::Net::SMTP] - end - - autoload :AdvAttrAccessor, 'action_mailer/adv_attr_accessor' - autoload :DeprecatedBody, 'action_mailer/deprecated_body' - autoload :Base, 'action_mailer/base' - autoload :DeliveryMethod, 'action_mailer/delivery_method' - autoload :Part, 'action_mailer/part' - autoload :PartContainer, 'action_mailer/part_container' - autoload :Quoting, 'action_mailer/quoting' - autoload :TestCase, 'action_mailer/test_case' - autoload :TestHelper, 'action_mailer/test_helper' - autoload :Utils, 'action_mailer/utils' + extend ::ActiveSupport::Autoload + + autoload :AdvAttrAccessor + autoload :DeprecatedBody + autoload :Base + autoload :DeliveryMethod + autoload :MailHelper + autoload :Part + autoload :PartContainer + autoload :Quoting + autoload :TestHelper + autoload :Utils end module Text + extend ActiveSupport::Autoload + autoload :Format, 'action_mailer/vendor/text_format' end module Net - autoload :SMTP, 'net/smtp' + extend ActiveSupport::Autoload + + autoload :SMTP end -autoload :MailHelper, 'action_mailer/mail_helper' require 'action_mailer/vendor/tmail' diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 7b952c462f..b5239ac0cd 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -258,7 +258,7 @@ module ActionMailer #:nodoc: include AbstractController::Layouts include AbstractController::Helpers - helper MailHelper + helper ActionMailer::MailHelper if Object.const_defined?(:ActionController) include ActionController::UrlWriter diff --git a/actionmailer/lib/action_mailer/delivery_method/file.rb b/actionmailer/lib/action_mailer/delivery_method/file.rb index 587ae37ffa..6c8cdf4450 100644 --- a/actionmailer/lib/action_mailer/delivery_method/file.rb +++ b/actionmailer/lib/action_mailer/delivery_method/file.rb @@ -6,7 +6,7 @@ module ActionMailer # A delivery method implementation which writes all mails to a file. class File < Method self.settings = { - :location => defined?(Rails) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" + :location => defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" } def perform_delivery(mail) diff --git a/actionmailer/lib/action_mailer/mail_helper.rb b/actionmailer/lib/action_mailer/mail_helper.rb index 351b966abe..9aa178cdef 100644 --- a/actionmailer/lib/action_mailer/mail_helper.rb +++ b/actionmailer/lib/action_mailer/mail_helper.rb @@ -1,17 +1,19 @@ -module MailHelper - # Uses Text::Format to take the text and format it, indented two spaces for - # each line, and wrapped at 72 columns. - def block_format(text) - formatted = text.split(/\n\r\n/).collect { |paragraph| - Text::Format.new( - :columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph - ).format - }.join("\n") +module ActionMailer + module MailHelper + # Uses Text::Format to take the text and format it, indented two spaces for + # each line, and wrapped at 72 columns. + def block_format(text) + formatted = text.split(/\n\r\n/).collect { |paragraph| + Text::Format.new( + :columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph + ).format + }.join("\n") - # Make list points stand on their own line - formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" } - formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" } + # Make list points stand on their own line + formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { |s| " #{$1} #{$2.strip}\n" } + formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { |s| " #{$1} #{$2.strip}\n" } - formatted + formatted + end end -end +end \ No newline at end of file diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 1a6c4278c9..688a2fe31c 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -2,15 +2,20 @@ require "active_support/core_ext/module/attr_internal" require "active_support/core_ext/module/delegation" module AbstractController - autoload :Base, "abstract_controller/base" - autoload :Callbacks, "abstract_controller/callbacks" - autoload :Helpers, "abstract_controller/helpers" - autoload :Layouts, "abstract_controller/layouts" - autoload :LocalizedCache, "abstract_controller/localized_cache" - autoload :Logger, "abstract_controller/logger" - autoload :RenderingController, "abstract_controller/rendering_controller" + extend ActiveSupport::Autoload + + autoload :Base + autoload :Callbacks + autoload :Helpers + autoload :Layouts + autoload :LocalizedCache + autoload :Logger + autoload :RenderingController + # === Exceptions - autoload :ActionNotFound, "abstract_controller/exceptions" - autoload :DoubleRenderError, "abstract_controller/exceptions" - autoload :Error, "abstract_controller/exceptions" + autoload_at "abstract_controller/exceptions" do + autoload :ActionNotFound + autoload :DoubleRenderError + autoload :Error + end end diff --git a/actionpack/lib/abstract_controller/rendering_controller.rb b/actionpack/lib/abstract_controller/rendering_controller.rb index 7054b9cf26..777e515d60 100644 --- a/actionpack/lib/abstract_controller/rendering_controller.rb +++ b/actionpack/lib/abstract_controller/rendering_controller.rb @@ -115,7 +115,7 @@ module AbstractController # _partial:: Whether or not the file to look up is a partial def _determine_template(options) if options.key?(:text) - options[:_template] = ActionView::TextTemplate.new(options[:text], format_for_text) + options[:_template] = ActionView::Template::Text.new(options[:text], format_for_text) elsif options.key?(:inline) handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb") template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {}) diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 03a40e4fce..f830223058 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -1,66 +1,72 @@ +require "active_support" + module ActionController - autoload :Base, "action_controller/base" - autoload :Benchmarking, "action_controller/metal/benchmarking" - autoload :ConditionalGet, "action_controller/metal/conditional_get" - autoload :Configuration, "action_controller/metal/configuration" - autoload :Head, "action_controller/metal/head" - autoload :Helpers, "action_controller/metal/helpers" - autoload :HideActions, "action_controller/metal/hide_actions" - autoload :Layouts, "action_controller/metal/layouts" - autoload :Metal, "action_controller/metal" - autoload :Middleware, "action_controller/middleware" - autoload :RackConvenience, "action_controller/metal/rack_convenience" - autoload :Rails2Compatibility, "action_controller/metal/compatibility" - autoload :Redirector, "action_controller/metal/redirector" - autoload :RenderingController, "action_controller/metal/rendering_controller" - autoload :RenderOptions, "action_controller/metal/render_options" - autoload :Rescue, "action_controller/metal/rescuable" - autoload :Responder, "action_controller/metal/responder" - autoload :Session, "action_controller/metal/session" - autoload :Testing, "action_controller/metal/testing" - autoload :UrlFor, "action_controller/metal/url_for" + extend ActiveSupport::Autoload - autoload :Caching, 'action_controller/caching' - autoload :Dispatcher, 'action_controller/dispatch/dispatcher' - autoload :Integration, 'action_controller/deprecated/integration_test' - autoload :IntegrationTest, 'action_controller/deprecated/integration_test' - autoload :MimeResponds, 'action_controller/metal/mime_responds' - autoload :PerformanceTest, 'action_controller/deprecated/performance_test' - autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes' - autoload :RecordIdentifier, 'action_controller/record_identifier' - autoload :Routing, 'action_controller/deprecated' - autoload :SessionManagement, 'action_controller/metal/session_management' - autoload :TestCase, 'action_controller/testing/test_case' - autoload :TestProcess, 'action_controller/testing/process' - autoload :UrlRewriter, 'action_controller/url_rewriter' - autoload :UrlWriter, 'action_controller/url_rewriter' + autoload :Base + autoload :Caching + autoload :PolymorphicRoutes + autoload :RecordIdentifier + autoload :UrlRewriter + autoload :Translation + autoload :Metal + autoload :Middleware - autoload :Verification, 'action_controller/metal/verification' - autoload :Flash, 'action_controller/metal/flash' - autoload :RequestForgeryProtection, 'action_controller/metal/request_forgery_protection' - autoload :Streaming, 'action_controller/metal/streaming' - autoload :HttpAuthentication, 'action_controller/metal/http_authentication' - autoload :FilterParameterLogging, 'action_controller/metal/filter_parameter_logging' - autoload :Translation, 'action_controller/translation' - autoload :Cookies, 'action_controller/metal/cookies' + autoload_under "metal" do + autoload :Benchmarking + autoload :ConditionalGet + autoload :Configuration + autoload :Head + autoload :Helpers + autoload :HideActions + autoload :Layouts + autoload :MimeResponds + autoload :RackConvenience + autoload :Compatibility + autoload :Redirector + autoload :RenderingController + autoload :RenderOptions + autoload :Rescue + autoload :Responder + autoload :Session + autoload :SessionManagement + autoload :UrlFor + autoload :Verification + autoload :Flash + autoload :RequestForgeryProtection + autoload :Streaming + autoload :HttpAuthentication + autoload :FilterParameterLogging + autoload :Cookies + end - autoload :ActionControllerError, 'action_controller/metal/exceptions' - autoload :RenderError, 'action_controller/metal/exceptions' - autoload :RoutingError, 'action_controller/metal/exceptions' - autoload :MethodNotAllowed, 'action_controller/metal/exceptions' - autoload :NotImplemented, 'action_controller/metal/exceptions' - autoload :UnknownController, 'action_controller/metal/exceptions' - autoload :MissingFile, 'action_controller/metal/exceptions' - autoload :RenderError, 'action_controller/metal/exceptions' - autoload :SessionOverflowError, 'action_controller/metal/exceptions' - autoload :UnknownHttpMethod, 'action_controller/metal/exceptions' -end + autoload :Dispatcher, 'action_controller/dispatch/dispatcher' + autoload :PerformanceTest, 'action_controller/deprecated/performance_test' + autoload :Routing, 'action_controller/deprecated' + autoload :Integration, 'action_controller/deprecated/integration_test' + autoload :IntegrationTest, 'action_controller/deprecated/integration_test' -autoload :HTML, 'action_controller/vendor/html-scanner' -autoload :AbstractController, 'abstract_controller' + autoload :UrlWriter, 'action_controller/url_rewriter' + + autoload_at "action_controller/metal/exceptions" do + autoload :ActionControllerError + autoload :RenderError + autoload :RoutingError + autoload :MethodNotAllowed + autoload :NotImplemented + autoload :UnknownController + autoload :MissingFile + autoload :RenderError + autoload :SessionOverflowError + autoload :UnknownHttpMethod + end +end +# All of these simply register additional autoloads +require 'abstract_controller' require 'action_dispatch' require 'action_view' +require 'action_controller/vendor/html-scanner' # Common ActiveSupport usage in ActionController require "active_support/concern" diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 4c026fe5f7..ed3984c9d4 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -24,7 +24,7 @@ module ActionController include ActionController::MimeResponds # Rails 2.x compatibility - include ActionController::Rails2Compatibility + include ActionController::Compatibility include ActionController::Cookies include ActionController::Session diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index 3caf759032..ad357cceda 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -30,10 +30,11 @@ module ActionController #:nodoc: # config.action_controller.cache_store = MyOwnStore.new("parameter") module Caching extend ActiveSupport::Concern + extend ActiveSupport::Autoload - autoload :Actions, 'action_controller/caching/actions' - autoload :Fragments, 'action_controller/caching/fragments' - autoload :Pages, 'action_controller/caching/pages' + autoload :Actions + autoload :Fragments + autoload :Pages autoload :Sweeper, 'action_controller/caching/sweeping' autoload :Sweeping, 'action_controller/caching/sweeping' diff --git a/actionpack/lib/action_controller/deprecated/integration_test.rb b/actionpack/lib/action_controller/deprecated/integration_test.rb index 86336b6bc4..05c8c0f156 100644 --- a/actionpack/lib/action_controller/deprecated/integration_test.rb +++ b/actionpack/lib/action_controller/deprecated/integration_test.rb @@ -1,2 +1,4 @@ +require "action_dispatch/testing/integration" + ActionController::Integration = ActionDispatch::Integration ActionController::IntegrationTest = ActionDispatch::IntegrationTest diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb index 0c264fcd09..a90f798cd5 100644 --- a/actionpack/lib/action_controller/metal/compatibility.rb +++ b/actionpack/lib/action_controller/metal/compatibility.rb @@ -1,5 +1,5 @@ module ActionController - module Rails2Compatibility + module Compatibility extend ActiveSupport::Concern class ::ActionController::ActionControllerError < StandardError #:nodoc: diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb index b4325e24ad..d0402e5bad 100644 --- a/actionpack/lib/action_controller/metal/helpers.rb +++ b/actionpack/lib/action_controller/metal/helpers.rb @@ -52,7 +52,7 @@ module ActionController included do # Set the default directory for helpers extlib_inheritable_accessor(:helpers_dir) do - defined?(Rails) ? "#{Rails.root}/app/helpers" : "app/helpers" + defined?(Rails.root) ? "#{Rails.root}/app/helpers" : "app/helpers" end end diff --git a/actionpack/lib/action_controller/metal/rescuable.rb b/actionpack/lib/action_controller/metal/rescuable.rb deleted file mode 100644 index bbca1b2179..0000000000 --- a/actionpack/lib/action_controller/metal/rescuable.rb +++ /dev/null @@ -1,13 +0,0 @@ -module ActionController #:nodoc: - module Rescue - extend ActiveSupport::Concern - include ActiveSupport::Rescuable - - private - def process_action(*args) - super - rescue Exception => exception - rescue_with_handler(exception) || raise(exception) - end - end -end diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb new file mode 100644 index 0000000000..bbca1b2179 --- /dev/null +++ b/actionpack/lib/action_controller/metal/rescue.rb @@ -0,0 +1,13 @@ +module ActionController #:nodoc: + module Rescue + extend ActiveSupport::Concern + include ActiveSupport::Rescuable + + private + def process_action(*args) + super + rescue Exception => exception + rescue_with_handler(exception) || raise(exception) + end + end +end diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb new file mode 100644 index 0000000000..7533a22299 --- /dev/null +++ b/actionpack/lib/action_controller/test_case.rb @@ -0,0 +1,348 @@ +require 'active_support/test_case' +require 'rack/session/abstract/id' +require 'action_controller/metal/testing' +require 'action_controller/testing/process' +require 'action_dispatch/test_case' + +module ActionController + class TestRequest < ActionDispatch::TestRequest #:nodoc: + def initialize(env = {}) + super + + self.session = TestSession.new + self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => ActiveSupport::SecureRandom.hex(16)) + end + + class Result < ::Array #:nodoc: + def to_s() join '/' end + def self.new_escaped(strings) + new strings.collect {|str| URI.unescape str} + end + end + + def assign_parameters(controller_path, action, parameters = {}) + parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action) + extra_keys = ActionController::Routing::Routes.extra_keys(parameters) + non_path_parameters = get? ? query_parameters : request_parameters + parameters.each do |key, value| + if value.is_a? Fixnum + value = value.to_s + elsif value.is_a? Array + value = Result.new(value) + end + + if extra_keys.include?(key.to_sym) + non_path_parameters[key] = value + else + path_parameters[key.to_s] = value + end + end + + params = self.request_parameters.dup + + %w(controller action only_path).each do |k| + params.delete(k) + params.delete(k.to_sym) + end + + data = params.to_query + @env['CONTENT_LENGTH'] = data.length.to_s + @env['rack.input'] = StringIO.new(data) + end + + def recycle! + @formats = nil + @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ } + @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } + @env['action_dispatch.request.query_parameters'] = {} + end + end + + class TestResponse < ActionDispatch::TestResponse + def recycle! + @status = 200 + @header = {} + @writer = lambda { |x| @body << x } + @block = nil + @length = 0 + @body = [] + @charset = nil + @content_type = nil + + @request = @template = nil + end + end + + class TestSession < ActionDispatch::Session::AbstractStore::SessionHash #:nodoc: + DEFAULT_OPTIONS = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS + + def initialize(session = {}) + replace(session.stringify_keys) + @loaded = true + end + end + + # Superclass for ActionController functional tests. Functional tests allow you to + # test a single controller action per test method. This should not be confused with + # integration tests (see ActionController::IntegrationTest), which are more like + # "stories" that can involve multiple controllers and mutliple actions (i.e. multiple + # different HTTP requests). + # + # == Basic example + # + # Functional tests are written as follows: + # 1. First, one uses the +get+, +post+, +put+, +delete+ or +head+ method to simulate + # an HTTP request. + # 2. Then, one asserts whether the current state is as expected. "State" can be anything: + # the controller's HTTP response, the database contents, etc. + # + # For example: + # + # class BooksControllerTest < ActionController::TestCase + # def test_create + # # Simulate a POST response with the given HTTP parameters. + # post(:create, :book => { :title => "Love Hina" }) + # + # # Assert that the controller tried to redirect us to + # # the created book's URI. + # assert_response :found + # + # # Assert that the controller really put the book in the database. + # assert_not_nil Book.find_by_title("Love Hina") + # end + # end + # + # == Special instance variables + # + # ActionController::TestCase will also automatically provide the following instance + # variables for use in the tests: + # + # @controller:: + # The controller instance that will be tested. + # @request:: + # An ActionController::TestRequest, representing the current HTTP + # request. You can modify this object before sending the HTTP request. For example, + # you might want to set some session properties before sending a GET request. + # @response:: + # An ActionController::TestResponse object, representing the response + # of the last HTTP response. In the above example, @response becomes valid + # after calling +post+. If the various assert methods are not sufficient, then you + # may use this object to inspect the HTTP response in detail. + # + # (Earlier versions of Rails required each functional test to subclass + # Test::Unit::TestCase and define @controller, @request, @response in +setup+.) + # + # == Controller is automatically inferred + # + # ActionController::TestCase will automatically infer the controller under test + # from the test class name. If the controller cannot be inferred from the test + # class name, you can explicitly set it with +tests+. + # + # class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase + # tests WidgetController + # end + # + # == Testing controller internals + # + # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions + # can be used against. These collections are: + # + # * assigns: Instance variables assigned in the action that are available for the view. + # * session: Objects being saved in the session. + # * flash: The flash objects currently in the session. + # * cookies: Cookies being sent to the user on this request. + # + # These collections can be used just like any other hash: + # + # assert_not_nil assigns(:person) # makes sure that a @person instance variable was set + # assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave" + # assert flash.empty? # makes sure that there's nothing in the flash + # + # For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To + # appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing. + # So assigns(:person) will work just like assigns["person"], but again, assigns[:person] will not work. + # + # On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url. + # + # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another + # action call which can then be asserted against. + # + # == Manipulating the request collections + # + # The collections described above link to the response, so you can test if what the actions were expected to do happened. But + # sometimes you also want to manipulate these collections in the incoming request. This is really only relevant for sessions + # and cookies, though. For sessions, you just do: + # + # @request.session[:key] = "value" + # @request.cookies["key"] = "value" + # + # == Testing named routes + # + # If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case. + # Example: + # + # assert_redirected_to page_url(:title => 'foo') + class TestCase < ActiveSupport::TestCase + include TestProcess + + # Executes a request simulating GET HTTP method and set/volley the response + def get(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "GET") + end + + # Executes a request simulating POST HTTP method and set/volley the response + def post(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "POST") + end + + # Executes a request simulating PUT HTTP method and set/volley the response + def put(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "PUT") + end + + # Executes a request simulating DELETE HTTP method and set/volley the response + def delete(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "DELETE") + end + + # Executes a request simulating HEAD HTTP method and set/volley the response + def head(action, parameters = nil, session = nil, flash = nil) + process(action, parameters, session, flash, "HEAD") + end + + def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil) + @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' + @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') + returning __send__(request_method, action, parameters, session, flash) do + @request.env.delete 'HTTP_X_REQUESTED_WITH' + @request.env.delete 'HTTP_ACCEPT' + end + end + alias xhr :xml_http_request + + def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET') + # Sanity check for required instance variables so we can give an + # understandable error message. + %w(@controller @request @response).each do |iv_name| + if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil? + raise "#{iv_name} is nil: make sure you set it in your test's setup method." + end + end + + @request.recycle! + @response.recycle! + @controller.response_body = nil + @controller.formats = nil + @controller.params = nil + + @html_document = nil + @request.env['REQUEST_METHOD'] = http_method + + parameters ||= {} + @request.assign_parameters(@controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters) + + @request.session = ActionController::TestSession.new(session) unless session.nil? + @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash + + @controller.request = @request + @controller.params.merge!(parameters) + build_request_uri(action, parameters) + Base.class_eval { include Testing } + @controller.process_with_new_base_test(@request, @response) + @response + end + + include ActionDispatch::Assertions + + # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline + # (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular + # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else + # than 0.0.0.0. + # + # The exception is stored in the exception accessor for further inspection. + module RaiseActionExceptions + def self.included(base) + base.class_eval do + attr_accessor :exception + protected :exception, :exception= + end + end + + protected + def rescue_action_without_handler(e) + self.exception = e + + if request.remote_addr == "0.0.0.0" + raise(e) + else + super(e) + end + end + end + + setup :setup_controller_request_and_response + + @@controller_class = nil + + class << self + # Sets the controller class name. Useful if the name can't be inferred from test class. + # Expects +controller_class+ as a constant. Example: tests WidgetController. + def tests(controller_class) + self.controller_class = controller_class + end + + def controller_class=(new_class) + prepare_controller_class(new_class) if new_class + write_inheritable_attribute(:controller_class, new_class) + end + + def controller_class + if current_controller_class = read_inheritable_attribute(:controller_class) + current_controller_class + else + self.controller_class = determine_default_controller_class(name) + end + end + + def determine_default_controller_class(name) + name.sub(/Test$/, '').constantize + rescue NameError + nil + end + + def prepare_controller_class(new_class) + new_class.send :include, RaiseActionExceptions + end + end + + def setup_controller_request_and_response + @request = TestRequest.new + @response = TestResponse.new + + if klass = self.class.controller_class + @controller ||= klass.new rescue nil + end + + if @controller + @controller.request = @request + @controller.params = {} + end + end + + # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local + def rescue_action_in_public! + @request.remote_addr = '208.77.188.166' # example.com + end + + private + def build_request_uri(action, parameters) + unless @request.env['REQUEST_URI'] + options = @controller.__send__(:rewrite_options, parameters) + options.update(:only_path => true, :action => action) + + url = ActionController::UrlRewriter.new(@request, parameters) + @request.request_uri = url.rewrite(options) + end + end + end +end diff --git a/actionpack/lib/action_controller/testing/test_case.rb b/actionpack/lib/action_controller/testing/test_case.rb deleted file mode 100644 index 01a55fe930..0000000000 --- a/actionpack/lib/action_controller/testing/test_case.rb +++ /dev/null @@ -1,345 +0,0 @@ -require 'active_support/test_case' -require 'rack/session/abstract/id' - -module ActionController - class TestRequest < ActionDispatch::TestRequest #:nodoc: - def initialize(env = {}) - super - - self.session = TestSession.new - self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => ActiveSupport::SecureRandom.hex(16)) - end - - class Result < ::Array #:nodoc: - def to_s() join '/' end - def self.new_escaped(strings) - new strings.collect {|str| URI.unescape str} - end - end - - def assign_parameters(controller_path, action, parameters = {}) - parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action) - extra_keys = ActionController::Routing::Routes.extra_keys(parameters) - non_path_parameters = get? ? query_parameters : request_parameters - parameters.each do |key, value| - if value.is_a? Fixnum - value = value.to_s - elsif value.is_a? Array - value = Result.new(value) - end - - if extra_keys.include?(key.to_sym) - non_path_parameters[key] = value - else - path_parameters[key.to_s] = value - end - end - - params = self.request_parameters.dup - - %w(controller action only_path).each do |k| - params.delete(k) - params.delete(k.to_sym) - end - - data = params.to_query - @env['CONTENT_LENGTH'] = data.length.to_s - @env['rack.input'] = StringIO.new(data) - end - - def recycle! - @formats = nil - @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ } - @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } - @env['action_dispatch.request.query_parameters'] = {} - end - end - - class TestResponse < ActionDispatch::TestResponse - def recycle! - @status = 200 - @header = {} - @writer = lambda { |x| @body << x } - @block = nil - @length = 0 - @body = [] - @charset = nil - @content_type = nil - - @request = @template = nil - end - end - - class TestSession < ActionDispatch::Session::AbstractStore::SessionHash #:nodoc: - DEFAULT_OPTIONS = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS - - def initialize(session = {}) - replace(session.stringify_keys) - @loaded = true - end - end - - # Superclass for ActionController functional tests. Functional tests allow you to - # test a single controller action per test method. This should not be confused with - # integration tests (see ActionController::IntegrationTest), which are more like - # "stories" that can involve multiple controllers and mutliple actions (i.e. multiple - # different HTTP requests). - # - # == Basic example - # - # Functional tests are written as follows: - # 1. First, one uses the +get+, +post+, +put+, +delete+ or +head+ method to simulate - # an HTTP request. - # 2. Then, one asserts whether the current state is as expected. "State" can be anything: - # the controller's HTTP response, the database contents, etc. - # - # For example: - # - # class BooksControllerTest < ActionController::TestCase - # def test_create - # # Simulate a POST response with the given HTTP parameters. - # post(:create, :book => { :title => "Love Hina" }) - # - # # Assert that the controller tried to redirect us to - # # the created book's URI. - # assert_response :found - # - # # Assert that the controller really put the book in the database. - # assert_not_nil Book.find_by_title("Love Hina") - # end - # end - # - # == Special instance variables - # - # ActionController::TestCase will also automatically provide the following instance - # variables for use in the tests: - # - # @controller:: - # The controller instance that will be tested. - # @request:: - # An ActionController::TestRequest, representing the current HTTP - # request. You can modify this object before sending the HTTP request. For example, - # you might want to set some session properties before sending a GET request. - # @response:: - # An ActionController::TestResponse object, representing the response - # of the last HTTP response. In the above example, @response becomes valid - # after calling +post+. If the various assert methods are not sufficient, then you - # may use this object to inspect the HTTP response in detail. - # - # (Earlier versions of Rails required each functional test to subclass - # Test::Unit::TestCase and define @controller, @request, @response in +setup+.) - # - # == Controller is automatically inferred - # - # ActionController::TestCase will automatically infer the controller under test - # from the test class name. If the controller cannot be inferred from the test - # class name, you can explicitly set it with +tests+. - # - # class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase - # tests WidgetController - # end - # - # == Testing controller internals - # - # In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions - # can be used against. These collections are: - # - # * assigns: Instance variables assigned in the action that are available for the view. - # * session: Objects being saved in the session. - # * flash: The flash objects currently in the session. - # * cookies: Cookies being sent to the user on this request. - # - # These collections can be used just like any other hash: - # - # assert_not_nil assigns(:person) # makes sure that a @person instance variable was set - # assert_equal "Dave", cookies[:name] # makes sure that a cookie called :name was set as "Dave" - # assert flash.empty? # makes sure that there's nothing in the flash - # - # For historic reasons, the assigns hash uses string-based keys. So assigns[:person] won't work, but assigns["person"] will. To - # appease our yearning for symbols, though, an alternative accessor has been devised using a method call instead of index referencing. - # So assigns(:person) will work just like assigns["person"], but again, assigns[:person] will not work. - # - # On top of the collections, you have the complete url that a given action redirected to available in redirect_to_url. - # - # For redirects within the same controller, you can even call follow_redirect and the redirect will be followed, triggering another - # action call which can then be asserted against. - # - # == Manipulating the request collections - # - # The collections described above link to the response, so you can test if what the actions were expected to do happened. But - # sometimes you also want to manipulate these collections in the incoming request. This is really only relevant for sessions - # and cookies, though. For sessions, you just do: - # - # @request.session[:key] = "value" - # @request.cookies["key"] = "value" - # - # == Testing named routes - # - # If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case. - # Example: - # - # assert_redirected_to page_url(:title => 'foo') - class TestCase < ActiveSupport::TestCase - include TestProcess - - # Executes a request simulating GET HTTP method and set/volley the response - def get(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "GET") - end - - # Executes a request simulating POST HTTP method and set/volley the response - def post(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "POST") - end - - # Executes a request simulating PUT HTTP method and set/volley the response - def put(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "PUT") - end - - # Executes a request simulating DELETE HTTP method and set/volley the response - def delete(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "DELETE") - end - - # Executes a request simulating HEAD HTTP method and set/volley the response - def head(action, parameters = nil, session = nil, flash = nil) - process(action, parameters, session, flash, "HEAD") - end - - def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil) - @request.env['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' - @request.env['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ') - returning __send__(request_method, action, parameters, session, flash) do - @request.env.delete 'HTTP_X_REQUESTED_WITH' - @request.env.delete 'HTTP_ACCEPT' - end - end - alias xhr :xml_http_request - - def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET') - # Sanity check for required instance variables so we can give an - # understandable error message. - %w(@controller @request @response).each do |iv_name| - if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil? - raise "#{iv_name} is nil: make sure you set it in your test's setup method." - end - end - - @request.recycle! - @response.recycle! - @controller.response_body = nil - @controller.formats = nil - @controller.params = nil - - @html_document = nil - @request.env['REQUEST_METHOD'] = http_method - - parameters ||= {} - @request.assign_parameters(@controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters) - - @request.session = ActionController::TestSession.new(session) unless session.nil? - @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash - - @controller.request = @request - @controller.params.merge!(parameters) - build_request_uri(action, parameters) - Base.class_eval { include Testing } - @controller.process_with_new_base_test(@request, @response) - @response - end - - include ActionDispatch::Assertions - - # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline - # (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular - # rescue_action process takes place. This means you can test your rescue_action code by setting remote_addr to something else - # than 0.0.0.0. - # - # The exception is stored in the exception accessor for further inspection. - module RaiseActionExceptions - def self.included(base) - base.class_eval do - attr_accessor :exception - protected :exception, :exception= - end - end - - protected - def rescue_action_without_handler(e) - self.exception = e - - if request.remote_addr == "0.0.0.0" - raise(e) - else - super(e) - end - end - end - - setup :setup_controller_request_and_response - - @@controller_class = nil - - class << self - # Sets the controller class name. Useful if the name can't be inferred from test class. - # Expects +controller_class+ as a constant. Example: tests WidgetController. - def tests(controller_class) - self.controller_class = controller_class - end - - def controller_class=(new_class) - prepare_controller_class(new_class) if new_class - write_inheritable_attribute(:controller_class, new_class) - end - - def controller_class - if current_controller_class = read_inheritable_attribute(:controller_class) - current_controller_class - else - self.controller_class = determine_default_controller_class(name) - end - end - - def determine_default_controller_class(name) - name.sub(/Test$/, '').constantize - rescue NameError - nil - end - - def prepare_controller_class(new_class) - new_class.send :include, RaiseActionExceptions - end - end - - def setup_controller_request_and_response - @request = TestRequest.new - @response = TestResponse.new - - if klass = self.class.controller_class - @controller ||= klass.new rescue nil - end - - if @controller - @controller.request = @request - @controller.params = {} - end - end - - # Cause the action to be rescued according to the regular rules for rescue_action when the visitor is not local - def rescue_action_in_public! - @request.remote_addr = '208.77.188.166' # example.com - end - - private - def build_request_uri(action, parameters) - unless @request.env['REQUEST_URI'] - options = @controller.__send__(:rewrite_options, parameters) - options.update(:only_path => true, :action => action) - - url = ActionController::UrlRewriter.new(@request, parameters) - @request.request_uri = url.rewrite(options) - end - end - end -end diff --git a/actionpack/lib/action_controller/vendor/html-scanner.rb b/actionpack/lib/action_controller/vendor/html-scanner.rb index f622d195ee..2cb20ddd05 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner.rb @@ -1,6 +1,8 @@ $LOAD_PATH << "#{File.dirname(__FILE__)}/html-scanner" module HTML + extend ActiveSupport::Autoload + autoload :CDATA, 'html/node' autoload :Document, 'html/document' autoload :FullSanitizer, 'html/sanitizer' diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 259814a322..e21dbc59cc 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -28,37 +28,38 @@ module Rack end module ActionDispatch - autoload :Request, 'action_dispatch/http/request' - autoload :Response, 'action_dispatch/http/response' - autoload :StatusCodes, 'action_dispatch/http/status_codes' - autoload :Utils, 'action_dispatch/http/utils' + extend ActiveSupport::Autoload - autoload :Callbacks, 'action_dispatch/middleware/callbacks' - autoload :MiddlewareStack, 'action_dispatch/middleware/stack' - autoload :ParamsParser, 'action_dispatch/middleware/params_parser' - autoload :Rescue, 'action_dispatch/middleware/rescue' - autoload :ShowExceptions, 'action_dispatch/middleware/show_exceptions' - autoload :Static, 'action_dispatch/middleware/static' - autoload :StringCoercion, 'action_dispatch/middleware/string_coercion' + autoload_under "http" do + autoload :Request + autoload :Response + autoload :StatusCodes + autoload :Utils + end - autoload :Routing, 'action_dispatch/routing' + autoload_under "middleware" do + autoload :Callbacks + autoload :ParamsParser + autoload :Rescue + autoload :ShowExceptions + autoload :Static + autoload :StringCoercion + end - autoload :Assertions, 'action_dispatch/testing/assertions' - autoload :Integration, 'action_dispatch/testing/integration' - autoload :IntegrationTest, 'action_dispatch/testing/integration' - autoload :PerformanceTest, 'action_dispatch/testing/performance_test' - autoload :TestRequest, 'action_dispatch/testing/test_request' - autoload :TestResponse, 'action_dispatch/testing/test_response' + autoload :MiddlewareStack, 'action_dispatch/middleware/stack' + autoload :Routing autoload :HTML, 'action_controller/vendor/html-scanner' module Http - autoload :Headers, 'action_dispatch/http/headers' + extend ActiveSupport::Autoload + + autoload :Headers end module Session autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store' - autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' + autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store' end end diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 036deec6d2..67c70a0418 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -29,7 +29,7 @@ module ActionDispatch 'ActionView::MissingTemplate' => 'missing_template', 'ActionController::RoutingError' => 'routing_error', ActionController::UnknownAction.name => 'unknown_action', - 'ActionView::TemplateError' => 'template_error' + 'ActionView::Template::Error' => 'template_error' }) FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'}, @@ -119,7 +119,7 @@ module ActionDispatch return unless logger ActiveSupport::Deprecation.silence do - if ActionView::TemplateError === exception + if ActionView::Template::Error === exception logger.fatal(exception.to_s) else logger.fatal( diff --git a/actionpack/lib/action_dispatch/test_case.rb b/actionpack/lib/action_dispatch/test_case.rb new file mode 100644 index 0000000000..afd708f06f --- /dev/null +++ b/actionpack/lib/action_dispatch/test_case.rb @@ -0,0 +1,6 @@ +require "action_dispatch/testing/assertions" +require "action_dispatch/testing/integration" +require "action_dispatch/testing/performance_test" +require "action_dispatch/testing/test_request" +require "action_dispatch/testing/test_response" +require "action_dispatch/testing/integration" \ No newline at end of file diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index f6f7ec0c8c..81ee19d996 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -24,27 +24,25 @@ require File.join(File.dirname(__FILE__), "action_pack") module ActionView - def self.load_all! - [Context, Base, TemplateError] + extend ActiveSupport::Autoload + + autoload :Base + autoload :Context + autoload :Template + autoload :Helpers + autoload :SafeBuffer + + + autoload_under "render" do + autoload :Partials + autoload :Rendering end - autoload :Base, 'action_view/base' - autoload :Context, 'action_view/context' - autoload :Helpers, 'action_view/helpers' autoload :MissingTemplate, 'action_view/base' - autoload :Partials, 'action_view/render/partials' autoload :Resolver, 'action_view/template/resolver' autoload :PathResolver, 'action_view/template/resolver' autoload :PathSet, 'action_view/paths' - autoload :Rendering, 'action_view/render/rendering' - autoload :Template, 'action_view/template/template' - autoload :TemplateError, 'action_view/template/error' - autoload :TemplateHandler, 'action_view/template/handler' - autoload :TemplateHandlers, 'action_view/template/handlers' - autoload :TextTemplate, 'action_view/template/text' - autoload :Helpers, 'action_view/helpers' autoload :FileSystemResolverWithFallback, 'action_view/template/resolver' - autoload :SafeBuffer, 'action_view/safe_buffer' end require 'action_view/erb/util' diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index c33695770f..d69e5109fa 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -196,7 +196,7 @@ module ActionView #:nodoc: end class << self - delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB' + delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB' delegate :logger, :to => 'ActionController::Base', :allow_nil => true end diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index 23bde61f9c..0059b79e5f 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -4,7 +4,7 @@ module ActionView #:nodoc: # TODO: Clean this up if obj.is_a?(String) if cache.nil? - cache = !defined?(Rails) || Rails.application.config.cache_classes + cache = !defined?(Rails.application) || Rails.application.config.cache_classes end FileSystemResolverWithFallback.new(obj, :cache => cache) else diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb new file mode 100644 index 0000000000..210ad508f5 --- /dev/null +++ b/actionpack/lib/action_view/template.rb @@ -0,0 +1,138 @@ +# encoding: utf-8 +# This is so that templates compiled in this file are UTF-8 + +require 'set' +require "action_view/template/resolver" + +module ActionView + class Template + extend ActiveSupport::Autoload + + autoload :Error + autoload :Handler + autoload :Handlers + autoload :Text + + extend Template::Handlers + attr_reader :source, :identifier, :handler, :mime_type, :formats, :details + + def initialize(source, identifier, handler, details) + @source = source + @identifier = identifier + @handler = handler + @details = details + @method_names = {} + + format = details.delete(:format) || begin + # TODO: Clean this up + handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html" + end + @mime_type = Mime::Type.lookup_by_extension(format.to_s) + @formats = [format.to_sym] + @formats << :html if format == :js + @details[:formats] = Array.wrap(format.to_sym) + end + + def render(view, locals, &block) + ActiveSupport::Notifications.instrument(:render_template, :identifier => identifier) do + method_name = compile(locals, view) + view.send(method_name, locals, &block) + end + rescue Exception => e + if e.is_a?(Template::Error) + e.sub_template_of(self) + raise e + else + raise Template::Error.new(self, view.assigns, e) + end + end + + # TODO: Figure out how to abstract this + def variable_name + @variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym + end + + # TODO: Figure out how to abstract this + def counter_name + @counter_name ||= "#{variable_name}_counter".to_sym + end + + # TODO: kill hax + def partial? + @details[:partial] + end + + def inspect + if defined?(Rails.root) + identifier.sub("#{Rails.root}/", '') + else + identifier + end + end + + private + def compile(locals, view) + method_name = build_method_name(locals) + + return method_name if view.respond_to?(method_name) + + locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join + + code = @handler.call(self) + if code.sub!(/\A(#.*coding.*)\n/, '') + encoding_comment = $1 + elsif defined?(Encoding) && Encoding.respond_to?(:default_external) + encoding_comment = "#coding:#{Encoding.default_external}" + end + + source = <<-end_src + def #{method_name}(local_assigns) + old_output_buffer = output_buffer;#{locals_code};#{code} + ensure + self.output_buffer = old_output_buffer + end + end_src + + if encoding_comment + source = "#{encoding_comment}\n#{source}" + line = -1 + else + line = 0 + end + + begin + ActionView::CompiledTemplates.module_eval(source, identifier, line) + method_name + rescue Exception => e # errors from template code + if logger = (view && view.logger) + logger.debug "ERROR: compiling #{method_name} RAISED #{e}" + logger.debug "Function body: #{source}" + logger.debug "Backtrace: #{e.backtrace.join("\n")}" + end + + raise ActionView::Template::Error.new(self, {}, e) + end + end + + class LocalsKey + @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } } + + def self.get(*locals) + @hash_keys[*locals] ||= new(klass, format, locale) + end + + attr_accessor :hash + def initialize(klass, format, locale) + @hash = locals.hash + end + + alias_method :eql?, :equal? + end + + def build_method_name(locals) + # TODO: is locals.keys.hash reliably the same? + @method_names[locals.keys.hash] ||= + "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_") + end + end +end diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index aa21606f76..a136d4333b 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -1,101 +1,103 @@ require "active_support/core_ext/enumerable" module ActionView - # The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a - # bunch of intimate details and uses it to report a very precise exception message. - class TemplateError < ActionViewError #:nodoc: - SOURCE_CODE_RADIUS = 3 + class Template + # The Template::Error exception is raised when the compilation of the template fails. This exception then gathers a + # bunch of intimate details and uses it to report a very precise exception message. + class Error < ActionViewError #:nodoc: + SOURCE_CODE_RADIUS = 3 - attr_reader :original_exception + attr_reader :original_exception - def initialize(template, assigns, original_exception) - @template, @assigns, @original_exception = template, assigns.dup, original_exception - @backtrace = compute_backtrace - end + def initialize(template, assigns, original_exception) + @template, @assigns, @original_exception = template, assigns.dup, original_exception + @backtrace = compute_backtrace + end - def file_name - @template.identifier - end + def file_name + @template.identifier + end - def message - ActiveSupport::Deprecation.silence { original_exception.message } - end + def message + ActiveSupport::Deprecation.silence { original_exception.message } + end - def clean_backtrace - if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) - Rails.backtrace_cleaner.clean(original_exception.backtrace) - else - original_exception.backtrace + def clean_backtrace + if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) + Rails.backtrace_cleaner.clean(original_exception.backtrace) + else + original_exception.backtrace + end end - end - def sub_template_message - if @sub_templates - "Trace of template inclusion: " + - @sub_templates.collect { |template| template.inspect }.join(", ") - else - "" + def sub_template_message + if @sub_templates + "Trace of template inclusion: " + + @sub_templates.collect { |template| template.inspect }.join(", ") + else + "" + end end - end - def source_extract(indentation = 0) - return unless num = line_number - num = num.to_i + def source_extract(indentation = 0) + return unless num = line_number + num = num.to_i - source_code = @template.source.split("\n") + source_code = @template.source.split("\n") - start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max - end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min + start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max + end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min - indent = ' ' * indentation - line_counter = start_on_line - return unless source_code = source_code[start_on_line..end_on_line] + indent = ' ' * indentation + line_counter = start_on_line + return unless source_code = source_code[start_on_line..end_on_line] - source_code.sum do |line| - line_counter += 1 - "#{indent}#{line_counter}: #{line}\n" + source_code.sum do |line| + line_counter += 1 + "#{indent}#{line_counter}: #{line}\n" + end end - end - def sub_template_of(template_path) - @sub_templates ||= [] - @sub_templates << template_path - end - - def line_number - @line_number ||= - if file_name - regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/ - - $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp } - end - end + def sub_template_of(template_path) + @sub_templates ||= [] + @sub_templates << template_path + end - def to_s - "\n#{self.class} (#{message}) #{source_location}:\n" + - "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n" - end + def line_number + @line_number ||= + if file_name + regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/ - # don't do anything nontrivial here. Any raised exception from here becomes fatal - # (and can't be rescued). - def backtrace - @backtrace - end + $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp } + end + end - private - def compute_backtrace - [ - "#{source_location.capitalize}\n\n#{source_extract(4)}\n " + - clean_backtrace.join("\n ") - ] + def to_s + "\n#{self.class} (#{message}) #{source_location}:\n" + + "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n" end - def source_location - if line_number - "on line ##{line_number} of " - else - 'in ' - end + file_name + # don't do anything nontrivial here. Any raised exception from here becomes fatal + # (and can't be rescued). + def backtrace + @backtrace end + + private + def compute_backtrace + [ + "#{source_location.capitalize}\n\n#{source_extract(4)}\n " + + clean_backtrace.join("\n ") + ] + end + + def source_location + if line_number + "on line ##{line_number} of " + else + 'in ' + end + file_name + end + end end end \ No newline at end of file diff --git a/actionpack/lib/action_view/template/handler.rb b/actionpack/lib/action_view/template/handler.rb index 4bf58b9fa8..5a46a27893 100644 --- a/actionpack/lib/action_view/template/handler.rb +++ b/actionpack/lib/action_view/template/handler.rb @@ -3,34 +3,39 @@ require "action_dispatch/http/mime_type" # Legacy TemplateHandler stub module ActionView - module TemplateHandlers #:nodoc: - module Compilable - def self.included(base) - base.extend(ClassMethods) - end + class Template + module Handlers #:nodoc: + module Compilable + def self.included(base) + base.extend(ClassMethods) + end - module ClassMethods - def call(template) - new.compile(template) + module ClassMethods + def call(template) + new.compile(template) + end end - end - def compile(template) - raise "Need to implement #{self.class.name}#compile(template)" - end + def compile(template) + raise "Need to implement #{self.class.name}#compile(template)" + end + end end - end - class TemplateHandler - extlib_inheritable_accessor :default_format - self.default_format = Mime::HTML + class Template::Handler + extlib_inheritable_accessor :default_format + self.default_format = Mime::HTML - def self.call(template) - raise "Need to implement #{self.class.name}#call(template)" - end + def self.call(template) + raise "Need to implement #{self.class.name}#call(template)" + end - def render(template, local_assigns) - raise "Need to implement #{self.class.name}#render(template, local_assigns)" + def render(template, local_assigns) + raise "Need to implement #{self.class.name}#render(template, local_assigns)" + end end end + + TemplateHandlers = Template::Handlers + TemplateHandler = Template::Handler end diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb index faf54b9fe5..35488c0391 100644 --- a/actionpack/lib/action_view/template/handlers.rb +++ b/actionpack/lib/action_view/template/handlers.rb @@ -1,52 +1,54 @@ module ActionView #:nodoc: - module TemplateHandlers #:nodoc: - autoload :ERB, 'action_view/template/handlers/erb' - autoload :RJS, 'action_view/template/handlers/rjs' - autoload :Builder, 'action_view/template/handlers/builder' - - def self.extended(base) - base.register_default_template_handler :erb, TemplateHandlers::ERB - base.register_template_handler :rjs, TemplateHandlers::RJS - base.register_template_handler :builder, TemplateHandlers::Builder - - # TODO: Depreciate old template extensions - base.register_template_handler :rhtml, TemplateHandlers::ERB - base.register_template_handler :rxml, TemplateHandlers::Builder - end - - @@template_handlers = {} - @@default_template_handlers = nil + class Template + module Handlers #:nodoc: + autoload :ERB, 'action_view/template/handlers/erb' + autoload :RJS, 'action_view/template/handlers/rjs' + autoload :Builder, 'action_view/template/handlers/builder' + + def self.extended(base) + base.register_default_template_handler :erb, ERB + base.register_template_handler :rjs, RJS + base.register_template_handler :builder, Builder + + # TODO: Depreciate old template extensions + base.register_template_handler :rhtml, ERB + base.register_template_handler :rxml, Builder + end + + @@template_handlers = {} + @@default_template_handlers = nil - def self.extensions - @@template_handlers.keys - end - - # Register a class that knows how to handle template files with the given - # extension. This can be used to implement new template types. - # The constructor for the class must take the ActiveView::Base instance - # as a parameter, and the class must implement a +render+ method that - # takes the contents of the template to render as well as the Hash of - # local assigns available to the template. The +render+ method ought to - # return the rendered template as a string. - def register_template_handler(extension, klass) - @@template_handlers[extension.to_sym] = klass - end - - def template_handler_extensions - @@template_handlers.keys.map {|key| key.to_s }.sort - end - - def registered_template_handler(extension) - extension && @@template_handlers[extension.to_sym] - end - - def register_default_template_handler(extension, klass) - register_template_handler(extension, klass) - @@default_template_handlers = klass - end - - def handler_class_for_extension(extension) - (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers + def self.extensions + @@template_handlers.keys + end + + # Register a class that knows how to handle template files with the given + # extension. This can be used to implement new template types. + # The constructor for the class must take the ActiveView::Base instance + # as a parameter, and the class must implement a +render+ method that + # takes the contents of the template to render as well as the Hash of + # local assigns available to the template. The +render+ method ought to + # return the rendered template as a string. + def register_template_handler(extension, klass) + @@template_handlers[extension.to_sym] = klass + end + + def template_handler_extensions + @@template_handlers.keys.map {|key| key.to_s }.sort + end + + def registered_template_handler(extension) + extension && @@template_handlers[extension.to_sym] + end + + def register_default_template_handler(extension, klass) + register_template_handler(extension, klass) + @@default_template_handlers = klass + end + + def handler_class_for_extension(extension) + (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers + end end end end diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb index 5f381f7bf0..a93cfca8aa 100644 --- a/actionpack/lib/action_view/template/handlers/builder.rb +++ b/actionpack/lib/action_view/template/handlers/builder.rb @@ -1,6 +1,6 @@ module ActionView - module TemplateHandlers - class Builder < TemplateHandler + module Template::Handlers + class Builder < Template::Handler include Compilable self.default_format = Mime::XML diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index 88aeb4b053..f8e6376589 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/string/output_safety' require 'erubis' module ActionView - module TemplateHandlers + module Template::Handlers class Erubis < ::Erubis::Eruby def add_preamble(src) src << "@output_buffer = ActionView::SafeBuffer.new;" @@ -26,7 +26,7 @@ module ActionView end end - class ERB < TemplateHandler + class ERB < Template::Handler include Compilable ## diff --git a/actionpack/lib/action_view/template/handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb index b1d15dc209..63e7dc0902 100644 --- a/actionpack/lib/action_view/template/handlers/rjs.rb +++ b/actionpack/lib/action_view/template/handlers/rjs.rb @@ -1,6 +1,6 @@ module ActionView - module TemplateHandlers - class RJS < TemplateHandler + module Template::Handlers + class RJS < Template::Handler include Compilable self.default_format = Mime::JS diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index 7336114e1b..a2f4ab2ef5 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -1,6 +1,6 @@ require "pathname" require "active_support/core_ext/class" -require "action_view/template/template" +require "action_view/template" module ActionView # Abstract superclass @@ -20,7 +20,7 @@ module ActionView register_detail(:locale) { [I18n.locale] } register_detail(:formats) { Mime::SET.symbols } register_detail(:handlers, :allow_nil => false) do - TemplateHandlers.extensions + Template::Handlers.extensions end def initialize(options = {}) @@ -65,7 +65,7 @@ module ActionView # as well as incorrectly putting part of the path in the template # name instead of the prefix. def normalize_name(name, prefix) - handlers = TemplateHandlers.extensions.join('|') + handlers = Template::Handlers.extensions.join('|') name = name.to_s.gsub(/\.(?:#{handlers})$/, '') parts = name.split('/') diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb deleted file mode 100644 index d1970ca3c7..0000000000 --- a/actionpack/lib/action_view/template/template.rb +++ /dev/null @@ -1,131 +0,0 @@ -# encoding: utf-8 -# This is so that templates compiled in this file are UTF-8 - -require 'set' -require "action_view/template/resolver" - -module ActionView - class Template - extend TemplateHandlers - attr_reader :source, :identifier, :handler, :mime_type, :formats, :details - - def initialize(source, identifier, handler, details) - @source = source - @identifier = identifier - @handler = handler - @details = details - @method_names = {} - - format = details.delete(:format) || begin - # TODO: Clean this up - handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html" - end - @mime_type = Mime::Type.lookup_by_extension(format.to_s) - @formats = [format.to_sym] - @formats << :html if format == :js - @details[:formats] = Array.wrap(format.to_sym) - end - - def render(view, locals, &block) - ActiveSupport::Notifications.instrument(:render_template, :identifier => identifier) do - method_name = compile(locals, view) - view.send(method_name, locals, &block) - end - rescue Exception => e - if e.is_a?(TemplateError) - e.sub_template_of(self) - raise e - else - raise TemplateError.new(self, view.assigns, e) - end - end - - # TODO: Figure out how to abstract this - def variable_name - @variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym - end - - # TODO: Figure out how to abstract this - def counter_name - @counter_name ||= "#{variable_name}_counter".to_sym - end - - # TODO: kill hax - def partial? - @details[:partial] - end - - def inspect - if defined?(Rails.root) - identifier.sub("#{Rails.root}/", '') - else - identifier - end - end - - private - def compile(locals, view) - method_name = build_method_name(locals) - - return method_name if view.respond_to?(method_name) - - locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join - - code = @handler.call(self) - if code.sub!(/\A(#.*coding.*)\n/, '') - encoding_comment = $1 - elsif defined?(Encoding) && Encoding.respond_to?(:default_external) - encoding_comment = "#coding:#{Encoding.default_external}" - end - - source = <<-end_src - def #{method_name}(local_assigns) - old_output_buffer = output_buffer;#{locals_code};#{code} - ensure - self.output_buffer = old_output_buffer - end - end_src - - if encoding_comment - source = "#{encoding_comment}\n#{source}" - line = -1 - else - line = 0 - end - - begin - ActionView::CompiledTemplates.module_eval(source, identifier, line) - method_name - rescue Exception => e # errors from template code - if logger = (view && view.logger) - logger.debug "ERROR: compiling #{method_name} RAISED #{e}" - logger.debug "Function body: #{source}" - logger.debug "Backtrace: #{e.backtrace.join("\n")}" - end - - raise ActionView::TemplateError.new(self, {}, e) - end - end - - class LocalsKey - @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } } - - def self.get(*locals) - @hash_keys[*locals] ||= new(klass, format, locale) - end - - attr_accessor :hash - def initialize(klass, format, locale) - @hash = locals.hash - end - - alias_method :eql?, :equal? - end - - def build_method_name(locals) - # TODO: is locals.keys.hash reliably the same? - @method_names[locals.keys.hash] ||= - "_render_template_#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_") - end - end -end diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb index f6e011a5ab..67e086d8bd 100644 --- a/actionpack/lib/action_view/template/text.rb +++ b/actionpack/lib/action_view/template/text.rb @@ -1,38 +1,40 @@ module ActionView #:nodoc: - class TextTemplate < String #:nodoc: - HTML = Mime[:html] - - def initialize(string, content_type = HTML) - super(string.to_s) - @content_type = Mime[content_type] || content_type - end - - def details - {:formats => [@content_type.to_sym]} - end - - def identifier - self - end - - def inspect - 'text template' - end - - def render(*args) - to_s - end - - def mime_type - @content_type - end - - def formats - [mime_type] - end - - def partial? - false + class Template + class Text < String #:nodoc: + HTML = Mime[:html] + + def initialize(string, content_type = HTML) + super(string.to_s) + @content_type = Mime[content_type] || content_type + end + + def details + {:formats => [@content_type.to_sym]} + end + + def identifier + self + end + + def inspect + 'text template' + end + + def render(*args) + to_s + end + + def mime_type + @content_type + end + + def formats + [mime_type] + end + + def partial? + false + end end end end diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 86bbad822d..ab5bc49cf9 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -1,5 +1,5 @@ require 'active_support/test_case' -require 'action_controller/testing/test_case' +require 'action_controller/test_case' module ActionView class Base diff --git a/actionpack/test/abstract/layouts_test.rb b/actionpack/test/abstract/layouts_test.rb index ae2f1bf1f2..5028c19b80 100644 --- a/actionpack/test/abstract/layouts_test.rb +++ b/actionpack/test/abstract/layouts_test.rb @@ -23,7 +23,7 @@ module AbstractControllerTests self.view_paths = [] def index - render :_template => ActionView::TextTemplate.new("Hello blank!") + render :_template => ActionView::Template::Text.new("Hello blank!") end end @@ -31,19 +31,19 @@ module AbstractControllerTests layout "hello" def index - render :_template => ActionView::TextTemplate.new("Hello string!") + render :_template => ActionView::Template::Text.new("Hello string!") end def overwrite_default - render :_template => ActionView::TextTemplate.new("Hello string!"), :layout => :default + render :_template => ActionView::Template::Text.new("Hello string!"), :layout => :default end def overwrite_false - render :_template => ActionView::TextTemplate.new("Hello string!"), :layout => false + render :_template => ActionView::Template::Text.new("Hello string!"), :layout => false end def overwrite_string - render :_template => ActionView::TextTemplate.new("Hello string!"), :layout => "omg" + render :_template => ActionView::Template::Text.new("Hello string!"), :layout => "omg" end def overwrite_skip @@ -72,7 +72,7 @@ module AbstractControllerTests layout :hello def index - render :_template => ActionView::TextTemplate.new("Hello symbol!") + render :_template => ActionView::Template::Text.new("Hello symbol!") end private def hello @@ -84,7 +84,7 @@ module AbstractControllerTests layout :no_hello def index - render :_template => ActionView::TextTemplate.new("Hello missing symbol!") + render :_template => ActionView::Template::Text.new("Hello missing symbol!") end private def no_hello @@ -96,7 +96,7 @@ module AbstractControllerTests layout :nilz def index - render :_template => ActionView::TextTemplate.new("Hello nilz!") + render :_template => ActionView::Template::Text.new("Hello nilz!") end def nilz() end @@ -106,7 +106,7 @@ module AbstractControllerTests layout :objekt def index - render :_template => ActionView::TextTemplate.new("Hello nilz!") + render :_template => ActionView::Template::Text.new("Hello nilz!") end def objekt @@ -118,7 +118,7 @@ module AbstractControllerTests layout :omg_no_method def index - render :_template => ActionView::TextTemplate.new("Hello boom!") + render :_template => ActionView::Template::Text.new("Hello boom!") end end @@ -126,7 +126,7 @@ module AbstractControllerTests layout "missing" def index - render :_template => ActionView::TextTemplate.new("Hello missing!") + render :_template => ActionView::Template::Text.new("Hello missing!") end end @@ -134,7 +134,7 @@ module AbstractControllerTests layout false def index - render :_template => ActionView::TextTemplate.new("Hello false!") + render :_template => ActionView::Template::Text.new("Hello false!") end end @@ -142,7 +142,7 @@ module AbstractControllerTests layout nil def index - render :_template => ActionView::TextTemplate.new("Hello nil!") + render :_template => ActionView::Template::Text.new("Hello nil!") end end diff --git a/actionpack/test/abstract/render_test.rb b/actionpack/test/abstract/render_test.rb index 45a4763fe4..331cb6f769 100644 --- a/actionpack/test/abstract/render_test.rb +++ b/actionpack/test/abstract/render_test.rb @@ -38,7 +38,7 @@ module AbstractController end def object - render :_template => ActionView::TextTemplate.new("With Object") + render :_template => ActionView::Template::Text.new("With Object") end end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index b9be163904..9030e562bb 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -191,7 +191,7 @@ class IsolatedHelpersTest < Test::Unit::TestCase end def test_helper_in_a - assert_raise(ActionView::TemplateError) { call_controller(A, "index") } + assert_raise(ActionView::Template::Error) { call_controller(A, "index") } end def test_helper_in_b diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index feb2f81cc1..f635253156 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -115,7 +115,7 @@ class RendersNoLayoutController < LayoutTest end class LayoutSetInResponseTest < ActionController::TestCase - include ActionView::TemplateHandlers + include ActionView::Template::Handlers def test_layout_set_when_using_default_layout @controller = DefaultLayoutController.new diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 35c51ca7ea..fdf3db1cdb 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -106,8 +106,8 @@ module RenderTestCases def test_render_partial_with_errors @view.render(:partial => "test/raise") - flunk "Render did not raise TemplateError" - rescue ActionView::TemplateError => e + flunk "Render did not raise Template::Error" + rescue ActionView::Template::Error => e assert_match "undefined local variable or method `doesnt_exist'", e.message assert_equal "", e.sub_template_message assert_equal "1", e.line_number @@ -116,8 +116,8 @@ module RenderTestCases def test_render_sub_template_with_errors @view.render(:file => "test/sub_template_raise") - flunk "Render did not raise TemplateError" - rescue ActionView::TemplateError => e + flunk "Render did not raise Template::Error" + rescue ActionView::Template::Error => e assert_match "undefined local variable or method `doesnt_exist'", e.message assert_equal "Trace of template inclusion: #{File.expand_path("#{FIXTURE_LOAD_PATH}/test/sub_template_raise.html.erb")}", e.sub_template_message assert_equal "1", e.line_number diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 505e16c195..8e14b6724f 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -26,28 +26,31 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path) require 'active_support' module ActiveModel - autoload :AttributeMethods, 'active_model/attribute_methods' - autoload :Conversion, 'active_model/conversion' - autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods' - autoload :Dirty, 'active_model/dirty' - autoload :Errors, 'active_model/errors' - autoload :Lint, 'active_model/lint' - autoload :Name, 'active_model/naming' - autoload :Naming, 'active_model/naming' - autoload :Observer, 'active_model/observing' - autoload :Observing, 'active_model/observing' - autoload :Serialization, 'active_model/serialization' - autoload :StateMachine, 'active_model/state_machine' - autoload :TestCase, 'active_model/test_case' - autoload :Translation, 'active_model/translation' - autoload :Validations, 'active_model/validations' - autoload :ValidationsRepairHelper, 'active_model/validations_repair_helper' - autoload :Validator, 'active_model/validator' - autoload :VERSION, 'active_model/version' + extend ActiveSupport::Autoload + + autoload :AttributeMethods + autoload :Conversion + autoload :DeprecatedErrorMethods + autoload :Dirty + autoload :Errors + autoload :Lint + autoload :Name, 'active_model/naming' + autoload :Naming + autoload :Observer, 'active_model/observing' + autoload :Observing + autoload :Serialization + autoload :StateMachine + autoload :Translation + autoload :Validations + autoload :ValidationsRepairHelper + autoload :Validator + autoload :VERSION module Serializers - autoload :JSON, 'active_model/serializers/json' - autoload :Xml, 'active_model/serializers/xml' + extend ActiveSupport::Autoload + + autoload :JSON + autoload :Xml end end diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb index c1a3f6a4a7..024f2378be 100644 --- a/activemodel/test/cases/helper.rb +++ b/activemodel/test/cases/helper.rb @@ -10,6 +10,7 @@ $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) require 'config' require 'active_model' +require 'active_model/test_case' # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true diff --git a/activemodel/test/cases/tests_database.rb b/activemodel/test/cases/tests_database.rb index 8dd00ea147..79668dd941 100644 --- a/activemodel/test/cases/tests_database.rb +++ b/activemodel/test/cases/tests_database.rb @@ -2,6 +2,7 @@ require 'logger' $:.unshift(File.dirname(__FILE__) + '/../../../activerecord/lib') require 'active_record' +require 'active_record/test_case' require 'active_record/fixtures' module ActiveModel diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 8195e78826..f935d15526 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -32,64 +32,66 @@ require 'active_model' require 'arel' module ActiveRecord - # TODO: Review explicit loads to see if they will automatically be handled by the initializer. - def self.load_all! - [Base, DynamicFinderMatch, ConnectionAdapters::AbstractAdapter] - end + extend ActiveSupport::Autoload - autoload :VERSION, 'active_record/version' + autoload :VERSION autoload :ActiveRecordError, 'active_record/base' autoload :ConnectionNotEstablished, 'active_record/base' - autoload :Aggregations, 'active_record/aggregations' - autoload :AssociationPreload, 'active_record/association_preload' - autoload :Associations, 'active_record/associations' - autoload :AttributeMethods, 'active_record/attribute_methods' - autoload :Attributes, 'active_record/attributes' - autoload :AutosaveAssociation, 'active_record/autosave_association' - autoload :Relation, 'active_record/relation' - autoload :Base, 'active_record/base' - autoload :Batches, 'active_record/batches' - autoload :Calculations, 'active_record/calculations' - autoload :Callbacks, 'active_record/callbacks' - autoload :DynamicFinderMatch, 'active_record/dynamic_finder_match' - autoload :DynamicScopeMatch, 'active_record/dynamic_scope_match' - autoload :Migration, 'active_record/migration' + autoload :Aggregations + autoload :AssociationPreload + autoload :Associations + autoload :AttributeMethods + autoload :Attributes + autoload :AutosaveAssociation + autoload :Relation + autoload :Base + autoload :Batches + autoload :Calculations + autoload :Callbacks + autoload :DynamicFinderMatch + autoload :DynamicScopeMatch + autoload :Migration autoload :Migrator, 'active_record/migration' - autoload :NamedScope, 'active_record/named_scope' - autoload :NestedAttributes, 'active_record/nested_attributes' - autoload :Observer, 'active_record/observer' - autoload :QueryCache, 'active_record/query_cache' - autoload :Reflection, 'active_record/reflection' - autoload :Schema, 'active_record/schema' - autoload :SchemaDumper, 'active_record/schema_dumper' - autoload :Serialization, 'active_record/serialization' - autoload :SessionStore, 'active_record/session_store' - autoload :StateMachine, 'active_record/state_machine' - autoload :TestCase, 'active_record/test_case' - autoload :Timestamp, 'active_record/timestamp' - autoload :Transactions, 'active_record/transactions' - autoload :Types, 'active_record/types' - autoload :Validations, 'active_record/validations' + autoload :NamedScope + autoload :NestedAttributes + autoload :Observer + autoload :QueryCache + autoload :Reflection + autoload :Schema + autoload :SchemaDumper + autoload :Serialization + autoload :SessionStore + autoload :StateMachine + autoload :Timestamp + autoload :Transactions + autoload :Types + autoload :Validations module AttributeMethods - autoload :BeforeTypeCast, 'active_record/attribute_methods/before_type_cast' - autoload :Dirty, 'active_record/attribute_methods/dirty' - autoload :PrimaryKey, 'active_record/attribute_methods/primary_key' - autoload :Query, 'active_record/attribute_methods/query' - autoload :Read, 'active_record/attribute_methods/read' - autoload :TimeZoneConversion, 'active_record/attribute_methods/time_zone_conversion' - autoload :Write, 'active_record/attribute_methods/write' + extend ActiveSupport::Autoload + + autoload :BeforeTypeCast + autoload :Dirty + autoload :PrimaryKey + autoload :Query + autoload :Read + autoload :TimeZoneConversion + autoload :Write end module Attributes - autoload :Aliasing, 'active_record/attributes/aliasing' - autoload :Store, 'active_record/attributes/store' - autoload :Typecasting, 'active_record/attributes/typecasting' + extend ActiveSupport::Autoload + + autoload :Aliasing + autoload :Store + autoload :Typecasting end module Type + extend ActiveSupport::Autoload + autoload :Number, 'active_record/types/number' autoload :Object, 'active_record/types/object' autoload :Serialize, 'active_record/types/serialize' @@ -98,12 +100,16 @@ module ActiveRecord end module Locking - autoload :Optimistic, 'active_record/locking/optimistic' - autoload :Pessimistic, 'active_record/locking/pessimistic' + extend ActiveSupport::Autoload + + autoload :Optimistic + autoload :Pessimistic end module ConnectionAdapters - autoload :AbstractAdapter, 'active_record/connection_adapters/abstract_adapter' + extend ActiveSupport::Autoload + + autoload :AbstractAdapter end end diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb index fd4c199b48..9357dd852f 100644 --- a/activeresource/lib/active_resource.rb +++ b/activeresource/lib/active_resource.rb @@ -30,11 +30,13 @@ $:.unshift(activemodel_path) if File.directory?(activemodel_path) require 'active_model' module ActiveResource - autoload :Base, 'active_resource/base' - autoload :Connection, 'active_resource/connection' - autoload :CustomMethods, 'active_resource/custom_methods' - autoload :Formats, 'active_resource/formats' - autoload :Observing, 'active_resource/observing' - autoload :Validations, 'active_resource/validations' - autoload :HttpMock, 'active_resource/http_mock' + extend ActiveSupport::Autoload + + autoload :Base + autoload :Connection + autoload :CustomMethods + autoload :Formats + autoload :Observing + autoload :Validations + autoload :HttpMock end diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb index 10849be20c..3d684ff347 100644 --- a/activeresource/test/abstract_unit.rb +++ b/activeresource/test/abstract_unit.rb @@ -13,6 +13,7 @@ require 'test/unit' require 'active_support' require 'active_support/test_case' require 'active_resource' +require 'active_model/test_case' $:.unshift "#{File.dirname(__FILE__)}/../test" require 'setter_trap' diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb index 1d3f7891ec..91349b810a 100644 --- a/activeresource/test/cases/base_test.rb +++ b/activeresource/test/cases/base_test.rb @@ -543,9 +543,9 @@ class BaseTest < Test::Unit::TestCase assert Person.collection_path(:gender => 'male', :student => true).include?('gender=male') assert Person.collection_path(:gender => 'male', :student => true).include?('student=true') - assert_equal '/people.xml?name%5B%5D=bob&name%5B%5D=your+uncle%2Bme&name%5B%5D=&name%5B%5D=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false]) + assert_equal '/people.xml?name[]=bob&name[]=your+uncle%2Bme&name[]=&name[]=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false]) - assert_equal '/people.xml?struct%5Ba%5D%5B%5D=2&struct%5Ba%5D%5B%5D=1&struct%5Bb%5D=fred', Person.collection_path(:struct => {:a => [2,1], 'b' => 'fred'}) + assert_equal '/people.xml?struct[a][]=2&struct[a][]=1&struct[b]=fred', Person.collection_path(:struct => {:a => [2,1], 'b' => 'fred'}) end def test_custom_element_path @@ -581,7 +581,7 @@ class BaseTest < Test::Unit::TestCase assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :person_id => 1, :type => 'work') assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, 'person_id' => 1, :type => 'work') assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :type => 'work', :person_id => 1) - assert_equal '/people/1/addresses/1.xml?type%5B%5D=work&type%5B%5D=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time']) + assert_equal '/people/1/addresses/1.xml?type[]=work&type[]=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time']) end def test_custom_element_path_with_prefix_and_parameters diff --git a/activesupport/lib/active_support/autoload.rb b/activesupport/lib/active_support/autoload.rb index 63f7338a68..e57cf24b25 100644 --- a/activesupport/lib/active_support/autoload.rb +++ b/activesupport/lib/active_support/autoload.rb @@ -1,28 +1,31 @@ +require "active_support/dependencies/autoload" + module ActiveSupport - autoload :BacktraceCleaner, 'active_support/backtrace_cleaner' - autoload :Base64, 'active_support/base64' - autoload :BasicObject, 'active_support/basic_object' - autoload :Benchmarkable, 'active_support/benchmarkable' - autoload :BufferedLogger, 'active_support/buffered_logger' - autoload :Cache, 'active_support/cache' - autoload :Callbacks, 'active_support/callbacks' - autoload :Concern, 'active_support/concern' - autoload :Configurable, 'active_support/configurable' - autoload :DependencyModule, 'active_support/dependency_module' - autoload :DeprecatedCallbacks, 'active_support/deprecated_callbacks' - autoload :Deprecation, 'active_support/deprecation' - autoload :Gzip, 'active_support/gzip' - autoload :Inflector, 'active_support/inflector' - autoload :Memoizable, 'active_support/memoizable' - autoload :MessageEncryptor, 'active_support/message_encryptor' - autoload :MessageVerifier, 'active_support/message_verifier' - autoload :Multibyte, 'active_support/multibyte' - autoload :OptionMerger, 'active_support/option_merger' - autoload :OrderedHash, 'active_support/ordered_hash' - autoload :OrderedOptions, 'active_support/ordered_options' - autoload :Notifications, 'active_support/notifications' - autoload :Rescuable, 'active_support/rescuable' - autoload :SecureRandom, 'active_support/secure_random' - autoload :StringInquirer, 'active_support/string_inquirer' - autoload :XmlMini, 'active_support/xml_mini' + extend ActiveSupport::Autoload + + autoload :BacktraceCleaner + autoload :Base64 + autoload :BasicObject + autoload :Benchmarkable + autoload :BufferedLogger + autoload :Cache + autoload :Callbacks + autoload :Concern + autoload :Configurable + autoload :DeprecatedCallbacks + autoload :Deprecation + autoload :Gzip + autoload :Inflector + autoload :Memoizable + autoload :MessageEncryptor + autoload :MessageVerifier + autoload :Multibyte + autoload :OptionMerger + autoload :OrderedHash + autoload :OrderedOptions + autoload :Notifications + autoload :Rescuable + autoload :SecureRandom + autoload :StringInquirer + autoload :XmlMini end diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb new file mode 100644 index 0000000000..973875f895 --- /dev/null +++ b/activesupport/lib/active_support/dependencies/autoload.rb @@ -0,0 +1,37 @@ +require "active_support/inflector/methods" + +module ActiveSupport + module Autoload + + @@autoloads = {} + @@under_path = nil + @@at_path = nil + + def autoload(const_name, path = @@at_path) + full = [self.name, @@under_path, const_name.to_s, path].compact.join("::") + location = path || Inflector.underscore(full) + + @@autoloads[const_name] = location + super const_name, location + end + + def autoload_under(path) + @@under_path, old_path = path, @@under_path + yield + ensure + @@under_path = old_path + end + + def autoload_at(path) + @@at_path, old_path = path, @@at_path + yield + ensure + @@at_path = old_path + end + + def self.eager_autoload! + @@autoloads.values.each {|file| require file } + end + + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index ab34f975f6..66e32fa5d7 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -1,450 +1,454 @@ -require 'ruby-prof' - -require 'fileutils' -require 'rails/version' - -module ActiveSupport - module Testing - module Performance - DEFAULTS = - if benchmark = ARGV.include?('--benchmark') # HAX for rake test - { :benchmark => true, - :runs => 4, - :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time], - :output => 'tmp/performance' } - else - { :benchmark => false, - :runs => 1, - :min_percent => 0.01, - :metrics => [:process_time, :memory, :objects], - :formats => [:flat, :graph_html, :call_tree], - :output => 'tmp/performance' } - end.freeze - - def self.included(base) - base.superclass_delegating_accessor :profile_options - base.profile_options = DEFAULTS - end +begin + require 'ruby-prof' + + require 'fileutils' + require 'rails/version' + require 'active_support/core_ext/class/delegating_attributes' + + module ActiveSupport + module Testing + module Performance + DEFAULTS = + if benchmark = ARGV.include?('--benchmark') # HAX for rake test + { :benchmark => true, + :runs => 4, + :metrics => [:wall_time, :memory, :objects, :gc_runs, :gc_time], + :output => 'tmp/performance' } + else + { :benchmark => false, + :runs => 1, + :min_percent => 0.01, + :metrics => [:process_time, :memory, :objects], + :formats => [:flat, :graph_html, :call_tree], + :output => 'tmp/performance' } + end.freeze + + def self.included(base) + base.superclass_delegating_accessor :profile_options + base.profile_options = DEFAULTS + end - def full_test_name - "#{self.class.name}##{method_name}" - end + def full_test_name + "#{self.class.name}##{method_name}" + end - def run(result) - return if method_name =~ /^default_test$/ + def run(result) + return if method_name =~ /^default_test$/ - yield(self.class::STARTED, name) - @_result = result + yield(self.class::STARTED, name) + @_result = result - run_warmup - if profile_options && metrics = profile_options[:metrics] - metrics.each do |metric_name| - if klass = Metrics[metric_name.to_sym] - run_profile(klass.new) - result.add_run + run_warmup + if profile_options && metrics = profile_options[:metrics] + metrics.each do |metric_name| + if klass = Metrics[metric_name.to_sym] + run_profile(klass.new) + result.add_run + end end end - end - yield(self.class::FINISHED, name) - end + yield(self.class::FINISHED, name) + end - def run_test(metric, mode) - run_callbacks :setup - setup - metric.send(mode) { __send__ @method_name } - rescue ::Test::Unit::AssertionFailedError => e - add_failure(e.message, e.backtrace) - rescue StandardError, ScriptError - add_error($!) - ensure - begin - teardown - run_callbacks :teardown, :enumerator => :reverse_each + def run_test(metric, mode) + run_callbacks :setup + setup + metric.send(mode) { __send__ @method_name } rescue ::Test::Unit::AssertionFailedError => e add_failure(e.message, e.backtrace) rescue StandardError, ScriptError add_error($!) + ensure + begin + teardown + run_callbacks :teardown, :enumerator => :reverse_each + rescue ::Test::Unit::AssertionFailedError => e + add_failure(e.message, e.backtrace) + rescue StandardError, ScriptError + add_error($!) + end end - end - protected - def run_warmup - GC.start + protected + def run_warmup + GC.start - time = Metrics::Time.new - run_test(time, :benchmark) - puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] + time = Metrics::Time.new + run_test(time, :benchmark) + puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] - GC.start - end - - def run_profile(metric) - klass = profile_options[:benchmark] ? Benchmarker : Profiler - performer = klass.new(self, metric) + GC.start + end - performer.run - puts performer.report - performer.record - end + def run_profile(metric) + klass = profile_options[:benchmark] ? Benchmarker : Profiler + performer = klass.new(self, metric) - class Performer - delegate :run_test, :profile_options, :full_test_name, :to => :@harness + performer.run + puts performer.report + performer.record + end - def initialize(harness, metric) - @harness, @metric = harness, metric - end + class Performer + delegate :run_test, :profile_options, :full_test_name, :to => :@harness - def report - rate = @total / profile_options[:runs] - '%20s: %s' % [@metric.name, @metric.format(rate)] - end + def initialize(harness, metric) + @harness, @metric = harness, metric + end - protected - def output_filename - "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" + def report + rate = @total / profile_options[:runs] + '%20s: %s' % [@metric.name, @metric.format(rate)] end - end - class Benchmarker < Performer - def run - profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } - @total = @metric.total + protected + def output_filename + "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" + end end - def record - avg = @metric.total / profile_options[:runs].to_i - now = Time.now.utc.xmlschema - with_output_file do |file| - file.puts "#{avg},#{now},#{environment}" + class Benchmarker < Performer + def run + profile_options[:runs].to_i.times { run_test(@metric, :benchmark) } + @total = @metric.total end - end - def environment - unless defined? @env - app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - - rails = Rails::VERSION::STRING - if File.directory?('vendor/rails/.git') - Dir.chdir('vendor/rails') do - rails += ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - end + def record + avg = @metric.total / profile_options[:runs].to_i + now = Time.now.utc.xmlschema + with_output_file do |file| + file.puts "#{avg},#{now},#{environment}" end - - ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' - ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" - - @env = [app, rails, ruby, RUBY_PLATFORM] * ',' end - @env - end + def environment + unless defined? @env + app = "#{$1}.#{$2}" if File.directory?('.git') && `git branch -v` =~ /^\* (\S+)\s+(\S+)/ - protected - HEADER = 'measurement,created_at,app,rails,ruby,platform' + rails = Rails::VERSION::STRING + if File.directory?('vendor/rails/.git') + Dir.chdir('vendor/rails') do + rails += ".#{$1}.#{$2}" if `git branch -v` =~ /^\* (\S+)\s+(\S+)/ + end + end - def with_output_file - fname = output_filename + ruby = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby' + ruby += "-#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" - if new = !File.exist?(fname) - FileUtils.mkdir_p(File.dirname(fname)) + @env = [app, rails, ruby, RUBY_PLATFORM] * ',' end - File.open(fname, 'ab') do |file| - file.puts(HEADER) if new - yield file - end + @env end - def output_filename - "#{super}.csv" - end - end + protected + HEADER = 'measurement,created_at,app,rails,ruby,platform' - class Profiler < Performer - def initialize(*args) - super - @supported = @metric.measure_mode rescue false - end + def with_output_file + fname = output_filename + + if new = !File.exist?(fname) + FileUtils.mkdir_p(File.dirname(fname)) + end - def run - return unless @supported + File.open(fname, 'ab') do |file| + file.puts(HEADER) if new + yield file + end + end - RubyProf.measure_mode = @metric.measure_mode - RubyProf.start - RubyProf.pause - profile_options[:runs].to_i.times { run_test(@metric, :profile) } - @data = RubyProf.stop - @total = @data.threads.values.sum(0) { |method_infos| method_infos.sort.last.total_time } + def output_filename + "#{super}.csv" + end end - def report - if @supported + class Profiler < Performer + def initialize(*args) super - else - '%20s: unsupported' % @metric.name + @supported = @metric.measure_mode rescue false end - end - def record - return unless @supported + def run + return unless @supported - klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact + RubyProf.measure_mode = @metric.measure_mode + RubyProf.start + RubyProf.pause + profile_options[:runs].to_i.times { run_test(@metric, :profile) } + @data = RubyProf.stop + @total = @data.threads.values.sum(0) { |method_infos| method_infos.sort.last.total_time } + end - klasses.each do |klass| - fname = output_filename(klass) - FileUtils.mkdir_p(File.dirname(fname)) - File.open(fname, 'wb') do |file| - klass.new(@data).print(file, profile_options.slice(:min_percent)) + def report + if @supported + super + else + '%20s: unsupported' % @metric.name end end - end - protected - def output_filename(printer_class) - suffix = - case printer_class.name.demodulize - when 'FlatPrinter'; 'flat.txt' - when 'GraphPrinter'; 'graph.txt' - when 'GraphHtmlPrinter'; 'graph.html' - when 'CallTreePrinter'; 'tree.txt' - else printer_class.name.sub(/Printer$/, '').underscore - end + def record + return unless @supported + + klasses = profile_options[:formats].map { |f| RubyProf.const_get("#{f.to_s.camelize}Printer") }.compact - "#{super()}_#{suffix}" + klasses.each do |klass| + fname = output_filename(klass) + FileUtils.mkdir_p(File.dirname(fname)) + File.open(fname, 'wb') do |file| + klass.new(@data).print(file, profile_options.slice(:min_percent)) + end + end end - end - module Metrics - def self.[](name) - const_get(name.to_s.camelize) - rescue NameError - nil + protected + def output_filename(printer_class) + suffix = + case printer_class.name.demodulize + when 'FlatPrinter'; 'flat.txt' + when 'GraphPrinter'; 'graph.txt' + when 'GraphHtmlPrinter'; 'graph.html' + when 'CallTreePrinter'; 'tree.txt' + else printer_class.name.sub(/Printer$/, '').underscore + end + + "#{super()}_#{suffix}" + end end - class Base - attr_reader :total - - def initialize - @total = 0 + module Metrics + def self.[](name) + const_get(name.to_s.camelize) + rescue NameError + nil end - def name - @name ||= self.class.name.demodulize.underscore - end + class Base + attr_reader :total - def measure_mode - self.class::Mode - end + def initialize + @total = 0 + end - def measure - 0 - end + def name + @name ||= self.class.name.demodulize.underscore + end - def benchmark - with_gc_stats do - before = measure - yield - @total += (measure - before) + def measure_mode + self.class::Mode end - end - def profile - RubyProf.resume - yield - ensure - RubyProf.pause - end + def measure + 0 + end - protected - if GC.respond_to?(:enable_stats) - def with_gc_stats - GC.enable_stats - yield - ensure - GC.disable_stats - end - elsif defined?(GC::Profiler) - def with_gc_stats - GC.start - GC.disable - GC::Profiler.enable - yield - ensure - GC::Profiler.disable - GC.enable - end - else - def with_gc_stats + def benchmark + with_gc_stats do + before = measure yield + @total += (measure - before) end end - end - class Time < Base - def measure - ::Time.now.to_f - end - - def format(measurement) - if measurement < 2 - '%d ms' % (measurement * 1000) - else - '%.2f sec' % measurement + def profile + RubyProf.resume + yield + ensure + RubyProf.pause end - end - end - - class ProcessTime < Time - Mode = RubyProf::PROCESS_TIME - - def measure - RubyProf.measure_process_time - end - end - - class WallTime < Time - Mode = RubyProf::WALL_TIME - def measure - RubyProf.measure_wall_time + protected + if GC.respond_to?(:enable_stats) + def with_gc_stats + GC.enable_stats + yield + ensure + GC.disable_stats + end + elsif defined?(GC::Profiler) + def with_gc_stats + GC.start + GC.disable + GC::Profiler.enable + yield + ensure + GC::Profiler.disable + GC.enable + end + else + def with_gc_stats + yield + end + end end - end - class CpuTime < Time - Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME) - - def initialize(*args) - # FIXME: yeah my CPU is 2.33 GHz - RubyProf.cpu_frequency = 2.33e9 - super - end + class Time < Base + def measure + ::Time.now.to_f + end - def measure - RubyProf.measure_cpu_time + def format(measurement) + if measurement < 2 + '%d ms' % (measurement * 1000) + else + '%.2f sec' % measurement + end + end end - end - class Memory < Base - Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) + class ProcessTime < Time + Mode = RubyProf::PROCESS_TIME - # ruby-prof wrapper - if RubyProf.respond_to?(:measure_memory) def measure - RubyProf.measure_memory / 1024.0 + RubyProf.measure_process_time end + end - # Ruby 1.8 + railsbench patch - elsif GC.respond_to?(:allocated_size) - def measure - GC.allocated_size / 1024.0 - end + class WallTime < Time + Mode = RubyProf::WALL_TIME - # Ruby 1.8 + lloyd patch - elsif GC.respond_to?(:heap_info) def measure - GC.heap_info['heap_current_memory'] / 1024.0 + RubyProf.measure_wall_time end + end - # Ruby 1.9 with total_malloc_allocated_size patch - elsif GC.respond_to?(:malloc_total_allocated_size) - def measure - GC.total_malloc_allocated_size / 1024.0 - end + class CpuTime < Time + Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME) - # Ruby 1.9 unpatched - elsif GC.respond_to?(:malloc_allocated_size) - def measure - GC.malloc_allocated_size / 1024.0 + def initialize(*args) + # FIXME: yeah my CPU is 2.33 GHz + RubyProf.cpu_frequency = 2.33e9 + super end - # Ruby 1.9 + GC profiler patch - elsif defined?(GC::Profiler) def measure - GC.enable - GC.start - kb = GC::Profiler.data.last[:HEAP_USE_SIZE] / 1024.0 - GC.disable - kb + RubyProf.measure_cpu_time end end - def format(measurement) - '%.2f KB' % measurement - end - end + class Memory < Base + Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) - class Objects < Base - Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) + # ruby-prof wrapper + if RubyProf.respond_to?(:measure_memory) + def measure + RubyProf.measure_memory / 1024.0 + end - if RubyProf.respond_to?(:measure_allocations) - def measure - RubyProf.measure_allocations - end + # Ruby 1.8 + railsbench patch + elsif GC.respond_to?(:allocated_size) + def measure + GC.allocated_size / 1024.0 + end - # Ruby 1.8 + railsbench patch - elsif ObjectSpace.respond_to?(:allocated_objects) - def measure - ObjectSpace.allocated_objects + # Ruby 1.8 + lloyd patch + elsif GC.respond_to?(:heap_info) + def measure + GC.heap_info['heap_current_memory'] / 1024.0 + end + + # Ruby 1.9 with total_malloc_allocated_size patch + elsif GC.respond_to?(:malloc_total_allocated_size) + def measure + GC.total_malloc_allocated_size / 1024.0 + end + + # Ruby 1.9 unpatched + elsif GC.respond_to?(:malloc_allocated_size) + def measure + GC.malloc_allocated_size / 1024.0 + end + + # Ruby 1.9 + GC profiler patch + elsif defined?(GC::Profiler) + def measure + GC.enable + GC.start + kb = GC::Profiler.data.last[:HEAP_USE_SIZE] / 1024.0 + GC.disable + kb + end end - # Ruby 1.9 + GC profiler patch - elsif defined?(GC::Profiler) - def measure - GC.enable - GC.start - last = GC::Profiler.data.last - count = last[:HEAP_LIVE_OBJECTS] + last[:HEAP_FREE_OBJECTS] - GC.disable - count + def format(measurement) + '%.2f KB' % measurement end end - def format(measurement) - measurement.to_i.to_s - end - end + class Objects < Base + Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) - class GcRuns < Base - Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) + if RubyProf.respond_to?(:measure_allocations) + def measure + RubyProf.measure_allocations + end - if RubyProf.respond_to?(:measure_gc_runs) - def measure - RubyProf.measure_gc_runs - end - elsif GC.respond_to?(:collections) - def measure - GC.collections - end - elsif GC.respond_to?(:heap_info) - def measure - GC.heap_info['num_gc_passes'] + # Ruby 1.8 + railsbench patch + elsif ObjectSpace.respond_to?(:allocated_objects) + def measure + ObjectSpace.allocated_objects + end + + # Ruby 1.9 + GC profiler patch + elsif defined?(GC::Profiler) + def measure + GC.enable + GC.start + last = GC::Profiler.data.last + count = last[:HEAP_LIVE_OBJECTS] + last[:HEAP_FREE_OBJECTS] + GC.disable + count + end end - end - def format(measurement) - measurement.to_i.to_s + def format(measurement) + measurement.to_i.to_s + end end - end - class GcTime < Base - Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + class GcRuns < Base + Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) - if RubyProf.respond_to?(:measure_gc_time) - def measure - RubyProf.measure_gc_time + if RubyProf.respond_to?(:measure_gc_runs) + def measure + RubyProf.measure_gc_runs + end + elsif GC.respond_to?(:collections) + def measure + GC.collections + end + elsif GC.respond_to?(:heap_info) + def measure + GC.heap_info['num_gc_passes'] + end end - elsif GC.respond_to?(:time) - def measure - GC.time + + def format(measurement) + measurement.to_i.to_s end end - def format(measurement) - '%d ms' % (measurement / 1000) + class GcTime < Base + Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + + if RubyProf.respond_to?(:measure_gc_time) + def measure + RubyProf.measure_gc_time + end + elsif GC.respond_to?(:time) + def measure + GC.time + end + end + + def format(measurement) + '%d ms' % (measurement / 1000) + end end end end end end -end +rescue LoadError +end \ No newline at end of file diff --git a/activesupport/test/autoload.rb b/activesupport/test/autoload.rb new file mode 100644 index 0000000000..5d8026a9ca --- /dev/null +++ b/activesupport/test/autoload.rb @@ -0,0 +1,80 @@ +require 'abstract_unit' + +class TestAutoloadModule < ActiveSupport::TestCase + include ActiveSupport::Testing::Isolation + + module ::Fixtures + extend ActiveSupport::Autoload + + module Autoload + extend ActiveSupport::Autoload + end + end + + test "the autoload module works like normal autoload" do + module ::Fixtures::Autoload + autoload :SomeClass, "fixtures/autoload/some_class" + end + + assert_nothing_raised { ::Fixtures::Autoload::SomeClass } + end + + test "when specifying an :eager constant it still works like normal autoload by default" do + module ::Fixtures::Autoload + autoload :SomeClass, "fixtures/autoload/some_class" + end + + assert !$LOADED_FEATURES.include?("fixtures/autoload/some_class.rb") + assert_nothing_raised { ::Fixtures::Autoload::SomeClass } + end + + test ":eager constants can be triggered via ActiveSupport::Autoload.eager_autoload!" do + module ::Fixtures::Autoload + autoload :SomeClass, "fixtures/autoload/some_class" + end + ActiveSupport::Autoload.eager_autoload! + assert $LOADED_FEATURES.include?("fixtures/autoload/some_class.rb") + assert_nothing_raised { ::Fixtures::Autoload::SomeClass } + end + + test "the location of autoloaded constants defaults to :name.underscore" do + module ::Fixtures::Autoload + autoload :SomeClass + end + + assert !$LOADED_FEATURES.include?("fixtures/autoload/some_class.rb") + assert_nothing_raised { ::Fixtures::Autoload::SomeClass } + end + + test "the location of :eager autoloaded constants defaults to :name.underscore" do + module ::Fixtures::Autoload + autoload :SomeClass + end + + ActiveSupport::Autoload.eager_autoload! + assert $LOADED_FEATURES.include?("fixtures/autoload/some_class.rb") + assert_nothing_raised { ::Fixtures::Autoload::SomeClass } + end + + test "a directory for a block of autoloads can be specified" do + module ::Fixtures + autoload_under "autoload" do + autoload :AnotherClass + end + end + + assert !$LOADED_FEATURES.include?("fixtures/autoload/another_class.rb") + assert_nothing_raised { ::Fixtures::AnotherClass } + end + + test "a path for a block of autoloads can be specified" do + module ::Fixtures + autoload_at "fixtures/autoload/another_class" do + autoload :AnotherClass + end + end + + assert !$LOADED_FEATURES.include?("fixtures/autoload/another_class.rb") + assert_nothing_raised { ::Fixtures::AnotherClass } + end +end \ No newline at end of file diff --git a/activesupport/test/fixtures/autoload/another_class.rb b/activesupport/test/fixtures/autoload/another_class.rb new file mode 100644 index 0000000000..a240b3de41 --- /dev/null +++ b/activesupport/test/fixtures/autoload/another_class.rb @@ -0,0 +1,2 @@ +class Fixtures::AnotherClass +end \ No newline at end of file diff --git a/activesupport/test/fixtures/autoload/some_class.rb b/activesupport/test/fixtures/autoload/some_class.rb new file mode 100644 index 0000000000..13b3c73ef5 --- /dev/null +++ b/activesupport/test/fixtures/autoload/some_class.rb @@ -0,0 +1,2 @@ +class Fixtures::Autoload::SomeClass +end \ No newline at end of file diff --git a/railties/lib/rails/console_app.rb b/railties/lib/rails/console_app.rb index 1ad62e5058..9a51d594d3 100644 --- a/railties/lib/rails/console_app.rb +++ b/railties/lib/rails/console_app.rb @@ -1,6 +1,7 @@ require 'active_support/all' require 'active_support/test_case' require 'action_controller' +require 'action_dispatch/test_case' # 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/rails/test_help.rb b/railties/lib/rails/test_help.rb index 9f6c42945f..0bf5f5c625 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -8,8 +8,14 @@ gem "rack-test", "~> 0.5.0" require 'test/unit' require 'active_support/core_ext/kernel/requires' + +# AP is always present +require 'action_controller/test_case' require 'action_view/test_case' +require 'action_dispatch/test_case' + require 'action_mailer/test_case' if defined?(ActionMailer) +require 'active_model/test_case' if defined?(ActiveModel) if defined?(ActiveRecord) require 'active_record/test_case' diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb index 39f23fa0be..558109ba1b 100644 --- a/railties/test/rails_info_controller_test.rb +++ b/railties/test/rails_info_controller_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'action_controller' +require 'action_controller/test_case' require 'rails/info' require 'rails/info_controller' -- cgit v1.2.3 From 96e0638ce2a287eb5c43266fb4eb3e656e9968cf Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Thu, 3 Dec 2009 09:06:01 -0800 Subject: Should fix a few Sam Ruby fails. --- actionmailer/lib/action_mailer.rb | 1 - .../action_dispatch/testing/performance_test.rb | 24 ++++++++-------- activesupport/lib/active_support.rb | 33 +++++++++++++++++++++- activesupport/lib/active_support/autoload.rb | 31 -------------------- 4 files changed, 45 insertions(+), 44 deletions(-) delete mode 100644 activesupport/lib/active_support/autoload.rb diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index 12e781b6c3..eec4040bcc 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -25,7 +25,6 @@ actionpack_path = "#{File.dirname(__FILE__)}/../../actionpack/lib" $:.unshift(actionpack_path) if File.directory?(actionpack_path) require 'action_controller' require 'action_view' -require 'active_support/autoload' module ActionMailer extend ::ActiveSupport::Autoload diff --git a/actionpack/lib/action_dispatch/testing/performance_test.rb b/actionpack/lib/action_dispatch/testing/performance_test.rb index b1ed9d31f4..1b9a6c18b7 100644 --- a/actionpack/lib/action_dispatch/testing/performance_test.rb +++ b/actionpack/lib/action_dispatch/testing/performance_test.rb @@ -1,15 +1,17 @@ require 'active_support/testing/performance' require 'active_support/testing/default' -module ActionDispatch - # An integration test that runs a code profiler on your test methods. - # Profiling output for combinations of each test method, measurement, and - # output format are written to your tmp/performance directory. - # - # By default, process_time is measured and both flat and graph_html output - # formats are written, so you'll have two output files per test method. - class PerformanceTest < ActionDispatch::IntegrationTest - include ActiveSupport::Testing::Performance - include ActiveSupport::Testing::Default +if defined?(ActiveSupport::Testing::Performance) + module ActionDispatch + # An integration test that runs a code profiler on your test methods. + # Profiling output for combinations of each test method, measurement, and + # output format are written to your tmp/performance directory. + # + # By default, process_time is measured and both flat and graph_html output + # formats are written, so you'll have two output files per test method. + class PerformanceTest < ActionDispatch::IntegrationTest + include ActiveSupport::Testing::Performance + include ActiveSupport::Testing::Default + end end -end +end \ No newline at end of file diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 0478ae4ebc..9e21b3faf3 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -34,7 +34,38 @@ module ActiveSupport end end -require 'active_support/autoload' +require "active_support/dependencies/autoload" + +module ActiveSupport + extend ActiveSupport::Autoload + + autoload :BacktraceCleaner + autoload :Base64 + autoload :BasicObject + autoload :Benchmarkable + autoload :BufferedLogger + autoload :Cache + autoload :Callbacks + autoload :Concern + autoload :Configurable + autoload :DeprecatedCallbacks + autoload :Deprecation + autoload :Gzip + autoload :Inflector + autoload :Memoizable + autoload :MessageEncryptor + autoload :MessageVerifier + autoload :Multibyte + autoload :OptionMerger + autoload :OrderedHash + autoload :OrderedOptions + autoload :Notifications + autoload :Rescuable + autoload :SecureRandom + autoload :StringInquirer + autoload :XmlMini +end + require 'active_support/vendor' require 'i18n' diff --git a/activesupport/lib/active_support/autoload.rb b/activesupport/lib/active_support/autoload.rb deleted file mode 100644 index e57cf24b25..0000000000 --- a/activesupport/lib/active_support/autoload.rb +++ /dev/null @@ -1,31 +0,0 @@ -require "active_support/dependencies/autoload" - -module ActiveSupport - extend ActiveSupport::Autoload - - autoload :BacktraceCleaner - autoload :Base64 - autoload :BasicObject - autoload :Benchmarkable - autoload :BufferedLogger - autoload :Cache - autoload :Callbacks - autoload :Concern - autoload :Configurable - autoload :DeprecatedCallbacks - autoload :Deprecation - autoload :Gzip - autoload :Inflector - autoload :Memoizable - autoload :MessageEncryptor - autoload :MessageVerifier - autoload :Multibyte - autoload :OptionMerger - autoload :OrderedHash - autoload :OrderedOptions - autoload :Notifications - autoload :Rescuable - autoload :SecureRandom - autoload :StringInquirer - autoload :XmlMini -end -- cgit v1.2.3 From e55284e8256461fc2440c41548ee9b4216f96b47 Mon Sep 17 00:00:00 2001 From: Ben Marini Date: Sun, 27 Sep 2009 16:25:18 -0700 Subject: Add support for Mysql column positioning via #add_column and #change_column add_column and change_column in the Mysql adapter now accept some additional options: :first => true # Put the column in front of all the columns :after => column_name # Put the colmn after 'column_name' add_column :new_col, :string, :first => true add_column :another_col, :integer, :default => 0, :after => :new_col [#3286 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/CHANGELOG | 2 + .../connection_adapters/mysql_adapter.rb | 15 +++++++ activerecord/test/cases/migration_test.rb | 47 ++++++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index d67b61dc07..d2aa44a23a 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* MySQL: add_ and change_column support positioning. #3286 [Ben Marini] + * Reset your Active Record counter caches with the reset_counter_cache class method. #1211 [Mike Breen] * Remove support for SQLite 2. Please upgrade to SQLite 3+ or install the plugin from git://github.com/rails/sqlite2_adapter.git [Pratik Naik] diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index ad36ff22e3..fa28bc64df 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -470,6 +470,13 @@ module ActiveRecord execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}" end + def add_column(table_name, column_name, type, options = {}) + add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" + add_column_options!(add_column_sql, options) + add_column_position!(add_column_sql, options) + execute(add_column_sql) + end + def change_column_default(table_name, column_name, default) #:nodoc: column = column_for(table_name, column_name) change_column table_name, column_name, column.sql_type, :default => default @@ -498,6 +505,7 @@ module ActiveRecord change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" add_column_options!(change_column_sql, options) + add_column_position!(change_column_sql, options) execute(change_column_sql) end @@ -529,6 +537,13 @@ module ActiveRecord end end + def add_column_position!(sql, options) + if options[:first] + sql << " FIRST" + elsif options[:after] + sql << " AFTER #{quote_column_name(options[:after])}" + end + end # SHOW VARIABLES LIKE 'name' def show_variable(name) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 6d3f938799..0ef34e440a 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -527,6 +527,53 @@ if ActiveRecord::Base.connection.supports_migrations? assert !Person.column_methods_hash.include?(:last_name) end + if current_adapter?(:MysqlAdapter) + def testing_table_for_positioning + Person.connection.create_table :testings, :id => false do |t| + t.column :first, :integer + t.column :second, :integer + t.column :third, :integer + end + + yield Person.connection + ensure + Person.connection.drop_table :testings rescue nil + end + protected :testing_table_for_positioning + + def test_column_positioning + testing_table_for_positioning do |conn| + assert_equal %w(first second third), conn.columns(:testings).map {|c| c.name } + end + end + + def test_add_column_with_positioning + testing_table_for_positioning do |conn| + conn.add_column :testings, :new_col, :integer + assert_equal %w(first second third new_col), conn.columns(:testings).map {|c| c.name } + end + testing_table_for_positioning do |conn| + conn.add_column :testings, :new_col, :integer, :first => true + assert_equal %w(new_col first second third), conn.columns(:testings).map {|c| c.name } + end + testing_table_for_positioning do |conn| + conn.add_column :testings, :new_col, :integer, :after => :first + assert_equal %w(first new_col second third), conn.columns(:testings).map {|c| c.name } + end + end + + def test_change_column_with_positioning + testing_table_for_positioning do |conn| + conn.change_column :testings, :second, :integer, :first => true + assert_equal %w(second first third), conn.columns(:testings).map {|c| c.name } + end + testing_table_for_positioning do |conn| + conn.change_column :testings, :second, :integer, :after => :third + assert_equal %w(first third second), conn.columns(:testings).map {|c| c.name } + end + end + end + def test_add_rename Person.delete_all -- cgit v1.2.3 From 32395899d7c97f69b508b7d7f9b7711f28586679 Mon Sep 17 00:00:00 2001 From: Gabe da Silveira Date: Wed, 2 Dec 2009 15:28:54 -0800 Subject: Replace reset_counter_cache with reset_counters that has API inline with existing update_counters method [#1211 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/CHANGELOG | 2 +- activerecord/lib/active_record/base.rb | 21 +++++++++++++-------- activerecord/test/cases/base_test.rb | 4 ++-- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index d2aa44a23a..1f7a95a53a 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -2,7 +2,7 @@ * MySQL: add_ and change_column support positioning. #3286 [Ben Marini] -* Reset your Active Record counter caches with the reset_counter_cache class method. #1211 [Mike Breen] +* Reset your Active Record counter caches with the reset_counter_cache class method. #1211 [Mike Breen, Gabe da Silveira] * Remove support for SQLite 2. Please upgrade to SQLite 3+ or install the plugin from git://github.com/rails/sqlite2_adapter.git [Pratik Naik] diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e04684dc43..321bba466e 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -967,20 +967,25 @@ module ActiveRecord #:nodoc: connection.select_value(sql, "#{name} Count").to_i end - # Reset a counter cache for all records. + # Resets one or more counter caches to their correct value using an SQL + # count query. This is useful when adding new counter caches, or if the + # counter has been corrupted or modified directly by SQL. # # ==== Parameters # - # * +association_name+ - The name of of the association counter cache to reset + # * +id+ - The id of the object you wish to reset a counter on. + # * +counters+ - One or more counter names to reset # # ==== Examples - # # For all Post records reset the comments_count - # Post.reset_counter_cache(:comments) - def reset_counter_cache(association) - child_class = reflect_on_association(association).klass - counter_name = child_class.reflect_on_association(self.name.downcase.to_sym).counter_cache_column + # + # # For Post with id #1 records reset the comments_count + # Post.reset_counters(1, :comments) + def reset_counters(id, *counters) + object = find(id) + counters.each do |association| + child_class = reflect_on_association(association).klass + counter_name = child_class.reflect_on_association(self.name.downcase.to_sym).counter_cache_column - find_each do |object| connection.update("UPDATE #{quoted_table_name} SET #{connection.quote_column_name(counter_name)} = #{object.send(association).count} WHERE #{connection.quote_column_name(primary_key)} = #{quote_value(object.id)}", "#{name} UPDATE") end end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 737ca01d46..4c16cb4804 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -680,13 +680,13 @@ class BasicsTest < ActiveRecord::TestCase assert_equal -2, Topic.find(2).replies_count end - def test_reset_counter_cache + def test_reset_counters assert_equal 1, Topic.find(1).replies_count Topic.increment_counter("replies_count", 1) assert_equal 2, Topic.find(1).replies_count - Topic.reset_counter_cache(:replies) + Topic.reset_counters(1, :replies) assert_equal 1, Topic.find(1).replies_count end -- cgit v1.2.3 From 4663f75f6b1c1e1de35e165cf7d01b83331775ae Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 5 Dec 2009 13:10:00 -0600 Subject: Update rackmount to fix some pending tests --- actionpack/actionpack.gemspec | 2 +- actionpack/test/controller/routing_test.rb | 22 +++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index e191a12997..23d478fd44 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |s| s.add_dependency('activemodel', '= 3.0.pre') s.add_dependency('rack', '~> 1.0.1') s.add_dependency('rack-test', '~> 0.5.0') - s.add_dependency('rack-mount', '~> 0.3.0') + s.add_dependency('rack-mount', '~> 0.3.2') s.add_dependency('erubis', '~> 2.6.5') s.require_path = 'lib' diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index b83c5792ba..4382227b47 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1437,15 +1437,13 @@ class RouteSetTest < ActiveSupport::TestCase )/x} end - pending do - url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'}) - assert_equal "/page/david", url - assert_raise ActionController::RoutingError do - url = set.generate({:controller => 'pages', :action => 'show', :name => 'davidjamis'}) - end - assert_raise ActionController::RoutingError do - url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'}) - end + url = set.generate({:controller => 'pages', :action => 'show', :name => 'david'}) + assert_equal "/page/david", url + assert_raise ActionController::RoutingError do + url = set.generate({:controller => 'pages', :action => 'show', :name => 'davidjamis'}) + end + assert_raise ActionController::RoutingError do + url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'}) end end @@ -1461,10 +1459,8 @@ class RouteSetTest < ActiveSupport::TestCase )/xi} end - pending do - url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'}) - assert_equal "/page/JAMIS", url - end + url = set.generate({:controller => 'pages', :action => 'show', :name => 'JAMIS'}) + assert_equal "/page/JAMIS", url end def test_route_requirement_recognize_with_xi_modifiers -- cgit v1.2.3 From adc2115132f22cbb772716c167f75346ab6d6c36 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 6 Dec 2009 17:23:43 -0800 Subject: Remove assert_raises usage that only test-unit 1 supports. Breaks minitest and test-unit 2. --- railties/test/application/configuration_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index fc21d4e5fd..1fcab8c651 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -50,8 +50,8 @@ module ApplicationTests Dir.chdir("#{app_path}/app") do require "#{app_path}/config/environment" - assert_raises(NoMethodError, /day/) { 1.day } + assert_raises(NoMethodError) { 1.day } end end end -end \ No newline at end of file +end -- cgit v1.2.3 From 071af66e9ede4057bf1d3e3d248b3949d74bc9c5 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 6 Dec 2009 18:03:18 -0800 Subject: Ruby 1.9.2: explicitly raise NoMethodError for explicit coercion attempts. Calling super gives us NameErrors. --- activesupport/lib/active_support/whiny_nil.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb index c4aaba7ab3..c3ed659d6b 100644 --- a/activesupport/lib/active_support/whiny_nil.rb +++ b/activesupport/lib/active_support/whiny_nil.rb @@ -45,7 +45,7 @@ class NilClass def method_missing(method, *args, &block) # Ruby 1.9.2: disallow explicit coercion via method_missing. if method == :to_ary || method == :to_str - super + raise NoMethodError, "undefined method `#{method}' for nil:NilClass" elsif klass = METHOD_CLASS_MAP[method] raise_nil_warning_for klass, method, caller else -- cgit v1.2.3 From 63e435955e16cf976a8382e757529f4597549f2e Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 6 Dec 2009 18:22:09 -0800 Subject: Ruby 1.9.2: explicitly raise NoMethodError for explicit coercion attempts --- activemodel/lib/active_model/attribute_methods.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 977a101277..8f855958c6 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -235,6 +235,10 @@ module ActiveModel # It's also possible to instantiate related objects, so a Client class belonging to the clients # table with a +master_id+ foreign key can instantiate master through Client#master. def method_missing(method_id, *args, &block) + if method_id == :to_ary || method_id == :to_str + raise NoMethodError, "undefined method `#{method_id}' for #{inspect}:#{self.class}" + end + method_name = method_id.to_s if match = match_attribute_method?(method_name) guard_private_attribute_method!(method_name, args) -- cgit v1.2.3 From 48127c637c3cc0892d49f90a05b0034b9a625d73 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 6 Dec 2009 20:36:40 -0600 Subject: Deprecate recalling generation params when the defaults are nil --- actionpack/test/controller/routing_test.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 4382227b47..ff8dfc2573 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1668,9 +1668,7 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal("/blog/2006/07/25", set.generate({:day => 25}, last_request)) assert_equal("/blog/2005", set.generate({:year => 2005}, last_request)) assert_equal("/blog/show/123", set.generate({:action => "show" , :id => 123}, last_request)) - pending do - assert_equal("/blog/2006/07/28", set.generate({:year => 2006}, last_request)) - end + assert_equal("/blog/2006", set.generate({:year => 2006}, last_request)) assert_equal("/blog/2006", set.generate({:year => 2006, :month => nil}, last_request)) end -- cgit v1.2.3 From 324fa688fcd8f5b6a8e9a0226b0cb3d2829e122c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 2 Dec 2009 22:48:05 -0200 Subject: Make controller.flash public to be used in responders. Signed-off-by: Yehuda Katz --- actionpack/lib/action_controller/metal/flash.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb index f43900faa0..b2d44c6c63 100644 --- a/actionpack/lib/action_controller/metal/flash.rb +++ b/actionpack/lib/action_controller/metal/flash.rb @@ -133,8 +133,20 @@ module ActionController #:nodoc: Array(key || keys).each { |k| used ? @used << k : @used.delete(k) } return key ? self[key] : self end + end + + # Access the contents of the flash. Use flash["notice"] to + # read a notice you put there or flash["notice"] = "hello" + # to put a new one. + def flash #:doc: + unless @_flash + @_flash = session["flash"] || FlashHash.new + @_flash.sweep end + @_flash + end + protected def process_action(method_name) super @@ -146,17 +158,5 @@ module ActionController #:nodoc: super @_flash = nil end - - # Access the contents of the flash. Use flash["notice"] to - # read a notice you put there or flash["notice"] = "hello" - # to put a new one. - def flash #:doc: - unless @_flash - @_flash = session["flash"] || FlashHash.new - @_flash.sweep - end - - @_flash - end end end -- cgit v1.2.3 From 2ecfa817c95f98247d1bd7fb28b208d3f950b43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 2 Dec 2009 22:47:09 -0200 Subject: Responder redirects to resource if destroy fails. Signed-off-by: Yehuda Katz --- .../lib/action_controller/metal/responder.rb | 9 +++-- actionpack/test/controller/mime_responds_test.rb | 42 +++++++++++++++++----- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index 6c76c57839..cb0e600871 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -80,6 +80,11 @@ module ActionController #:nodoc: class Responder attr_reader :controller, :request, :format, :resource, :resources, :options + ACTIONS_FOR_VERBS = { + :post => :new, + :put => :edit + } + def initialize(controller, resources, options={}) @controller = controller @request = controller.request @@ -138,7 +143,7 @@ module ActionController #:nodoc: def navigation_behavior(error) if get? raise error - elsif has_errors? + elsif has_errors? && default_action render :action => default_action else redirect_to resource_location @@ -209,7 +214,7 @@ module ActionController #:nodoc: # the verb is post. # def default_action - @action || (request.post? ? :new : :edit) + @action ||= ACTIONS_FOR_VERBS[request.method] end end end diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index c1fa74b8c8..c857d85c39 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -599,14 +599,18 @@ class RespondWithControllerTest < ActionController::TestCase end end - def test_using_resource_for_post_with_html + def test_using_resource_for_post_with_html_redirects_on_success with_test_route_set do post :using_resource assert_equal "text/html", @response.content_type assert_equal 302, @response.status assert_equal "http://www.example.com/customers/13", @response.location assert @response.redirect? + end + end + def test_using_resource_for_post_with_html_rerender_on_failure + with_test_route_set do errors = { :name => :invalid } Customer.any_instance.stubs(:errors).returns(errors) post :using_resource @@ -617,16 +621,20 @@ class RespondWithControllerTest < ActionController::TestCase end end - def test_using_resource_for_post_with_xml + def test_using_resource_for_post_with_xml_yields_created_on_success with_test_route_set do @request.accept = "application/xml" - post :using_resource assert_equal "application/xml", @response.content_type assert_equal 201, @response.status assert_equal "david", @response.body assert_equal "http://www.example.com/customers/13", @response.location + end + end + def test_using_resource_for_post_with_xml_yields_unprocessable_entity_on_failure + with_test_route_set do + @request.accept = "application/xml" errors = { :name => :invalid } Customer.any_instance.stubs(:errors).returns(errors) post :using_resource @@ -637,14 +645,18 @@ class RespondWithControllerTest < ActionController::TestCase end end - def test_using_resource_for_put_with_html + def test_using_resource_for_put_with_html_redirects_on_success with_test_route_set do put :using_resource assert_equal "text/html", @response.content_type assert_equal 302, @response.status assert_equal "http://www.example.com/customers/13", @response.location assert @response.redirect? + end + end + def test_using_resource_for_put_with_html_rerender_on_failure + with_test_route_set do errors = { :name => :invalid } Customer.any_instance.stubs(:errors).returns(errors) put :using_resource @@ -655,14 +667,16 @@ class RespondWithControllerTest < ActionController::TestCase end end - def test_using_resource_for_put_with_xml + def test_using_resource_for_put_with_xml_yields_ok_on_success @request.accept = "application/xml" - put :using_resource assert_equal "application/xml", @response.content_type assert_equal 200, @response.status assert_equal " ", @response.body + end + def test_using_resource_for_put_with_xml_yields_unprocessable_entity_on_failure + @request.accept = "application/xml" errors = { :name => :invalid } Customer.any_instance.stubs(:errors).returns(errors) put :using_resource @@ -672,7 +686,7 @@ class RespondWithControllerTest < ActionController::TestCase assert_nil @response.location end - def test_using_resource_for_delete_with_html + def test_using_resource_for_delete_with_html_redirects_on_success with_test_route_set do Customer.any_instance.stubs(:destroyed?).returns(true) delete :using_resource @@ -682,7 +696,7 @@ class RespondWithControllerTest < ActionController::TestCase end end - def test_using_resource_for_delete_with_xml + def test_using_resource_for_delete_with_xml_yields_ok_on_success Customer.any_instance.stubs(:destroyed?).returns(true) @request.accept = "application/xml" delete :using_resource @@ -691,6 +705,18 @@ class RespondWithControllerTest < ActionController::TestCase assert_equal " ", @response.body end + def test_using_resource_for_delete_with_html_redirects_on_failure + with_test_route_set do + errors = { :name => :invalid } + Customer.any_instance.stubs(:errors).returns(errors) + Customer.any_instance.stubs(:destroyed?).returns(false) + delete :using_resource + assert_equal "text/html", @response.content_type + assert_equal 302, @response.status + assert_equal "http://www.example.com/customers/13", @response.location + end + end + def test_using_resource_with_parent_for_get @request.accept = "application/xml" get :using_resource_with_parent -- cgit v1.2.3 From 66375434b6c7e03a396bbeda3f7029dea2a59f23 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 7 Dec 2009 17:22:09 -0600 Subject: Pass symbol in as route name when match is used with a symbol --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 400039353c..d9724161a9 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -349,7 +349,7 @@ module ActionDispatch options = args.extract_options! if args.length > 1 - args.each { |path| match(path, options) } + args.each { |path| match(path, options.reverse_merge(:as => path.to_sym)) } return self end -- cgit v1.2.3 From 40ad54e3811913c2bc60c7ee292fa48862f12001 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 7 Dec 2009 18:28:02 -0600 Subject: Allow scope to take :path and :controller options --- actionpack/lib/action_dispatch/routing/mapper.rb | 34 +++++++++++++++--------- actionpack/test/dispatch/routing_test.rb | 2 +- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index d9724161a9..fab8a227bf 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -216,6 +216,27 @@ module ActionDispatch def scope(*args) options = args.extract_options! + case args.first + when String + options[:path] = args.first + when Symbol + options[:controller] = args.first + end + + if path = options.delete(:path) + path_set = true + path, @scope[:path] = @scope[:path], "#{@scope[:path]}#{Rack::Mount::Utils.normalize_path(path)}" + else + path_set = false + end + + if controller = options.delete(:controller) + controller_set = true + controller, @scope[:controller] = @scope[:controller], controller + else + controller_set = false + end + constraints = options.delete(:constraints) || {} unless constraints.is_a?(Hash) block, constraints = constraints, {} @@ -225,19 +246,6 @@ module ActionDispatch options, @scope[:options] = @scope[:options], (@scope[:options] || {}).merge(options) - path_set = controller_set = false - - case args.first - when String - path_set = true - path = args.first - path, @scope[:path] = @scope[:path], "#{@scope[:path]}#{Rack::Mount::Utils.normalize_path(path)}" - when Symbol - controller_set = true - controller = args.first - controller, @scope[:controller] = @scope[:controller], controller - end - yield self diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index b8bcdc2808..85616b5a59 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -94,7 +94,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest controller :articles do scope 'articles' do - scope ':title', :title => /[a-z]+/, :as => :with_title do + scope :path => ':title', :title => /[a-z]+/, :as => :with_title do match ':id', :to => :with_id end end -- cgit v1.2.3 From e8489b43e2746d9b3605eef86731232fa823ce69 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 7 Dec 2009 19:24:33 -0600 Subject: Allow name_prefix to be pass into scope --- actionpack/lib/action_dispatch/routing/mapper.rb | 60 ++++++++---------------- actionpack/test/dispatch/routing_test.rb | 12 ++--- 2 files changed, 24 insertions(+), 48 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index fab8a227bf..abcaa529ae 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -4,16 +4,12 @@ module ActionDispatch module Resources class Resource #:nodoc: attr_reader :plural, :singular - attr_reader :path_prefix, :name_prefix def initialize(entities, options = {}) entities = entities.to_s @plural = entities.pluralize @singular = entities.singularize - - @path_prefix = options[:path_prefix] - @name_prefix = options[:name_prefix] end def name @@ -25,41 +21,17 @@ module ActionDispatch end def member_name - if name_prefix - "#{name_prefix}_#{singular}" - else - singular - end + singular end def collection_name - if name_prefix - "#{name_prefix}_#{plural}" - else - plural - end - end - - def new_name - if name_prefix - "new_#{name_prefix}_#{singular}" - else - "new_#{singular}" - end - end - - def edit_name - if name_prefix - "edit_#{name_prefix}_#{singular}" - else - "edit_#{singular}" - end + plural end end class SingletonResource < Resource #:nodoc: def initialize(entity, options = {}) - super(entity) + super end def name @@ -76,8 +48,7 @@ module ActionDispatch return self end - name_prefix = @scope[:options][:name_prefix] if @scope[:options] - resource = SingletonResource.new(resources.pop, :name_prefix => name_prefix) + resource = SingletonResource.new(resources.pop) if @scope[:scope_level] == :resources parent_resource = @scope[:scope_level_options][:name] @@ -99,8 +70,8 @@ module ActionDispatch post "", :to => :create put "", :to => :update delete "", :to => :destroy - get "new", :to => :new, :as => resource.new_name - get "edit", :to => :edit, :as => resource.edit_name + get "new", :to => :new, :as => "new_#{resource.singular}" + get "edit", :to => :edit, :as => "edit_#{resource.singular}" end end end @@ -117,8 +88,7 @@ module ActionDispatch return self end - name_prefix = @scope[:options][:name_prefix] if @scope[:options] - resource = Resource.new(resources.pop, :name_prefix => name_prefix) + resource = Resource.new(resources.pop) if @scope[:scope_level] == :resources parent_resource = @scope[:scope_level_options][:name] @@ -139,14 +109,14 @@ module ActionDispatch collection do get "", :to => :index, :as => resource.collection_name post "", :to => :create - get "new", :to => :new, :as => resource.new_name + get "new", :to => :new, :as => "new_#{resource.singular}" end member do get "", :to => :show, :as => resource.member_name put "", :to => :update delete "", :to => :destroy - get "edit", :to => :edit, :as => resource.edit_name + get "edit", :to => :edit, :as => "edit_#{resource.singular}" end end end @@ -230,6 +200,13 @@ module ActionDispatch path_set = false end + if name_prefix = options.delete(:name_prefix) + name_prefix_set = true + name_prefix, @scope[:name_prefix] = @scope[:name_prefix], (@scope[:name_prefix] ? "#{@scope[:name_prefix]}_#{name_prefix}" : name_prefix) + else + name_prefix_set = false + end + if controller = options.delete(:controller) controller_set = true controller, @scope[:controller] = @scope[:controller], controller @@ -251,6 +228,7 @@ module ActionDispatch self ensure @scope[:path] = path if path_set + @scope[:name_prefix] = name_prefix if name_prefix_set @scope[:controller] = controller if controller_set @scope[:options] = options @scope[:blocks] = blocks @@ -404,7 +382,9 @@ module ActionDispatch validate_defaults!(app, defaults, segment_keys) app = Constraints.new(app, blocks) - @set.add_route(app, conditions, requirements, defaults, options[:as]) + name = @scope[:name_prefix] ? "#{@scope[:name_prefix]}_#{options[:as]}" : options[:as] + + @set.add_route(app, conditions, requirements, defaults, name) self end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 85616b5a59..86cf2ce335 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -93,7 +93,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end controller :articles do - scope 'articles' do + scope 'articles', :name_prefix => 'article' do scope :path => ':title', :title => /[a-z]+/, :as => :with_title do match ':id', :to => :with_id end @@ -255,9 +255,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/projects/1/companies/1/avatar' assert_equal 'avatars#show', @response.body - pending do - assert_equal '/projects/1/companies/1/avatar', project_company_avatar_path(:project_id => '1', :company_id => '1') - end + assert_equal '/projects/1/companies/1/avatar', project_company_avatar_path(:project_id => '1', :company_id => '1') end end @@ -337,9 +335,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/projects/1/posts/1/subscription' assert_equal 'subscriptions#show', @response.body - pending do - assert_equal '/projects/1/posts/1/subscription', project_post_subscription_path(:project_id => '1', :post_id => '1') - end + assert_equal '/projects/1/posts/1/subscription', project_post_subscription_path(:project_id => '1', :post_id => '1') get '/projects/1/posts/1/comments' assert_equal 'comments#index', @response.body @@ -407,7 +403,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_raise(ActionController::RoutingError) { get '/articles/123/1' } - assert_equal '/articles/rails/1', with_title_path(:title => 'rails', :id => 1) + assert_equal '/articles/rails/1', article_with_title_path(:title => 'rails', :id => 1) end end -- cgit v1.2.3 From 5835447b6fbc956f22011fc33bcc882db144c7c1 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 7 Dec 2009 19:31:29 -0600 Subject: named_prefix doesn't join with "_" --- actionpack/lib/action_dispatch/routing/mapper.rb | 8 ++++---- actionpack/test/dispatch/routing_test.rb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index abcaa529ae..7eb648cedf 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -63,7 +63,7 @@ module ActionDispatch controller(resource.controller) do namespace(resource.name) do - with_scope_level(:resource, :name => resource.singular, :name_prefix => resource.member_name) do + with_scope_level(:resource, :name => resource.singular, :name_prefix => "#{resource.member_name}_") do yield if block_given? get "", :to => :show, :as => resource.member_name @@ -103,7 +103,7 @@ module ActionDispatch controller(resource.controller) do namespace(resource.name) do - with_scope_level(:resources, :name => resource.singular, :name_prefix => resource.member_name) do + with_scope_level(:resources, :name => resource.singular, :name_prefix => "#{resource.member_name}_") do yield if block_given? collection do @@ -202,7 +202,7 @@ module ActionDispatch if name_prefix = options.delete(:name_prefix) name_prefix_set = true - name_prefix, @scope[:name_prefix] = @scope[:name_prefix], (@scope[:name_prefix] ? "#{@scope[:name_prefix]}_#{name_prefix}" : name_prefix) + name_prefix, @scope[:name_prefix] = @scope[:name_prefix], (@scope[:name_prefix] ? "#{@scope[:name_prefix]}#{name_prefix}" : name_prefix) else name_prefix_set = false end @@ -382,7 +382,7 @@ module ActionDispatch validate_defaults!(app, defaults, segment_keys) app = Constraints.new(app, blocks) - name = @scope[:name_prefix] ? "#{@scope[:name_prefix]}_#{options[:as]}" : options[:as] + name = @scope[:name_prefix] ? "#{@scope[:name_prefix]}#{options[:as]}" : options[:as] @set.add_route(app, conditions, requirements, defaults, name) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 86cf2ce335..0a35868d7c 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -93,7 +93,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end controller :articles do - scope 'articles', :name_prefix => 'article' do + scope 'articles', :name_prefix => 'article_' do scope :path => ':title', :title => /[a-z]+/, :as => :with_title do match ':id', :to => :with_id end -- cgit v1.2.3 From e600b41c7f2029b1fb4b75b90acc3379acf95d2b Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 7 Dec 2009 19:47:47 -0600 Subject: Cleanup resource scoping by passing down the parent resource object in the scope --- actionpack/lib/action_dispatch/routing/mapper.rb | 31 +++++++++++++++--------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 7eb648cedf..9ca4e16802 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -27,6 +27,14 @@ module ActionDispatch def collection_name plural end + + def id_segment + ":#{singular}_id" + end + + def member_name_prefix + "#{member_name}_" + end end class SingletonResource < Resource #:nodoc: @@ -51,10 +59,8 @@ module ActionDispatch resource = SingletonResource.new(resources.pop) if @scope[:scope_level] == :resources - parent_resource = @scope[:scope_level_options][:name] - parent_named_prefix = @scope[:scope_level_options][:name_prefix] with_scope_level(:member) do - scope(":#{parent_resource}_id", :name_prefix => parent_named_prefix) do + scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name_prefix) do resource(resource.name, options, &block) end end @@ -63,7 +69,7 @@ module ActionDispatch controller(resource.controller) do namespace(resource.name) do - with_scope_level(:resource, :name => resource.singular, :name_prefix => "#{resource.member_name}_") do + with_scope_level(:resource, resource) do yield if block_given? get "", :to => :show, :as => resource.member_name @@ -91,10 +97,8 @@ module ActionDispatch resource = Resource.new(resources.pop) if @scope[:scope_level] == :resources - parent_resource = @scope[:scope_level_options][:name] - parent_named_prefix = @scope[:scope_level_options][:name_prefix] with_scope_level(:member) do - scope(":#{parent_resource}_id", :name_prefix => parent_named_prefix) do + scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name_prefix) do resources(resource.name, options, &block) end end @@ -103,7 +107,7 @@ module ActionDispatch controller(resource.controller) do namespace(resource.name) do - with_scope_level(:resources, :name => resource.singular, :name_prefix => "#{resource.member_name}_") do + with_scope_level(:resources, resource) do yield if block_given? collection do @@ -165,14 +169,19 @@ module ActionDispatch super end + protected + def parent_resource + @scope[:scope_level_resource] + end + private - def with_scope_level(kind, options = {}) + def with_scope_level(kind, resource = parent_resource) old, @scope[:scope_level] = @scope[:scope_level], kind - old_options, @scope[:scope_level_options] = @scope[:scope_level_options], options + old_resource, @scope[:scope_level_resource] = @scope[:scope_level_resource], resource yield ensure @scope[:scope_level] = old - @scope[:scope_level_options] = old_options + @scope[:scope_level_resource] = old_resource end end -- cgit v1.2.3 From e86a82c52c2c5edb8c95f9fd882b491dd1f550f4 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 7 Dec 2009 19:50:13 -0600 Subject: Move name_prefix merging into Scoping concern --- actionpack/lib/action_dispatch/routing/mapper.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 9ca4e16802..8dbf33d5f9 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -258,7 +258,13 @@ module ActionDispatch def match(*args) options = args.extract_options! + options = (@scope[:options] || {}).merge(options) + + if @scope[:name_prefix] + options[:as] = "#{@scope[:name_prefix]}#{options[:as]}" + end + args.push(options) super(*args) end @@ -391,9 +397,7 @@ module ActionDispatch validate_defaults!(app, defaults, segment_keys) app = Constraints.new(app, blocks) - name = @scope[:name_prefix] ? "#{@scope[:name_prefix]}#{options[:as]}" : options[:as] - - @set.add_route(app, conditions, requirements, defaults, name) + @set.add_route(app, conditions, requirements, defaults, options[:as]) self end -- cgit v1.2.3 From 81d7227c9b8f4a74a74cf3b2cb183ce130b3f679 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 7 Dec 2009 19:59:23 -0600 Subject: Move base mapper methods into Base module so plugins can easily extend the mapper --- actionpack/lib/action_dispatch/routing/mapper.rb | 492 ++++++++++++----------- 1 file changed, 247 insertions(+), 245 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 8dbf33d5f9..1a742f61e6 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -1,6 +1,249 @@ module ActionDispatch module Routing class Mapper + class Constraints + def new(app, constraints = []) + if constraints.any? + super(app, constraints) + else + app + end + end + + def initialize(app, constraints = []) + @app, @constraints = app, constraints + end + + def call(env) + req = Rack::Request.new(env) + + @constraints.each { |constraint| + if constraint.respond_to?(:matches?) && !constraint.matches?(req) + return [417, {}, []] + elsif constraint.respond_to?(:call) && !constraint.call(req) + return [417, {}, []] + end + } + + @app.call(env) + end + end + + module Base + def initialize(set) + @set = set + end + + def root(options = {}) + match '/', options.merge(:as => :root) + end + + def match(*args) + options = args.extract_options! + + if args.length > 1 + args.each { |path| match(path, options.reverse_merge(:as => path.to_sym)) } + return self + end + + if args.first.is_a?(Symbol) + return match(args.first.to_s, options.merge(:to => args.first.to_sym)) + end + + path = args.first + + conditions, defaults = {}, {} + + path = nil if path == "" + path = Rack::Mount::Utils.normalize_path(path) if path + path = "#{@scope[:path]}#{path}" if @scope[:path] + + raise ArgumentError, "path is required" unless path + + constraints = options[:constraints] || {} + unless constraints.is_a?(Hash) + block, constraints = constraints, {} + end + blocks = ((@scope[:blocks] || []) + [block]).compact + constraints = (@scope[:constraints] || {}).merge(constraints) + options.each { |k, v| constraints[k] = v if v.is_a?(Regexp) } + + conditions[:path_info] = path + requirements = constraints.dup + + path_regexp = Rack::Mount::Strexp.compile(path, constraints, SEPARATORS) + segment_keys = Rack::Mount::RegexpWithNamedGroups.new(path_regexp).names + constraints.reject! { |k, v| segment_keys.include?(k.to_s) } + conditions.merge!(constraints) + + requirements[:controller] ||= @set.controller_constraints + + if via = options[:via] + via = Array(via).map { |m| m.to_s.upcase } + conditions[:request_method] = Regexp.union(*via) + end + + defaults[:controller] ||= @scope[:controller].to_s if @scope[:controller] + + app = initialize_app_endpoint(options, defaults) + validate_defaults!(app, defaults, segment_keys) + app = Constraints.new(app, blocks) + + @set.add_route(app, conditions, requirements, defaults, options[:as]) + + self + end + + private + def initialize_app_endpoint(options, defaults) + app = nil + + if options[:to].respond_to?(:call) + app = options[:to] + defaults.delete(:controller) + defaults.delete(:action) + elsif options[:to].is_a?(String) + defaults[:controller], defaults[:action] = options[:to].split('#') + elsif options[:to].is_a?(Symbol) + defaults[:action] = options[:to].to_s + end + + app || Routing::RouteSet::Dispatcher.new(:defaults => defaults) + end + + def validate_defaults!(app, defaults, segment_keys) + return unless app.is_a?(Routing::RouteSet::Dispatcher) + + unless defaults.include?(:controller) || segment_keys.include?("controller") + raise ArgumentError, "missing :controller" + end + + unless defaults.include?(:action) || segment_keys.include?("action") + raise ArgumentError, "missing :action" + end + end + end + + module HttpHelpers + def get(*args, &block) + map_method(:get, *args, &block) + end + + def post(*args, &block) + map_method(:post, *args, &block) + end + + def put(*args, &block) + map_method(:put, *args, &block) + end + + def delete(*args, &block) + map_method(:delete, *args, &block) + end + + def redirect(path, options = {}) + status = options[:status] || 301 + lambda { |env| + req = Rack::Request.new(env) + url = req.scheme + '://' + req.host + path + [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] + } + end + + private + def map_method(method, *args, &block) + options = args.extract_options! + options[:via] = method + args.push(options) + match(*args, &block) + self + end + end + + module Scoping + def initialize(*args) + @scope = {} + super + end + + def scope(*args) + options = args.extract_options! + + case args.first + when String + options[:path] = args.first + when Symbol + options[:controller] = args.first + end + + if path = options.delete(:path) + path_set = true + path, @scope[:path] = @scope[:path], "#{@scope[:path]}#{Rack::Mount::Utils.normalize_path(path)}" + else + path_set = false + end + + if name_prefix = options.delete(:name_prefix) + name_prefix_set = true + name_prefix, @scope[:name_prefix] = @scope[:name_prefix], (@scope[:name_prefix] ? "#{@scope[:name_prefix]}#{name_prefix}" : name_prefix) + else + name_prefix_set = false + end + + if controller = options.delete(:controller) + controller_set = true + controller, @scope[:controller] = @scope[:controller], controller + else + controller_set = false + end + + constraints = options.delete(:constraints) || {} + unless constraints.is_a?(Hash) + block, constraints = constraints, {} + end + constraints, @scope[:constraints] = @scope[:constraints], (@scope[:constraints] || {}).merge(constraints) + blocks, @scope[:blocks] = @scope[:blocks], (@scope[:blocks] || []) + [block] + + options, @scope[:options] = @scope[:options], (@scope[:options] || {}).merge(options) + + yield + + self + ensure + @scope[:path] = path if path_set + @scope[:name_prefix] = name_prefix if name_prefix_set + @scope[:controller] = controller if controller_set + @scope[:options] = options + @scope[:blocks] = blocks + @scope[:constraints] = constraints + end + + def controller(controller) + scope(controller.to_sym) { yield } + end + + def namespace(path) + scope(path.to_s) { yield } + end + + def constraints(constraints = {}) + scope(:constraints => constraints) { yield } + end + + def match(*args) + options = args.extract_options! + + options = (@scope[:options] || {}).merge(options) + + if @scope[:name_prefix] + options[:as] = "#{@scope[:name_prefix]}#{options[:as]}" + end + + args.push(options) + super(*args) + end + end + module Resources class Resource #:nodoc: attr_reader :plural, :singular @@ -185,251 +428,10 @@ module ActionDispatch end end - module Scoping - def self.extended(object) - object.instance_eval do - @scope = {} - end - end - - def scope(*args) - options = args.extract_options! - - case args.first - when String - options[:path] = args.first - when Symbol - options[:controller] = args.first - end - - if path = options.delete(:path) - path_set = true - path, @scope[:path] = @scope[:path], "#{@scope[:path]}#{Rack::Mount::Utils.normalize_path(path)}" - else - path_set = false - end - - if name_prefix = options.delete(:name_prefix) - name_prefix_set = true - name_prefix, @scope[:name_prefix] = @scope[:name_prefix], (@scope[:name_prefix] ? "#{@scope[:name_prefix]}#{name_prefix}" : name_prefix) - else - name_prefix_set = false - end - - if controller = options.delete(:controller) - controller_set = true - controller, @scope[:controller] = @scope[:controller], controller - else - controller_set = false - end - - constraints = options.delete(:constraints) || {} - unless constraints.is_a?(Hash) - block, constraints = constraints, {} - end - constraints, @scope[:constraints] = @scope[:constraints], (@scope[:constraints] || {}).merge(constraints) - blocks, @scope[:blocks] = @scope[:blocks], (@scope[:blocks] || []) + [block] - - options, @scope[:options] = @scope[:options], (@scope[:options] || {}).merge(options) - - yield - - self - ensure - @scope[:path] = path if path_set - @scope[:name_prefix] = name_prefix if name_prefix_set - @scope[:controller] = controller if controller_set - @scope[:options] = options - @scope[:blocks] = blocks - @scope[:constraints] = constraints - end - - def controller(controller) - scope(controller.to_sym) { yield } - end - - def namespace(path) - scope(path.to_s) { yield } - end - - def constraints(constraints = {}) - scope(:constraints => constraints) { yield } - end - - def match(*args) - options = args.extract_options! - - options = (@scope[:options] || {}).merge(options) - - if @scope[:name_prefix] - options[:as] = "#{@scope[:name_prefix]}#{options[:as]}" - end - - args.push(options) - super(*args) - end - end - - module HttpHelpers - def get(*args, &block) - map_method(:get, *args, &block) - end - - def post(*args, &block) - map_method(:post, *args, &block) - end - - def put(*args, &block) - map_method(:put, *args, &block) - end - - def delete(*args, &block) - map_method(:delete, *args, &block) - end - - def redirect(path, options = {}) - status = options[:status] || 301 - lambda { |env| - req = Rack::Request.new(env) - url = req.scheme + '://' + req.host + path - [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] - } - end - - private - def map_method(method, *args, &block) - options = args.extract_options! - options[:via] = method - args.push(options) - match(*args, &block) - self - end - end - - class Constraints - def new(app, constraints = []) - if constraints.any? - super(app, constraints) - else - app - end - end - - def initialize(app, constraints = []) - @app, @constraints = app, constraints - end - - def call(env) - req = Rack::Request.new(env) - - @constraints.each { |constraint| - if constraint.respond_to?(:matches?) && !constraint.matches?(req) - return [417, {}, []] - elsif constraint.respond_to?(:call) && !constraint.call(req) - return [417, {}, []] - end - } - - @app.call(env) - end - end - - def initialize(set) - @set = set - - extend HttpHelpers - extend Scoping - extend Resources - end - - def root(options = {}) - match '/', options.merge(:as => :root) - end - - def match(*args) - options = args.extract_options! - - if args.length > 1 - args.each { |path| match(path, options.reverse_merge(:as => path.to_sym)) } - return self - end - - if args.first.is_a?(Symbol) - return match(args.first.to_s, options.merge(:to => args.first.to_sym)) - end - - path = args.first - - conditions, defaults = {}, {} - - path = nil if path == "" - path = Rack::Mount::Utils.normalize_path(path) if path - path = "#{@scope[:path]}#{path}" if @scope[:path] - - raise ArgumentError, "path is required" unless path - - constraints = options[:constraints] || {} - unless constraints.is_a?(Hash) - block, constraints = constraints, {} - end - blocks = ((@scope[:blocks] || []) + [block]).compact - constraints = (@scope[:constraints] || {}).merge(constraints) - options.each { |k, v| constraints[k] = v if v.is_a?(Regexp) } - - conditions[:path_info] = path - requirements = constraints.dup - - path_regexp = Rack::Mount::Strexp.compile(path, constraints, SEPARATORS) - segment_keys = Rack::Mount::RegexpWithNamedGroups.new(path_regexp).names - constraints.reject! { |k, v| segment_keys.include?(k.to_s) } - conditions.merge!(constraints) - - requirements[:controller] ||= @set.controller_constraints - - if via = options[:via] - via = Array(via).map { |m| m.to_s.upcase } - conditions[:request_method] = Regexp.union(*via) - end - - defaults[:controller] ||= @scope[:controller].to_s if @scope[:controller] - - app = initialize_app_endpoint(options, defaults) - validate_defaults!(app, defaults, segment_keys) - app = Constraints.new(app, blocks) - - @set.add_route(app, conditions, requirements, defaults, options[:as]) - - self - end - - private - def initialize_app_endpoint(options, defaults) - app = nil - - if options[:to].respond_to?(:call) - app = options[:to] - defaults.delete(:controller) - defaults.delete(:action) - elsif options[:to].is_a?(String) - defaults[:controller], defaults[:action] = options[:to].split('#') - elsif options[:to].is_a?(Symbol) - defaults[:action] = options[:to].to_s - end - - app || Routing::RouteSet::Dispatcher.new(:defaults => defaults) - end - - def validate_defaults!(app, defaults, segment_keys) - return unless app.is_a?(Routing::RouteSet::Dispatcher) - - unless defaults.include?(:controller) || segment_keys.include?("controller") - raise ArgumentError, "missing :controller" - end - - unless defaults.include?(:action) || segment_keys.include?("action") - raise ArgumentError, "missing :action" - end - end + include Base + include HttpHelpers + include Scoping + include Resources end end end -- cgit v1.2.3 From 0c34e3f41ae79bb675c74b697ef92bad59b25f7f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 7 Dec 2009 20:11:57 -0600 Subject: Ignore name_prefix unless there is an explicit name --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 1a742f61e6..86470c2017 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -235,7 +235,7 @@ module ActionDispatch options = (@scope[:options] || {}).merge(options) - if @scope[:name_prefix] + if @scope[:name_prefix] && options[:as] options[:as] = "#{@scope[:name_prefix]}#{options[:as]}" end -- cgit v1.2.3 From 1fc58a889d72e9a36167b41fc3cd055c1f58774e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 7 Dec 2009 20:57:01 -0600 Subject: Fixed named prefix scope in resource member and collection actions --- actionpack/lib/action_dispatch/routing/mapper.rb | 60 ++++++++++++++---------- actionpack/test/dispatch/routing_test.rb | 22 +++------ 2 files changed, 40 insertions(+), 42 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 86470c2017..7647bdeb5d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -41,15 +41,6 @@ module ActionDispatch def match(*args) options = args.extract_options! - if args.length > 1 - args.each { |path| match(path, options.reverse_merge(:as => path.to_sym)) } - return self - end - - if args.first.is_a?(Symbol) - return match(args.first.to_s, options.merge(:to => args.first.to_sym)) - end - path = args.first conditions, defaults = {}, {} @@ -185,7 +176,7 @@ module ActionDispatch if name_prefix = options.delete(:name_prefix) name_prefix_set = true - name_prefix, @scope[:name_prefix] = @scope[:name_prefix], (@scope[:name_prefix] ? "#{@scope[:name_prefix]}#{name_prefix}" : name_prefix) + name_prefix, @scope[:name_prefix] = @scope[:name_prefix], (@scope[:name_prefix] ? "#{@scope[:name_prefix]}_#{name_prefix}" : name_prefix) else name_prefix_set = false end @@ -235,8 +226,10 @@ module ActionDispatch options = (@scope[:options] || {}).merge(options) - if @scope[:name_prefix] && options[:as] - options[:as] = "#{@scope[:name_prefix]}#{options[:as]}" + if @scope[:name_prefix] && !options[:as].blank? + options[:as] = "#{@scope[:name_prefix]}_#{options[:as]}" + elsif @scope[:name_prefix] && options[:as].blank? + options[:as] = @scope[:name_prefix].to_s end args.push(options) @@ -274,10 +267,6 @@ module ActionDispatch def id_segment ":#{singular}_id" end - - def member_name_prefix - "#{member_name}_" - end end class SingletonResource < Resource #:nodoc: @@ -303,7 +292,7 @@ module ActionDispatch if @scope[:scope_level] == :resources with_scope_level(:member) do - scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name_prefix) do + scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name) do resource(resource.name, options, &block) end end @@ -341,7 +330,7 @@ module ActionDispatch if @scope[:scope_level] == :resources with_scope_level(:member) do - scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name_prefix) do + scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name) do resources(resource.name, options, &block) end end @@ -353,17 +342,19 @@ module ActionDispatch with_scope_level(:resources, resource) do yield if block_given? - collection do + with_scope_level(:collection) do get "", :to => :index, :as => resource.collection_name post "", :to => :create get "new", :to => :new, :as => "new_#{resource.singular}" end - member do - get "", :to => :show, :as => resource.member_name - put "", :to => :update - delete "", :to => :destroy - get "edit", :to => :edit, :as => "edit_#{resource.singular}" + with_scope_level(:member) do + scope(":id") do + get "", :to => :show, :as => resource.member_name + put "", :to => :update + delete "", :to => :destroy + get "edit", :to => :edit, :as => "edit_#{resource.singular}" + end end end end @@ -378,7 +369,9 @@ module ActionDispatch end with_scope_level(:collection) do - yield + scope(:name_prefix => parent_resource.member_name, :as => "") do + yield + end end end @@ -388,7 +381,7 @@ module ActionDispatch end with_scope_level(:member) do - scope(":id") do + scope(":id", :name_prefix => parent_resource.member_name, :as => "") do yield end end @@ -396,6 +389,21 @@ module ActionDispatch def match(*args) options = args.extract_options! + + if args.length > 1 + args.each { |path| match(path, options) } + return self + end + + if args.first.is_a?(Symbol) + begin + old_name_prefix, @scope[:name_prefix] = @scope[:name_prefix], "#{args.first}_#{@scope[:name_prefix]}" + return match(args.first.to_s, options.merge(:to => args.first.to_sym)) + ensure + @scope[:name_prefix] = old_name_prefix + end + end + args.push(options) case options.delete(:on) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 0a35868d7c..9262b1c7db 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -93,7 +93,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end controller :articles do - scope 'articles', :name_prefix => 'article_' do + scope 'articles', :name_prefix => 'article' do scope :path => ':title', :title => /[a-z]+/, :as => :with_title do match ':id', :to => :with_id end @@ -267,9 +267,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest post '/projects/1/images/1/revise' assert_equal 'images#revise', @response.body - pending do - assert_equal '/projects/1/images/1/revise', revise_project_image_path(:project_id => '1', :id => '1') - end + assert_equal '/projects/1/images/1/revise', revise_project_image_path(:project_id => '1', :id => '1') end end @@ -291,21 +289,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest put '/projects/1/people/1/accessible_projects' assert_equal 'people#accessible_projects', @response.body - pending do - assert_equal '/projects/1/people/1/accessible_projects', accessible_projects_project_person_path(:project_id => '1', :id => '1') - end + assert_equal '/projects/1/people/1/accessible_projects', accessible_projects_project_person_path(:project_id => '1', :id => '1') post '/projects/1/people/1/resend' assert_equal 'people#resend', @response.body - pending do - assert_equal '/projects/1/people/1/resend', resend_project_person_path(:project_id => '1', :id => '1') - end + assert_equal '/projects/1/people/1/resend', resend_project_person_path(:project_id => '1', :id => '1') post '/projects/1/people/1/generate_new_password' assert_equal 'people#generate_new_password', @response.body - pending do - assert_equal '/projects/1/people/1/generate_new_password', generate_new_password_project_person_path(:project_id => '1', :id => '1') - end + assert_equal '/projects/1/people/1/generate_new_password', generate_new_password_project_person_path(:project_id => '1', :id => '1') end end @@ -329,9 +321,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest post '/projects/1/posts/1/preview' assert_equal 'posts#preview', @response.body - pending do - assert_equal '/projects/1/posts/1/preview', preview_project_post_path(:project_id => '1', :id => '1') - end + assert_equal '/projects/1/posts/1/preview', preview_project_post_path(:project_id => '1', :id => '1') get '/projects/1/posts/1/subscription' assert_equal 'subscriptions#show', @response.body -- cgit v1.2.3 From 3d91d7f0a2bd4b1e104dd8847a2fe9f206c916ca Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 8 Dec 2009 15:31:56 -0600 Subject: Routes added under resource collection should be prefixed with resource collection name --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- actionpack/test/dispatch/routing_test.rb | 17 ++++------------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 7647bdeb5d..513c6a5c5f 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -369,7 +369,7 @@ module ActionDispatch end with_scope_level(:collection) do - scope(:name_prefix => parent_resource.member_name, :as => "") do + scope(:name_prefix => parent_resource.collection_name, :as => "") do yield end end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 9262b1c7db..029bec2124 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -236,10 +236,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest put '/projects/1/participants/update_all' assert_equal 'participants#update_all', @response.body - - pending do - assert_equal '/projects/1/participants/update_all', update_all_project_participants_path(:project_id => '1') - end + assert_equal '/projects/1/participants/update_all', update_all_project_participants_path(:project_id => '1') end end @@ -309,15 +306,11 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/projects/1/posts/archive' assert_equal 'posts#archive', @response.body - pending do - assert_equal '/projects/1/posts/archive', archive_project_posts_path(:project_id => '1') - end + assert_equal '/projects/1/posts/archive', archive_project_posts_path(:project_id => '1') get '/projects/1/posts/toggle_view' assert_equal 'posts#toggle_view', @response.body - pending do - assert_equal '/projects/1/posts/toggle_view', toggle_view_project_posts_path(:project_id => '1') - end + assert_equal '/projects/1/posts/toggle_view', toggle_view_project_posts_path(:project_id => '1') post '/projects/1/posts/1/preview' assert_equal 'posts#preview', @response.body @@ -333,9 +326,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest post '/projects/1/posts/1/comments/preview' assert_equal 'comments#preview', @response.body - pending do - assert_equal '/projects/1/posts/1/comments/preview', preview_project_post_comments_path(:project_id => '1', :post_id => '1') - end + assert_equal '/projects/1/posts/1/comments/preview', preview_project_post_comments_path(:project_id => '1', :post_id => '1') end end -- cgit v1.2.3 From 33658ea1ae4170f4b0b5123e240d79bb292719e7 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 8 Dec 2009 15:50:44 -0600 Subject: Don't use name prefix by itself unless as is an empty string --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 513c6a5c5f..e05845a04f 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -228,7 +228,7 @@ module ActionDispatch if @scope[:name_prefix] && !options[:as].blank? options[:as] = "#{@scope[:name_prefix]}_#{options[:as]}" - elsif @scope[:name_prefix] && options[:as].blank? + elsif @scope[:name_prefix] && options[:as] == "" options[:as] = @scope[:name_prefix].to_s end -- cgit v1.2.3 From c4df6332a4d8292dd7d6bd6a1badc896a2323d11 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 8 Dec 2009 16:06:46 -0600 Subject: Seperate scope level for nesting resources --- actionpack/lib/action_dispatch/routing/mapper.rb | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index e05845a04f..24b04088e6 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -291,10 +291,8 @@ module ActionDispatch resource = SingletonResource.new(resources.pop) if @scope[:scope_level] == :resources - with_scope_level(:member) do - scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name) do - resource(resource.name, options, &block) - end + nested do + resource(resource.name, options, &block) end return self end @@ -329,10 +327,8 @@ module ActionDispatch resource = Resource.new(resources.pop) if @scope[:scope_level] == :resources - with_scope_level(:member) do - scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name) do - resources(resource.name, options, &block) - end + nested do + resources(resource.name, options, &block) end return self end @@ -387,6 +383,18 @@ module ActionDispatch end end + def nested + unless @scope[:scope_level] == :resources + raise ArgumentError, "can't use nested outside resources scope" + end + + with_scope_level(:nested) do + scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name) do + yield + end + end + end + def match(*args) options = args.extract_options! -- cgit v1.2.3 From ce5f27b04b1ff25eed520a3d06b3b9c150536e21 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 8 Dec 2009 16:13:00 -0600 Subject: Remove double scoping blocks and just use one --- actionpack/lib/action_dispatch/routing/mapper.rb | 52 +++++++++++------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 24b04088e6..8cb7745aff 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -297,18 +297,16 @@ module ActionDispatch return self end - controller(resource.controller) do - namespace(resource.name) do - with_scope_level(:resource, resource) do - yield if block_given? - - get "", :to => :show, :as => resource.member_name - post "", :to => :create - put "", :to => :update - delete "", :to => :destroy - get "new", :to => :new, :as => "new_#{resource.singular}" - get "edit", :to => :edit, :as => "edit_#{resource.singular}" - end + scope(:path => resource.name, :controller => resource.controller) do + with_scope_level(:resource, resource) do + yield if block_given? + + get "", :to => :show, :as => resource.member_name + post "", :to => :create + put "", :to => :update + delete "", :to => :destroy + get "new", :to => :new, :as => "new_#{resource.singular}" + get "edit", :to => :edit, :as => "edit_#{resource.singular}" end end @@ -333,24 +331,22 @@ module ActionDispatch return self end - controller(resource.controller) do - namespace(resource.name) do - with_scope_level(:resources, resource) do - yield if block_given? + scope(:path => resource.name, :controller => resource.controller) do + with_scope_level(:resources, resource) do + yield if block_given? - with_scope_level(:collection) do - get "", :to => :index, :as => resource.collection_name - post "", :to => :create - get "new", :to => :new, :as => "new_#{resource.singular}" - end + with_scope_level(:collection) do + get "", :to => :index, :as => resource.collection_name + post "", :to => :create + get "new", :to => :new, :as => "new_#{resource.singular}" + end - with_scope_level(:member) do - scope(":id") do - get "", :to => :show, :as => resource.member_name - put "", :to => :update - delete "", :to => :destroy - get "edit", :to => :edit, :as => "edit_#{resource.singular}" - end + with_scope_level(:member) do + scope(":id") do + get "", :to => :show, :as => resource.member_name + put "", :to => :update + delete "", :to => :destroy + get "edit", :to => :edit, :as => "edit_#{resource.singular}" end end end -- cgit v1.2.3 From ac711043ecec0dd15a159eff8081be8f10584be0 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 8 Dec 2009 16:15:25 -0600 Subject: Fix ambiguous access_token scoping example --- actionpack/test/dispatch/routing_test.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 029bec2124..97cacc4a02 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -58,8 +58,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end resources :people do - namespace ":access_token" do - resource :avatar + nested do + namespace ":access_token" do + resource :avatar + end end member do @@ -280,9 +282,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/projects/1/people/1/7a2dec8/avatar' assert_equal 'avatars#show', @response.body - pending do - assert_equal '/projects/1/people/1/7a2dec8/avatar', project_person_avatar_path(:project_id => '1', :person_id => '1', :access_token => '7a2dec8') - end + assert_equal '/projects/1/people/1/7a2dec8/avatar', project_person_avatar_path(:project_id => '1', :person_id => '1', :access_token => '7a2dec8') put '/projects/1/people/1/accessible_projects' assert_equal 'people#accessible_projects', @response.body -- cgit v1.2.3 From 2be5e088d27f17cd7210cbfd227aff2e5be6b800 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 8 Dec 2009 16:52:26 -0600 Subject: Use new routing dsl in tests --- actionpack/test/abstract_unit.rb | 2 +- .../test/activerecord/active_record_store_test.rb | 2 +- .../test/controller/action_pack_assertions_test.rb | 22 ++++++++-------- actionpack/test/controller/base_test.rb | 6 ++--- actionpack/test/controller/mime_responds_test.rb | 8 +++--- actionpack/test/controller/redirect_test.rb | 4 +-- actionpack/test/controller/render_test.rb | 30 +++++++++++----------- actionpack/test/controller/render_xml_test.rb | 4 +-- actionpack/test/controller/test_test.rb | 11 -------- actionpack/test/controller/url_rewriter_test.rb | 4 +-- actionpack/test/controller/webservice_test.rb | 4 +-- .../request/multipart_params_parsing_test.rb | 2 +- actionpack/test/template/atom_feed_helper_test.rb | 12 ++++----- actionpack/test/template/test_case_test.rb | 4 +-- actionpack/test/template/url_helper_test.rb | 8 +++--- 15 files changed, 56 insertions(+), 67 deletions(-) diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 9d055da4b9..eab9f8b83d 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -83,7 +83,7 @@ class ActiveSupport::TestCase # have been loaded. setup_once do ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' + match ':controller(/:action(/:id))' end end end diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb index c6c079f88c..61bee1b66c 100644 --- a/actionpack/test/activerecord/active_record_store_test.rb +++ b/actionpack/test/activerecord/active_record_store_test.rb @@ -155,7 +155,7 @@ class ActiveRecordStoreTest < ActionController::IntegrationTest def with_test_route_set(options = {}) with_routing do |set| set.draw do |map| - map.connect "/:action", :controller => "active_record_store_test/test" + match ':action', :to => 'active_record_store_test/test' end @app = ActiveRecord::SessionStore.new(set, options.reverse_merge(:key => '_session_id')) yield diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 901cb940ea..d54be9bdc0 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -221,8 +221,8 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_assert_redirect_to_named_route with_routing do |set| set.draw do |map| - map.route_one 'route_one', :controller => 'action_pack_assertions', :action => 'nothing' - map.connect ':controller/:action/:id' + match 'route_one', :to => 'action_pack_assertions#nothing', :as => :route_one + match ':controller/:action' end set.install_helpers @@ -235,9 +235,9 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_assert_redirect_to_named_route_failure with_routing do |set| set.draw do |map| - map.route_one 'route_one', :controller => 'action_pack_assertions', :action => 'nothing', :id => 'one' - map.route_two 'route_two', :controller => 'action_pack_assertions', :action => 'nothing', :id => 'two' - map.connect ':controller/:action/:id' + match 'route_one', :to => 'action_pack_assertions#nothing', :as => :route_one + match 'route_two', :to => 'action_pack_assertions#nothing', :id => 'two', :as => :route_two + match ':controller/:action' end process :redirect_to_named_route assert_raise(ActiveSupport::TestCase::Assertion) do @@ -255,8 +255,8 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_assert_redirect_to_nested_named_route with_routing do |set| set.draw do |map| - map.admin_inner_module 'admin/inner_module', :controller => 'admin/inner_module', :action => 'index' - map.connect ':controller/:action/:id' + match 'admin/inner_module', :to => 'admin/inner_module#index', :as => :admin_inner_module + match ':controller/:action' end @controller = Admin::InnerModuleController.new process :redirect_to_index @@ -268,8 +268,8 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase def test_assert_redirected_to_top_level_named_route_from_nested_controller with_routing do |set| set.draw do |map| - map.top_level '/action_pack_assertions/:id', :controller => 'action_pack_assertions', :action => 'index' - map.connect ':controller/:action/:id' + match '/action_pack_assertions/:id', :to => 'action_pack_assertions#index', :as => :top_level + match ':controller/:action' end @controller = Admin::InnerModuleController.new process :redirect_to_top_level_named_route @@ -282,8 +282,8 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase with_routing do |set| set.draw do |map| # this controller exists in the admin namespace as well which is the only difference from previous test - map.top_level '/user/:id', :controller => 'user', :action => 'index' - map.connect ':controller/:action/:id' + match '/user/:id', :to => 'user#index', :as => :top_level + match ':controller/:action' end @controller = Admin::InnerModuleController.new process :redirect_to_top_level_named_route diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index b57550a69a..8f8ada8d8c 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -179,8 +179,8 @@ class DefaultUrlOptionsTest < ActionController::TestCase def test_default_url_options_are_used_if_set with_routing do |set| set.draw do |map| - map.default_url_options 'default_url_options', :controller => 'default_url_options' - map.connect ':controller/:action/:id' + match 'default_url_options', :to => 'default_url_options#default_url_options_action', :as => :default_url_options + match ':controller/:action' end get :default_url_options_action # Make a dummy request so that the controller is initialized properly. @@ -210,7 +210,7 @@ class EnsureNamedRoutesWorksTicket22BugTest < ActionController::TestCase def test_named_routes_still_work with_routing do |set| set.draw do |map| - map.resources :things + resources :things end EmptyController.send :include, ActionController::UrlWriter diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index c1fa74b8c8..dd1f2f3d3c 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -827,9 +827,11 @@ class RespondWithControllerTest < ActionController::TestCase def with_test_route_set with_routing do |set| set.draw do |map| - map.resources :customers - map.resources :quiz_stores, :has_many => :customers - map.connect ":controller/:action/:id" + resources :customers + resources :quiz_stores do + resources :customers + end + match ":controller/:action" end yield end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index ea278fd8f0..570ff4a41b 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -233,8 +233,8 @@ class RedirectTest < ActionController::TestCase def test_redirect_to_record with_routing do |set| set.draw do |map| - map.resources :workshops - map.connect ':controller/:action/:id' + resources :workshops + match ':controller/:action' end get :redirect_to_existing_record diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index cffa970011..f26b15d2e0 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -39,35 +39,35 @@ class TestController < ActionController::Base render :action => 'hello_world' end end - + def conditional_hello_with_public_header if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123], :public => true) render :action => 'hello_world' end end - + def conditional_hello_with_public_header_and_expires_at expires_in 1.minute if stale?(:last_modified => Time.now.utc.beginning_of_day, :etag => [:foo, 123], :public => true) render :action => 'hello_world' end end - + def conditional_hello_with_expires_in expires_in 60.1.seconds render :action => 'hello_world' end - + def conditional_hello_with_expires_in_with_public expires_in 1.minute, :public => true render :action => 'hello_world' end - + def conditional_hello_with_expires_in_with_public_with_more_keys expires_in 1.minute, :public => true, 'max-stale' => 5.hours render :action => 'hello_world' end - + def conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax expires_in 1.minute, :public => true, :private => nil, 'max-stale' => 5.hours render :action => 'hello_world' @@ -272,7 +272,7 @@ class TestController < ActionController::Base def builder_layout_test render :action => "hello", :layout => "layouts/builder" end - + # :move: test this in ActionView def builder_partial_test render :action => "hello_world_container" @@ -1093,8 +1093,8 @@ class RenderTest < ActionController::TestCase def test_head_with_location_object with_routing do |set| set.draw do |map| - map.resources :customers - map.connect ':controller/:action/:id' + resources :customers + match ':controller/:action' end get :head_with_location_object @@ -1306,22 +1306,22 @@ class ExpiresInRenderTest < ActionController::TestCase def setup @request.host = "www.nextangle.com" end - + def test_expires_in_header get :conditional_hello_with_expires_in assert_equal "max-age=60, private", @response.headers["Cache-Control"] end - + def test_expires_in_header_with_public get :conditional_hello_with_expires_in_with_public assert_equal "max-age=60, public", @response.headers["Cache-Control"] end - + def test_expires_in_header_with_additional_headers get :conditional_hello_with_expires_in_with_public_with_more_keys assert_equal "max-age=60, public, max-stale=18000", @response.headers["Cache-Control"] end - + def test_expires_in_old_syntax get :conditional_hello_with_expires_in_with_public_with_more_keys_old_syntax assert_equal "max-age=60, public, max-stale=18000", @response.headers["Cache-Control"] @@ -1425,12 +1425,12 @@ class EtagRenderTest < ActionController::TestCase get :conditional_hello_with_bangs assert_response :not_modified end - + def test_etag_with_public_true_should_set_header get :conditional_hello_with_public_header assert_equal "public", @response.headers['Cache-Control'] end - + def test_etag_with_public_true_should_set_header_and_retain_other_headers get :conditional_hello_with_public_header_and_expires_at assert_equal "max-age=60, public", @response.headers['Cache-Control'] diff --git a/actionpack/test/controller/render_xml_test.rb b/actionpack/test/controller/render_xml_test.rb index 68a52c3e8c..b5b0d0b9d5 100644 --- a/actionpack/test/controller/render_xml_test.rb +++ b/actionpack/test/controller/render_xml_test.rb @@ -61,8 +61,8 @@ class RenderXmlTest < ActionController::TestCase def test_rendering_with_object_location_should_set_header_with_url_for with_routing do |set| set.draw do |map| - map.resources :customers - map.connect ':controller/:action/:id' + resources :customers + match ':controller/:action' end get :render_with_object_location diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index 375878b755..52020922f2 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -628,17 +628,6 @@ XML assert_nothing_raised(NoMethodError) { @response.binary_content } end end - - protected - def with_foo_routing - with_routing do |set| - set.draw do |map| - map.generate_url 'foo', :controller => 'test' - map.connect ':controller/:action/:id' - end - yield set - end - end end class InferringClassNameTest < ActionController::TestCase diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb index 3b14cbb2d8..ffc0353b00 100644 --- a/actionpack/test/controller/url_rewriter_test.rb +++ b/actionpack/test/controller/url_rewriter_test.rb @@ -247,7 +247,7 @@ class UrlWriterTests < ActionController::TestCase with_routing do |set| set.draw do |map| - map.home '/home/sweet/home/:user', :controller => 'home', :action => 'index' + match '/home/sweet/home/:user', :to => 'home#index', :as => :home end kls = Class.new { include ActionController::UrlWriter } @@ -264,7 +264,7 @@ class UrlWriterTests < ActionController::TestCase with_routing do |set| set.draw do |map| match 'home/sweet/home/:user', :to => 'home#index', :as => :home - map.connect ':controller/:action/:id' + match ':controller/:action/:id' end # We need to create a new class in order to install the new named route. diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index 0514c098bf..5882a8cfa3 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -255,9 +255,7 @@ class WebServiceTest < ActionController::IntegrationTest def with_test_route_set with_routing do |set| set.draw do |map| - map.with_options :controller => "web_service_test/test" do |c| - c.connect "/", :action => "assign_parameters" - end + match '/', :to => 'web_service_test/test#assign_parameters' end yield end diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb index 301080842e..40c5ac2d09 100644 --- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb @@ -151,7 +151,7 @@ class MultipartParamsParsingTest < ActionController::IntegrationTest def with_test_routing with_routing do |set| set.draw do |map| - map.connect ':action', :controller => "multipart_params_parsing_test/test" + match ':action', :to => 'multipart_params_parsing_test/test' end yield end diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb index 6a5fb0acff..347cb98303 100644 --- a/actionpack/test/template/atom_feed_helper_test.rb +++ b/actionpack/test/template/atom_feed_helper_test.rb @@ -187,10 +187,9 @@ class ScrollsController < ActionController::Base end protected - - def rescue_action(e) - raise(e) - end + def rescue_action(e) + raise(e) + end end class AtomFeedTest < ActionController::TestCase @@ -311,11 +310,12 @@ class AtomFeedTest < ActionController::TestCase assert_select "summary div p", :text => "after 2" end end -private + + private def with_restful_routing(resources) with_routing do |set| set.draw do |map| - map.resources(resources) + resources(resources) end yield end diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb index 05a409d05a..9a448ce328 100644 --- a/actionpack/test/template/test_case_test.rb +++ b/actionpack/test/template/test_case_test.rb @@ -114,7 +114,7 @@ module ActionView test "is able to use named routes" do with_routing do |set| - set.draw { |map| map.resources :contents } + set.draw { |map| resources :contents } assert_equal 'http://test.host/contents/new', new_content_url assert_equal 'http://test.host/contents/1', content_url(:id => 1) end @@ -122,7 +122,7 @@ module ActionView test "named routes can be used from helper included in view" do with_routing do |set| - set.draw { |map| map.resources :contents } + set.draw { |map| resources :contents } _helpers.module_eval do def render_from_helper new_content_url diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 5c463a4f60..d4b58efe1e 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -451,7 +451,7 @@ class UrlHelperControllerTest < ActionController::TestCase def with_url_helper_routing with_routing do |set| set.draw do |map| - map.show_named_route 'url_helper_controller_test/url_helper/show_named_route', :controller => 'url_helper_controller_test/url_helper', :action => 'show_named_route' + match 'url_helper_controller_test/url_helper/show_named_route', :to => 'url_helper_controller_test/url_helper#show_named_route', :as => :show_named_route end yield end @@ -505,7 +505,7 @@ class LinkToUnlessCurrentWithControllerTest < ActionController::TestCase def with_restful_routing with_routing do |set| set.draw do |map| - map.resources :tasks + resources :tasks end yield end @@ -625,8 +625,8 @@ class PolymorphicControllerTest < ActionController::TestCase def with_restful_routing with_routing do |set| set.draw do |map| - map.resources :workshops do |w| - w.resources :sessions + resources :workshops do + resources :sessions end end yield -- cgit v1.2.3 From 511cef296bd07fa43794e029e12e4cd1053aa8d1 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 8 Dec 2009 17:19:49 -0600 Subject: Tack format onto resource routes --- actionpack/lib/action_dispatch/routing/mapper.rb | 44 ++++++++++++------------ actionpack/test/dispatch/routing_test.rb | 18 ++++++++-- 2 files changed, 37 insertions(+), 25 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 8cb7745aff..3b185c2576 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -46,8 +46,8 @@ module ActionDispatch conditions, defaults = {}, {} path = nil if path == "" - path = Rack::Mount::Utils.normalize_path(path) if path path = "#{@scope[:path]}#{path}" if @scope[:path] + path = Rack::Mount::Utils.normalize_path(path) if path raise ArgumentError, "path is required" unless path @@ -169,7 +169,7 @@ module ActionDispatch if path = options.delete(:path) path_set = true - path, @scope[:path] = @scope[:path], "#{@scope[:path]}#{Rack::Mount::Utils.normalize_path(path)}" + path, @scope[:path] = @scope[:path], Rack::Mount::Utils.normalize_path(@scope[:path].to_s + path.to_s) else path_set = false end @@ -214,7 +214,7 @@ module ActionDispatch end def namespace(path) - scope(path.to_s) { yield } + scope("/#{path}") { yield } end def constraints(constraints = {}) @@ -297,16 +297,16 @@ module ActionDispatch return self end - scope(:path => resource.name, :controller => resource.controller) do + scope(:path => "/#{resource.name}", :controller => resource.controller) do with_scope_level(:resource, resource) do yield if block_given? - get "", :to => :show, :as => resource.member_name - post "", :to => :create - put "", :to => :update - delete "", :to => :destroy - get "new", :to => :new, :as => "new_#{resource.singular}" - get "edit", :to => :edit, :as => "edit_#{resource.singular}" + get "(.:format)", :to => :show, :as => resource.member_name + post "(.:format)", :to => :create + put "(.:format)", :to => :update + delete "(.:format)", :to => :destroy + get "/new(.:format)", :to => :new, :as => "new_#{resource.singular}" + get "/edit(.:format)", :to => :edit, :as => "edit_#{resource.singular}" end end @@ -331,22 +331,22 @@ module ActionDispatch return self end - scope(:path => resource.name, :controller => resource.controller) do + scope(:path => "/#{resource.name}", :controller => resource.controller) do with_scope_level(:resources, resource) do yield if block_given? with_scope_level(:collection) do - get "", :to => :index, :as => resource.collection_name - post "", :to => :create - get "new", :to => :new, :as => "new_#{resource.singular}" + get "(.:format)", :to => :index, :as => resource.collection_name + post "(.:format)", :to => :create + get "/new(.:format)", :to => :new, :as => "new_#{resource.singular}" end with_scope_level(:member) do - scope(":id") do - get "", :to => :show, :as => resource.member_name - put "", :to => :update - delete "", :to => :destroy - get "edit", :to => :edit, :as => "edit_#{resource.singular}" + scope("/:id") do + get "(.:format)", :to => :show, :as => resource.member_name + put "(.:format)", :to => :update + delete "(.:format)", :to => :destroy + get "/edit(.:format)", :to => :edit, :as => "edit_#{resource.singular}" end end end @@ -373,7 +373,7 @@ module ActionDispatch end with_scope_level(:member) do - scope(":id", :name_prefix => parent_resource.member_name, :as => "") do + scope("/:id", :name_prefix => parent_resource.member_name, :as => "") do yield end end @@ -385,7 +385,7 @@ module ActionDispatch end with_scope_level(:nested) do - scope(parent_resource.id_segment, :name_prefix => parent_resource.member_name) do + scope("/#{parent_resource.id_segment}", :name_prefix => parent_resource.member_name) do yield end end @@ -402,7 +402,7 @@ module ActionDispatch if args.first.is_a?(Symbol) begin old_name_prefix, @scope[:name_prefix] = @scope[:name_prefix], "#{args.first}_#{@scope[:name_prefix]}" - return match(args.first.to_s, options.merge(:to => args.first.to_sym)) + return match("/#{args.first}(.:format)", options.merge(:to => args.first.to_sym)) ensure @scope[:name_prefix] = old_name_prefix end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 97cacc4a02..01088e9d8f 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -95,9 +95,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end controller :articles do - scope 'articles', :name_prefix => 'article' do - scope :path => ':title', :title => /[a-z]+/, :as => :with_title do - match ':id', :to => :with_id + scope '/articles', :name_prefix => 'article' do + scope :path => '/:title', :title => /[a-z]+/, :as => :with_title do + match '/:id', :to => :with_id end end end @@ -196,14 +196,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'projects#index', @response.body assert_equal '/projects', projects_path + get '/projects.xml' + assert_equal 'projects#index', @response.body + assert_equal '/projects.xml', projects_path(:format => 'xml') + get '/projects/new' assert_equal 'projects#new', @response.body assert_equal '/projects/new', new_project_path + get '/projects/new.xml' + assert_equal 'projects#new', @response.body + assert_equal '/projects/new.xml', new_project_path(:format => 'xml') + get '/projects/1' assert_equal 'projects#show', @response.body assert_equal '/projects/1', project_path(:id => '1') + get '/projects/1.xml' + assert_equal 'projects#show', @response.body + assert_equal '/projects/1.xml', project_path(:id => '1', :format => 'xml') + get '/projects/1/edit' assert_equal 'projects#edit', @response.body assert_equal '/projects/1/edit', edit_project_path(:id => '1') -- cgit v1.2.3 From 9fbde11b8b2a43206bcd1a135e763751cae98264 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 8 Dec 2009 17:41:00 -0600 Subject: More test porting --- actionpack/test/controller/caching_test.rb | 7 +++---- actionpack/test/controller/test_test.rb | 6 +++--- actionpack/test/controller/url_rewriter_test.rb | 6 +++--- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 682a8f3995..4ea2e57741 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -70,8 +70,8 @@ class PageCachingTest < ActionController::TestCase def test_page_caching_resources_saves_to_correct_path_with_extension_even_if_default_route with_routing do |set| set.draw do |map| - map.main '', :controller => 'posts', :format => nil - map.formatted_posts 'posts.:format', :controller => 'posts' + match 'posts.:format', :to => 'posts#index', :as => :formatted_posts + match '/', :to => 'posts#index', :as => :main end @params[:format] = 'rss' assert_equal '/posts.rss', @rewriter.rewrite(@params) @@ -422,8 +422,7 @@ class ActionCacheTest < ActionController::TestCase def test_xml_version_of_resource_is_treated_as_different_cache with_routing do |set| set.draw do |map| - map.connect ':controller/:action.:format' - map.connect ':controller/:action' + match ':controller(/:action(.:format))' end get :index, :format => 'xml' diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index 52020922f2..a788b2495e 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -456,8 +456,8 @@ XML def test_array_path_parameter_handled_properly with_routing do |set| set.draw do |map| - map.connect 'file/*path', :controller => 'test_test/test', :action => 'test_params' - map.connect ':controller/:action/:id' + match 'file/*path', :to => 'test_test/test#test_params' + match ':controller/:action' end get :test_params, :path => ['hello', 'world'] @@ -662,7 +662,7 @@ class NamedRoutesControllerTest < ActionController::TestCase def test_should_be_able_to_use_named_routes_before_a_request_is_done with_routing do |set| - set.draw { |map| map.resources :contents } + set.draw { |map| resources :contents } assert_equal 'http://test.host/contents/new', new_content_url assert_equal 'http://test.host/contents/1', content_url(:id => 1) end diff --git a/actionpack/test/controller/url_rewriter_test.rb b/actionpack/test/controller/url_rewriter_test.rb index ffc0353b00..428f40b9f8 100644 --- a/actionpack/test/controller/url_rewriter_test.rb +++ b/actionpack/test/controller/url_rewriter_test.rb @@ -331,8 +331,8 @@ class UrlWriterTests < ActionController::TestCase def test_named_routes_with_nil_keys with_routing do |set| set.draw do |map| - map.main '', :controller => 'posts', :format => nil - map.resources :posts + match 'posts.:format', :to => 'posts#index', :as => :posts + match '/', :to => 'posts#index', :as => :main end # We need to create a new class in order to install the new named route. @@ -350,7 +350,7 @@ class UrlWriterTests < ActionController::TestCase def test_formatted_url_methods_are_deprecated with_routing do |set| set.draw do |map| - map.resources :posts + resources :posts end # We need to create a new class in order to install the new named route. kls = Class.new { include ActionController::UrlWriter } -- cgit v1.2.3 From f9d570bdd80010825c21eb32204471ba525915fa Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 9 Dec 2009 13:38:47 -0800 Subject: Simpler RenderOption API -- removes the need for registering the types and extending a module --- .../lib/action_controller/metal/render_options.rb | 101 +++++++++------------ actionpack/test/controller/render_other_test.rb | 14 +++ 2 files changed, 59 insertions(+), 56 deletions(-) diff --git a/actionpack/lib/action_controller/metal/render_options.rb b/actionpack/lib/action_controller/metal/render_options.rb index 0d69ca10df..b6a7ca0eda 100644 --- a/actionpack/lib/action_controller/metal/render_options.rb +++ b/actionpack/lib/action_controller/metal/render_options.rb @@ -1,19 +1,24 @@ module ActionController + + def self.add_renderer(key, &block) + RenderOptions.add(key, &block) + end + module RenderOptions extend ActiveSupport::Concern included do extlib_inheritable_accessor :_renderers - self._renderers = [] + self._renderers = {} end module ClassMethods def _write_render_options - renderers = _renderers.map do |r| + renderers = _renderers.map do |name, value| <<-RUBY_EVAL - if options.key?(:#{r}) + if options.key?(:#{name}) _process_options(options) - return render_#{r}(options[:#{r}], options) + return _render_option_#{name}(options[:#{name}], options) end RUBY_EVAL end @@ -25,79 +30,63 @@ module ActionController RUBY_EVAL end - def _add_render_option(name) - _renderers << name + def use_renderers(*args) + args.each do |key| + _renderers[key] = RENDERERS[key] + end _write_render_options end + alias use_renderer use_renderers end def render_to_body(options) _handle_render_options(options) || super end - end - - module RenderOption #:nodoc: - def self.extended(base) - base.extend ActiveSupport::Concern - base.send :include, ::ActionController::RenderOptions - def base.register_renderer(name) - included { _add_render_option(name) } - end + RENDERERS = {} + def self.add(key, &block) + define_method("_render_option_#{key}", &block) + RENDERERS[key] = block + All._write_render_options end - end - module RenderOptions - module Json - extend RenderOption - register_renderer :json + module All + extend ActiveSupport::Concern + include RenderOptions - def render_json(json, options) - json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) - json = "#{options[:callback]}(#{json})" unless options[:callback].blank? - self.content_type ||= Mime::JSON - self.response_body = json + INCLUDED = [] + included do + self._renderers = RENDERERS + _write_render_options + INCLUDED << self end - end - - module Js - extend RenderOption - register_renderer :js - def render_js(js, options) - self.content_type ||= Mime::JS - self.response_body = js.respond_to?(:to_js) ? js.to_js : js + def self._write_render_options + INCLUDED.each(&:_write_render_options) end end - module Xml - extend RenderOption - register_renderer :xml - - def render_xml(xml, options) - self.content_type ||= Mime::XML - self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml - end + add :json do |json, options| + json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str) + json = "#{options[:callback]}(#{json})" unless options[:callback].blank? + self.content_type ||= Mime::JSON + self.response_body = json end - module RJS - extend RenderOption - register_renderer :update - - def render_update(proc, options) - generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc) - self.content_type = Mime::JS - self.response_body = generator.to_s - end + add :js do |js, options| + self.content_type ||= Mime::JS + self.response_body = js.respond_to?(:to_js) ? js.to_js : js end - module All - extend ActiveSupport::Concern + add :xml do |xml, options| + self.content_type ||= Mime::XML + self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml + end - include ActionController::RenderOptions::Json - include ActionController::RenderOptions::Js - include ActionController::RenderOptions::Xml - include ActionController::RenderOptions::RJS + add :update do |proc, options| + generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc) + self.content_type = Mime::JS + self.response_body = generator.to_s end end end diff --git a/actionpack/test/controller/render_other_test.rb b/actionpack/test/controller/render_other_test.rb index 51c3c55545..dfc4f2db8c 100644 --- a/actionpack/test/controller/render_other_test.rb +++ b/actionpack/test/controller/render_other_test.rb @@ -2,6 +2,11 @@ require 'abstract_unit' require 'controller/fake_models' require 'pathname' +ActionController.add_renderer :simon do |says, options| + self.content_type = Mime::TEXT + self.response_body = "Simon says: #{says}" +end + class RenderOtherTest < ActionController::TestCase class TestController < ActionController::Base protect_from_forgery @@ -109,6 +114,10 @@ class RenderOtherTest < ActionController::TestCase end end + def render_simon_says + render :simon => "foo" + end + private def default_render if @alternate_default_render @@ -240,4 +249,9 @@ class RenderOtherTest < ActionController::TestCase xhr :get, :render_alternate_default assert_equal %(Element.replace("foo", "partial html");), @response.body end + + def test_using_custom_render_option + get :render_simon_says + assert_equal "Simon says: foo", @response.body + end end -- cgit v1.2.3 From 0fec53f243079a60df817cab55100a136fafc1c9 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 9 Dec 2009 18:57:16 -0600 Subject: Scaffolding generates new routing dsl examples --- .../rails/app/templates/config/routes.rb | 57 ++++++++++++++-------- .../rails/resource/resource_generator.rb | 2 +- railties/lib/rails/generators/rails/scaffold/USAGE | 2 +- .../plugins/engines/engine/config/routes.rb | 2 +- railties/test/generators/actions_test.rb | 2 +- .../test/generators/resource_generator_test.rb | 6 +-- .../test/generators/scaffold_generator_test.rb | 4 +- railties/test/rails_info_controller_test.rb | 2 +- 8 files changed, 47 insertions(+), 30 deletions(-) diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb index ea14ce1bfc..5d110f6a1c 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb @@ -1,43 +1,60 @@ ActionController::Routing::Routes.draw do |map| - # The priority is based upon order of creation: first created -> highest priority. + # The priority is based upon order of creation: + # first created -> highest priority. # Sample of regular route: - # map.connect 'products/:id', :controller => 'catalog', :action => 'view' + # match 'products/:id', :to => 'catalog#view' # Keep in mind you can assign values other than :controller and :action # Sample of named route: - # map.purchase 'products/:id/purchase', :controller => 'catalog', :action => 'purchase' + # match 'products/:id/purchase', :to => 'catalog#purchase', :as => :purchase # This route can be invoked with purchase_url(:id => product.id) # Sample resource route (maps HTTP verbs to controller actions automatically): - # map.resources :products + # resources :products # Sample resource route with options: - # map.resources :products, :member => { :short => :get, :toggle => :post }, :collection => { :sold => :get } + # resources :products do + # member do + # get :short + # post :toggle + # end + # + # collection do + # get :sold + # end + # end # Sample resource route with sub-resources: - # map.resources :products, :has_many => [ :comments, :sales ], :has_one => :seller - + # resources :products do + # resources :comments, :sales + # resource :seller + # end + # Sample resource route with more complex sub-resources - # map.resources :products do |products| - # products.resources :comments - # products.resources :sales, :collection => { :recent => :get } + # resources :products do + # resources :comments + # resources :sales do + # get :recent, :on => :collection + # end # end # Sample resource route within a namespace: - # map.namespace :admin do |admin| - # # Directs /admin/products/* to Admin::ProductsController (app/controllers/admin/products_controller.rb) - # admin.resources :products + # namespace :admin do + # # Directs /admin/products/* to Admin::ProductsController + # # (app/controllers/admin/products_controller.rb) + # resources :products # end - # You can have the root of your site routed with map.root -- just remember to delete public/index.html. - # map.root :controller => "welcome" + # You can have the root of your site routed with map.root + # just remember to delete public/index.html. + # root :to => "welcome" # See how all your routes lay out with "rake routes" - # Install the default routes as the lowest priority. - # Note: These default routes make all actions in every controller accessible via GET requests. You should - # consider removing or commenting them out if you're using named routes and resources. - map.connect ':controller/:action/:id' - map.connect ':controller/:action/:id.:format' + # Install the default route as the lowest priority. + # Note: The default route make all actions in every controller accessible + # via GET requests. You should consider removing or commenting it out if + # you're using named routes and resources. + match ':controller(/:action(/:id(.:format)))' end diff --git a/railties/lib/rails/generators/rails/resource/resource_generator.rb b/railties/lib/rails/generators/rails/resource/resource_generator.rb index e49f9aea1b..a89ce7faed 100644 --- a/railties/lib/rails/generators/rails/resource/resource_generator.rb +++ b/railties/lib/rails/generators/rails/resource/resource_generator.rb @@ -16,7 +16,7 @@ module Rails class_option :singleton, :type => :boolean, :desc => "Supply to create a singleton controller" def add_resource_route - route "map.resource#{:s unless options[:singleton]} :#{pluralize?(file_name)}" + route "resource#{:s unless options[:singleton]} :#{pluralize?(file_name)}" end protected diff --git a/railties/lib/rails/generators/rails/scaffold/USAGE b/railties/lib/rails/generators/rails/scaffold/USAGE index 71edd2f469..530ccdaf0a 100644 --- a/railties/lib/rails/generators/rails/scaffold/USAGE +++ b/railties/lib/rails/generators/rails/scaffold/USAGE @@ -17,7 +17,7 @@ Description: For example, 'scaffold post title:string body:text published:boolean' gives you a model with those three attributes, a controller that handles the create/show/update/destroy, forms to create and edit your posts, and - an index that lists them all, as well as a map.resources :posts + an index that lists them all, as well as a resources :posts declaration in config/routes.rb. If you want to remove all the generated files, run diff --git a/railties/test/fixtures/plugins/engines/engine/config/routes.rb b/railties/test/fixtures/plugins/engines/engine/config/routes.rb index cca8d1b146..da44595693 100644 --- a/railties/test/fixtures/plugins/engines/engine/config/routes.rb +++ b/railties/test/fixtures/plugins/engines/engine/config/routes.rb @@ -1,3 +1,3 @@ ActionController::Routing::Routes.draw do |map| - map.connect '/engine', :controller => "engine" + match '/engine', :to => "engine" end diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 7d03a37f2a..b69f23c965 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -171,7 +171,7 @@ class ActionsTest < GeneratorsTestCase def test_route_should_add_data_to_the_routes_block_in_config_routes run_generator - route_command = "map.route '/login', :controller => 'sessions', :action => 'new'" + route_command = "route '/login', :controller => 'sessions', :action => 'new'" action :route, route_command assert_file 'config/routes.rb', /#{Regexp.escape(route_command)}/ end diff --git a/railties/test/generators/resource_generator_test.rb b/railties/test/generators/resource_generator_test.rb index 886af01b22..dff3908ea1 100644 --- a/railties/test/generators/resource_generator_test.rb +++ b/railties/test/generators/resource_generator_test.rb @@ -62,7 +62,7 @@ class ResourceGeneratorTest < GeneratorsTestCase run_generator assert_file "config/routes.rb" do |route| - assert_match /map\.resources :accounts$/, route + assert_match /resources :accounts$/, route end end @@ -70,7 +70,7 @@ class ResourceGeneratorTest < GeneratorsTestCase run_generator ["account", "--singleton"] assert_file "config/routes.rb" do |route| - assert_match /map\.resource :account$/, route + assert_match /resource :account$/, route end end @@ -93,7 +93,7 @@ class ResourceGeneratorTest < GeneratorsTestCase run_generator ["account"], :behavior => :revoke assert_file "config/routes.rb" do |route| - assert_no_match /map\.resources :accounts$/, route + assert_no_match /resources :accounts$/, route end end diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index c0652c034f..0b961cee19 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -25,7 +25,7 @@ class ScaffoldGeneratorTest < GeneratorsTestCase # Route assert_file "config/routes.rb" do |route| - assert_match /map\.resources :product_lines$/, route + assert_match /resources :product_lines$/, route end # Controller @@ -99,7 +99,7 @@ class ScaffoldGeneratorTest < GeneratorsTestCase # Route assert_file "config/routes.rb" do |route| - assert_no_match /map\.resources :product_lines$/, route + assert_no_match /resources :product_lines$/, route end # Controller diff --git a/railties/test/rails_info_controller_test.rb b/railties/test/rails_info_controller_test.rb index 558109ba1b..435bd34925 100644 --- a/railties/test/rails_info_controller_test.rb +++ b/railties/test/rails_info_controller_test.rb @@ -16,7 +16,7 @@ class InfoControllerTest < ActionController::TestCase def setup ActionController::Routing::Routes.draw do |map| - map.connect ':controller/:action/:id' + match ':controller/:action' end @controller.stubs(:consider_all_requests_local => false, :local_request? => true) end -- cgit v1.2.3 From b2ef6d13f0a0b3d07b8a2f9a1ec64988ba7e4392 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 9 Dec 2009 20:39:42 -0600 Subject: Fixed old routing mapper example in generated routes.rb --- railties/lib/rails/generators/actions.rb | 2 +- railties/lib/rails/generators/rails/app/templates/config/routes.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 57abc151d2..2efdf29127 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -269,7 +269,7 @@ module Rails # # === Example # - # route "map.root :controller => :welcome" + # route "root :to => 'welcome'" # def route(routing_code) log :route, routing_code diff --git a/railties/lib/rails/generators/rails/app/templates/config/routes.rb b/railties/lib/rails/generators/rails/app/templates/config/routes.rb index 5d110f6a1c..e0f9ca8a12 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/routes.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/routes.rb @@ -46,7 +46,7 @@ ActionController::Routing::Routes.draw do |map| # resources :products # end - # You can have the root of your site routed with map.root + # You can have the root of your site routed with "root" # just remember to delete public/index.html. # root :to => "welcome" -- cgit v1.2.3 From ec5434c3c2631bb47b568eede397c3bd596eeb88 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 9 Dec 2009 20:46:32 -0600 Subject: Check block arity passed to routes draw so you don't need to use |map| --- actionpack/lib/action_dispatch/routing/route_set.rb | 9 ++++++++- actionpack/test/dispatch/routing_test.rb | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index a8073c2105..0e83ea3b7e 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -216,7 +216,14 @@ module ActionDispatch def draw(&block) clear! - Mapper.new(self).instance_exec(DeprecatedMapper.new(self), &block) + + mapper = Mapper.new(self) + if block.arity == 1 + mapper.instance_exec(DeprecatedMapper.new(self), &block) + else + mapper.instance_exec(&block) + end + @set.add_route(NotFound) install_helpers @set.freeze diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 01088e9d8f..22ef48b668 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -14,7 +14,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest stub_controllers do |routes| Routes = routes - Routes.draw do |map| + Routes.draw do controller :sessions do get 'login', :to => :new, :as => :login post 'login', :to => :create -- cgit v1.2.3 From c517a4fb9a6388ab561615f8556043ce03d531bd Mon Sep 17 00:00:00 2001 From: Nathan Weizenbaum Date: Thu, 10 Dec 2009 13:07:22 -0800 Subject: Add autoloads for ActionView::Template* classes, and an ActionView::TemplateError alias for ActionView::Template::Error. Signed-off-by: Yehuda Katz --- actionpack/lib/action_view.rb | 4 ++++ actionpack/lib/action_view/template/error.rb | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 81ee19d996..06238ca747 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -43,6 +43,10 @@ module ActionView autoload :PathResolver, 'action_view/template/resolver' autoload :PathSet, 'action_view/paths' autoload :FileSystemResolverWithFallback, 'action_view/template/resolver' + + autoload :TemplateError, 'action_view/template/error' + autoload :TemplateHandler, 'action_view/template' + autoload :TemplateHandlers, 'action_view/template' end require 'action_view/erb/util' diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index a136d4333b..648f708d3d 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -100,4 +100,6 @@ module ActionView end end end -end \ No newline at end of file + + TemplateError = Template::Error +end -- cgit v1.2.3 From 1b82590c36bb1404f7b7a9aa9cf196253685d73f Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 10 Dec 2009 22:02:50 -0600 Subject: Reduce usage of interpret_status. It should also return a integer not a string. --- actionpack/lib/action_controller/base.rb | 16 ++++++++++++---- actionpack/lib/action_dispatch/http/status_codes.rb | 17 ----------------- .../lib/action_dispatch/middleware/show_exceptions.rb | 4 +--- 3 files changed, 13 insertions(+), 24 deletions(-) diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index ed3984c9d4..48bfbab215 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -19,7 +19,6 @@ module ActionController # Legacy modules include SessionManagement - include ActionDispatch::StatusCodes include ActionController::Caching include ActionController::MimeResponds @@ -91,7 +90,7 @@ module ActionController end if options[:status] - options[:status] = interpret_status(options[:status]).to_i + options[:status] = _interpret_status(options[:status]) end options[:update] = blk if block_given? @@ -140,9 +139,9 @@ module ActionController raise ActionControllerError.new("Cannot redirect to nil!") if options.nil? status = if options.is_a?(Hash) && options.key?(:status) - interpret_status(options.delete(:status)) + _interpret_status(options.delete(:status)) elsif response_status.key?(:status) - interpret_status(response_status[:status]) + _interpret_status(response_status[:status]) else 302 end @@ -164,5 +163,14 @@ module ActionController super(url, status) end + + private + def _interpret_status(status) + if status.is_a?(Symbol) + (ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[status] || 500) + else + status.to_i + end + end end end diff --git a/actionpack/lib/action_dispatch/http/status_codes.rb b/actionpack/lib/action_dispatch/http/status_codes.rb index 5bac842ec1..ea1475e827 100644 --- a/actionpack/lib/action_dispatch/http/status_codes.rb +++ b/actionpack/lib/action_dispatch/http/status_codes.rb @@ -21,22 +21,5 @@ module ActionDispatch hash[ActiveSupport::Inflector.underscore(message.gsub(/ /, "")).to_sym] = code hash }.freeze - - private - # Given a status parameter, determine whether it needs to be converted - # to a string. If it is a fixnum, use the STATUS_CODES hash to lookup - # the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE - # hash to convert it. - def interpret_status(status) - case status - when Fixnum then - "#{status} #{STATUS_CODES[status]}".strip - when Symbol then - interpret_status(SYMBOL_TO_STATUS_CODE[status] || - "500 Unknown Status #{status.inspect}") - else - status.to_s - end - end end end diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 67c70a0418..471d18491c 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -2,8 +2,6 @@ require "active_support/core_ext/exception" module ActionDispatch class ShowExceptions - include StatusCodes - LOCALHOST = '127.0.0.1'.freeze RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates') @@ -104,7 +102,7 @@ module ActionDispatch end def status_code(exception) - interpret_status(@@rescue_responses[exception.class.name]).to_i + ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[@@rescue_responses[exception.class.name]] end def render(status, body) -- cgit v1.2.3 From d1191507bc8ffa4f7fe59b1cfb03dfbf6b73a798 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 10 Dec 2009 22:57:07 -0600 Subject: Cleanup generate_extras build/parse mess --- actionpack/lib/action_dispatch/routing/route_set.rb | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 0e83ea3b7e..664babd12e 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -433,7 +433,7 @@ module ActionDispatch raise ActionController::RoutingError, "No route matches #{options.inspect}" end - uri, params = result + path, params = result params.each do |k, v| if v params[k] = v @@ -442,16 +442,10 @@ module ActionDispatch end end - uri << "?#{params.to_query}" if uri && params.any? - path = uri - if path && method == :generate_extras - uri = URI(path) - extras = uri.query ? - Rack::Utils.parse_nested_query(uri.query).keys.map { |k| k.to_sym } : - [] - [uri.path, extras] + [path, params.keys] elsif path + path << "?#{params.to_query}" if params.any? path else raise ActionController::RoutingError, "No route matches #{options.inspect}" -- cgit v1.2.3 From 2f90d700498294a9a4da0baa5317c0e6feaaf176 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 10 Dec 2009 23:45:04 -0600 Subject: Kill RouteSet#recognize --- .../lib/action_dispatch/routing/route_set.rb | 12 -- .../action_dispatch/testing/assertions/routing.rb | 6 +- actionpack/test/controller/routing_test.rb | 194 ++++++++------------- 3 files changed, 72 insertions(+), 140 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 664babd12e..8d56c4d087 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -479,12 +479,6 @@ module ActionDispatch end end - def recognize(request) - params = recognize_path(request.path, extract_request_environment(request)) - request.path_parameters = params.with_indifferent_access - "#{params[:controller].to_s.camelize}Controller".constantize - end - def recognize_path(path, environment = {}, rescue_error = true) method = (environment[:method] || "GET").to_s.upcase @@ -499,12 +493,6 @@ module ActionDispatch status, headers, body = call(env) body end - - # Subclasses and plugins may override this method to extract further attributes - # from the request, for use by route conditions and such. - def extract_request_environment(request) - { :method => request.method } - end end end end diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index e6d6b5a3ef..4bc5275e04 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -134,9 +134,11 @@ module ActionDispatch # Assume given controller request = ActionController::TestRequest.new request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method - request.path = path + request.path = path + + params = ActionController::Routing::Routes.recognize_path(path, { :method => request.method }) + request.path_parameters = params.with_indifferent_access - ActionController::Routing::Routes.recognize(request) request end end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index ff8dfc2573..d52da58807 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -554,10 +554,6 @@ class LegacyRouteSetTests < Test::Unit::TestCase end def setup_request_method_routes_for(method) - @request = ActionController::TestRequest.new - @request.env["REQUEST_METHOD"] = method - @request.request_uri = "/match" - rs.draw do |r| r.connect '/match', :controller => 'books', :action => 'get', :conditions => { :method => :get } r.connect '/match', :controller => 'books', :action => 'post', :conditions => { :method => :post } @@ -569,8 +565,8 @@ class LegacyRouteSetTests < Test::Unit::TestCase %w(GET POST PUT DELETE).each do |request_method| define_method("test_request_method_recognized_with_#{request_method}") do setup_request_method_routes_for(request_method) - assert_nothing_raised { rs.recognize(@request) } - assert_equal request_method.downcase, @request.path_parameters[:action] + params = rs.recognize_path("/match", :method => request_method) + assert_equal request_method.downcase, params[:action] end end @@ -580,18 +576,11 @@ class LegacyRouteSetTests < Test::Unit::TestCase r.connect '/match', :controller => 'books', :action => 'not_get_or_post' end - @request = ActionController::TestRequest.new - @request.env["REQUEST_METHOD"] = 'POST' - @request.request_uri = "/match" - assert_nothing_raised { rs.recognize(@request) } - assert_equal 'get_or_post', @request.path_parameters[:action] + params = rs.recognize_path("/match", :method => :post) + assert_equal 'get_or_post', params[:action] - # have to recreate or else the RouteSet uses a cached version: - @request = ActionController::TestRequest.new - @request.env["REQUEST_METHOD"] = 'PUT' - @request.request_uri = "/match" - assert_nothing_raised { rs.recognize(@request) } - assert_equal 'not_get_or_post', @request.path_parameters[:action] + params = rs.recognize_path("/match", :method => :put) + assert_equal 'not_get_or_post', params[:action] end def test_subpath_recognized @@ -745,9 +734,7 @@ class RouteSetTest < ActiveSupport::TestCase set.draw do |map| map.connect '/users/index', :controller => :users, :action => :index end - @request = ActionController::TestRequest.new - @request.request_uri = '/users/index' - assert_nothing_raised { set.recognize(@request) } + params = set.recognize_path('/users/index', :method => :get) assert_equal 1, set.routes.size end @@ -980,55 +967,37 @@ class RouteSetTest < ActiveSupport::TestCase end end - request.request_uri = "/people" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("index", request.path_parameters[:action]) - request.recycle! + params = set.recognize_path("/people", :method => :get) + assert_equal("index", params[:action]) - request.env["REQUEST_METHOD"] = "POST" - assert_nothing_raised { set.recognize(request) } - assert_equal("create", request.path_parameters[:action]) - request.recycle! + params = set.recognize_path("/people", :method => :post) + assert_equal("create", params[:action]) - request.env["REQUEST_METHOD"] = "PUT" - assert_nothing_raised { set.recognize(request) } - assert_equal("update", request.path_parameters[:action]) - request.recycle! + params = set.recognize_path("/people", :method => :put) + assert_equal("update", params[:action]) - assert_raise(ActionController::UnknownHttpMethod) { - request.env["REQUEST_METHOD"] = "BACON" - set.recognize(request) + assert_raise(ActionController::NotImplemented) { + set.recognize_path("/people", :method => :bacon) } - request.recycle! - - request.request_uri = "/people/5" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("show", request.path_parameters[:action]) - assert_equal("5", request.path_parameters[:id]) - request.recycle! - - request.env["REQUEST_METHOD"] = "PUT" - assert_nothing_raised { set.recognize(request) } - assert_equal("update", request.path_parameters[:action]) - assert_equal("5", request.path_parameters[:id]) - request.recycle! - - request.env["REQUEST_METHOD"] = "DELETE" - assert_nothing_raised { set.recognize(request) } - assert_equal("destroy", request.path_parameters[:action]) - assert_equal("5", request.path_parameters[:id]) - request.recycle! + + params = set.recognize_path("/people/5", :method => :get) + assert_equal("show", params[:action]) + assert_equal("5", params[:id]) + + params = set.recognize_path("/people/5", :method => :put) + assert_equal("update", params[:action]) + assert_equal("5", params[:id]) + + params = set.recognize_path("/people/5", :method => :delete) + assert_equal("destroy", params[:action]) + assert_equal("5", params[:id]) begin - request.env["REQUEST_METHOD"] = "POST" - set.recognize(request) + set.recognize_path("/people/5", :method => :post) flunk 'Should have raised MethodNotAllowed' rescue ActionController::MethodNotAllowed => e assert_equal [:get, :put, :delete], e.allowed_methods end - request.recycle! end def test_recognize_with_alias_in_conditions @@ -1038,17 +1007,13 @@ class RouteSetTest < ActiveSupport::TestCase map.root :people end - request.path = "/people" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("people", request.path_parameters[:controller]) - assert_equal("index", request.path_parameters[:action]) + params = set.recognize_path("/people", :method => :get) + assert_equal("people", params[:controller]) + assert_equal("index", params[:action]) - request.path = "/" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("people", request.path_parameters[:controller]) - assert_equal("index", request.path_parameters[:action]) + params = set.recognize_path("/", :method => :get) + assert_equal("people", params[:controller]) + assert_equal("index", params[:action]) end def test_typo_recognition @@ -1058,14 +1023,12 @@ class RouteSetTest < ActiveSupport::TestCase :year => /\d{4}/, :day => /\d{1,2}/, :month => /\d{1,2}/ end - request.path = "/articles/2005/11/05/a-very-interesting-article" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("permalink", request.path_parameters[:action]) - assert_equal("2005", request.path_parameters[:year]) - assert_equal("11", request.path_parameters[:month]) - assert_equal("05", request.path_parameters[:day]) - assert_equal("a-very-interesting-article", request.path_parameters[:title]) + params = set.recognize_path("/articles/2005/11/05/a-very-interesting-article", :method => :get) + assert_equal("permalink", params[:action]) + assert_equal("2005", params[:year]) + assert_equal("11", params[:month]) + assert_equal("05", params[:day]) + assert_equal("a-very-interesting-article", params[:title]) end def test_routing_traversal_does_not_load_extra_classes @@ -1074,9 +1037,7 @@ class RouteSetTest < ActiveSupport::TestCase map.connect '/profile', :controller => 'profile' end - request.path = '/profile' - - set.recognize(request) rescue nil + params = set.recognize_path("/profile") rescue nil assert !Object.const_defined?("Profiler__"), "Profiler should not be loaded" end @@ -1090,24 +1051,17 @@ class RouteSetTest < ActiveSupport::TestCase end end - request.request_uri = "/people/5" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("show", request.path_parameters[:action]) - assert_equal("5", request.path_parameters[:id]) - request.recycle! + params = set.recognize_path("/people/5", :method => :get) + assert_equal("show", params[:action]) + assert_equal("5", params[:id]) - request.env["REQUEST_METHOD"] = "PUT" - assert_nothing_raised { set.recognize(request) } - assert_equal("update", request.path_parameters[:action]) - request.recycle! + params = set.recognize_path("/people/5", :method => :put) + assert_equal("update", params[:action]) - request.request_uri = "/people/5.png" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("show", request.path_parameters[:action]) - assert_equal("5", request.path_parameters[:id]) - assert_equal("png", request.path_parameters[:_format]) + params = set.recognize_path("/people/5.png", :method => :get) + assert_equal("show", params[:action]) + assert_equal("5", params[:id]) + assert_equal("png", params[:_format]) end def test_generate_with_default_action @@ -1123,11 +1077,9 @@ class RouteSetTest < ActiveSupport::TestCase def test_root_map set.draw { |map| map.root :controller => "people" } - request.path = "" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("people", request.path_parameters[:controller]) - assert_equal("index", request.path_parameters[:action]) + params = set.recognize_path("", :method => :get) + assert_equal("people", params[:controller]) + assert_equal("index", params[:action]) end def test_namespace @@ -1139,11 +1091,9 @@ class RouteSetTest < ActiveSupport::TestCase end - request.path = "/api/inventory" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("api/products", request.path_parameters[:controller]) - assert_equal("inventory", request.path_parameters[:action]) + params = set.recognize_path("/api/inventory", :method => :get) + assert_equal("api/products", params[:controller]) + assert_equal("inventory", params[:action]) end def test_namespaced_root_map @@ -1155,11 +1105,9 @@ class RouteSetTest < ActiveSupport::TestCase end - request.path = "/api" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("api/products", request.path_parameters[:controller]) - assert_equal("index", request.path_parameters[:action]) + params = set.recognize_path("/api", :method => :get) + assert_equal("api/products", params[:controller]) + assert_equal("index", params[:action]) end def test_namespace_with_path_prefix @@ -1169,11 +1117,9 @@ class RouteSetTest < ActiveSupport::TestCase end end - request.path = "/prefix/inventory" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("api/products", request.path_parameters[:controller]) - assert_equal("inventory", request.path_parameters[:action]) + params = set.recognize_path("/prefix/inventory", :method => :get) + assert_equal("api/products", params[:controller]) + assert_equal("inventory", params[:action]) end def test_namespace_with_blank_path_prefix @@ -1183,11 +1129,9 @@ class RouteSetTest < ActiveSupport::TestCase end end - request.path = "/inventory" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("api/products", request.path_parameters[:controller]) - assert_equal("inventory", request.path_parameters[:action]) + params = set.recognize_path("/inventory", :method => :get) + assert_equal("api/products", params[:controller]) + assert_equal("inventory", params[:action]) end def test_generate_changes_controller_module @@ -1316,11 +1260,9 @@ class RouteSetTest < ActiveSupport::TestCase end end - request.path = "/projects/1/milestones" - request.env["REQUEST_METHOD"] = "GET" - assert_nothing_raised { set.recognize(request) } - assert_equal("milestones", request.path_parameters[:controller]) - assert_equal("index", request.path_parameters[:action]) + params = set.recognize_path("/projects/1/milestones", :method => :get) + assert_equal("milestones", params[:controller]) + assert_equal("index", params[:action]) end def test_setting_root_in_namespace_using_symbol -- cgit v1.2.3 From 588225f8852c4b60bfba38f16d8797a41e175400 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 11 Dec 2009 00:01:22 -0600 Subject: Remove fancy method not allowed resource exceptions since they are too much of a hack --- .../lib/action_controller/metal/exceptions.rb | 11 +--------- .../lib/action_dispatch/routing/route_set.rb | 24 +--------------------- actionpack/test/controller/resources_test.rb | 8 ++++---- actionpack/test/controller/routing_test.rb | 9 +++----- 4 files changed, 9 insertions(+), 43 deletions(-) diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb index b9d23da3e0..07024d0a9a 100644 --- a/actionpack/lib/action_controller/metal/exceptions.rb +++ b/actionpack/lib/action_controller/metal/exceptions.rb @@ -18,18 +18,9 @@ module ActionController def initialize(*allowed_methods) super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.") - @allowed_methods = allowed_methods - end - - def allowed_methods_header - allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', ' - end - - def handle_response!(response) - response.headers['Allow'] ||= allowed_methods_header end end - + class NotImplemented < MethodNotAllowed #:nodoc: end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 8d56c4d087..d71ed1d1db 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -456,30 +456,9 @@ module ActionDispatch def call(env) @set.call(env) - rescue ActionController::RoutingError => e - raise e if env['action_controller.rescue_error'] == false - - method, path = env['REQUEST_METHOD'].downcase.to_sym, env['PATH_INFO'] - - # Route was not recognized. Try to find out why (maybe wrong verb). - allows = HTTP_METHODS.select { |verb| - begin - recognize_path(path, {:method => verb}, false) - rescue ActionController::RoutingError - nil - end - } - - if !HTTP_METHODS.include?(method) - raise ActionController::NotImplemented.new(*allows) - elsif !allows.empty? - raise ActionController::MethodNotAllowed.new(*allows) - else - raise e - end end - def recognize_path(path, environment = {}, rescue_error = true) + def recognize_path(path, environment = {}) method = (environment[:method] || "GET").to_s.upcase begin @@ -489,7 +468,6 @@ module ActionDispatch end env['action_controller.recognize'] = true - env['action_controller.rescue_error'] = rescue_error status, headers, body = call(env) body end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 04e9acf855..1a03396ae9 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -403,7 +403,7 @@ class ResourcesTest < ActionController::TestCase with_restful_routing :messages do assert_restful_routes_for :messages do |options| assert_recognizes(options.merge(:action => "new"), :path => "/messages/new", :method => :get) - assert_raise(ActionController::MethodNotAllowed) do + assert_raise(ActionController::RoutingError) do ActionController::Routing::Routes.recognize_path("/messages/new", :method => :post) end end @@ -689,11 +689,11 @@ class ResourcesTest < ActionController::TestCase options = { :controller => controller_name.to_s } collection_path = "/#{controller_name}" - assert_raise(ActionController::MethodNotAllowed) do + assert_raise(ActionController::RoutingError) do assert_recognizes(options.merge(:action => 'update'), :path => collection_path, :method => :put) end - assert_raise(ActionController::MethodNotAllowed) do + assert_raise(ActionController::RoutingError) do assert_recognizes(options.merge(:action => 'destroy'), :path => collection_path, :method => :delete) end end @@ -1378,7 +1378,7 @@ class ResourcesTest < ActionController::TestCase end def assert_not_recognizes(expected_options, path) - assert_raise ActionController::RoutingError, ActionController::MethodNotAllowed, Assertion do + assert_raise ActionController::RoutingError, Assertion do assert_recognizes(expected_options, path) end end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index d52da58807..a9a970d67d 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -976,7 +976,7 @@ class RouteSetTest < ActiveSupport::TestCase params = set.recognize_path("/people", :method => :put) assert_equal("update", params[:action]) - assert_raise(ActionController::NotImplemented) { + assert_raise(ActionController::RoutingError) { set.recognize_path("/people", :method => :bacon) } @@ -992,12 +992,9 @@ class RouteSetTest < ActiveSupport::TestCase assert_equal("destroy", params[:action]) assert_equal("5", params[:id]) - begin + assert_raise(ActionController::RoutingError) { set.recognize_path("/people/5", :method => :post) - flunk 'Should have raised MethodNotAllowed' - rescue ActionController::MethodNotAllowed => e - assert_equal [:get, :put, :delete], e.allowed_methods - end + } end def test_recognize_with_alias_in_conditions -- cgit v1.2.3 From 61e9f2023baead046c7f8ba7c89a7496bf49d1be Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 11 Dec 2009 00:11:16 -0600 Subject: Use rackmounts recognize api and don't piggyback recognize_path on top of rack call --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- .../lib/action_dispatch/routing/route_set.rb | 49 +++++++++++++--------- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 3b185c2576..2fa0aab5df 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -2,7 +2,7 @@ module ActionDispatch module Routing class Mapper class Constraints - def new(app, constraints = []) + def self.new(app, constraints = []) if constraints.any? super(app, constraints) else diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index d71ed1d1db..8afd42a293 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -18,36 +18,37 @@ module ActionDispatch def call(env) params = env[PARAMETERS_KEY] + prepare_params!(params) + + unless controller = controller(params) + return [417, {}, []] + end + + controller.action(params[:action]).call(env) + end + + def prepare_params!(params) merge_default_action!(params) split_glob_param!(params) if @glob_param + params.each do |key, value| if value.is_a?(String) value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding) params[key] = URI.unescape(value) end end + end - unless controller = controller(params) - return [417, {}, []] - end - - if env['action_controller.recognize'] - [200, {}, params] - else - controller.action(params[:action]).call(env) + def controller(params) + if params && params.has_key?(:controller) + controller = "#{params[:controller].camelize}Controller" + ActiveSupport::Inflector.constantize(controller) end + rescue NameError + nil end private - def controller(params) - if params && params.has_key?(:controller) - controller = "#{params[:controller].camelize}Controller" - ActiveSupport::Inflector.constantize(controller) - end - rescue NameError - nil - end - def merge_default_action!(params) params[:action] ||= 'index' end @@ -460,6 +461,7 @@ module ActionDispatch def recognize_path(path, environment = {}) method = (environment[:method] || "GET").to_s.upcase + path = Rack::Mount::Utils.normalize_path(path) begin env = Rack::MockRequest.env_for(path, {:method => method}) @@ -467,9 +469,16 @@ module ActionDispatch raise ActionController::RoutingError, e.message end - env['action_controller.recognize'] = true - status, headers, body = call(env) - body + req = Rack::Request.new(env) + @set.recognize(req) do |route, params| + dispatcher = route.app + if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params) + dispatcher.prepare_params!(params) + return params + end + end + + raise ActionController::RoutingError, "No route matches #{path.inspect} with #{environment.inspect}" end end end -- cgit v1.2.3 From 2297eaed5b195ea42b99d062ad45f87dde9d3c60 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 11 Dec 2009 12:46:50 -0600 Subject: "new" and "edit" name routes always need to be prepend to the named_route [#3561 state:resolved] --- actionpack/lib/action_dispatch/routing/mapper.rb | 29 +++++++++++++++++++----- actionpack/test/dispatch/routing_test.rb | 14 ++++++++++++ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 2fa0aab5df..d480af876d 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -338,7 +338,9 @@ module ActionDispatch with_scope_level(:collection) do get "(.:format)", :to => :index, :as => resource.collection_name post "(.:format)", :to => :create - get "/new(.:format)", :to => :new, :as => "new_#{resource.singular}" + with_exclusive_name_prefix :new do + get "/new(.:format)", :to => :new, :as => resource.singular + end end with_scope_level(:member) do @@ -346,7 +348,9 @@ module ActionDispatch get "(.:format)", :to => :show, :as => resource.member_name put "(.:format)", :to => :update delete "(.:format)", :to => :destroy - get "/edit(.:format)", :to => :edit, :as => "edit_#{resource.singular}" + with_exclusive_name_prefix :edit do + get "/edit(.:format)", :to => :edit, :as => resource.singular + end end end end @@ -400,11 +404,8 @@ module ActionDispatch end if args.first.is_a?(Symbol) - begin - old_name_prefix, @scope[:name_prefix] = @scope[:name_prefix], "#{args.first}_#{@scope[:name_prefix]}" + with_exclusive_name_prefix(args.first) do return match("/#{args.first}(.:format)", options.merge(:to => args.first.to_sym)) - ensure - @scope[:name_prefix] = old_name_prefix end end @@ -430,6 +431,22 @@ module ActionDispatch end private + def with_exclusive_name_prefix(prefix) + begin + old_name_prefix = @scope[:name_prefix] + + if !old_name_prefix.blank? + @scope[:name_prefix] = "#{prefix}_#{@scope[:name_prefix]}" + else + @scope[:name_prefix] = prefix.to_s + end + + yield + ensure + @scope[:name_prefix] = old_name_prefix + end + end + def with_scope_level(kind, resource = parent_resource) old, @scope[:scope_level] = @scope[:scope_level], kind old_resource, @scope[:scope_level_resource] = @scope[:scope_level_resource], resource diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 22ef48b668..425796b460 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -228,9 +228,23 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'involvements#index', @response.body assert_equal '/projects/1/involvements', project_involvements_path(:project_id => '1') + get '/projects/1/involvements/new' + assert_equal 'involvements#new', @response.body + assert_equal '/projects/1/involvements/new', new_project_involvement_path(:project_id => '1') + get '/projects/1/involvements/1' assert_equal 'involvements#show', @response.body assert_equal '/projects/1/involvements/1', project_involvement_path(:project_id => '1', :id => '1') + + put '/projects/1/involvements/1' + assert_equal 'involvements#update', @response.body + + delete '/projects/1/involvements/1' + assert_equal 'involvements#destroy', @response.body + + get '/projects/1/involvements/1/edit' + assert_equal 'involvements#edit', @response.body + assert_equal '/projects/1/involvements/1/edit', edit_project_involvement_path(:project_id => '1', :id => '1') end end -- cgit v1.2.3 From 96deabace71d5cb3ee12c7b99e459db5e2acb1a6 Mon Sep 17 00:00:00 2001 From: John Pignata Date: Sat, 5 Dec 2009 11:57:13 -0500 Subject: Fix postgresql AR test failure Due to the ordering of the returning result set, the test fails under the postgresql adapter. Order results by id prior to checking the first item [#3542 state:committed] Signed-off-by: Jeremy Kemper --- activerecord/test/cases/associations/has_many_associations_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 3c490c1eeb..05e7e380a8 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1180,11 +1180,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_normal_method_call_in_association_proxy - assert_equal 'Welcome to the weblog', Comment.all.map { |comment| comment.post }.first.title + assert_equal 'Welcome to the weblog', Comment.all.map { |comment| comment.post }.sort_by(&:id).first.title end def test_instance_eval_in_association_proxy - assert_equal 'Welcome to the weblog', Comment.all.map { |comment| comment.post }.first.instance_eval{title} + assert_equal 'Welcome to the weblog', Comment.all.map { |comment| comment.post }.sort_by(&:id).first.instance_eval{title} end end -- cgit v1.2.3 From ee395fe626760e897abd9e881b54d3cc3f407d31 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 12 Dec 2009 18:09:44 -0600 Subject: TestProcess belongs in AD --- actionpack/lib/action_controller/test_case.rb | 3 +- .../lib/action_controller/testing/process.rb | 111 --------------------- actionpack/lib/action_dispatch/test_case.rb | 1 + .../action_dispatch/testing/assertions/routing.rb | 40 ++++++++ .../lib/action_dispatch/testing/assertions/tag.rb | 19 +++- .../lib/action_dispatch/testing/integration.rb | 9 +- .../lib/action_dispatch/testing/test_process.rb | 42 ++++++++ actionpack/lib/action_view/test_case.rb | 3 +- actionpack/test/abstract_unit.rb | 2 +- actionpack/test/controller/test_test.rb | 8 +- 10 files changed, 109 insertions(+), 129 deletions(-) delete mode 100644 actionpack/lib/action_controller/testing/process.rb create mode 100644 actionpack/lib/action_dispatch/testing/test_process.rb diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 7533a22299..14cd0dc7e0 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -1,7 +1,6 @@ require 'active_support/test_case' require 'rack/session/abstract/id' require 'action_controller/metal/testing' -require 'action_controller/testing/process' require 'action_dispatch/test_case' module ActionController @@ -183,7 +182,7 @@ module ActionController # # assert_redirected_to page_url(:title => 'foo') class TestCase < ActiveSupport::TestCase - include TestProcess + include ActionDispatch::TestProcess # Executes a request simulating GET HTTP method and set/volley the response def get(action, parameters = nil, session = nil, flash = nil) diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb deleted file mode 100644 index 323cce6a2f..0000000000 --- a/actionpack/lib/action_controller/testing/process.rb +++ /dev/null @@ -1,111 +0,0 @@ -require 'active_support/core_ext/object/conversions' -require "rack/test" - -module ActionController #:nodoc: - # Essentially generates a modified Tempfile object similar to the object - # you'd get from the standard library CGI module in a multipart - # request. This means you can use an ActionController::TestUploadedFile - # object in the params of a test request in order to simulate - # a file upload. - # - # Usage example, within a functional test: - # post :change_avatar, :avatar => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + '/files/spongebob.png', 'image/png') - # - # Pass a true third parameter to ensure the uploaded file is opened in binary mode (only required for Windows): - # post :change_avatar, :avatar => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + '/files/spongebob.png', 'image/png', :binary) - TestUploadedFile = Rack::Test::UploadedFile - - module TestProcess - def assigns(key = nil) - assigns = {} - @controller.instance_variable_names.each do |ivar| - next if ActionController::Base.protected_instance_variables.include?(ivar) - assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar) - end - - key.nil? ? assigns : assigns[key.to_s] - end - - def session - @request.session - end - - def flash - @request.flash - end - - def cookies - @request.cookies.merge(@response.cookies) - end - - def redirect_to_url - @response.redirect_url - end - - def html_document - xml = @response.content_type =~ /xml$/ - @html_document ||= HTML::Document.new(@response.body, false, xml) - end - - def find_tag(conditions) - html_document.find(conditions) - end - - def find_all_tag(conditions) - html_document.find_all(conditions) - end - - def method_missing(selector, *args, &block) - if @controller && ActionController::Routing::Routes.named_routes.helpers.include?(selector) - @controller.send(selector, *args, &block) - else - super - end - end - - # Shortcut for ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + path, type): - # - # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png') - # - # To upload binary files on Windows, pass :binary as the last parameter. - # This will not affect other platforms: - # - # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary) - def fixture_file_upload(path, mime_type = nil, binary = false) - fixture_path = ActionController::TestCase.send(:fixture_path) if ActionController::TestCase.respond_to?(:fixture_path) - ActionController::TestUploadedFile.new("#{fixture_path}#{path}", mime_type, binary) - end - - # A helper to make it easier to test different route configurations. - # This method temporarily replaces ActionController::Routing::Routes - # with a new RouteSet instance. - # - # The new instance is yielded to the passed block. Typically the block - # will create some routes using map.draw { map.connect ... }: - # - # with_routing do |set| - # set.draw do |map| - # map.connect ':controller/:action/:id' - # assert_equal( - # ['/content/10/show', {}], - # map.generate(:controller => 'content', :id => 10, :action => 'show') - # end - # end - # end - # - def with_routing - real_routes = ActionController::Routing::Routes - ActionController::Routing.module_eval { remove_const :Routes } - - temporary_routes = ActionController::Routing::RouteSet.new - ActionController::Routing.module_eval { const_set :Routes, temporary_routes } - - yield temporary_routes - ensure - if ActionController::Routing.const_defined? :Routes - ActionController::Routing.module_eval { remove_const :Routes } - end - ActionController::Routing.const_set(:Routes, real_routes) if real_routes - end - end -end diff --git a/actionpack/lib/action_dispatch/test_case.rb b/actionpack/lib/action_dispatch/test_case.rb index afd708f06f..0b3dfaae79 100644 --- a/actionpack/lib/action_dispatch/test_case.rb +++ b/actionpack/lib/action_dispatch/test_case.rb @@ -1,3 +1,4 @@ +require "rack/test" require "action_dispatch/testing/assertions" require "action_dispatch/testing/integration" require "action_dispatch/testing/performance_test" diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index 4bc5275e04..794fb888b7 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -126,6 +126,46 @@ module ActionDispatch assert_generates(path.is_a?(Hash) ? path[:path] : path, options, defaults, extras, message) end + # A helper to make it easier to test different route configurations. + # This method temporarily replaces ActionController::Routing::Routes + # with a new RouteSet instance. + # + # The new instance is yielded to the passed block. Typically the block + # will create some routes using map.draw { map.connect ... }: + # + # with_routing do |set| + # set.draw do |map| + # map.connect ':controller/:action/:id' + # assert_equal( + # ['/content/10/show', {}], + # map.generate(:controller => 'content', :id => 10, :action => 'show') + # end + # end + # end + # + def with_routing + real_routes = ActionController::Routing::Routes + ActionController::Routing.module_eval { remove_const :Routes } + + temporary_routes = ActionController::Routing::RouteSet.new + ActionController::Routing.module_eval { const_set :Routes, temporary_routes } + + yield temporary_routes + ensure + if ActionController::Routing.const_defined? :Routes + ActionController::Routing.module_eval { remove_const :Routes } + end + ActionController::Routing.const_set(:Routes, real_routes) if real_routes + end + + def method_missing(selector, *args, &block) + if @controller && ActionController::Routing::Routes.named_routes.helpers.include?(selector) + @controller.send(selector, *args, &block) + else + super + end + end + private # Recognizes the route for a given path. def recognized_request_for(path, request_method = nil) diff --git a/actionpack/lib/action_dispatch/testing/assertions/tag.rb b/actionpack/lib/action_dispatch/testing/assertions/tag.rb index ef6867576e..b74dcb1fe4 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/tag.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/tag.rb @@ -76,10 +76,10 @@ module ActionDispatch # # Assert that there is a "span" containing between 2 and 4 "em" tags # # as immediate children # assert_tag :tag => "span", - # :children => { :count => 2..4, :only => { :tag => "em" } } + # :children => { :count => 2..4, :only => { :tag => "em" } } # # # Get funky: assert that there is a "div", with an "ul" ancestor - # # and an "li" parent (with "class" = "enum"), and containing a + # # and an "li" parent (with "class" = "enum"), and containing a # # "span" descendant that contains text matching /hello world/ # assert_tag :tag => "div", # :ancestor => { :tag => "ul" }, @@ -98,7 +98,7 @@ module ActionDispatch tag = find_tag(opts) assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}" end - + # Identical to +assert_tag+, but asserts that a matching tag does _not_ # exist. (See +assert_tag+ for a full discussion of the syntax.) # @@ -118,6 +118,19 @@ module ActionDispatch tag = find_tag(opts) assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}" end + + def find_tag(conditions) + html_document.find(conditions) + end + + def find_all_tag(conditions) + html_document.find_all(conditions) + end + + def html_document + xml = @response.content_type =~ /xml$/ + @html_document ||= HTML::Document.new(@response.body, false, xml) + end end end end diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 40d6f97b2a..76021dc059 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -2,9 +2,8 @@ require 'stringio' require 'uri' require 'active_support/test_case' require 'active_support/core_ext/object/metaclass' - -# TODO: Remove circular dependency on ActionController -require 'action_controller/testing/process' +require 'action_dispatch/testing/test_process' +require 'rack/test' module ActionDispatch module Integration #:nodoc: @@ -128,9 +127,7 @@ module ActionDispatch DEFAULT_HOST = "www.example.com" include Test::Unit::Assertions - include ActionDispatch::Assertions - include ActionController::TestProcess - include RequestHelpers + include TestProcess, RequestHelpers, Assertions %w( status status_message headers body redirect? ).each do |method| delegate method, :to => :response, :allow_nil => true diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb new file mode 100644 index 0000000000..eae703e1b6 --- /dev/null +++ b/actionpack/lib/action_dispatch/testing/test_process.rb @@ -0,0 +1,42 @@ +module ActionDispatch + module TestProcess + def assigns(key = nil) + assigns = {} + @controller.instance_variable_names.each do |ivar| + next if ActionController::Base.protected_instance_variables.include?(ivar) + assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar) + end + + key.nil? ? assigns : assigns[key.to_s] + end + + def session + @request.session + end + + def flash + @request.flash + end + + def cookies + @request.cookies.merge(@response.cookies) + end + + def redirect_to_url + @response.redirect_url + end + + # Shortcut for ARack::Test::UploadedFile.new(ActionController::TestCase.fixture_path + path, type): + # + # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png') + # + # To upload binary files on Windows, pass :binary as the last parameter. + # This will not affect other platforms: + # + # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary) + def fixture_file_upload(path, mime_type = nil, binary = false) + fixture_path = ActionController::TestCase.send(:fixture_path) if ActionController::TestCase.respond_to?(:fixture_path) + Rack::Test::UploadedFile.new("#{fixture_path}#{path}", mime_type, binary) + end + end +end diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index ab5bc49cf9..be9a2ed50d 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -39,8 +39,7 @@ module ActionView end end - include ActionDispatch::Assertions - include ActionController::TestProcess + include ActionDispatch::Assertions, ActionDispatch::TestProcess include ActionView::Context include ActionController::PolymorphicRoutes diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index eab9f8b83d..4dae1ab873 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -198,7 +198,7 @@ module ActionController Base.view_paths = FIXTURE_LOAD_PATH class TestCase - include TestProcess + include ActionDispatch::TestProcess def assert_template(options = {}, message = nil) validate_request! diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index a788b2495e..0f074b32e6 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -563,7 +563,7 @@ XML expected = File.read(path) expected.force_encoding(Encoding::BINARY) if expected.respond_to?(:force_encoding) - file = ActionController::TestUploadedFile.new(path, content_type) + file = Rack::Test::UploadedFile.new(path, content_type) assert_equal filename, file.original_filename assert_equal content_type, file.content_type assert_equal file.path, file.local_path @@ -580,10 +580,10 @@ XML path = "#{FILES_DIR}/#{filename}" content_type = 'image/png' - binary_uploaded_file = ActionController::TestUploadedFile.new(path, content_type, :binary) + binary_uploaded_file = Rack::Test::UploadedFile.new(path, content_type, :binary) assert_equal File.open(path, READ_BINARY).read, binary_uploaded_file.read - plain_uploaded_file = ActionController::TestUploadedFile.new(path, content_type) + plain_uploaded_file = Rack::Test::UploadedFile.new(path, content_type) assert_equal File.open(path, READ_PLAIN).read, plain_uploaded_file.read end @@ -605,7 +605,7 @@ XML end def test_test_uploaded_file_exception_when_file_doesnt_exist - assert_raise(RuntimeError) { ActionController::TestUploadedFile.new('non_existent_file') } + assert_raise(RuntimeError) { Rack::Test::UploadedFile.new('non_existent_file') } end def test_redirect_url_only_cares_about_location_header -- cgit v1.2.3 From 018dafe574d370165547516ffef43394e11ab4da Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 12 Dec 2009 18:41:26 -0600 Subject: Allow autoloads to opt out of eager loading --- .../deprecated/integration_test.rb | 2 - actionpack/lib/action_controller/test_case.rb | 1 - actionpack/lib/action_dispatch.rb | 49 +++++++++++++--------- actionpack/lib/action_dispatch/http/request.rb | 3 +- actionpack/lib/action_dispatch/test_case.rb | 7 ---- .../lib/action_dispatch/testing/integration.rb | 1 - .../lib/active_support/dependencies/autoload.rb | 20 +++++++-- 7 files changed, 47 insertions(+), 36 deletions(-) delete mode 100644 actionpack/lib/action_dispatch/test_case.rb diff --git a/actionpack/lib/action_controller/deprecated/integration_test.rb b/actionpack/lib/action_controller/deprecated/integration_test.rb index 05c8c0f156..86336b6bc4 100644 --- a/actionpack/lib/action_controller/deprecated/integration_test.rb +++ b/actionpack/lib/action_controller/deprecated/integration_test.rb @@ -1,4 +1,2 @@ -require "action_dispatch/testing/integration" - ActionController::Integration = ActionDispatch::Integration ActionController::IntegrationTest = ActionDispatch::IntegrationTest diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 14cd0dc7e0..398ea52495 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -1,7 +1,6 @@ require 'active_support/test_case' require 'rack/session/abstract/id' require 'action_controller/metal/testing' -require 'action_dispatch/test_case' module ActionController class TestRequest < ActionDispatch::TestRequest #:nodoc: diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index e21dbc59cc..d1c191d652 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -30,38 +30,47 @@ end module ActionDispatch extend ActiveSupport::Autoload - autoload_under "http" do + autoload_under 'http' do autoload :Request autoload :Response autoload :StatusCodes autoload :Utils end - autoload_under "middleware" do - autoload :Callbacks - autoload :ParamsParser - autoload :Rescue - autoload :ShowExceptions - autoload :Static - autoload :StringCoercion - end + deferrable do + autoload_under 'middleware' do + autoload :Callbacks + autoload :ParamsParser + autoload :Rescue + autoload :ShowExceptions + autoload :Static + autoload :StringCoercion + end - autoload :MiddlewareStack, 'action_dispatch/middleware/stack' - autoload :Routing + autoload :MiddlewareStack, 'action_dispatch/middleware/stack' + autoload :Routing - autoload :HTML, 'action_controller/vendor/html-scanner' + module Http + autoload :Headers, 'action_dispatch/http/headers' + end - module Http - extend ActiveSupport::Autoload + module Session + autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store' + autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' + autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store' + end - autoload :Headers + autoload_under 'testing' do + autoload :Assertions + autoload :Integration + autoload :PerformanceTest + autoload :TestProcess + autoload :TestRequest + autoload :TestResponse + end end - module Session - autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store' - autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store' - autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store' - end + autoload :HTML, 'action_controller/vendor/html-scanner' end autoload :Mime, 'action_dispatch/http/mime_type' diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 6a52854961..7d1f5a4504 100755 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -6,6 +6,7 @@ require 'active_support/memoizable' require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/string/access' +require 'action_dispatch/http/headers' module ActionDispatch class Request < Rack::Request @@ -117,7 +118,7 @@ module ActionDispatch end end end - + def if_modified_since if since = env['HTTP_IF_MODIFIED_SINCE'] Time.rfc2822(since) rescue nil diff --git a/actionpack/lib/action_dispatch/test_case.rb b/actionpack/lib/action_dispatch/test_case.rb deleted file mode 100644 index 0b3dfaae79..0000000000 --- a/actionpack/lib/action_dispatch/test_case.rb +++ /dev/null @@ -1,7 +0,0 @@ -require "rack/test" -require "action_dispatch/testing/assertions" -require "action_dispatch/testing/integration" -require "action_dispatch/testing/performance_test" -require "action_dispatch/testing/test_request" -require "action_dispatch/testing/test_response" -require "action_dispatch/testing/integration" \ No newline at end of file diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 76021dc059..5c127dfe37 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -2,7 +2,6 @@ require 'stringio' require 'uri' require 'active_support/test_case' require 'active_support/core_ext/object/metaclass' -require 'action_dispatch/testing/test_process' require 'rack/test' module ActionDispatch diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb index 973875f895..96ab04c61a 100644 --- a/activesupport/lib/active_support/dependencies/autoload.rb +++ b/activesupport/lib/active_support/dependencies/autoload.rb @@ -2,16 +2,18 @@ require "active_support/inflector/methods" module ActiveSupport module Autoload - @@autoloads = {} @@under_path = nil @@at_path = nil + @@autoload_defer = false def autoload(const_name, path = @@at_path) full = [self.name, @@under_path, const_name.to_s, path].compact.join("::") location = path || Inflector.underscore(full) - @@autoloads[const_name] = location + unless @@autoload_defer + @@autoloads[const_name] = location + end super const_name, location end @@ -29,9 +31,19 @@ module ActiveSupport @@at_path = old_path end + def deferrable + old_defer, @@autoload_defer = @@autoload_defer, true + yield + ensure + @@autoload_defer = old_defer + end + def self.eager_autoload! - @@autoloads.values.each {|file| require file } + @@autoloads.values.each { |file| require file } end + def autoloads + @@autoloads + end end -end \ No newline at end of file +end -- cgit v1.2.3 From 4b4e517bf1d51e749a6cedb2dd5203b19ab080e5 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 12 Dec 2009 18:48:34 -0600 Subject: Relocate AbstractController exceptions into their proper parent modules --- actionpack/lib/abstract_controller.rb | 7 ------- actionpack/lib/abstract_controller/base.rb | 4 +++- actionpack/lib/abstract_controller/exceptions.rb | 12 ------------ .../lib/abstract_controller/rendering_controller.rb | 20 ++++++++++++++------ 4 files changed, 17 insertions(+), 26 deletions(-) delete mode 100644 actionpack/lib/abstract_controller/exceptions.rb diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 688a2fe31c..af3623e97e 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -11,11 +11,4 @@ module AbstractController autoload :LocalizedCache autoload :Logger autoload :RenderingController - - # === Exceptions - autoload_at "abstract_controller/exceptions" do - autoload :ActionNotFound - autoload :DoubleRenderError - autoload :Error - end end diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index f5b1c9e4d1..70b5f5b3ef 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -1,4 +1,6 @@ module AbstractController + class Error < StandardError; end + class ActionNotFound < StandardError; end class Base attr_internal :response_body @@ -74,7 +76,7 @@ module AbstractController abstract! # Calls the action going through the entire action dispatch stack. - # + # # The actual method that is called is determined by calling # #method_for_action. If no method can handle the action, then an # ActionNotFound error is raised. diff --git a/actionpack/lib/abstract_controller/exceptions.rb b/actionpack/lib/abstract_controller/exceptions.rb deleted file mode 100644 index b671516de1..0000000000 --- a/actionpack/lib/abstract_controller/exceptions.rb +++ /dev/null @@ -1,12 +0,0 @@ -module AbstractController - class Error < StandardError; end - class ActionNotFound < StandardError; end - - class DoubleRenderError < Error - DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"." - - def initialize(message = nil) - super(message || DEFAULT_MESSAGE) - end - end -end diff --git a/actionpack/lib/abstract_controller/rendering_controller.rb b/actionpack/lib/abstract_controller/rendering_controller.rb index 777e515d60..d6d2c3e191 100644 --- a/actionpack/lib/abstract_controller/rendering_controller.rb +++ b/actionpack/lib/abstract_controller/rendering_controller.rb @@ -1,6 +1,14 @@ require "abstract_controller/logger" module AbstractController + class DoubleRenderError < Error + DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"." + + def initialize(message = nil) + super(message || DEFAULT_MESSAGE) + end + end + module RenderingController extend ActiveSupport::Concern @@ -21,7 +29,7 @@ module AbstractController # An instance of a view class. The default view class is ActionView::Base # # The view class must have the following methods: - # View.for_controller[controller] Create a new ActionView instance for a + # View.for_controller[controller] Create a new ActionView instance for a # controller # View#render_partial[options] # - responsible for setting options[:_template] @@ -152,12 +160,12 @@ module AbstractController module ClassMethods def clear_template_caches! end - + # Append a path to the list of view paths for this controller. # # ==== Parameters - # path:: If a String is provided, it gets converted into - # the default view path. You may also provide a custom view path + # path:: If a String is provided, it gets converted into + # the default view path. You may also provide a custom view path # (see ActionView::ViewPathSet for more information) def append_view_path(path) self.view_paths << path @@ -166,8 +174,8 @@ module AbstractController # Prepend a path to the list of view paths for this controller. # # ==== Parameters - # path:: If a String is provided, it gets converted into - # the default view path. You may also provide a custom view path + # path:: If a String is provided, it gets converted into + # the default view path. You may also provide a custom view path # (see ActionView::ViewPathSet for more information) def prepend_view_path(path) clear_template_caches! -- cgit v1.2.3 From 9cc99498178f1e71da8b54f985d0483ea377421d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 12 Dec 2009 19:28:03 -0600 Subject: All AbstractController modules are deferrable --- actionpack/lib/abstract_controller.rb | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index af3623e97e..92d2971ec3 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -4,11 +4,13 @@ require "active_support/core_ext/module/delegation" module AbstractController extend ActiveSupport::Autoload - autoload :Base - autoload :Callbacks - autoload :Helpers - autoload :Layouts - autoload :LocalizedCache - autoload :Logger - autoload :RenderingController + deferrable do + autoload :Base + autoload :Callbacks + autoload :Helpers + autoload :Layouts + autoload :LocalizedCache + autoload :Logger + autoload :RenderingController + end end -- cgit v1.2.3 From 289c9a24fcd27665f5544c4d647f3a60ae9c790e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 12 Dec 2009 19:41:58 -0600 Subject: Nearly all AC modules can be deferred --- actionpack/lib/action_controller.rb | 84 +++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 40 deletions(-) diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index f830223058..2113d791f5 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -3,51 +3,55 @@ require "active_support" module ActionController extend ActiveSupport::Autoload - autoload :Base - autoload :Caching - autoload :PolymorphicRoutes - autoload :RecordIdentifier - autoload :UrlRewriter - autoload :Translation - autoload :Metal - autoload :Middleware + deferrable do + autoload :Base + autoload :Caching + autoload :PolymorphicRoutes + autoload :Translation + autoload :Metal + autoload :Middleware - autoload_under "metal" do - autoload :Benchmarking - autoload :ConditionalGet - autoload :Configuration - autoload :Head - autoload :Helpers - autoload :HideActions - autoload :Layouts - autoload :MimeResponds - autoload :RackConvenience - autoload :Compatibility - autoload :Redirector - autoload :RenderingController - autoload :RenderOptions - autoload :Rescue - autoload :Responder - autoload :Session - autoload :SessionManagement - autoload :UrlFor - autoload :Verification - autoload :Flash - autoload :RequestForgeryProtection - autoload :Streaming - autoload :HttpAuthentication - autoload :FilterParameterLogging - autoload :Cookies - end + autoload_under "metal" do + autoload :Benchmarking + autoload :ConditionalGet + autoload :Configuration + autoload :Head + autoload :Helpers + autoload :HideActions + autoload :Layouts + autoload :MimeResponds + autoload :RackConvenience + autoload :Compatibility + autoload :Redirector + autoload :RenderingController + autoload :RenderOptions + autoload :Rescue + autoload :Responder + autoload :Session + autoload :SessionManagement + autoload :UrlFor + autoload :Verification + autoload :Flash + autoload :RequestForgeryProtection + autoload :Streaming + autoload :HttpAuthentication + autoload :FilterParameterLogging + autoload :Cookies + end - autoload :Dispatcher, 'action_controller/dispatch/dispatcher' - autoload :PerformanceTest, 'action_controller/deprecated/performance_test' - autoload :Routing, 'action_controller/deprecated' - autoload :Integration, 'action_controller/deprecated/integration_test' - autoload :IntegrationTest, 'action_controller/deprecated/integration_test' + autoload :Dispatcher, 'action_controller/dispatch/dispatcher' + autoload :PerformanceTest, 'action_controller/deprecated/performance_test' + autoload :Routing, 'action_controller/deprecated' + autoload :Integration, 'action_controller/deprecated/integration_test' + autoload :IntegrationTest, 'action_controller/deprecated/integration_test' + end + autoload :RecordIdentifier + autoload :UrlRewriter autoload :UrlWriter, 'action_controller/url_rewriter' + # TODO: Don't autoload exceptions, setup explicit + # requires for files that need them autoload_at "action_controller/metal/exceptions" do autoload :ActionControllerError autoload :RenderError -- cgit v1.2.3 From 71cd5b9568ddb3fee52318d79e899979887bc087 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 12 Dec 2009 19:44:52 -0600 Subject: Don't require AD test_case --- railties/lib/rails/console_app.rb | 1 - railties/lib/rails/test_help.rb | 1 - 2 files changed, 2 deletions(-) diff --git a/railties/lib/rails/console_app.rb b/railties/lib/rails/console_app.rb index 9a51d594d3..1ad62e5058 100644 --- a/railties/lib/rails/console_app.rb +++ b/railties/lib/rails/console_app.rb @@ -1,7 +1,6 @@ require 'active_support/all' require 'active_support/test_case' require 'action_controller' -require 'action_dispatch/test_case' # 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/rails/test_help.rb b/railties/lib/rails/test_help.rb index 0bf5f5c625..b89b7b5c27 100644 --- a/railties/lib/rails/test_help.rb +++ b/railties/lib/rails/test_help.rb @@ -12,7 +12,6 @@ require 'active_support/core_ext/kernel/requires' # AP is always present require 'action_controller/test_case' require 'action_view/test_case' -require 'action_dispatch/test_case' require 'action_mailer/test_case' if defined?(ActionMailer) require 'active_model/test_case' if defined?(ActiveModel) -- cgit v1.2.3 From 39b708be96f470555bdc6d2c81f252f9f175f899 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 12 Dec 2009 19:50:12 -0600 Subject: rendering controller needs base --- actionpack/lib/abstract_controller/rendering_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/actionpack/lib/abstract_controller/rendering_controller.rb b/actionpack/lib/abstract_controller/rendering_controller.rb index d6d2c3e191..7f2243d4ef 100644 --- a/actionpack/lib/abstract_controller/rendering_controller.rb +++ b/actionpack/lib/abstract_controller/rendering_controller.rb @@ -1,3 +1,4 @@ +require "abstract_controller/base" require "abstract_controller/logger" module AbstractController -- cgit v1.2.3 From 8a50f8a545ebde9cbae4187d1b1d5a59a29711f3 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 12 Dec 2009 19:58:54 -0600 Subject: Revert "Fix instance_eval calls to association proxies" I think it may of broke the build. Lets see. This reverts commit 49e943c4f0ac3459bd53023167aaa08fc8e46733. Conflicts: activerecord/test/cases/associations/has_many_associations_test.rb --- activerecord/lib/active_record/associations/association_proxy.rb | 8 ++++++-- .../test/cases/associations/has_many_associations_test.rb | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb index 6ad1e06300..7d8f4670fa 100644 --- a/activerecord/lib/active_record/associations/association_proxy.rb +++ b/activerecord/lib/active_record/associations/association_proxy.rb @@ -200,14 +200,18 @@ module ActiveRecord private # Forwards any missing method call to the \target. - def method_missing(method, *args, &block) + def method_missing(method, *args) if load_target unless @target.respond_to?(method) message = "undefined method `#{method.to_s}' for \"#{@target}\":#{@target.class.to_s}" raise NoMethodError, message end - @target.send(method, *args, &block) + if block_given? + @target.send(method, *args) { |*block_args| yield(*block_args) } + else + @target.send(method, *args) + end end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 05e7e380a8..551a1da0c6 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1187,4 +1187,3 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 'Welcome to the weblog', Comment.all.map { |comment| comment.post }.sort_by(&:id).first.instance_eval{title} end end - -- cgit v1.2.3 From 25bb301d9c392d7c38ac8e6f5b64ed64e7b95022 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 12 Dec 2009 21:03:45 -0600 Subject: Forgot to revert tests from that last commit --- .../test/cases/associations/has_many_associations_test.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 551a1da0c6..ce7eedbb54 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1178,12 +1178,4 @@ class HasManyAssociationsTest < ActiveRecord::TestCase client = firm.clients_using_primary_key.create!(:name => 'test') assert_equal firm.name, client.firm_name end - - def test_normal_method_call_in_association_proxy - assert_equal 'Welcome to the weblog', Comment.all.map { |comment| comment.post }.sort_by(&:id).first.title - end - - def test_instance_eval_in_association_proxy - assert_equal 'Welcome to the weblog', Comment.all.map { |comment| comment.post }.sort_by(&:id).first.instance_eval{title} - end end -- cgit v1.2.3 From 0f7f90d25bece35194b2d0035de8874e869d299e Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 13 Dec 2009 06:29:50 -0500 Subject: Extract descendents a bit so it can take advantage of Rubinius' __subclasses__. --- .../lib/active_support/core_ext/class/removal.rb | 6 +- .../active_support/core_ext/object/extending.rb | 73 ++++++++++++---------- 2 files changed, 45 insertions(+), 34 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/class/removal.rb b/activesupport/lib/active_support/core_ext/class/removal.rb index 2dea3c24d5..652be4ed78 100644 --- a/activesupport/lib/active_support/core_ext/class/removal.rb +++ b/activesupport/lib/active_support/core_ext/class/removal.rb @@ -2,7 +2,11 @@ require 'active_support/core_ext/object/extending' require 'active_support/core_ext/module/introspection' class Class #:nodoc: - + + def reachable? + eval("defined?(::#{self}) && ::#{self}.equal?(self)") + end + # Unassociates the class with its subclasses and removes the subclasses # themselves. # diff --git a/activesupport/lib/active_support/core_ext/object/extending.rb b/activesupport/lib/active_support/core_ext/object/extending.rb index 0cc74c8298..de8121f274 100644 --- a/activesupport/lib/active_support/core_ext/object/extending.rb +++ b/activesupport/lib/active_support/core_ext/object/extending.rb @@ -1,46 +1,53 @@ -class Object - def remove_subclasses_of(*superclasses) #:nodoc: - Class.remove_class(*subclasses_of(*superclasses)) - end - - begin - ObjectSpace.each_object(Class.new) {} - - # Exclude this class unless it's a subclass of our supers and is defined. - # We check defined? in case we find a removed class that has yet to be - # garbage collected. This also fails for anonymous classes -- please - # submit a patch if you have a workaround. - def subclasses_of(*superclasses) #:nodoc: +class Class + # Rubinius + if defined?(Class.__subclasses__) + def descendents subclasses = [] - - superclasses.each do |sup| - ObjectSpace.each_object(class << sup; self; end) do |k| - if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id")) - subclasses << k - end - end - end - + __subclasses__.each {|k| subclasses << k; subclasses.concat k.descendents } subclasses end - rescue RuntimeError - # JRuby and any implementations which cannot handle the objectspace traversal - # above fall back to this implementation - def subclasses_of(*superclasses) #:nodoc: - subclasses = [] + else + # MRI + begin + ObjectSpace.each_object(Class.new) {} - superclasses.each do |sup| + def descendents + subclasses = [] + ObjectSpace.each_object(class << self; self; end) do |k| + subclasses << k unless k == self + end + subclasses + end + # JRuby + rescue StandardError + def descendents + subclasses = [] ObjectSpace.each_object(Class) do |k| - if superclasses.any? { |superclass| k < superclass } && - (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id")) - subclasses << k - end + subclasses << k if k < self end subclasses.uniq! + subclasses end - subclasses end end +end + +class Object + def remove_subclasses_of(*superclasses) #:nodoc: + Class.remove_class(*subclasses_of(*superclasses)) + end + + # Exclude this class unless it's a subclass of our supers and is defined. + # We check defined? in case we find a removed class that has yet to be + # garbage collected. This also fails for anonymous classes -- please + # submit a patch if you have a workaround. + def subclasses_of(*superclasses) #:nodoc: + subclasses = [] + superclasses.each do |klass| + subclasses.concat klass.descendents.select {|k| k.name.blank? || k.reachable?} + end + subclasses + end def extended_by #:nodoc: ancestors = class << self; ancestors end -- cgit v1.2.3 From ffb351713ea440deb4909fe35e031c5c40fdfdd9 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 13 Dec 2009 10:40:00 -0500 Subject: Modify backtrace test to take implementations with slightly different backtrace output into consideration. --- activesupport/test/deprecation_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index a3ae39d071..cf27357b32 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -62,7 +62,7 @@ class DeprecationTest < ActiveSupport::TestCase end def test_deprecate_class_method - assert_deprecated(/none is deprecated.*test_deprecate_class_method at/) do + assert_deprecated(/none is deprecated.*test_deprecate_class_method/) do assert_equal 1, @dtc.none end -- cgit v1.2.3 From f70079efb10f6af1187dc55dfbecc87136dc4738 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Dec 2009 15:42:48 -0600 Subject: Fix warnings in railties configuration and initializeable --- railties/lib/rails/configuration.rb | 22 +++++++++++++--------- railties/lib/rails/initializable.rb | 4 ++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 3f43a48e2e..0449fe4ce5 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -2,15 +2,19 @@ require 'active_support/ordered_options' module Rails class Configuration - attr_accessor :cache_classes, :load_paths, :load_once_paths, :after_initialize_blocks, - :frameworks, :framework_root_path, :root, :gems, :plugins, - :i18n, :gems, :whiny_nils, :consider_all_requests_local, - :action_controller, :active_record, :action_view, :active_support, - :action_mailer, :active_resource, - :reload_plugins, :log_path, :log_level, :logger, :preload_frameworks, - :database_configuration_file, :cache_store, :time_zone, - :view_path, :metals, :controller_paths, :routes_configuration_file, - :eager_load_paths, :dependency_loading, :paths, :serve_static_assets + attr_accessor :action_controller, :action_mailer, :action_view, + :active_record, :active_resource, :active_support, + :after_initialize_blocks, :cache_classes, + :consider_all_requests_local, :dependency_loading, :gems, + :load_once_paths, :logger, :metals, :plugins, + :preload_frameworks, :reload_plugins, :serve_static_assets, + :time_zone, :whiny_nils + + attr_writer :cache_store, :controller_paths, + :database_configuration_file, :eager_load_paths, + :frameworks, :framework_root_path, :i18n, :load_paths, + :log_level, :log_path, :paths, :routes_configuration_file, + :view_path def initialize @load_once_paths = [] diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index 96234739cf..add10bd207 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -5,7 +5,7 @@ module Rails end class Initializer - attr_reader :name, :before, :after, :global, :block + attr_reader :name, :block def initialize(name, context, options, &block) @name, @context, @options, @block = name, context, options, block @@ -62,7 +62,7 @@ module Rails end def run_initializers(*args) - return if @ran + return if instance_variable_defined?(:@ran) initializers.each do |initializer| initializer.run(*args) end -- cgit v1.2.3 From 2130566acf5ebe73217af40d25a18507dfb0fd99 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Dec 2009 15:47:52 -0600 Subject: Fix warnings in AD::Response --- actionpack/lib/action_dispatch/http/response.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 4f35a00247..378fd5e61d 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -49,6 +49,9 @@ module ActionDispatch # :nodoc: @body, @cookie = [], [] @sending_file = false + @blank = false + @etag = nil + yield self if block_given? end -- cgit v1.2.3 From 1c52bca2664457af5c004545f90c1eb3d47c487c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Dec 2009 15:54:27 -0600 Subject: Fix warning in AC flash --- actionpack/lib/action_controller/metal/flash.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb index b2d44c6c63..9d08ed6081 100644 --- a/actionpack/lib/action_controller/metal/flash.rb +++ b/actionpack/lib/action_controller/metal/flash.rb @@ -149,6 +149,7 @@ module ActionController #:nodoc: protected def process_action(method_name) + @_flash = nil super @_flash.store(session) if @_flash @_flash = nil -- cgit v1.2.3 From 819a353c444569b7db7d0795dc3b40c745c5bc17 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Dec 2009 15:55:05 -0600 Subject: Hush AP test suite --- actionpack/Rakefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 99bdcc95fa..863daa4b44 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -31,7 +31,6 @@ Rake::TestTask.new(:test_action_pack) do |t| # this will not happen automatically and the tests (as a whole) will error t.test_files = Dir.glob('test/{abstract,controller,dispatch,template}/**/*_test.rb').sort - t.verbose = true # t.warning = true end @@ -45,7 +44,6 @@ desc 'ActiveRecord Integration Tests' Rake::TestTask.new(:test_active_record_integration) do |t| t.libs << 'test' t.test_files = Dir.glob("test/activerecord/*_test.rb") - t.verbose = true end # Genereate the RDoc documentation -- cgit v1.2.3 From bcb686054a101c346eb13fee2053f08a585a7b55 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Dec 2009 16:01:23 -0600 Subject: Hush loading AR unless it fails --- actionpack/test/active_record_unit.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/actionpack/test/active_record_unit.rb b/actionpack/test/active_record_unit.rb index 9e0c66055d..9a094cf66b 100644 --- a/actionpack/test/active_record_unit.rb +++ b/actionpack/test/active_record_unit.rb @@ -11,19 +11,15 @@ class ActiveRecordTestConnector end # Try to grab AR -if defined?(ActiveRecord) && defined?(Fixtures) - $stderr.puts 'Active Record is already loaded, running tests' -else - $stderr.print 'Attempting to load Active Record... ' +unless defined?(ActiveRecord) && defined?(Fixtures) begin PATH_TO_AR = "#{File.dirname(__FILE__)}/../../activerecord/lib" raise LoadError, "#{PATH_TO_AR} doesn't exist" unless File.directory?(PATH_TO_AR) $LOAD_PATH.unshift PATH_TO_AR require 'active_record' require 'active_record/fixtures' - $stderr.puts 'success' rescue LoadError => e - $stderr.print "failed. Skipping Active Record assertion tests: #{e}" + $stderr.print "Failed to load Active Record. Skipping Active Record assertion tests: #{e}" ActiveRecordTestConnector.able_to_connect = false end end -- cgit v1.2.3 From 70c3e825fc184c7267d226c7b365af4db17f58b7 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Dec 2009 16:07:46 -0600 Subject: Fix response_body warning in AC --- actionpack/lib/abstract_controller/base.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index 70b5f5b3ef..905d04e20d 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -90,6 +90,8 @@ module AbstractController raise ActionNotFound, "The action '#{action}' could not be found" end + @_response_body = nil + process_action(action_name) end -- cgit v1.2.3 From ec99eca013ce96fa1fa628510038a9eafa46d3c5 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Dec 2009 16:51:13 -0600 Subject: Fix loading plugin and engine route sets --- .../lib/action_dispatch/routing/route_set.rb | 25 +++++++++++--- railties/test/application/routing_test.rb | 38 ++++++++++++++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 8afd42a293..6f35e9b4e3 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -212,11 +212,14 @@ module ActionDispatch self.routes = [] self.named_routes = NamedRouteCollection.new + @clear_before_draw = true + @finalize_set_on_draw = true + clear! end def draw(&block) - clear! + clear! if @clear_before_draw mapper = Mapper.new(self) if block.arity == 1 @@ -225,9 +228,13 @@ module ActionDispatch mapper.instance_exec(&block) end - @set.add_route(NotFound) - install_helpers - @set.freeze + if @finalize_set_on_draw + @set.add_route(NotFound) + install_helpers + @set.freeze + end + + nil end def clear! @@ -283,7 +290,15 @@ module ActionDispatch def load_routes! if configuration_files.any? - configuration_files.each { |config| load(config) } + @finalize_set_on_draw = false + configuration_files.each_with_index do |config, index| + @finalize_set_on_draw = true if index == (configuration_files.length - 1) + load(config) + @clear_before_draw = false if index == 0 + end + @clear_before_draw = true + @finalize_set_on_draw = true + @routes_last_modified = routes_changed_at else draw do |map| diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb index 1bfec3805b..752218b943 100644 --- a/railties/test/application/routing_test.rb +++ b/railties/test/application/routing_test.rb @@ -81,5 +81,43 @@ module ApplicationTests get '/admin/foo' assert_equal 'admin::foo', last_response.body end + + test "merges with plugin routes" do + controller 'foo', <<-RUBY + class FooController < ActionController::Base + def index + render :text => "foo" + end + end + RUBY + + app_file 'config/routes.rb', <<-RUBY + ActionController::Routing::Routes.draw do |map| + match 'foo', :to => 'foo#index' + end + RUBY + + plugin 'bar', 'require File.dirname(__FILE__) + "/app/controllers/bar"' do |plugin| + plugin.write 'app/controllers/bar.rb', <<-RUBY + class BarController < ActionController::Base + def index + render :text => "bar" + end + end + RUBY + + plugin.write 'config/routes.rb', <<-RUBY + ActionController::Routing::Routes.draw do |map| + match 'bar', :to => 'bar#index' + end + RUBY + end + + get '/foo' + assert_equal 'foo', last_response.body + + get '/bar' + assert_equal 'bar', last_response.body + end end end -- cgit v1.2.3 From ce970a8bb9f124d19d28270b1ffc8c4532bbbcc1 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Dec 2009 16:52:22 -0600 Subject: Remove route loading tests since it should be tested by railties --- actionpack/test/controller/routing_test.rb | 72 ------------------------------ 1 file changed, 72 deletions(-) diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index a9a970d67d..c15eaade58 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -1623,78 +1623,6 @@ class RouteSetTest < ActiveSupport::TestCase end end -class RouteLoadingTest < Test::Unit::TestCase - def setup - routes.instance_variable_set '@routes_last_modified', nil - Object.remove_const(:RAILS_ROOT) if defined?(::RAILS_ROOT) - Object.const_set :RAILS_ROOT, '.' - routes.add_configuration_file(File.join(RAILS_ROOT, 'config', 'routes.rb')) - - @stat = stub_everything - end - - def teardown - ActionController::Routing::Routes.configuration_files.clear - Object.send :remove_const, :RAILS_ROOT - end - - def test_load - File.expects(:stat).returns(@stat) - routes.expects(:load).with(regexp_matches(/routes\.rb$/)) - - routes.reload - end - - def test_no_reload_when_not_modified - @stat.expects(:mtime).times(2).returns(1) - File.expects(:stat).times(2).returns(@stat) - routes.expects(:load).with(regexp_matches(/routes\.rb$/)).at_most_once - - 2.times { routes.reload } - end - - def test_reload_when_modified - @stat.expects(:mtime).at_least(2).returns(1, 2) - File.expects(:stat).at_least(2).returns(@stat) - routes.expects(:load).with(regexp_matches(/routes\.rb$/)).times(2) - - 2.times { routes.reload } - end - - def test_bang_forces_reload - @stat.expects(:mtime).at_least(2).returns(1) - File.expects(:stat).at_least(2).returns(@stat) - routes.expects(:load).with(regexp_matches(/routes\.rb$/)).times(2) - - 2.times { routes.reload! } - end - - def test_load_with_configuration - routes.configuration_files.clear - routes.add_configuration_file("foobarbaz") - File.expects(:stat).returns(@stat) - routes.expects(:load).with("foobarbaz") - - routes.reload - end - - def test_load_multiple_configurations - routes.add_configuration_file("engines.rb") - - File.expects(:stat).at_least_once.returns(@stat) - - routes.expects(:load).with('./config/routes.rb') - routes.expects(:load).with('engines.rb') - - routes.reload - end - - private - def routes - ActionController::Routing::Routes - end -end - class RackMountIntegrationTests < ActiveSupport::TestCase Model = Struct.new(:to_param) -- cgit v1.2.3 From 5f8e48cbd297aca4add4b48efa2136ba6ac851b1 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Dec 2009 17:54:41 -0600 Subject: Move route reloading into railties --- .../lib/action_controller/dispatch/dispatcher.rb | 5 - .../lib/action_dispatch/routing/route_set.rb | 109 ++++----------------- .../action_dispatch/testing/assertions/routing.rb | 2 - actionpack/test/controller/dispatcher_test.rb | 13 --- railties/lib/rails/application.rb | 49 ++++++++- railties/lib/rails/console_app.rb | 2 +- railties/lib/rails/plugin.rb | 4 +- 7 files changed, 69 insertions(+), 115 deletions(-) diff --git a/actionpack/lib/action_controller/dispatch/dispatcher.rb b/actionpack/lib/action_controller/dispatch/dispatcher.rb index e04da42637..cf02757cf6 100644 --- a/actionpack/lib/action_controller/dispatch/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatch/dispatcher.rb @@ -13,11 +13,6 @@ module ActionController # Run prepare callbacks before every request in development mode self.prepare_each_request = true - # Development mode callbacks - ActionDispatch::Callbacks.before_dispatch do |app| - ActionController::Routing::Routes.reload - end - ActionDispatch::Callbacks.after_dispatch do # Cleanup the application before processing the current request. ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 6f35e9b4e3..bf2443c1be 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -203,23 +203,18 @@ module ActionDispatch end end - attr_accessor :routes, :named_routes, :configuration_files, :controller_paths + attr_accessor :routes, :named_routes + attr_accessor :disable_clear_and_finalize def initialize - self.configuration_files = [] - self.controller_paths = [] - self.routes = [] self.named_routes = NamedRouteCollection.new - @clear_before_draw = true - @finalize_set_on_draw = true - - clear! + @disable_clear_and_finalize = false end def draw(&block) - clear! if @clear_before_draw + clear! unless @disable_clear_and_finalize mapper = Mapper.new(self) if block.arity == 1 @@ -228,16 +223,20 @@ module ActionDispatch mapper.instance_exec(&block) end - if @finalize_set_on_draw - @set.add_route(NotFound) - install_helpers - @set.freeze - end + finalize! unless @disable_clear_and_finalize nil end + def finalize! + @set.add_route(NotFound) + install_helpers + @set.freeze + end + def clear! + # Clear the controller cache so we may discover new ones + @controller_constraints = nil routes.clear named_routes.clear @set = ::Rack::Mount::RouteSet.new(:parameters_key => PARAMETERS_KEY) @@ -252,75 +251,6 @@ module ActionDispatch routes.empty? end - def add_configuration_file(path) - self.configuration_files << path - end - - # Deprecated accessor - def configuration_file=(path) - add_configuration_file(path) - end - - # Deprecated accessor - def configuration_file - configuration_files - end - - def load! - # Clear the controller cache so we may discover new ones - @controller_constraints = nil - - load_routes! - end - - # reload! will always force a reload whereas load checks the timestamp first - alias reload! load! - - def reload - if configuration_files.any? && @routes_last_modified - if routes_changed_at == @routes_last_modified - return # routes didn't change, don't reload - else - @routes_last_modified = routes_changed_at - end - end - - load! - end - - def load_routes! - if configuration_files.any? - @finalize_set_on_draw = false - configuration_files.each_with_index do |config, index| - @finalize_set_on_draw = true if index == (configuration_files.length - 1) - load(config) - @clear_before_draw = false if index == 0 - end - @clear_before_draw = true - @finalize_set_on_draw = true - - @routes_last_modified = routes_changed_at - else - draw do |map| - map.connect ":controller/:action/:id" - end - end - end - - def routes_changed_at - routes_changed_at = nil - - configuration_files.each do |config| - config_changed_at = File.stat(config).mtime - - if routes_changed_at.nil? || config_changed_at > routes_changed_at - routes_changed_at = config_changed_at - end - end - - routes_changed_at - end - CONTROLLER_REGEXP = /[_a-zA-Z0-9]+/ def controller_constraints @@ -340,11 +270,14 @@ module ActionDispatch namespaces << controller_name.split('/')[0...-1].join('/') end - # Find namespaces in controllers/ directory - controller_paths.each do |load_path| - load_path = File.expand_path(load_path) - Dir["#{load_path}/**/*_controller.rb"].collect do |path| - namespaces << File.dirname(path).sub(/#{load_path}\/?/, '') + # TODO: Move this into Railties + if defined?(Rails.application) + # Find namespaces in controllers/ directory + Rails.application.configuration.controller_paths.each do |load_path| + load_path = File.expand_path(load_path) + Dir["#{load_path}/**/*_controller.rb"].collect do |path| + namespaces << File.dirname(path).sub(/#{load_path}\/?/, '') + end end end diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb index 794fb888b7..fc477afb17 100644 --- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb +++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb @@ -46,7 +46,6 @@ module ActionDispatch request_method = nil end - ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? request = recognized_request_for(path, request_method) expected_options = expected_options.clone @@ -80,7 +79,6 @@ module ActionDispatch def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil) expected_path = "/#{expected_path}" unless expected_path[0] == ?/ # Load routes.rb if it hasn't been loaded. - ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults) found_extras = options.reject {|k, v| ! extra_keys.include? k} diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index 622d67287d..64f1ad7610 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -15,7 +15,6 @@ class DispatcherTest < Test::Unit::TestCase ActionDispatch::Callbacks.reset_callbacks(:call) ActionController::Routing::Routes.stubs(:call).returns([200, {}, 'response']) - ActionController::Routing::Routes.stubs(:reload) Dispatcher.stubs(:require_dependency) end @@ -28,18 +27,6 @@ class DispatcherTest < Test::Unit::TestCase dispatch(false) end - def test_reloads_routes_before_dispatch_if_in_loading_mode - ActionController::Routing::Routes.expects(:reload).once - dispatch(false) - end - - def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode - ActionController::Routing::Routes.expects(:reload).never - ActiveSupport::Dependencies.expects(:clear).never - - dispatch - end - def test_prepare_callbacks a = b = c = nil ActionDispatch::Callbacks.to_prepare { |*args| a = b = c = 1 } diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 110311558c..047d252625 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -42,8 +42,13 @@ module Rails end end + attr_reader :route_configuration_files + def initialize Rails.application ||= self + + @route_configuration_files = [] + run_initializers(self) end @@ -65,6 +70,32 @@ module Rails ActionController::Routing::Routes end + def routes_changed_at + routes_changed_at = nil + + route_configuration_files.each do |config| + config_changed_at = File.stat(config).mtime + + if routes_changed_at.nil? || config_changed_at > routes_changed_at + routes_changed_at = config_changed_at + end + end + + routes_changed_at + end + + def reload_routes! + routes.disable_clear_and_finalize = true + + routes.clear! + route_configuration_files.each { |config| load(config) } + routes.finalize! + + nil + ensure + routes.disable_clear_and_finalize = false + end + def initializers initializers = super plugins.each { |p| initializers += p.initializers } @@ -359,6 +390,18 @@ module Rails next unless configuration.frameworks.include?(:action_controller) require 'rails/dispatcher' unless defined?(::Dispatcher) Dispatcher.define_dispatcher_callbacks(configuration.cache_classes) + + unless configuration.cache_classes + # Setup dev mode route reloading + routes_last_modified = routes_changed_at + reload_routes = lambda do + unless routes_changed_at == routes_last_modified + routes_last_modified = routes_changed_at + reload_routes! + end + end + ActionDispatch::Callbacks.before_dispatch { |callbacks| reload_routes } + end end # Routing must be initialized after plugins to allow the former to extend the routes @@ -368,10 +411,8 @@ module Rails # loading module used to lazily load controllers (Configuration#controller_paths). initializer :initialize_routing do next unless configuration.frameworks.include?(:action_controller) - - ActionController::Routing::Routes.controller_paths += configuration.controller_paths - ActionController::Routing::Routes.add_configuration_file(configuration.routes_configuration_file) - ActionController::Routing::Routes.reload! + route_configuration_files << configuration.routes_configuration_file + reload_routes! end # # # Observers are loaded after plugins in case Observers or observed models are modified by plugins. diff --git a/railties/lib/rails/console_app.rb b/railties/lib/rails/console_app.rb index 1ad62e5058..2c4a7a51e8 100644 --- a/railties/lib/rails/console_app.rb +++ b/railties/lib/rails/console_app.rb @@ -27,6 +27,6 @@ end def reload! puts "Reloading..." ActionDispatch::Callbacks.new(lambda {}, true) - ActionController::Routing::Routes.reload + Rails.application.reload_routes! true end diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb index 86bf032641..8025135a64 100644 --- a/railties/lib/rails/plugin.rb +++ b/railties/lib/rails/plugin.rb @@ -55,8 +55,8 @@ module Rails initializer :add_routing_file, :after => :initialize_routing do |app| routing_file = "#{path}/config/routes.rb" if File.exist?(routing_file) - app.routes.add_configuration_file(routing_file) - app.routes.reload! + app.route_configuration_files << routing_file + app.reload_routes! end end end -- cgit v1.2.3 From 7b61541ea53c309847753d5553baa2eed9cc6282 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 14 Dec 2009 18:00:14 -0800 Subject: Add Enumerable#exclude? to bring parity to Enumerable#include? and avoid if !x.include?/else calls [DHH] --- activesupport/CHANGELOG | 2 ++ activesupport/lib/active_support/core_ext/enumerable.rb | 5 +++++ activesupport/test/core_ext/enumerable_test.rb | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 4edeadf10c..cc4a2ff90e 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Add Enumerable#exclude? to bring parity to Enumerable#include? and avoid if !x.include?/else calls [DHH] + * Update Edinburgh TimeZone to use "Europe/London" instead of "Europe/Dublin" #3310 [Phil Ross] * Update bundled TZInfo to v0.3.15 [Geoff Buesing] diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index b11c916f61..d0821a7c68 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -101,6 +101,11 @@ module Enumerable size = block_given? ? select(&block).size : self.size size > 1 end + + # The negative of the Enumerable#include?. Returns true if the collection does not include the object. + def exclude?(object) + !include?(object) + end end class Range #:nodoc: diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb index 66f5f9fbde..4650b796b6 100644 --- a/activesupport/test/core_ext/enumerable_test.rb +++ b/activesupport/test/core_ext/enumerable_test.rb @@ -89,4 +89,9 @@ class EnumerableTests < Test::Unit::TestCase assert ![ 1, 2 ].many? {|x| x > 1 } assert [ 1, 2, 2 ].many? {|x| x > 1 } end + + def test_exclude? + assert [ 1 ].exclude?(2) + assert ![ 1 ].exclude?(1) + end end -- cgit v1.2.3 From 2ae8300489aaaaaa65f50f3557090b265a2dcc6a Mon Sep 17 00:00:00 2001 From: Serguei Filimonov Date: Fri, 11 Dec 2009 13:08:00 -0800 Subject: Added #to_i to DateTime in ActiveSupport so #to_yaml works correctly on ActiveRecord models with DateTime attributes. --- activerecord/test/cases/yaml_serialization_test.rb | 11 +++++++++++ .../lib/active_support/core_ext/date_time/conversions.rb | 15 +++++++++++++-- activesupport/test/core_ext/date_time_ext_test.rb | 4 ++++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 activerecord/test/cases/yaml_serialization_test.rb diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb new file mode 100644 index 0000000000..f221def6b6 --- /dev/null +++ b/activerecord/test/cases/yaml_serialization_test.rb @@ -0,0 +1,11 @@ +require "cases/helper" +require 'models/topic' + +class YamlSerializationTest < ActiveRecord::TestCase + def test_to_yaml_with_time_with_zone_should_not_raise_exception + Time.zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"] + ActiveRecord::Base.time_zone_aware_attributes = true + topic = Topic.new(:written_on => DateTime.now) + assert_nothing_raised { topic.to_yaml } + end +end diff --git a/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/activesupport/lib/active_support/core_ext/date_time/conversions.rb index 5f01bc4fd6..47a31839a6 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -78,7 +78,18 @@ class DateTime # Converts self to a floating-point number of seconds since the Unix epoch def to_f - days_since_unix_epoch = self - ::DateTime.civil(1970) - (days_since_unix_epoch * 86_400).to_f + seconds_since_unix_epoch.to_f + end + + # Converts self to an integer number of seconds since the Unix epoch + def to_i + seconds_since_unix_epoch.to_i + end + + private + + def seconds_since_unix_epoch + seconds_per_day = 86_400 + (self - ::DateTime.civil(1970)) * seconds_per_day end end diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 4341ead488..278c05797b 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -350,6 +350,10 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_equal 946684800.0, DateTime.civil(1999,12,31,19,0,0,Rational(-5,24)).to_f end + def test_to_i + assert_equal 946684800, DateTime.civil(2000).to_i + end + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz -- cgit v1.2.3 From a4b19277b2a4085da3bb0f1e05584496103eeb2a Mon Sep 17 00:00:00 2001 From: Geoff Buesing Date: Tue, 15 Dec 2009 08:28:24 -0600 Subject: Add test for TimeWithZone#to_i with wrapped DateTime --- activesupport/test/core_ext/time_with_zone_test.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index bb60968a4f..3a12100e86 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -284,6 +284,12 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal 946684800, result assert result.is_a?(Integer) end + + def test_to_i_with_wrapped_datetime + datetime = DateTime.civil(2000, 1, 1, 0) + twz = ActiveSupport::TimeWithZone.new(datetime, @time_zone) + assert_equal 946684800, twz.to_i + end def test_to_time assert_equal @twz, @twz.to_time -- cgit v1.2.3 From f0bbc647c2086e9536c9d2b4ea7c4c18fe2edd3e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 15 Dec 2009 10:48:56 -0600 Subject: Procs don't call themselves Fixes dev mode reloading [#3574 state:resolved] --- railties/lib/rails/application.rb | 2 +- railties/test/application/routing_test.rb | 32 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 047d252625..bc74ac8646 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -400,7 +400,7 @@ module Rails reload_routes! end end - ActionDispatch::Callbacks.before_dispatch { |callbacks| reload_routes } + ActionDispatch::Callbacks.before_dispatch { |callbacks| reload_routes.call } end end diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb index 752218b943..decde056fd 100644 --- a/railties/test/application/routing_test.rb +++ b/railties/test/application/routing_test.rb @@ -119,5 +119,37 @@ module ApplicationTests get '/bar' assert_equal 'bar', last_response.body end + + test "reloads routes when configuration is changed" do + controller :foo, <<-RUBY + class FooController < ActionController::Base + def bar + render :text => "bar" + end + + def baz + render :text => "baz" + end + end + RUBY + + app_file 'config/routes.rb', <<-RUBY + ActionController::Routing::Routes.draw do |map| + match 'foo', :to => 'foo#bar' + end + RUBY + + get '/foo' + assert_equal 'bar', last_response.body + + app_file 'config/routes.rb', <<-RUBY + ActionController::Routing::Routes.draw do |map| + match 'foo', :to => 'foo#baz' + end + RUBY + + get '/foo' + assert_equal 'baz', last_response.body + end end end -- cgit v1.2.3 From 1b27f5c4f72384081083fa025b6b10e5ab02ae5e Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Tue, 15 Dec 2009 22:07:12 -0800 Subject: Add the ability for plugins to set default configuration settings. --- railties/lib/rails.rb | 5 +- railties/lib/rails/application.rb | 8 ++- railties/lib/rails/configuration.rb | 68 +++++++++++++++++++------ railties/lib/rails/plugin.rb | 21 ++++++++ railties/test/application/configuration_test.rb | 21 ++++++++ railties/test/application/initializer_test.rb | 1 + railties/test/plugins/configuration_test.rb | 36 +++++++++++++ 7 files changed, 142 insertions(+), 18 deletions(-) create mode 100644 railties/test/plugins/configuration_test.rb diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index b7cae9a9ac..85aeb4af24 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -3,9 +3,11 @@ require "pathname" require 'active_support' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/logger' +require 'action_dispatch' require 'rails/initializable' require 'rails/application' +require 'rails/plugin' require 'rails/railties_path' require 'rails/version' require 'rails/rack' @@ -14,7 +16,6 @@ require 'rails/core' require 'rails/configuration' require 'rails/deprecation' require 'rails/initializer' -require 'rails/plugin' require 'rails/ruby_version_check' # For Ruby 1.8, this initialization sets $KCODE to 'u' to enable the @@ -29,4 +30,4 @@ else Encoding.default_external = Encoding::UTF_8 end -RAILS_ENV = (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development").dup unless defined?(RAILS_ENV) \ No newline at end of file +RAILS_ENV = (ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "development").dup unless defined?(RAILS_ENV) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index bc74ac8646..9e57354047 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -13,7 +13,11 @@ module Rails end def config - @config ||= Configuration.new + @config ||= begin + config = Configuration.new + Plugin.plugins.each { |p| config.merge(p.config) } + config + end end # TODO: change the plugin loader to use config @@ -104,6 +108,8 @@ module Rails def plugins @plugins ||= begin + plugin_names = config.plugins || [:all] + Plugin.plugins.select { |p| plugin_names.include?(p.plugin_name) } + Plugin::Vendored.all(config.plugins || [:all], config.paths.vendor.plugins) end end diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 0449fe4ce5..0fa42091dd 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -1,10 +1,54 @@ require 'active_support/ordered_options' module Rails - class Configuration - attr_accessor :action_controller, :action_mailer, :action_view, - :active_record, :active_resource, :active_support, - :after_initialize_blocks, :cache_classes, + # Temporarily separate the plugin configuration class from the main + # configuration class while this bit is being cleaned up. + class Plugin::Configuration + + def initialize + @options = Hash.new { |h,k| h[k] = ActiveSupport::OrderedOptions.new } + end + + def middleware + @middleware ||= ActionDispatch::MiddlewareStack.new + end + + def respond_to?(name) + super || name.to_s =~ config_key_regexp + end + + def merge(config) + @options = config.options.merge(@options) + end + + protected + + attr_reader :options + + private + + def method_missing(name, *args, &blk) + if name.to_s =~ config_key_regexp + return $2 == '=' ? @options[$1] = args.first : @options[$1] + end + + super + end + + def config_key_regexp + bits = config_keys.map { |n| Regexp.escape(n.to_s) }.join('|') + /^(#{bits})(?:=)?$/ + end + + def config_keys + ([ :active_support, :active_record, :action_controller, + :action_view, :action_mailer, :active_resource ] + + Plugin.plugin_names).map { |n| n.to_s }.uniq + end + end + + class Configuration < Plugin::Configuration + attr_accessor :after_initialize_blocks, :cache_classes, :consider_all_requests_local, :dependency_loading, :gems, :load_once_paths, :logger, :metals, :plugins, :preload_frameworks, :reload_plugins, :serve_static_assets, @@ -17,15 +61,11 @@ module Rails :view_path def initialize + super @load_once_paths = [] @after_initialize_blocks = [] @dependency_loading = true @serve_static_assets = true - - for framework in frameworks - self.send("#{framework}=", ActiveSupport::OrderedOptions.new) - end - self.active_support = ActiveSupport::OrderedOptions.new end def after_initialize(&blk) @@ -84,7 +124,10 @@ module Rails self.preload_frameworks = true self.cache_classes = true self.dependency_loading = false - self.action_controller.allow_concurrency = true + + if respond_to?(:action_controller) + action_controller.allow_concurrency = true + end self end @@ -103,11 +146,6 @@ module Rails defined?(::RAILS_FRAMEWORK_ROOT) ? ::RAILS_FRAMEWORK_ROOT : "#{root}/vendor/rails" end - def middleware - require 'action_dispatch' - @middleware ||= ActionDispatch::MiddlewareStack.new - end - # Loads and returns the contents of the #database_configuration_file. The # contents of the file are processed via ERB before being sent through # YAML::load. diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb index 8025135a64..90dc1ad8dd 100644 --- a/railties/lib/rails/plugin.rb +++ b/railties/lib/rails/plugin.rb @@ -2,6 +2,27 @@ module Rails class Plugin include Initializable + def self.plugin_name + @plugin_name || name.demodulize.underscore + end + + def self.inherited(klass) + @plugins ||= [] + @plugins << klass unless klass == Vendored + end + + def self.plugins + @plugins + end + + def self.plugin_names + plugins.map { |p| p.plugin_name } + end + + def self.config + @config ||= Configuration.new + end + class Vendored < Plugin def self.all(list, paths) plugins = [] diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index 1fcab8c651..89337b7f66 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -53,5 +53,26 @@ module ApplicationTests assert_raises(NoMethodError) { 1.day } end end + + test "marking the application as threadsafe sets the correct config variables" do + add_to_config <<-RUBY + config.threadsafe! + RUBY + + require "#{app_path}/config/application" + assert AppTemplate.configuration.action_controller.allow_concurrency + end + + test "the application can be marked as threadsafe when there are no frameworks" do + FileUtils.rm_rf("#{app_path}/config/environments") + add_to_config <<-RUBY + config.frameworks = [] + config.threadsafe! + RUBY + + assert_nothing_raised do + require "#{app_path}/config/application" + end + end end end diff --git a/railties/test/application/initializer_test.rb b/railties/test/application/initializer_test.rb index 56582c5772..fa00d287ca 100644 --- a/railties/test/application/initializer_test.rb +++ b/railties/test/application/initializer_test.rb @@ -6,6 +6,7 @@ module ApplicationTests def setup build_app + FileUtils.rm_rf("#{app_path}/config/environments") boot_rails require "rails" end diff --git a/railties/test/plugins/configuration_test.rb b/railties/test/plugins/configuration_test.rb new file mode 100644 index 0000000000..edf8bb37f5 --- /dev/null +++ b/railties/test/plugins/configuration_test.rb @@ -0,0 +1,36 @@ +require "isolation/abstract_unit" + +module PluginsTest + class ConfigurationTest < Test::Unit::TestCase + def setup + build_app + boot_rails + require "rails" + end + + test "config is available to plugins" do + class Foo < Rails::Plugin ; end + assert_nil Foo.config.action_controller.foo + end + + test "a config name is available for the plugin" do + class Foo < Rails::Plugin ; config.foo.greetings = "hello" ; end + assert_equal "hello", Foo.config.foo.greetings + end + + test "plugin configurations are available in the application" do + class Foo < Rails::Plugin ; config.foo.greetings = "hello" ; end + require "#{app_path}/config/application" + assert_equal "hello", AppTemplate.config.foo.greetings + end + + test "plugin config merges are deep" do + class Foo < Rails::Plugin ; config.foo.greetings = 'hello' ; end + class MyApp < Rails::Application + config.foo.bar = "bar" + end + assert_equal "hello", MyApp.config.foo.greetings + assert_equal "bar", MyApp.config.foo.bar + end + end +end \ No newline at end of file -- cgit v1.2.3 From bf6af5f71917b5615edb5905729b22772133eea4 Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 16 Dec 2009 10:49:06 -0600 Subject: When passing force_reload = true to an association, don't use the query cache [#1827 state:resolved] Signed-off-by: Joshua Peek --- activerecord/lib/active_record/associations.rb | 4 ++-- activerecord/test/cases/associations_test.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 0fcd288fc5..8dcb3a7711 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1325,7 +1325,7 @@ module ActiveRecord if association.nil? || force_reload association = association_proxy_class.new(self, reflection) - retval = association.reload + retval = force_reload ? reflection.klass.uncached { association.reload } : association.reload if retval.nil? and association_proxy_class == BelongsToAssociation association_instance_set(reflection.name, nil) return nil @@ -1370,7 +1370,7 @@ module ActiveRecord association_instance_set(reflection.name, association) end - association.reload if force_reload + reflection.klass.uncached { association.reload } if force_reload association end diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index e429c1d157..9bc34bd750 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -64,6 +64,16 @@ class AssociationsTest < ActiveRecord::TestCase assert !firm.clients(true).empty?, "New firm should have reloaded client objects" assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count" end + + def test_force_reload_is_uncached + firm = Firm.create!("name" => "A New Firm, Inc") + client = Client.create!("name" => "TheClient.com", :firm => firm) + ActiveRecord::Base.cache do + firm.clients.each {} + assert_queries(0) { assert_not_nil firm.clients.each {} } + assert_queries(1) { assert_not_nil firm.clients(true).each {} } + end + end end -- cgit v1.2.3 From ee51ddcc2730a19e996069fedbe8c934161c5251 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 16 Dec 2009 10:52:52 -0600 Subject: Hush AS test suite --- activesupport/Rakefile | 1 - 1 file changed, 1 deletion(-) diff --git a/activesupport/Rakefile b/activesupport/Rakefile index 2ada91830f..08af1d6fca 100644 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile @@ -18,7 +18,6 @@ task :default => :test Rake::TestTask.new do |t| t.libs << 'test' t.pattern = 'test/**/*_test.rb' - t.verbose = true t.warning = true end -- cgit v1.2.3 From 1ca567e634a20e7f691e637d24b7b6c381342a2d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 16 Dec 2009 10:56:10 -0600 Subject: Hush AM test suite --- actionmailer/Rakefile | 1 - 1 file changed, 1 deletion(-) diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index 1a7ece5068..6c19371514 100644 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -23,7 +23,6 @@ task :default => [ :test ] Rake::TestTask.new { |t| t.libs << "test" t.pattern = 'test/*_test.rb' - t.verbose = true t.warning = true } -- cgit v1.2.3 From e1ee13e5fb1b6e901eca68070c9fdb0108a5716c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 16 Dec 2009 11:03:13 -0600 Subject: Hush ARes test suite --- activeresource/Rakefile | 1 - 1 file changed, 1 deletion(-) diff --git a/activeresource/Rakefile b/activeresource/Rakefile index 6566e84d4c..1f9d0392c1 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -31,7 +31,6 @@ Rake::TestTask.new { |t| t.libs << activesupport_path if File.directory?(activesupport_path) t.libs << "test" t.pattern = 'test/**/*_test.rb' - t.verbose = true t.warning = true } -- cgit v1.2.3 From c4df01385273cf47180cb28c0cc6120cc1a097ca Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 16 Dec 2009 11:03:40 -0600 Subject: ARes rake task should not worry about AS being in path --- activeresource/Rakefile | 2 -- 1 file changed, 2 deletions(-) diff --git a/activeresource/Rakefile b/activeresource/Rakefile index 1f9d0392c1..9fa1f86914 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -27,8 +27,6 @@ task :default => [ :test ] # Run the unit tests Rake::TestTask.new { |t| - activesupport_path = "#{File.dirname(__FILE__)}/../activesupport/lib" - t.libs << activesupport_path if File.directory?(activesupport_path) t.libs << "test" t.pattern = 'test/**/*_test.rb' t.warning = true -- cgit v1.2.3 From 1ab35020c152575c84d4ca95842d55be307e1fee Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 16 Dec 2009 11:05:48 -0600 Subject: Hush AMo test suite --- activemodel/Rakefile | 1 - 1 file changed, 1 deletion(-) diff --git a/activemodel/Rakefile b/activemodel/Rakefile index 1f4a8466c9..f098ce0671 100755 --- a/activemodel/Rakefile +++ b/activemodel/Rakefile @@ -15,7 +15,6 @@ task :default => :test Rake::TestTask.new do |t| t.libs << "#{dir}/test" t.test_files = Dir.glob("#{dir}/test/cases/**/*_test.rb").sort - t.verbose = true t.warning = true end -- cgit v1.2.3 From 7ee5843c3cedfe36a680d5b28aa31eef45296c50 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 16 Dec 2009 11:56:51 -0600 Subject: Fully expand relative rails framework paths and make sure we aren't adding any to the load path more than once. --- actionmailer/lib/action_mailer.rb | 5 +++-- actionmailer/test/abstract_unit.rb | 7 ++----- actionpack/lib/abstract_controller.rb | 8 ++++++-- actionpack/lib/action_controller.rb | 4 +++- actionpack/lib/action_dispatch.rb | 8 ++++---- actionpack/lib/action_view.rb | 13 ++++++------- actionpack/test/abstract_unit.rb | 15 +++++++-------- activemodel/lib/active_model.rb | 5 +++-- activemodel/test/cases/helper.rb | 6 ++---- activerecord/lib/active_record.rb | 15 ++++++++------- activerecord/test/cases/helper.rb | 6 ++---- activeresource/lib/active_resource.rb | 11 ++++++----- activeresource/test/abstract_unit.rb | 8 +++----- railties/bin/rails | 7 ++++--- railties/lib/rails/generators.rb | 5 +++-- 15 files changed, 62 insertions(+), 61 deletions(-) diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb index eec4040bcc..f439eb175c 100644 --- a/actionmailer/lib/action_mailer.rb +++ b/actionmailer/lib/action_mailer.rb @@ -21,8 +21,9 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -actionpack_path = "#{File.dirname(__FILE__)}/../../actionpack/lib" -$:.unshift(actionpack_path) if File.directory?(actionpack_path) +actionpack_path = File.expand_path('../../../actionpack/lib', __FILE__) +$:.unshift(actionpack_path) if File.directory?(actionpack_path) && !$:.include?(actionpack_path) + require 'action_controller' require 'action_view' diff --git a/actionmailer/test/abstract_unit.rb b/actionmailer/test/abstract_unit.rb index e84b3b0d23..af6f1bc92e 100644 --- a/actionmailer/test/abstract_unit.rb +++ b/actionmailer/test/abstract_unit.rb @@ -1,12 +1,9 @@ -root = File.expand_path('../../..', __FILE__) begin - require "#{root}/vendor/gems/environment" + require File.expand_path('../../../vendor/gems/environment', __FILE__) rescue LoadError - $:.unshift("#{root}/activesupport/lib") - $:.unshift("#{root}/actionpack/lib") end -lib = File.expand_path("#{File.dirname(__FILE__)}/../lib") +lib = File.expand_path('../../lib', __FILE__) $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) require 'rubygems' diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb index 92d2971ec3..109a3a3385 100644 --- a/actionpack/lib/abstract_controller.rb +++ b/actionpack/lib/abstract_controller.rb @@ -1,5 +1,9 @@ -require "active_support/core_ext/module/attr_internal" -require "active_support/core_ext/module/delegation" +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) + +require 'active_support' +require 'active_support/core_ext/module/attr_internal' +require 'active_support/core_ext/module/delegation' module AbstractController extend ActiveSupport::Autoload diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 2113d791f5..37ff10e852 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -1,4 +1,6 @@ -require "active_support" +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) +require 'active_support' module ActionController extend ActiveSupport::Autoload diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index d1c191d652..feed6a8e25 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -21,6 +21,10 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) +require 'active_support' + require 'rack' module Rack @@ -74,7 +78,3 @@ module ActionDispatch end autoload :Mime, 'action_dispatch/http/mime_type' - -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift activesupport_path if File.directory?(activesupport_path) -require 'active_support' diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 06238ca747..c3e42ac0d5 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -21,7 +21,12 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -require File.join(File.dirname(__FILE__), "action_pack") +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) +require 'active_support' +require 'active_support/core_ext/class/attribute_accessors' + +require 'action_pack' module ActionView extend ActiveSupport::Autoload @@ -51,10 +56,4 @@ end require 'action_view/erb/util' - I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml" - -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift activesupport_path if File.directory?(activesupport_path) -require 'active_support' -require 'active_support/core_ext/class/attribute_accessors' diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 4dae1ab873..a9341b60df 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -1,12 +1,9 @@ -root = File.expand_path('../../..', __FILE__) begin - require "#{root}/vendor/gems/environment" + require File.expand_path('../../../vendor/gems/environment', __FILE__) rescue LoadError - $:.unshift "#{root}/activesupport/lib" - $:.unshift "#{root}/activemodel/lib" end -lib = File.expand_path("#{File.dirname(__FILE__)}/../lib") +lib = File.expand_path('../../lib', __FILE__) $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) $:.unshift(File.dirname(__FILE__) + '/lib') @@ -16,18 +13,20 @@ $:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers') ENV['TMPDIR'] = File.join(File.dirname(__FILE__), 'tmp') require 'test/unit' -require 'active_support' -require 'active_support/test_case' require 'abstract_controller' require 'action_controller' require 'action_view' require 'action_view/base' require 'action_dispatch' -require 'active_model' require 'fixture_template' +require 'active_support/test_case' require 'action_view/test_case' require 'active_support/dependencies' +activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__) +$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path) +require 'active_model' + begin require 'ruby-debug' Debugger.settings[:autoeval] = true diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 8e14b6724f..e0de27b96d 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -21,10 +21,11 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift(activesupport_path) if File.directory?(activesupport_path) +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) require 'active_support' + module ActiveModel extend ActiveSupport::Autoload diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb index 024f2378be..30193956ea 100644 --- a/activemodel/test/cases/helper.rb +++ b/activemodel/test/cases/helper.rb @@ -1,11 +1,9 @@ -root = File.expand_path('../../../..', __FILE__) begin - require "#{root}/vendor/gems/environment" + require File.expand_path('../../../../vendor/gems/environment', __FILE__) rescue LoadError - $:.unshift("#{root}/activesupport/lib") end -lib = File.expand_path("#{File.dirname(__FILE__)}/../../lib") +lib = File.expand_path('../../../lib', __FILE__) $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) require 'config' diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index f935d15526..2376bbd04a 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -21,11 +21,12 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift(activesupport_path) if File.directory?(activesupport_path) -activemodel_path = "#{File.dirname(__FILE__)}/../../activemodel/lib" -$:.unshift(activemodel_path) if File.directory?(activemodel_path) +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) + +activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__) +$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path) require 'active_support' require 'active_model' @@ -91,7 +92,7 @@ module ActiveRecord module Type extend ActiveSupport::Autoload - + autoload :Number, 'active_record/types/number' autoload :Object, 'active_record/types/object' autoload :Serialize, 'active_record/types/serialize' @@ -101,14 +102,14 @@ module ActiveRecord module Locking extend ActiveSupport::Autoload - + autoload :Optimistic autoload :Pessimistic end module ConnectionAdapters extend ActiveSupport::Autoload - + autoload :AbstractAdapter end end diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index 25613da912..307320b964 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -1,11 +1,9 @@ -root = File.expand_path('../../../..', __FILE__) begin - require "#{root}/vendor/gems/environment" + require File.expand_path('../../../../vendor/gems/environment', __FILE__) rescue LoadError - $:.unshift("#{root}/activesupport/lib") end -lib = File.expand_path("#{File.dirname(__FILE__)}/../../lib") +lib = File.expand_path('../../../lib', __FILE__) $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) require 'config' diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb index 9357dd852f..84baf4227a 100644 --- a/activeresource/lib/active_resource.rb +++ b/activeresource/lib/active_resource.rb @@ -21,12 +21,13 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ -activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib" -$:.unshift(activesupport_path) if File.directory?(activesupport_path) -require 'active_support' +activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) + +activemodel_path = File.expand_path('../../../activemodel/lib', __FILE__) +$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path) -activemodel_path = "#{File.dirname(__FILE__)}/../../activemodel/lib" -$:.unshift(activemodel_path) if File.directory?(activemodel_path) +require 'active_support' require 'active_model' module ActiveResource diff --git a/activeresource/test/abstract_unit.rb b/activeresource/test/abstract_unit.rb index 3d684ff347..5fa6d3023b 100644 --- a/activeresource/test/abstract_unit.rb +++ b/activeresource/test/abstract_unit.rb @@ -1,18 +1,16 @@ -root = File.expand_path('../../..', __FILE__) begin - require "#{root}/vendor/gems/environment" + require File.expand_path('../../../vendor/gems/environment', __FILE__) rescue LoadError - $:.unshift("#{root}/activesupport/lib") end -lib = File.expand_path("#{File.dirname(__FILE__)}/../lib") +lib = File.expand_path('../../lib', __FILE__) $:.unshift(lib) unless $:.include?('lib') || $:.include?(lib) require 'rubygems' require 'test/unit' +require 'active_resource' require 'active_support' require 'active_support/test_case' -require 'active_resource' require 'active_model/test_case' $:.unshift "#{File.dirname(__FILE__)}/../test" diff --git a/railties/bin/rails b/railties/bin/rails index 808df97429..0f51d5739f 100755 --- a/railties/bin/rails +++ b/railties/bin/rails @@ -4,9 +4,10 @@ rescue LoadError # If people are not using gems, the load path must still # be correct. # TODO: Remove the begin / rescue block somehow - $:.unshift File.dirname(__FILE__) + '/../lib' - $:.unshift File.dirname(__FILE__) + '/../../activesupport/lib' - retry + $:.unshift File.expand_path('../../lib', __FILE__) + $:.unshift File.expand_path('../../../activesupport/lib', __FILE__) + $:.unshift File.expand_path('../../../actionpack/lib', __FILE__) + require 'rails/ruby_version_check' end Signal.trap("INT") { puts; exit } diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index ffb9cfe1cd..0e66c9f58f 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -1,5 +1,6 @@ -activesupport_path = "#{File.dirname(__FILE__)}/../../../activesupport/lib" -$LOAD_PATH.unshift(activesupport_path) if File.directory?(activesupport_path) +activesupport_path = File.expand_path('../../../../activesupport/lib', __FILE__) +$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) + require 'active_support' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/metaclass' -- cgit v1.2.3 From 7217d64f615ec064f15c9b2999e98e54997fe67c Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 16 Dec 2009 16:11:42 -0600 Subject: Use AbstractController error constants --- actionpack/lib/action_dispatch/middleware/show_exceptions.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 471d18491c..bd87764f5b 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -10,8 +10,7 @@ module ActionDispatch @@rescue_responses = Hash.new(:internal_server_error) @@rescue_responses.update({ 'ActionController::RoutingError' => :not_found, - # TODO: Clean this up after the switch - ActionController::UnknownAction.name => :not_found, + 'AbstractController::ActionNotFound' => :not_found, 'ActiveRecord::RecordNotFound' => :not_found, 'ActiveRecord::StaleObjectError' => :conflict, 'ActiveRecord::RecordInvalid' => :unprocessable_entity, @@ -26,7 +25,7 @@ module ActionDispatch @@rescue_templates.update({ 'ActionView::MissingTemplate' => 'missing_template', 'ActionController::RoutingError' => 'routing_error', - ActionController::UnknownAction.name => 'unknown_action', + 'AbstractController::ActionNotFound' => 'unknown_action', 'ActionView::Template::Error' => 'template_error' }) -- cgit v1.2.3 From b9d4ceb43c9497fb1c47d8b1e1e6a24a9e157384 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 16 Dec 2009 16:12:04 -0600 Subject: Need to lazy realize middleware options so they can be modified in the environment --- railties/lib/rails/application.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 9e57354047..e65c20de2c 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -210,9 +210,9 @@ module Rails initializer :initialize_middleware_stack do if config.frameworks.include?(:action_controller) - config.middleware.use(::Rack::Lock) unless ActionController::Base.allow_concurrency - config.middleware.use(ActionDispatch::ShowExceptions, ActionController::Base.consider_all_requests_local) - config.middleware.use(ActionDispatch::Callbacks, ActionController::Dispatcher.prepare_each_request) + config.middleware.use(::Rack::Lock, :if => lambda { ActionController::Base.allow_concurrency }) + config.middleware.use(ActionDispatch::ShowExceptions, lambda { ActionController::Base.consider_all_requests_local }) + config.middleware.use(ActionDispatch::Callbacks, lambda { ActionController::Dispatcher.prepare_each_request }) config.middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options }) config.middleware.use(ActionDispatch::ParamsParser) config.middleware.use(::Rack::MethodOverride) -- cgit v1.2.3