From c1a8690d582c08777055caf449c03f85b4c8aa4b Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 18 Aug 2008 22:38:58 -0500 Subject: Consistently use the framework's configured logger and avoid reverting to RAILS_DEFAULT_LOGGER unless necessary. --- railties/lib/fcgi_handler.rb | 4 +--- railties/lib/initializer.rb | 15 ++++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'railties') diff --git a/railties/lib/fcgi_handler.rb b/railties/lib/fcgi_handler.rb index 722aa1940c..1bb55b9275 100644 --- a/railties/lib/fcgi_handler.rb +++ b/railties/lib/fcgi_handler.rb @@ -18,7 +18,6 @@ class RailsFCGIHandler attr_accessor :log_file_path attr_accessor :gc_request_period - # Initialize and run the FastCGI instance, passing arguments through to new. def self.process!(*args, &block) new(*args, &block).process! @@ -68,7 +67,6 @@ class RailsFCGIHandler end end - protected def process_each_request(provider) cgi = nil @@ -197,7 +195,7 @@ class RailsFCGIHandler # close resources as they won't be closed by # the OS when using exec logger.close rescue nil - RAILS_DEFAULT_LOGGER.close rescue nil + Rails.logger.close rescue nil exec(command_line) end diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 6576cd368b..70c6a629ec 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -33,7 +33,11 @@ module Rails end def logger - RAILS_DEFAULT_LOGGER + if defined?(RAILS_DEFAULT_LOGGER) + RAILS_DEFAULT_LOGGER + else + nil + end end def root @@ -403,7 +407,7 @@ Run `rake gems:install` to install the missing gems. # +STDERR+, with a log level of +WARN+. def initialize_logger # if the environment has explicitly defined a logger, use it - return if defined?(RAILS_DEFAULT_LOGGER) + return if Rails.logger unless logger = configuration.logger begin @@ -431,10 +435,11 @@ Run `rake gems:install` to install the missing gems. # RAILS_DEFAULT_LOGGER. def initialize_framework_logging for framework in ([ :active_record, :action_controller, :action_mailer ] & configuration.frameworks) - framework.to_s.camelize.constantize.const_get("Base").logger ||= RAILS_DEFAULT_LOGGER + framework.to_s.camelize.constantize.const_get("Base").logger ||= Rails.logger end - RAILS_CACHE.logger ||= RAILS_DEFAULT_LOGGER + ActiveSupport::Dependencies.logger ||= Rails.logger + Rails.cache.logger ||= Rails.logger end # Sets +ActionController::Base#view_paths+ and +ActionMailer::Base#template_root+ @@ -531,7 +536,7 @@ Run `rake gems:install` to install the missing gems. return unless configuration.frameworks.include?(:action_controller) require 'dispatcher' unless defined?(::Dispatcher) Dispatcher.define_dispatcher_callbacks(configuration.cache_classes) - Dispatcher.new(RAILS_DEFAULT_LOGGER).send :run_callbacks, :prepare_dispatch + Dispatcher.new(Rails.logger).send :run_callbacks, :prepare_dispatch end def disable_dependency_loading -- cgit v1.2.3 From e9ae2b2f4cae3e9ba4fc8ce91de951d18879af8b Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 19 Aug 2008 00:18:26 -0500 Subject: Added rack logger middleware that tails the environment log --- railties/lib/rails/rack.rb | 1 + railties/lib/rails/rack/logger.rb | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 railties/lib/rails/rack/logger.rb (limited to 'railties') diff --git a/railties/lib/rails/rack.rb b/railties/lib/rails/rack.rb index abcd0741bf..b4f32c2d95 100644 --- a/railties/lib/rails/rack.rb +++ b/railties/lib/rails/rack.rb @@ -1,5 +1,6 @@ module Rails module Rack + autoload :Logger, "rails/rack/logger" autoload :Static, "rails/rack/static" end end diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb new file mode 100644 index 0000000000..89d02e45a9 --- /dev/null +++ b/railties/lib/rails/rack/logger.rb @@ -0,0 +1,28 @@ +module Rails + module Rack + class Logger + EnvironmentLog = "#{File.expand_path(Rails.root)}/log/#{Rails.env}.log" + + def initialize(app, log = nil) + @app = app + @path = Pathname.new(log || EnvironmentLog).cleanpath + @cursor = ::File.size(@path) + @last_checked = Time.now + end + + def call(env) + response = @app.call(env) + ::File.open(@path, 'r') do |f| + f.seek @cursor + if f.mtime > @last_checked + contents = f.read + @last_checked = f.mtime + @cursor += contents.length + print contents + end + end + response + end + end + end +end -- cgit v1.2.3 From bd7edcf286421b090b8de5674422a7316ed043a9 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 19 Aug 2008 16:46:15 -0500 Subject: Removed config.ru template from app generator --- .../lib/rails_generator/generators/applications/app/app_generator.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'railties') diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb index 98fe163455..80e8eabfd3 100644 --- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb +++ b/railties/lib/rails_generator/generators/applications/app/app_generator.rb @@ -46,7 +46,6 @@ class AppGenerator < Rails::Generator::Base # Root m.file "fresh_rakefile", "Rakefile" m.file "README", "README" - m.file "config.ru", "config.ru" # Application m.template "helpers/application.rb", "app/controllers/application.rb", :assigns => { :app_name => @app_name, :app_secret => md5.hexdigest } -- cgit v1.2.3 From 5df8ff1d6b6c35a7b5924f95ad12693984323513 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 19 Aug 2008 17:14:17 -0500 Subject: Touch file with git revision when freezing edge --- railties/lib/tasks/framework.rake | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'railties') diff --git a/railties/lib/tasks/framework.rake b/railties/lib/tasks/framework.rake index 71aea09867..66ab78c3b2 100644 --- a/railties/lib/tasks/framework.rake +++ b/railties/lib/tasks/framework.rake @@ -43,9 +43,12 @@ namespace :rails do require 'open-uri' version = ENV["RELEASE"] || "edge" target = "rails_#{version}.zip" + commits = "http://github.com/api/v1/yaml/rails/rails/commits/master" url = "http://dev.rubyonrails.org/archives/#{target}" chdir 'vendor' do + latest_revision = YAML.load(open(commits))["commits"].first["id"] + puts "Downloading Rails from #{url}" File.open('rails.zip', 'wb') do |dst| open url do |src| @@ -61,6 +64,8 @@ namespace :rails do %w(rails.zip rails/Rakefile rails/cleanlogs.sh rails/pushgems.rb rails/release.rb).each do |goner| rm_f goner end + + touch "rails/REVISION_#{latest_revision}" end puts 'Updating current scripts, javascripts, and configuration settings' -- cgit v1.2.3 From 89d1c77dd012f087c091e0f23874c582ea4e3703 Mon Sep 17 00:00:00 2001 From: Tom Ward Date: Tue, 19 Aug 2008 13:19:41 +0100 Subject: Initializer to sort files before eager loading. [#859 state:resolved] Changed Rails::Initializer to sort files before eager loading them. This ensures that any files in a parent directory will be loaded before files in a subdirectory of the 'same' name. i.e. zoo.rb will be loaded before zoo/reptile_house.rb Signed-off-by: Pratik Naik --- railties/lib/initializer.rb | 2 +- railties/test/fixtures/eager/zoo.rb | 3 +++ railties/test/fixtures/eager/zoo/reptile_house.rb | 2 ++ railties/test/initializer_test.rb | 18 ++++++++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 railties/test/fixtures/eager/zoo.rb create mode 100644 railties/test/fixtures/eager/zoo/reptile_house.rb (limited to 'railties') diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 70c6a629ec..ee847e5561 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -356,7 +356,7 @@ Run `rake gems:install` to install the missing gems. if configuration.cache_classes configuration.eager_load_paths.each do |load_path| matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/ - Dir.glob("#{load_path}/**/*.rb").each do |file| + Dir.glob("#{load_path}/**/*.rb").sort.each do |file| require_dependency file.sub(matcher, '\1') end end diff --git a/railties/test/fixtures/eager/zoo.rb b/railties/test/fixtures/eager/zoo.rb new file mode 100644 index 0000000000..8b10ef984b --- /dev/null +++ b/railties/test/fixtures/eager/zoo.rb @@ -0,0 +1,3 @@ +class Zoo + include ReptileHouse +end \ No newline at end of file diff --git a/railties/test/fixtures/eager/zoo/reptile_house.rb b/railties/test/fixtures/eager/zoo/reptile_house.rb new file mode 100644 index 0000000000..82bbafce79 --- /dev/null +++ b/railties/test/fixtures/eager/zoo/reptile_house.rb @@ -0,0 +1,2 @@ +module Zoo::ReptileHouse +end \ No newline at end of file diff --git a/railties/test/initializer_test.rb b/railties/test/initializer_test.rb index 07303a510e..5147eeb482 100644 --- a/railties/test/initializer_test.rb +++ b/railties/test/initializer_test.rb @@ -30,6 +30,24 @@ class Initializer_load_environment_Test < Test::Unit::TestCase end +class Initializer_eager_loading_Test < Test::Unit::TestCase + def setup + @config = ConfigurationMock.new("") + @config.cache_classes = true + @config.load_paths = [File.expand_path(File.dirname(__FILE__) + "/fixtures/eager")] + @config.eager_load_paths = [File.expand_path(File.dirname(__FILE__) + "/fixtures/eager")] + @initializer = Rails::Initializer.new(@config) + @initializer.set_load_path + @initializer.set_autoload_paths + end + + def test_eager_loading_loads_parent_classes_before_children + assert_nothing_raised do + @initializer.load_application_classes + end + end +end + uses_mocha 'Initializer after_initialize' do class Initializer_after_initialize_with_blocks_environment_Test < Test::Unit::TestCase def setup -- cgit v1.2.3 From 4e4277b9e0a77c62dd1e253156bcde9e4a1a16b2 Mon Sep 17 00:00:00 2001 From: Chris Lloyd Date: Thu, 7 Aug 2008 11:00:18 +1000 Subject: Fixed that rake doc:plugins to uses UTF-8. [#573 state:resolved] Signed-off-by: Pratik Naik --- railties/lib/tasks/documentation.rake | 1 + 1 file changed, 1 insertion(+) (limited to 'railties') diff --git a/railties/lib/tasks/documentation.rake b/railties/lib/tasks/documentation.rake index 331b2450a3..f4927a0ef1 100644 --- a/railties/lib/tasks/documentation.rake +++ b/railties/lib/tasks/documentation.rake @@ -62,6 +62,7 @@ namespace :doc do options << "-o doc/plugins/#{plugin}" options << "--title '#{plugin.titlecase} Plugin Documentation'" options << '--line-numbers' << '--inline-source' + options << '--charset' << 'utf-8' options << '-T html' files.include("#{plugin_base}/lib/**/*.rb") -- cgit v1.2.3 From cf28109158054fbab91de2d6d86efe1b40e68d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Sat, 23 Aug 2008 18:05:52 +0300 Subject: Always require activesupport, even if its constant already exists This is needed because the existance of the ActiveSupport constant by itself does not guarantee that the whole library has been loaded. Also load the StringInquirer in the Rails#env method as the it might be called inside the initializer block before activesupport itself has been loaded. --- railties/lib/initializer.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'railties') diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index ee847e5561..008f1de8fa 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -49,6 +49,7 @@ module Rails end def env + require 'active_support/string_inquirer' ActiveSupport::StringInquirer.new(RAILS_ENV) end -- cgit v1.2.3 From 9223a919111867e6b47b2627c30d8eb21e8ac7d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Sat, 23 Aug 2008 20:58:44 +0300 Subject: Generate belongs_to associations automatically for 'references' types [#640 state:resolved] --- .../generators/components/model/templates/model.rb | 3 +++ railties/test/generators/rails_model_generator_test.rb | 8 ++++++++ 2 files changed, 11 insertions(+) (limited to 'railties') diff --git a/railties/lib/rails_generator/generators/components/model/templates/model.rb b/railties/lib/rails_generator/generators/components/model/templates/model.rb index 8d4c89e912..8bf93b9e8b 100644 --- a/railties/lib/rails_generator/generators/components/model/templates/model.rb +++ b/railties/lib/rails_generator/generators/components/model/templates/model.rb @@ -1,2 +1,5 @@ class <%= class_name %> < ActiveRecord::Base +<% attributes.select { |a| a.type.to_s == 'references' }.each do |attribute| -%> + belongs_to :<%= attribute.name %> +<% end -%> end diff --git a/railties/test/generators/rails_model_generator_test.rb b/railties/test/generators/rails_model_generator_test.rb index 0bfc338566..67cd3aa26f 100644 --- a/railties/test/generators/rails_model_generator_test.rb +++ b/railties/test/generators/rails_model_generator_test.rb @@ -29,4 +29,12 @@ class RailsModelGeneratorTest < GeneratorTestCase assert_generated_column t, :created_at, :timestamp end end + + def test_model_with_reference_attributes_generates_belongs_to_associations + run_generator('model', %w(Product name:string supplier:references)) + + assert_generated_model_for :product do |body| + assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'" + end + end end -- cgit v1.2.3 From b1f3c6e6ec08a40fc7153ee3ba78d51b466a58e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Sat, 23 Aug 2008 21:54:43 +0300 Subject: Generate belongs_to association when generating a model --- railties/lib/rails_generator/generated_attribute.rb | 4 ++++ .../generators/components/model/templates/model.rb | 2 +- railties/test/generators/rails_model_generator_test.rb | 8 ++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/lib/rails_generator/generated_attribute.rb b/railties/lib/rails_generator/generated_attribute.rb index 25af3931de..a3d4a01142 100644 --- a/railties/lib/rails_generator/generated_attribute.rb +++ b/railties/lib/rails_generator/generated_attribute.rb @@ -37,6 +37,10 @@ module Rails "" end end + + def reference? + [ :references, :belongs_to ].include?(self.type) + end end end end diff --git a/railties/lib/rails_generator/generators/components/model/templates/model.rb b/railties/lib/rails_generator/generators/components/model/templates/model.rb index 8bf93b9e8b..6fcf393bdf 100644 --- a/railties/lib/rails_generator/generators/components/model/templates/model.rb +++ b/railties/lib/rails_generator/generators/components/model/templates/model.rb @@ -1,5 +1,5 @@ class <%= class_name %> < ActiveRecord::Base -<% attributes.select { |a| a.type.to_s == 'references' }.each do |attribute| -%> +<% attributes.select(&:reference?).each do |attribute| -%> belongs_to :<%= attribute.name %> <% end -%> end diff --git a/railties/test/generators/rails_model_generator_test.rb b/railties/test/generators/rails_model_generator_test.rb index 67cd3aa26f..aea2abafba 100644 --- a/railties/test/generators/rails_model_generator_test.rb +++ b/railties/test/generators/rails_model_generator_test.rb @@ -37,4 +37,12 @@ class RailsModelGeneratorTest < GeneratorTestCase assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'" end end + + def test_model_with_belongs_to_attributes_generates_belongs_to_associations + run_generator('model', %w(Product name:string supplier:belongs_to)) + + assert_generated_model_for :product do |body| + assert body =~ /^\s+belongs_to :supplier/, "#{body.inspect} should contain 'belongs_to :supplier'" + end + end end -- cgit v1.2.3 From eb2b81c766a6f9bf3c27ae0a494ae82956fb8555 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 24 Aug 2008 13:19:38 +0200 Subject: Reverse the priority of the mysql commands in dbconsole --- railties/lib/commands/dbconsole.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/lib/commands/dbconsole.rb b/railties/lib/commands/dbconsole.rb index 442526ae32..5be3b5dd8e 100644 --- a/railties/lib/commands/dbconsole.rb +++ b/railties/lib/commands/dbconsole.rb @@ -47,7 +47,7 @@ when "mysql" args << config['database'] - exec(find_cmd('mysql5', 'mysql'), *args) + exec(find_cmd('mysql', 'mysql5'), *args) when "postgresql" ENV['PGUSER'] = config["username"] if config["username"] -- cgit v1.2.3 From f9f1ab4e3ddeacadf2a7bce021d742f08f67905f Mon Sep 17 00:00:00 2001 From: "Hongli Lai (Phusion" Date: Thu, 10 Jul 2008 13:21:08 +0200 Subject: When an unexpected exception is caught, tell the administrator to read the log file for more information about the error. This should make things less confusing for developers who are new to Rails. Signed-off-by: Michael Koziarski --- railties/html/500.html | 2 ++ railties/test/error_page_test.rb | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 railties/test/error_page_test.rb (limited to 'railties') diff --git a/railties/html/500.html b/railties/html/500.html index 0e9c14f4c6..328fdcc3bc 100644 --- a/railties/html/500.html +++ b/railties/html/500.html @@ -25,6 +25,8 @@

We're sorry, but something went wrong.

We've been notified about this issue and we'll take a look at it shortly.

+

(If you're the administrator of this website, then please read + the log file "<%=h RAILS_ENV %>.log" to find out what went wrong.)

\ No newline at end of file diff --git a/railties/test/error_page_test.rb b/railties/test/error_page_test.rb new file mode 100644 index 0000000000..0e43700eb6 --- /dev/null +++ b/railties/test/error_page_test.rb @@ -0,0 +1,37 @@ +require 'abstract_unit' +require 'action_controller' +require 'action_controller/test_process' + +RAILS_ENV = "test" + +module Rails + def self.public_path + File.expand_path(File.join(File.dirname(__FILE__), "..", "html")) + end +end + +class ErrorPageController < ActionController::Base + def crash + raise StandardError, "crash!" + end +end + +ActionController::Routing::Routes.draw do |map| + map.connect ':controller/:action/:id' +end + +class ErrorPageControllerTest < Test::Unit::TestCase + def setup + @controller = ErrorPageController.new + @request = ActionController::TestRequest.new + @response = ActionController::TestResponse.new + + ActionController::Base.consider_all_requests_local = false + end + + def test_500_error_page_instructs_system_administrator_to_check_log_file + get :crash + expected_log_file = "#{RAILS_ENV}.log" + assert_not_nil @response.body.index(expected_log_file) + end +end -- cgit v1.2.3 From c111522d5b8cd108756240a0348d515d6acee46c Mon Sep 17 00:00:00 2001 From: "Hongli Lai (Phusion" Date: Tue, 26 Aug 2008 15:59:03 +0200 Subject: The 'rails' command was broken by the last commit. Fix that. Signed-off-by: Michael Koziarski --- railties/html/500.html | 5 +++-- railties/test/error_page_test.rb | 8 +++++++- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'railties') diff --git a/railties/html/500.html b/railties/html/500.html index 328fdcc3bc..74142cb04a 100644 --- a/railties/html/500.html +++ b/railties/html/500.html @@ -26,7 +26,8 @@

We're sorry, but something went wrong.

We've been notified about this issue and we'll take a look at it shortly.

(If you're the administrator of this website, then please read - the log file "<%=h RAILS_ENV %>.log" to find out what went wrong.)

+ the log file "<%= "<%s>" % "%=h RAILS_ENV %" %>.log" + to find out what went wrong.)

- \ No newline at end of file + diff --git a/railties/test/error_page_test.rb b/railties/test/error_page_test.rb index 0e43700eb6..844f889aad 100644 --- a/railties/test/error_page_test.rb +++ b/railties/test/error_page_test.rb @@ -3,10 +3,12 @@ require 'action_controller' require 'action_controller/test_process' RAILS_ENV = "test" +CURRENT_DIR = File.expand_path(File.dirname(__FILE__)) +HTML_DIR = File.expand_path(File.join(CURRENT_DIR, "..", "html")) module Rails def self.public_path - File.expand_path(File.join(File.dirname(__FILE__), "..", "html")) + CURRENT_DIR end end @@ -30,6 +32,10 @@ class ErrorPageControllerTest < Test::Unit::TestCase end def test_500_error_page_instructs_system_administrator_to_check_log_file + template = ERB.new(File.read(File.join(HTML_DIR, "500.html"))) + File.open(File.join(CURRENT_DIR, "500.html"), "w") do |f| + f.write(template.result) + end get :crash expected_log_file = "#{RAILS_ENV}.log" assert_not_nil @response.body.index(expected_log_file) -- cgit v1.2.3 From b3411ff59eb1e1c31f98f58f117a2ffaaf0c3ff5 Mon Sep 17 00:00:00 2001 From: "Hongli Lai (Phusion" Date: Wed, 27 Aug 2008 12:42:08 +0200 Subject: Deprecate Rails::SecretKeyGenerator in favor of ActiveSupport::SecureRandom. SecureRandom has a few minor security enhancements and can be used as a drop-in replacement Signed-off-by: Michael Koziarski [#913 state:committed] --- .../generators/applications/app/app_generator.rb | 3 +- .../lib/rails_generator/secret_key_generator.rb | 151 +-------------------- railties/lib/tasks/misc.rake | 7 +- railties/test/secret_key_generation_test.rb | 8 -- 4 files changed, 8 insertions(+), 161 deletions(-) (limited to 'railties') diff --git a/railties/lib/rails_generator/generators/applications/app/app_generator.rb b/railties/lib/rails_generator/generators/applications/app/app_generator.rb index 80e8eabfd3..9849948339 100644 --- a/railties/lib/rails_generator/generators/applications/app/app_generator.rb +++ b/railties/lib/rails_generator/generators/applications/app/app_generator.rb @@ -1,6 +1,5 @@ require 'rbconfig' require 'digest/md5' -require 'rails_generator/secret_key_generator' class AppGenerator < Rails::Generator::Base DEFAULT_SHEBANG = File.join(Config::CONFIG['bindir'], @@ -36,7 +35,7 @@ class AppGenerator < Rails::Generator::Base md5 << @app_name # Do our best to generate a secure secret key for CookieStore - secret = Rails::SecretKeyGenerator.new(@app_name).generate_secret + secret = ActiveSupport::SecureRandom.hex(64) record do |m| # Root directory and all subdirectories. diff --git a/railties/lib/rails_generator/secret_key_generator.rb b/railties/lib/rails_generator/secret_key_generator.rb index 5ae492312e..e92ca125a2 100644 --- a/railties/lib/rails_generator/secret_key_generator.rb +++ b/railties/lib/rails_generator/secret_key_generator.rb @@ -5,160 +5,17 @@ module Rails # # generator = Rails::SecretKeyGenerator("some unique identifier, such as the application name") # generator.generate_secret # => "f3f1be90053fa851... (some long string)" + # + # This class is *deprecated* in Rails 2.2 in favor of ActiveSupport::SecureRandom. + # It is currently a wrapper around ActiveSupport::SecureRandom. class SecretKeyGenerator - GENERATORS = [ :secure_random, :win32_api, :urandom, :openssl, :prng ].freeze - def initialize(identifier) - @identifier = identifier end # Generate a random secret key with the best possible method available on # the current platform. def generate_secret - generator = GENERATORS.find do |g| - self.class.send("supports_#{g}?") - end - send("generate_secret_with_#{generator}") - end - - # Generate a random secret key by using the Win32 API. Raises LoadError - # if the current platform cannot make use of the Win32 API. Raises - # SystemCallError if some other error occurred. - def generate_secret_with_win32_api - # Following code is based on David Garamond's GUID library for Ruby. - require 'Win32API' - - crypt_acquire_context = Win32API.new("advapi32", "CryptAcquireContext", - 'PPPII', 'L') - crypt_gen_random = Win32API.new("advapi32", "CryptGenRandom", - 'LIP', 'L') - crypt_release_context = Win32API.new("advapi32", "CryptReleaseContext", - 'LI', 'L') - prov_rsa_full = 1 - crypt_verifycontext = 0xF0000000 - - hProvStr = " " * 4 - if crypt_acquire_context.call(hProvStr, nil, nil, prov_rsa_full, - crypt_verifycontext) == 0 - raise SystemCallError, "CryptAcquireContext failed: #{lastWin32ErrorMessage}" - end - hProv, = hProvStr.unpack('L') - bytes = " " * 64 - if crypt_gen_random.call(hProv, bytes.size, bytes) == 0 - raise SystemCallError, "CryptGenRandom failed: #{lastWin32ErrorMessage}" - end - if crypt_release_context.call(hProv, 0) == 0 - raise SystemCallError, "CryptReleaseContext failed: #{lastWin32ErrorMessage}" - end - bytes.unpack("H*")[0] - end - - # Generate a random secret key with Ruby 1.9's SecureRandom module. - # Raises LoadError if the current Ruby version does not support - # SecureRandom. - def generate_secret_with_secure_random - require 'securerandom' - return SecureRandom.hex(64) - end - - # Generate a random secret key with OpenSSL. If OpenSSL is not - # already loaded, then this method will attempt to load it. - # LoadError will be raised if that fails. - def generate_secret_with_openssl - require 'openssl' - if !File.exist?("/dev/urandom") - # OpenSSL transparently seeds the random number generator with - # data from /dev/urandom. On platforms where that is not - # available, such as Windows, we have to provide OpenSSL with - # our own seed. Unfortunately there's no way to provide a - # secure seed without OS support, so we'll have to do with - # rand() and Time.now.usec(). - OpenSSL::Random.seed(rand(0).to_s + Time.now.usec.to_s) - end - data = OpenSSL::BN.rand(2048, -1, false).to_s - - if OpenSSL::OPENSSL_VERSION_NUMBER > 0x00908000 - OpenSSL::Digest::SHA512.new(data).hexdigest - else - generate_secret_with_prng - end + ActiveSupport::SecureRandom.hex(64) end - - # Generate a random secret key with /dev/urandom. - # Raises SystemCallError on failure. - def generate_secret_with_urandom - return File.read("/dev/urandom", 64).unpack("H*")[0] - end - - # Generate a random secret key with Ruby's pseudo random number generator, - # as well as some environment information. - # - # This is the least cryptographically secure way to generate a secret key, - # and should be avoided whenever possible. - def generate_secret_with_prng - require 'digest/sha2' - sha = Digest::SHA2.new(512) - now = Time.now - sha << now.to_s - sha << String(now.usec) - sha << String(rand(0)) - sha << String($$) - sha << @identifier - return sha.hexdigest - end - - private - def lastWin32ErrorMessage - # Following code is based on David Garamond's GUID library for Ruby. - get_last_error = Win32API.new("kernel32", "GetLastError", '', 'L') - format_message = Win32API.new("kernel32", "FormatMessageA", - 'LPLLPLPPPPPPPP', 'L') - format_message_ignore_inserts = 0x00000200 - format_message_from_system = 0x00001000 - - code = get_last_error.call - msg = "\0" * 1024 - len = format_message.call(format_message_ignore_inserts + - format_message_from_system, 0, - code, 0, msg, 1024, nil, nil, - nil, nil, nil, nil, nil, nil) - msg[0, len].tr("\r", '').chomp - end - - def self.supports_secure_random? - begin - require 'securerandom' - true - rescue LoadError - false - end - end - - def self.supports_win32_api? - return false unless RUBY_PLATFORM =~ /(:?mswin|mingw)/ - begin - require 'Win32API' - true - rescue LoadError - false - end - end - - def self.supports_urandom? - File.exist?('/dev/urandom') - end - - def self.supports_openssl? - begin - require 'openssl' - true - rescue LoadError - false - end - end - - def self.supports_prng? - true - end end end diff --git a/railties/lib/tasks/misc.rake b/railties/lib/tasks/misc.rake index 33bbba1101..5c99725203 100644 --- a/railties/lib/tasks/misc.rake +++ b/railties/lib/tasks/misc.rake @@ -3,10 +3,9 @@ task :environment do require(File.join(RAILS_ROOT, 'config', 'environment')) end -require 'rails_generator/secret_key_generator' -desc 'Generate a crytographically secure secret key. This is typically used to generate a secret for cookie sessions. Pass a unique identifier to the generator using ID="some unique identifier" for greater security.' +desc 'Generate a crytographically secure secret key. This is typically used to generate a secret for cookie sessions.' task :secret do - puts Rails::SecretKeyGenerator.new(ENV['ID']).generate_secret + puts ActiveSupport::SecureRandom.hex(64) end require 'active_support' @@ -54,4 +53,4 @@ namespace :time do puts "\n" end end -end \ No newline at end of file +end diff --git a/railties/test/secret_key_generation_test.rb b/railties/test/secret_key_generation_test.rb index ea1b0dae31..2a04cff856 100644 --- a/railties/test/secret_key_generation_test.rb +++ b/railties/test/secret_key_generation_test.rb @@ -33,12 +33,4 @@ class SecretKeyGenerationTest < Test::Unit::TestCase def test_secret_key_generation assert @generator.generate_secret.length >= SECRET_KEY_MIN_LENGTH end - - Rails::SecretKeyGenerator::GENERATORS.each do |generator| - if Rails::SecretKeyGenerator.send("supports_#{generator}?") - define_method("test_secret_key_generation_with_#{generator}") do - assert @generator.send("generate_secret_with_#{generator}").length >= SECRET_KEY_MIN_LENGTH - end - end - end end -- cgit v1.2.3 From b7cd4ded9350fff5edee2298dc04a55e6b285233 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Wed, 27 Aug 2008 15:18:07 +0200 Subject: Formally deprecate the old secret key generator --- railties/lib/rails_generator/secret_key_generator.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'railties') diff --git a/railties/lib/rails_generator/secret_key_generator.rb b/railties/lib/rails_generator/secret_key_generator.rb index e92ca125a2..553811d35d 100644 --- a/railties/lib/rails_generator/secret_key_generator.rb +++ b/railties/lib/rails_generator/secret_key_generator.rb @@ -17,5 +17,6 @@ module Rails def generate_secret ActiveSupport::SecureRandom.hex(64) end + deprecate :generate_secret=>"You should use ActiveSupport::SecureRandom.hex(64)" end end -- cgit v1.2.3 From 8b6870cfae8d50a2ffd4f024d33d51aadaa6a6f7 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Thu, 28 Aug 2008 12:47:18 +0200 Subject: Prevent deprecation warning in the tests --- railties/test/secret_key_generation_test.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/test/secret_key_generation_test.rb b/railties/test/secret_key_generation_test.rb index 2a04cff856..7269f98ce5 100644 --- a/railties/test/secret_key_generation_test.rb +++ b/railties/test/secret_key_generation_test.rb @@ -31,6 +31,8 @@ class SecretKeyGenerationTest < Test::Unit::TestCase end def test_secret_key_generation - assert @generator.generate_secret.length >= SECRET_KEY_MIN_LENGTH + assert_deprecated /ActiveSupport::SecureRandom\.hex\(64\)/ do + assert @generator.generate_secret.length >= SECRET_KEY_MIN_LENGTH + end end end -- cgit v1.2.3 From 1a81f156bd81990a50ecf13986b2131dbd20a62b Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 15 Apr 2008 08:32:05 -0500 Subject: Change all databases.rake adapter 'when' statements to use regexes. This is more friendly to JRuby/JDBC adapters (with names like 'jdbcmysql') and leaves the door open to alternate implementations of adapters in the future. --- railties/lib/tasks/databases.rake | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'railties') diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake index 21c81b3fb5..08294a6853 100644 --- a/railties/lib/tasks/databases.rake +++ b/railties/lib/tasks/databases.rake @@ -32,7 +32,7 @@ namespace :db do ActiveRecord::Base.connection rescue case config['adapter'] - when 'mysql' + when /mysql/ @charset = ENV['CHARSET'] || 'utf8' @collation = ENV['COLLATION'] || 'utf8_general_ci' begin @@ -42,7 +42,7 @@ namespace :db do rescue $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['charset'] || @charset}, collation: #{config['collation'] || @collation} (if you set the charset manually, make sure you have a matching collation)" end - when 'postgresql' + when /postgresql/ @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8' begin ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) @@ -52,9 +52,9 @@ namespace :db do $stderr.puts $!, *($!.backtrace) $stderr.puts "Couldn't create database for #{config.inspect}" end - when 'sqlite' + when /sqlite$/ `sqlite "#{config['database']}"` - when 'sqlite3' + when /sqlite3$/ `sqlite3 "#{config['database']}"` end else @@ -239,7 +239,7 @@ namespace :db do task :dump => :environment do abcs = ActiveRecord::Base.configurations case abcs[RAILS_ENV]["adapter"] - when "mysql", "oci", "oracle" + when /mysql/, /oci/, /oracle/ ActiveRecord::Base.establish_connection(abcs[RAILS_ENV]) File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } when "postgresql" @@ -250,13 +250,13 @@ namespace :db do search_path = "--schema=#{search_path}" if search_path `pg_dump -i -U "#{abcs[RAILS_ENV]["username"]}" -s -x -O -f db/#{RAILS_ENV}_structure.sql #{search_path} #{abcs[RAILS_ENV]["database"]}` raise "Error dumping database" if $?.exitstatus == 1 - when "sqlite", "sqlite3" + when /sqlite/ dbfile = abcs[RAILS_ENV]["database"] || abcs[RAILS_ENV]["dbfile"] `#{abcs[RAILS_ENV]["adapter"]} #{dbfile} .schema > db/#{RAILS_ENV}_structure.sql` - when "sqlserver" + when /sqlserver/ `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /f db\\#{RAILS_ENV}_structure.sql /q /A /r` `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /F db\ /q /A /r` - when "firebird" + when /firebird/ set_firebird_env(abcs[RAILS_ENV]) db_string = firebird_db_string(abcs[RAILS_ENV]) sh "isql -a #{db_string} > #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql" @@ -285,13 +285,13 @@ namespace :db do task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do abcs = ActiveRecord::Base.configurations case abcs["test"]["adapter"] - when "mysql" + when /mysql/ ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table| ActiveRecord::Base.connection.execute(table) end - when "postgresql" + when /postgresql/ ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"] ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"] ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"] @@ -301,12 +301,12 @@ namespace :db do `#{abcs["test"]["adapter"]} #{dbfile} < #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql` when "sqlserver" `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` - when "oci", "oracle" + when /oci/, /oracle/ ActiveRecord::Base.establish_connection(:test) IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl| ActiveRecord::Base.connection.execute(ddl) end - when "firebird" + when /firebird/ set_firebird_env(abcs["test"]) db_string = firebird_db_string(abcs["test"]) sh "isql -i #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql #{db_string}" @@ -319,7 +319,7 @@ namespace :db do task :purge => :environment do abcs = ActiveRecord::Base.configurations case abcs["test"]["adapter"] - when "mysql" + when /mysql/ ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"]) when "postgresql" @@ -329,16 +329,16 @@ namespace :db do when "sqlite","sqlite3" dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"] File.delete(dbfile) if File.exist?(dbfile) - when "sqlserver" + when /sqlserver/ dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-') `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}` `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` - when "oci", "oracle" + when /oci/, /oracle/ ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl| ActiveRecord::Base.connection.execute(ddl) end - when "firebird" + when /firebird/ ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.recreate_database! else @@ -372,9 +372,9 @@ end def drop_database(config) case config['adapter'] - when 'mysql' + when /mysql/ ActiveRecord::Base.connection.drop_database config['database'] - when /^sqlite/ + when /sqlite/ FileUtils.rm(File.join(RAILS_ROOT, config['database'])) when 'postgresql' ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) -- cgit v1.2.3 From 3151d96663874ac65bc49dd645b24605cd4817e0 Mon Sep 17 00:00:00 2001 From: Nick Sieger Date: Thu, 8 May 2008 20:01:57 -0700 Subject: Revert "Change all databases.rake adapter 'when' statements to use regexes." This reverts commit 3d2512d38d2e28b3ea669139f7c7b0307522aa72. --- railties/lib/tasks/databases.rake | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) (limited to 'railties') diff --git a/railties/lib/tasks/databases.rake b/railties/lib/tasks/databases.rake index 08294a6853..21c81b3fb5 100644 --- a/railties/lib/tasks/databases.rake +++ b/railties/lib/tasks/databases.rake @@ -32,7 +32,7 @@ namespace :db do ActiveRecord::Base.connection rescue case config['adapter'] - when /mysql/ + when 'mysql' @charset = ENV['CHARSET'] || 'utf8' @collation = ENV['COLLATION'] || 'utf8_general_ci' begin @@ -42,7 +42,7 @@ namespace :db do rescue $stderr.puts "Couldn't create database for #{config.inspect}, charset: #{config['charset'] || @charset}, collation: #{config['collation'] || @collation} (if you set the charset manually, make sure you have a matching collation)" end - when /postgresql/ + when 'postgresql' @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8' begin ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) @@ -52,9 +52,9 @@ namespace :db do $stderr.puts $!, *($!.backtrace) $stderr.puts "Couldn't create database for #{config.inspect}" end - when /sqlite$/ + when 'sqlite' `sqlite "#{config['database']}"` - when /sqlite3$/ + when 'sqlite3' `sqlite3 "#{config['database']}"` end else @@ -239,7 +239,7 @@ namespace :db do task :dump => :environment do abcs = ActiveRecord::Base.configurations case abcs[RAILS_ENV]["adapter"] - when /mysql/, /oci/, /oracle/ + when "mysql", "oci", "oracle" ActiveRecord::Base.establish_connection(abcs[RAILS_ENV]) File.open("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } when "postgresql" @@ -250,13 +250,13 @@ namespace :db do search_path = "--schema=#{search_path}" if search_path `pg_dump -i -U "#{abcs[RAILS_ENV]["username"]}" -s -x -O -f db/#{RAILS_ENV}_structure.sql #{search_path} #{abcs[RAILS_ENV]["database"]}` raise "Error dumping database" if $?.exitstatus == 1 - when /sqlite/ + when "sqlite", "sqlite3" dbfile = abcs[RAILS_ENV]["database"] || abcs[RAILS_ENV]["dbfile"] `#{abcs[RAILS_ENV]["adapter"]} #{dbfile} .schema > db/#{RAILS_ENV}_structure.sql` - when /sqlserver/ + when "sqlserver" `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /f db\\#{RAILS_ENV}_structure.sql /q /A /r` `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /F db\ /q /A /r` - when /firebird/ + when "firebird" set_firebird_env(abcs[RAILS_ENV]) db_string = firebird_db_string(abcs[RAILS_ENV]) sh "isql -a #{db_string} > #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql" @@ -285,13 +285,13 @@ namespace :db do task :clone_structure => [ "db:structure:dump", "db:test:purge" ] do abcs = ActiveRecord::Base.configurations case abcs["test"]["adapter"] - when /mysql/ + when "mysql" ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table| ActiveRecord::Base.connection.execute(table) end - when /postgresql/ + when "postgresql" ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"] ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"] ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"] @@ -301,12 +301,12 @@ namespace :db do `#{abcs["test"]["adapter"]} #{dbfile} < #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql` when "sqlserver" `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` - when /oci/, /oracle/ + when "oci", "oracle" ActiveRecord::Base.establish_connection(:test) IO.readlines("#{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl| ActiveRecord::Base.connection.execute(ddl) end - when /firebird/ + when "firebird" set_firebird_env(abcs["test"]) db_string = firebird_db_string(abcs["test"]) sh "isql -i #{RAILS_ROOT}/db/#{RAILS_ENV}_structure.sql #{db_string}" @@ -319,7 +319,7 @@ namespace :db do task :purge => :environment do abcs = ActiveRecord::Base.configurations case abcs["test"]["adapter"] - when /mysql/ + when "mysql" ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.recreate_database(abcs["test"]["database"]) when "postgresql" @@ -329,16 +329,16 @@ namespace :db do when "sqlite","sqlite3" dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"] File.delete(dbfile) if File.exist?(dbfile) - when /sqlserver/ + when "sqlserver" dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-') `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}` `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` - when /oci/, /oracle/ + when "oci", "oracle" ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl| ActiveRecord::Base.connection.execute(ddl) end - when /firebird/ + when "firebird" ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.recreate_database! else @@ -372,9 +372,9 @@ end def drop_database(config) case config['adapter'] - when /mysql/ + when 'mysql' ActiveRecord::Base.connection.drop_database config['database'] - when /sqlite/ + when /^sqlite/ FileUtils.rm(File.join(RAILS_ROOT, config['database'])) when 'postgresql' ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public')) -- cgit v1.2.3 From c1b949869c3de8fc62f8ab072bbd77542b1d3605 Mon Sep 17 00:00:00 2001 From: Nick Sieger Date: Fri, 22 Aug 2008 15:44:59 -0500 Subject: Remove call to active_record.allow_concurrency since it's deprecated --- railties/lib/initializer.rb | 1 - 1 file changed, 1 deletion(-) (limited to 'railties') diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 008f1de8fa..4c1f96d505 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -784,7 +784,6 @@ Run `rake gems:install` to install the missing gems. def threadsafe! self.cache_classes = true self.dependency_loading = false - self.active_record.allow_concurrency = true self.action_controller.allow_concurrency = true self end -- cgit v1.2.3 From efa6620a2a7b8ae7b42664ab81faef7df1368939 Mon Sep 17 00:00:00 2001 From: Nathaniel Bibler Date: Fri, 29 Aug 2008 23:36:16 -0400 Subject: Added optional rake doc:app TITLE environment parameter [#939 state:resolved] Signed-off-by: Jeremy Kemper --- railties/lib/tasks/documentation.rake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'railties') diff --git a/railties/lib/tasks/documentation.rake b/railties/lib/tasks/documentation.rake index f4927a0ef1..4ef838626a 100644 --- a/railties/lib/tasks/documentation.rake +++ b/railties/lib/tasks/documentation.rake @@ -1,9 +1,9 @@ namespace :doc do - desc "Generate documentation for the application. Set custom template with TEMPLATE=/path/to/rdoc/template.rb" + desc "Generate documentation for the application. Set custom template with TEMPLATE=/path/to/rdoc/template.rb or title with TITLE=\"Custom Title\"" Rake::RDocTask.new("app") { |rdoc| rdoc.rdoc_dir = 'doc/app' rdoc.template = ENV['template'] if ENV['template'] - rdoc.title = "Rails Application Documentation" + rdoc.title = ENV['title'] || "Rails Application Documentation" rdoc.options << '--line-numbers' << '--inline-source' rdoc.options << '--charset' << 'utf-8' rdoc.rdoc_files.include('doc/README_FOR_APP') -- cgit v1.2.3 From 36ee17d458b86c5f3f371810160e8839d318bbf1 Mon Sep 17 00:00:00 2001 From: Mike Gunderloy Date: Sun, 31 Aug 2008 09:31:19 -0500 Subject: Added "Routing from the Outside In" guide. --- railties/doc/guides/routing/routing_outside_in.txt | 763 +++++++++++++++++++++ 1 file changed, 763 insertions(+) create mode 100644 railties/doc/guides/routing/routing_outside_in.txt (limited to 'railties') diff --git a/railties/doc/guides/routing/routing_outside_in.txt b/railties/doc/guides/routing/routing_outside_in.txt new file mode 100644 index 0000000000..7817fa66d3 --- /dev/null +++ b/railties/doc/guides/routing/routing_outside_in.txt @@ -0,0 +1,763 @@ +Rails Routing from the Outside In +================================= + +This guide covers the user-facing features of Rails routing. By referring to this guide, you will be able to: + +* Understand the purpose of routing +* Decipher the code in +routing.rb+ +* Construct your own routes, using either the classic hash style or the now-preferred RESTful style +* Identify how a route will map to a controller and action + +== The Dual Purpose of Routing + +Rails routing is a two-way piece of machinery - rather as if you could turn pigs into sausage, and then turn sausage back into pigs. Specifically, it both connects incoming HTTP requests to the code in your application's controllers, and helps you generate URLs without having to hard-code them as strings. + +=== Connecting URLs to Code + +When your Rails application receives an incoming HTTP request, say + +------------------------------------------------------- +GET /patient/17 +------------------------------------------------------- + +the routing engine within Rails is the piece of code that dispatches the request to the appropriate spot in your application. In this case, the application would most likely end up running the +show+ action within the +patients+ controller, displaying the details of the patient whose ID is 17. + +=== Generating URLs from Code + +Routing also works in reverse. If your application contains this code: + +[source, ruby] +------------------------------------------------------- +@patient = Patient.find(17) +<%= link_to "Patient Record", patient_path(@patient) %> +------------------------------------------------------- + +Then the routing engine is the piece that translates that to a link to a URL such as +http://example.com/patient/17+. By using routing in this way, you can reduce the brittleness of your application as compared to one with hard-coded URLs, and make your code easier to read and understand. + +== Quick Tour of Routes.rb + +There are two components to routing in Rails: the routing engine itself, which is supplied as part of Rails, and the file +config/routes.rb+, which contains the actual routes that will be used by your application. Learning exactly what you can put in +routes.rb+ is the main topic of this guide, but before we dig in let's get a quick overview. + +=== Processing the File + +In format, +routes.rb+ is nothing more than one big block sent to +ActionController::Routing::Routes.draw+. Within this block, you can have comments, but it's likely that most of your content will be individual lines of code - each line being a route in your application. You'll find five main types of content in this file: + +* RESTful Routes +* Named Routes +* Nested Routes +* Regular (old-style) Routes +* Default Routes + +Each of these types of route is covered in more detail later in this guide. + +The +routes.rb+ file is processed from top to bottom when a request comes in. The request will be dispatched to the first matching route. If there is no matching route, then Rails returns HTTP status 404 to the caller. + +=== RESTful Routes + +RESTful routes take advantage of the built-in REST orientation of Rails to wrap up a lot of routing information in a single declaration. A RESTful route looks like this: + +[source, ruby] +------------------------------------------------------- +map.resources :books +------------------------------------------------------- + +=== Named Routes + +Named routes give you very readable links in your code, as well as handling incoming requests. Here's a typical named route: + +[source, ruby] +------------------------------------------------------- +map.login '/login', :controller => 'sessions', :action => 'new' +------------------------------------------------------- + +=== Nested Routes + +Nested routes let you declare that one resource is contained within another resource. You'll see later on how this translates to URLs and paths in your code. For example, if your application includes parts, each of which belongs to an assembly, you might have this nested route declaration: + +[source, ruby] +------------------------------------------------------- +map.resources :assemblies do |assemblies| + assemblies.resources :parts +end +------------------------------------------------------- + +=== Regular (old-style) Routes + +In many applications, you'll still see the older-style (non-RESTful) routing, which explicitly connects the parts of a URL to a particular action. For example, + +[source, ruby] +------------------------------------------------------- +map.connect 'parts/:number', :controller => 'inventory', :action => 'show' +------------------------------------------------------- + +=== Default Routes + +The default routes are a safety net that catch otherwise-unrouted requests. Many Rails applications will contain this pair of default routes: + +[source, ruby] +------------------------------------------------------- +map.connect ':controller/:action/:id' +map.connect ':controller/:action/:id.:format' +------------------------------------------------------- + +These default routes are automatically generated when you create a new Rails application. If you're using RESTful routing, you will probably want to remove them. + +== RESTful Routing: the New Default + +RESTful routing is the new standard for routing in Rails, and it's the one that you should prefer for new applications. It can take a little while to understand how RESTful routing works, but it's worth the effort; your code will be easier to read and you'll be working with Rails, rather than fighting against it, when you use this style of routing. + +=== What is REST? + +The foundation of RESTful routing is generally considered to be Roy Fielding's doctoral thesis, link:http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm[Architectural Styles and the Design of Network-based Software Architectures]. Fortunately, you need not read this entire document to understand how REST works in Rails. REST, an acronym for Representational State Transfer, boils down to two main principles for our purposes: + +* Using resource identifiers (which, for the purposes of discussion, you can think of as URLs) to represent resources +* Transferring representations of the state of that resource between system components. + +For example, to a Rails application a request such as this: + ++DELETE /photos/17+ + +would be understood to refer to a photo resource with the ID of 17, and to indicate a desired action - deleting that resource. REST is a natural style for the architecture of web applications, and Rails makes it even more natural by using conventions to shield you from some of the RESTful complexities. + +=== CRUD, Verbs, and Actions + +In Rails, a RESTful route provides a mapping between HTTP verbs, controller actions, and (implicitly) CRUD operations in a database. A single entry in the routing file, such as + +[source, ruby] +------------------------------------------------------- +map.resources photos +------------------------------------------------------- + +creates seven different routes in your application: + +[grid="all"] +`----------`---------------`-----------`--------`------------------------------------------- +HTTP verb URL controller action used for +-------------------------------------------------------------------------------------------- +GET /photos Photos index display a list of all photos +GET /photos/new Photos new return an HTML form for creating a new photo +POST /photos Photos create create a new photo +GET /photo/1 Photos show display a specific photo +GET /photo/1/edit Photos edit return an HTML form for editing a photo +PUT /photo/1 Photos update update a specific photo +DELETE /photo/1 Photos destroy delete a specific photo +-------------------------------------------------------------------------------------------- + +For the specific routes (those that reference just a single resource), the identifier for the resource will be available within the corresponding controller action as +params[:id]+. + +TIP: If you consistently use RESTful routes in your application, you should disable the default routes in +routes.rb+ so that Rails will enforce the mapping between HTTP verbs and routes. + +=== URLs and Paths + +Creating a RESTful route will also make available a pile of helpers within your application: + +* +photos_url+ and +photos_path+ map to the path for the index and create actions +* +new_photo_url+ and +new_photo_path+ map to the path for the new action +* +edit_photo_url+ and +edit_photo_path+ map to the path for the edit action +* +photo_url+ and +photo_path+ map to the path for the show, update, and destroy actions + +NOTE: Because routing makes use of the HTTP verb as well as the path in the request to dispatch requests, the seven routes generated by a RESTful routing entry only give rise to four pairs of helpers. + +In each case, the +_url+ helper generates a string containing the entire URL that the application will understand, while the +_path+ helper generates a string containing the relative path from the root of the application. For example: + +[source, ruby] +------------------------------------------------------- +photos_url # => "http://www.example.com/photos" +photos_path # => "/photos" +------------------------------------------------------- + +=== Singular Resources + +You can also apply RESTful routing to singleton resources within your application. In this case, you use +map.resource+ instead of +map.resources+ and the route generation is slightly different. For example, a routing entry of + +[source, ruby] +------------------------------------------------------- +map.resource geocoder +------------------------------------------------------- + +creates seven different routes in your application: + +[grid="all"] +`----------`---------------`-----------`--------`------------------------------------------- +HTTP verb URL controller action used for +-------------------------------------------------------------------------------------------- +GET /geocoder/new Geocoders new return an HTML form for creating the new geocoder +POST /geocoder Geocoders create create the new geocoder +GET /geocoder Geocoders show display the one and only geocoder resource +GET /geocoder/edit Geocoders edit return an HTML form for editing the geocoder +PUT /geocoder Geocoders update update the one and only geocoder resource +DELETE /geocoder Geocoders destroy delete the geocoder resource +-------------------------------------------------------------------------------------------- + +NOTE: Even though the name of the resource is singular in +routes.rb+, the matching controller is still plural. + +A singular RESTful route generates an abbreviated set of helpers: + +* +new_geocoder_url+ and +new_geocoder_path+ map to the path for the new action +* +edit_geocoder_url+ and +edit_geocoder_path+ map to the path for the edit action +* +geocoder_url+ and +geocoder_path+ map to the path for the create, show, update, and destroy actions + +=== Customizing Resources + +Although the conventions of RESTful routing are likely to be sufficient for many applications, there are a number of ways to customize the way that RESTful routes work. These options include: + +* +:controller+ +* +:singular+ +* +:requirements+ +* +:conditions+ +* +:as+ +* +:path_names+ +* +:path_prefix+ +* +:name_prefix+ + +You can also add additional routes via the +:member+ and +:collection+ options, which are discussed later in this guide. + +==== Using :controller + +The +:controller+ option lets you use a controller name that is different from the public-facing resource name. For example, this routing entry: + +[source, ruby] +------------------------------------------------------- +map.resources photos, :controller => "images" +------------------------------------------------------- + +will recognize incoming URLs containing +photo+ but route the requests to the Images controller: + +[grid="all"] +`----------`---------------`-----------`--------`------------------------------------------- +HTTP verb URL controller action used for +-------------------------------------------------------------------------------------------- +GET /photos Images index display a list of all images +GET /photos/new Images new return an HTML form for creating a new image +POST /photos Images create create a new image +GET /photo/1 Images show display a specific image +GET /photo/1/edit Images edit return an HTML form for editing a image +PUT /photo/1 Images update update a specific image +DELETE /photo/1 Images destroy delete a specific image +-------------------------------------------------------------------------------------------- + +NOTE: The helpers will be generated with the name of the resource, not the name of the controller. So in this case, you'd still get +photos_path+, +photos_new_path+, and so on. + +==== Using :singular + +If for some reason Rails isn't doing what you want in converting the plural resource name to a singular name in member routes, you can override its judgment with the +:singular+ option: + +[source, ruby] +------------------------------------------------------- +map.resources :teeth, :singular => "tooth" +------------------------------------------------------- + +TIP: Depending on the other code in your application, you may prefer to add additional rules to the +Inflector+ class instead. + +==== Using :requirements + +You an use the +:requirements+ option in a RESTful route to impose a format on the implied +:id+ parameter in the singular routes. For example: + +[source, ruby] +------------------------------------------------------- +map.resources photos, :requirements => {:id => /[A-Z][A-Z][0-9]+/} +------------------------------------------------------- + +This declaration constrains the +:id+ parameter to match the supplied regular expression. So, in this case, +/photos/1+ would no longer be recognized by this route, but +/photos/RR27+ would. + +==== Using :conditions + +Conditions in Rails routing are currently used only to set the HTTP verb for individual routes. Although in theory you can set this for RESTful routes, in practice there is no good reason to do so. (You'll learn more about conditions in the discussion of old-style routing later in this guide.) + +==== Using :as + +The +:as+ option lets you override the normal naming for the actual generated paths. For example: + +[source, ruby] +------------------------------------------------------- +map.resources photos, :as => "images" +------------------------------------------------------- + +will recognize incoming URLs containing +image+ but route the requests to the Photos controller: + +[grid="all"] +`----------`---------------`-----------`--------`------------------------------------------- +HTTP verb URL controller action used for +-------------------------------------------------------------------------------------------- +GET /images Photos index display a list of all photos +GET /images/new Photos new return an HTML form for creating a new photo +POST /images Photos create create a new photo +GET /image/1 Photos show display a specific photo +GET /image/1/edit Photos edit return an HTML form for editing a photo +PUT /image/1 Photos update update a specific photo +DELETE /image/1 Photos destroy delete a specific photo +-------------------------------------------------------------------------------------------- + +NOTE: The helpers will be generated with the name of the resource, not the path name. So in this case, you'd still get +photos_path+, +photos_new_path+, and so on. + +==== Using :path_names + +The +:path_names+ option lets you override the automatically-generated "new" and "edit" segments in URLs: + +[source, ruby] +------------------------------------------------------- +map.resources photos, :path_names => { :new => 'make', :edit => 'change' } +------------------------------------------------------- + +This would cause the routing to recognize URLs such as + +------------------------------------------------------- +/photos/make +/photos/1/change +------------------------------------------------------- + +NOTE: The actual action names aren't changed by this option; the two URLs show would still route to the new and edit actions. + +TIP: If you find yourself wanting to change this option uniformly for all of your routes, you can set a default in your environment: + +[source, ruby] +------------------------------------------------------- +config.action_controller.resources_path_names = { :new => 'make', :edit => 'change' } +------------------------------------------------------- + +==== Using :path_prefix + +The +:path_prefix+ option lets you add additional parameters that will be prefixed to the recognized paths. For example, suppose each photo in your application belongs to a particular photographer. In that case, you might declare this route: + +[source, ruby] +------------------------------------------------------- +map.resources photos, :path_prefix => '/photographers/:photographer_id' +------------------------------------------------------- + +Routes recognized by this entry would include: + +------------------------------------------------------- +/photographers/1/photo/2 +/photographers/1/photos +------------------------------------------------------- + +NOTE: In most cases, it's simpler to recognize URLs of this sort by creating nested resources, as discussed in the next section. + +==== Using :name_prefix + +You can use the :name_prefix option to avoid collisions between routes. This is most useful when you have two resources with the same name that use +:path_prefix+ to map differently. For example: + +[source, ruby] +------------------------------------------------------- +map.resources photos, :path_prefix => '/photographers/:photographer_id', :name_prefix => 'photographer_' +map.resources photos, :path_prefix => '/agencies/:agency_id', :name_prefix => 'agency_' +------------------------------------------------------- + +This combination will give you route helpers such as +photographer_photos_path+ and +agency_photo_edit_path+ to use in your code. + +=== Nested Resources + +It's common to have resources that are logically children of other resources. For example, suppose your application includes these models: + +[source, ruby] +------------------------------------------------------- +class Magazine < ActiveRecord::Base + has_many :ads +end + +class Ad < ActiveRecord::Base + belongs_to :magazine +end +------------------------------------------------------- + +Each ad is logically subservient to one magazine. Nested routes allow you to capture this relationship in your routing. In this case, you might include this route declaration: + +[source, ruby] +------------------------------------------------------- +map.resources :magazines do |magazine| + magazine.resources :ads +end +------------------------------------------------------- + +In addition to the routes for magazines, this declaration will also create routes for ads, each of which requires the specification of a magazine in the URL: + +[grid="all"] +`----------`-----------------------`-----------`--------`------------------------------------------- +HTTP verb URL controller action used for +-------------------------------------------------------------------------------------------- +GET /magazines/1/ads Ads index display a list of all ads for a specific magazine +GET /magazines/1/ads/new Ads new return an HTML form for creating a new ad belonging to a specific magazine +POST /magazines/1/ads Ads create create a new photo belonging to a specific magazine +GET /magazines/1/ad/1 Ads show display a specific photo belonging to a specific magazine +GET /magazines/1/ad/1/edit Ads edit return an HTML form for editing a photo belonging to a specific magazine +PUT /magazines/1/ad/1 Ads update update a specific photo belonging to a specific magazine +DELETE /magazines/1/ad/1 Ads destroy delete a specific photo belonging to a specific magazine +-------------------------------------------------------------------------------------------- + +This will also create routing helpers such as +magazine_ads_url+ and +magazine_edit_ad_path+. + +==== Using :name_prefix + +The +:name_prefix+ option overrides the automatically-generated prefix in nested route helpers. For example, + +[source, ruby] +------------------------------------------------------- +map.resources :magazines do |magazine| + magazine.resources :ads, :name_prefix => 'periodical' +end +------------------------------------------------------- + +This will create routing helpers such as +periodical_ads_url+ and +periodical_edit_ad_path+. You can even use +:name_prefix+ to suppress the prefix entirely: + +[source, ruby] +------------------------------------------------------- +map.resources :magazines do |magazine| + magazine.resources :ads, :name_prefix => nil +end +------------------------------------------------------- + +This will create routing helpers such as +ads_url+ and +edit_ad_path+. Note that calling these will still require supplying an article id: + +[source, ruby] +------------------------------------------------------- +ads_url(@magazine) +edit_ad_path(@magazine, @ad) +------------------------------------------------------- + +==== Using :has_one and :has_many + +The +:has_one+ and +:has_many+ options provide a succinct notation for simple nested routes. Use +:has_one+ to nest a singleton resource, or +:has_many+ to nest a plural resource: + +[source, ruby] +------------------------------------------------------- +map.resources :photos, :has_one => :photographer, :has_many => [:publications, :versions] +------------------------------------------------------- + +This has the same effect as this set of declarations: + +[source, ruby] +------------------------------------------------------- +map.resources :photos do |photo| + photo.resource :photographer + photo.resources :publications + photo.resources :versions +end +------------------------------------------------------- + +==== Limits to Nesting + +You can nest resources within other nested resources if you like. For example: + +[source, ruby] +------------------------------------------------------- +map.resources :publisher do |publisher| + publisher.resources :magazine do |magazine| + magazine.resources :photos + end +end +------------------------------------------------------- + +However, without the use of +name_prefix => nil+, deeply-nested resources quickly become cumbersome. In this case, for example, the application would recognize URLs such as + +------------------------------------------------------- +/publishers/1/magazines/2/photos/3 +------------------------------------------------------- + +The corresponding route helper would be +publisher_magazine_photo_url+, requiring you to specify objects at all three levels. + +==== Shallow Nesting + +The +:shallow+ option provides an elegant solution to the difficulties of deeply-nested routes. If you specify this option at any level of routing, then paths for nested resources which reference a specific member (that is, those with an +:id+ parameter) will not use the parent path prefix or name prefix. To see what this means, consider this set of routes: + +[source, ruby] +------------------------------------------------------- +map.resources :publisher, :shallow => true do |publisher| + publisher.resources :magazine do |magazine| + magazine.resources :photos + end +end +------------------------------------------------------- + +This will enable recognition of (among others) these routes: + +------------------------------------------------------- +/publishers/1 ==> publisher_path(1) +/publishers/1/magazines ==> publisher_magazines_path(1) +/magazines/2 ==> magazine_path(2) +/magazines/2/photos ==> magazines_photos_path(2) +/photos/3 ==> photo_path(3) +------------------------------------------------------- + +=== Adding More RESTful Actions + +You are not limited to the seven routes that RESTful routing creates by default. If you like, you may add additional member routes (those which apply to a single instance of the resource), additional new routes (those that apply to creating a new resource), or additional collection routes (those which apply to the collection of resources as a whole). + +==== Adding Member Routes + +To add a member route, use the +:member+ option: + +[source, ruby] +------------------------------------------------------- +map.resources :photos, :member => { :preview => :get } +------------------------------------------------------- + +This will enable Rails to recognize URLs such as +/photos/1/preview+ using the GET HTTP verb, and route them to the preview action of the Photos controller. It will also create a +preview_photo+ route helper. + +Within the hash of member routes, each route name specifies the HTTP verb that it will recognize. You can use +:get+, +:put+, +:post+, +:delete+, or +:any+ here. + +==== Adding Collection Routes + +To add a collection route, use the +:collection+ option: + +[source, ruby] +------------------------------------------------------- +map.resources :photos, :collection => { :search => :get } +------------------------------------------------------- + +This will enable Rails to recognize URLs such as +/photos/search+ using the GET HTTP verb, and route them to the search action of the Photos controller. It will also create a +search_photos+ route helper. + +==== Adding New Routes + +To add a new route (one that creates a new resource), use the +:new+ option: + +[source, ruby] +------------------------------------------------------- +map.resources :photos, :new => { :upload => :post } +------------------------------------------------------- + +This will enable Rails to recognize URLs such as +/photos/upload+ using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create a +upload_photos+ route helper. + +TIP: If you want to redefine the verbs accepted by one of the standard actions, you can do so by explicitly mapping that action. For example: + +[source, ruby] +------------------------------------------------------- +map.resources :photos, :new => { :new => :any } +------------------------------------------------------- + +This will allow the new action to be invoked by any request to +photos/new+, no matter what HTTP verb you use. + +==== A Note of Caution + +If you find yourself adding many extra actions to a RESTful route, it's time to stop and ask yourself whether you're disguising the presence of another resource that would be better split off on its own. When the +:member+ and +:collection+ hashes become a dumping-ground, RESTful routes lose the advantage of easy readability that is one of their strongest points. + +== Regular (Old-Style) Routes + +Before there was RESTful routing, there was simple Rails routing - a way to map URLs to controllers and actions. With regular routing, you don't get the masses of routes automatically generated by RESTful routing. Instead, you must set up each route within your application separately. + +While RESTful routing has become the Rails standard, there are still plenty of places where the simpler regular routing works fine. You can even mix the two styles within a single application. + +=== Bound Parameters + +When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. Two of these symbols are special: +:controller+ maps to the name of a controller in your application, and +:action+ maps to the name of an action within that controller. For example, consider one of the default Rails routes: + +[source, ruby] +------------------------------------------------------- +map.connect ':controller/:action/:id' +------------------------------------------------------- + +If an incoming request of +/photos/show/1+ is processed by this route (because it hasn't matched any previous route in the file), then the result will be to invoke the +show+ action of the +Photos+ controller, and to make the final parameter (1) available as +params[:id]+. + +=== Wildcard Components + +You can set up as many wildcard symbols within a regular route as you like. Anything other than +:controller+ or +:action+ will be available to the matching action as part of the params hash. So, if you set up this route: + +[source, ruby] +------------------------------------------------------- +map.connect ':controller/:action/:id/:userid:' +------------------------------------------------------- + +An incoming URL of +/photos/show/1/2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be set to 2. + +=== Static Text + +You can specify static text when creating a route. In this case, the static text is used only for matching the incoming requests: + +[source, ruby] +------------------------------------------------------- +map.connect ':controller/:action/:id/with_user/:userid:' +------------------------------------------------------- + +This route would respond to URLs such as +/photos/show/1/with_user/2+. + +=== Querystring Parameters + +Rails routing automatically picks up querystring parameters and makes them available in the +params+ hash. For example, with this route: + +[source, ruby] +------------------------------------------------------- +map.connect ':controller/:action/:id' +------------------------------------------------------- + +An incoming URL of +/photos/show/1?user_id=2+ will be dispatched to the +show+ action of the +Photos+ controller. +params[:id]+ will be set to 1, and +params[:user_id]+ will be equal to 2. + +=== Defining Defaults + +You do not need to explicitly use the +:controller+ and +:action+ symbols within a route. You can supply defaults for these two parameters in a hash: + +[source, ruby] +------------------------------------------------------- +map.connect 'photo/:id', :controller => 'photos', :action => 'show' +------------------------------------------------------- + +With this route, an incoming URL of +/photos/12+ would be dispatched to the +show+ action within the +Photos+ controller. + +=== Named Routes + +Regular routes need not use the +connect+ method. You can use any other name here to create a _named route_. For example, + +[source, ruby] +------------------------------------------------------- +map.logout '/logout', :controller => 'sessions', :action => 'destroy' +------------------------------------------------------- + +This will do two things. First, requests to +/logout+ will be sent to the +destroy+ method of the +Sessions+ controller. Second, Rails will maintain the +logout_path+ and +logout_url+ helpers for use within your code. + +=== Route Requirements + +You can use the +:requirements+ option to enforce a format for any parameter in a route: + +[source, ruby] +------------------------------------------------------- +map.connect 'photo/:id', :controller => 'photos', :action => 'show', + :requirements => { :id => /[A-Z]\d{5}/ } +------------------------------------------------------- + +This route would respond to URLs such as +/photo/A12345+. You can more succinctly express the same route this way: + +[source, ruby] +------------------------------------------------------- +map.connect 'photo/:id', :controller => 'photos', :action => 'show', + :id => /[A-Z]\d{5}/ +------------------------------------------------------- + +=== Route Conditions + +Route conditions (introduced with the +:conditions+ option) are designed to implement restrictions on routes. Currently, the only supported restriction is +:method+: + +[source, ruby] +------------------------------------------------------- +map.connect 'photo/:id', :controller => 'photos', :action => 'show', + :conditions => { :method => :get } +------------------------------------------------------- + +As with conditions in RESTful routes, you can specify +:get+, +:post+, +:put+, +:delete+, or +:any+ for the acceptable method. + +=== Route Globbing + +Route globbing is a way to specify that a particular parameter (which must be the last parameter in the route) should engulf all the remaining parts of a route. For example + +[source, ruby] +------------------------------------------------------- +map.connect 'photo/*other', :controller => 'photos', :action => 'unknown', +------------------------------------------------------- + +This route would match +photo/12+ or +/photo/long/path/to/12+ equally well, creating an array of path segments as the value of +params[:other]+. + +=== Route Options + +You can use +:with_options+ to simplify defining groups of similar routes: + +[source, ruby] +------------------------------------------------------- +map.with_options :controller => 'photo' do |photo| + photo.list '', :action => 'index' + photo.delete ':id/delete', :action => 'delete' + photo.edit ':id/edit', :action => 'edit' +end +------------------------------------------------------- + +The importance of +map.with_options+ has declined with the introduction of RESTful routes. + +== Formats and respond_to + +There's one more way in which routing can do different things depending on differences in the incoming HTTP request: by issuing a response that corresponds to what the request specifies that it will accept. In Rails routing, you can control this with the special +:format+ parameter in the route. + +For instance, consider the second of the default routes in the boilerplate +routes.rb+ file: + +[source, ruby] +------------------------------------------------------- +map.connect ':controller/:action/:id.:format' +------------------------------------------------------- + +This route matches requests such as +/photo/new/1.xml+ or +/photo/show/2.rss+. Within the appropriate action code, you can issue different responses depending on the requested format: + +[source, ruby] +------------------------------------------------------- +respond_to do |format| + format.html # return the default template for HTML + format.xml { render :xml => @photo.to_xml } +end +------------------------------------------------------- + +=== Specifying the Format with an HTTP Header + +If there is no +:format+ parameter in the route, Rails will automatically look at the HTTP Accept header to determine the desired format. + +=== Recognized MIME types + +By default, Rails recognizes +html+, +text+, +json+, +csv+, +xml+, +rss+, +atom+, and +yaml+ as acceptable response types. If you need types beyond this, you can register them in your environment: + +[source, ruby] +------------------------------------------------------- +Mime::Type.register "image/jpg", :jpg +------------------------------------------------------- + +== The Default Routes + +When you create a new Rails application, +routes.rb+ is initialized with two default routes: + +[source, ruby] +------------------------------------------------------- +map.connect ':controller/:action/:id' +map.connect ':controller/:action/:id.:format' +------------------------------------------------------- + +These routes provide reasonable defaults for many URLs, if you're not using RESTful routing. + +NOTE: The default routes will make every action of every controller in your application accessible to GET requests. If you've designed your application to make consistent use of RESTful and named routes, you should comment out the default routes. + +== The Empty Route + +Don't confuse the default routes with the empty route. The empty route has one specific purpose: to route requests that come in to the root of the web site. For example, if your site is example.com, then requests to +http://example.com+ or +http://example.com/+ will be handled by the empty route. + +=== Using map.root + +The preferred way to set up the empty route is with the +map.root+ command: + +[source, ruby] +------------------------------------------------------- +map.root :controller => "pages", :action => "main" +------------------------------------------------------- + +The use of the +root+ method tells Rails that this route applies to requests for the root of the site. + +For better readability, you can specify an already-created route in your call to +map.root+: + +[source, ruby] +------------------------------------------------------- +map.index :controller => "pages", :action => "main" +map.root :index +------------------------------------------------------- + +Because of the top-down processing of the file, the named route must be specified _before_ the call to +map.route+. + +=== Connecting the Empty String + +You can also specify an empty route by explicitly connecting the empty string: + +[source, ruby] +------------------------------------------------------- +map.connect '', :controller => "pages", :action => "main" +------------------------------------------------------- + +TIP: If the empty route does not seem to be working in your application, make sure that you have deleted the file +public/index.html+ from your Rails tree. + +== Dumping Routes with rake + +If you want a complete list of all of the available routes in your application, run the +rake routes+ command. This will dump all of your routes to the console, in the same order that they appear in +routes.rb+. For each route, you'll see: + +* The route name (if any) +* The HTTP verb used (if the route doesn't respond to all verbs) +* The URL pattern +* The routing parameters that will be generated by this URL + +For example, here's a small section of the +rake routes+ output for a RESTful route: + +------------------------------------------------------------------------------------------------------- + users GET /users {:controller=>"users", :action=>"index"} +formatted_users GET /users.:format {:controller=>"users", :action=>"index"} + POST /users {:controller=>"users", :action=>"create"} + POST /users.:format {:controller=>"users", :action=>"create"} +------------------------------------------------------------------------------------------------------- + +TIP: You'll find that the output from +rake routes+ is much more readable if you widen your terminal window until the output lines don't wrap. -- cgit v1.2.3 From a1eb4e11c2cccb91483fa15f1a1a0b2ae518d2cf Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 31 Aug 2008 13:15:26 -0700 Subject: Get rid of 'Object#send!'. It was originally added because it's in Ruby 1.9, but it has since been removed from 1.9. Signed-off-by: Jeremy Kemper Conflicts: actionpack/test/controller/layout_test.rb --- railties/lib/initializer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 008f1de8fa..0e2c7b827f 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -488,7 +488,7 @@ Run `rake gems:install` to install the missing gems. # If assigned value cannot be matched to a TimeZone, an exception will be raised. def initialize_time_zone if configuration.time_zone - zone_default = Time.send!(:get_zone, configuration.time_zone) + zone_default = Time.__send__(:get_zone, configuration.time_zone) unless zone_default raise %{Value assigned to config.time_zone not recognized. Run "rake -D time" for a list of tasks for finding appropriate time zone names.} end -- cgit v1.2.3 From b36d000975824518d64bcdbd731287c25e9af604 Mon Sep 17 00:00:00 2001 From: Nigel Ramsay Date: Wed, 3 Sep 2008 15:57:06 +1200 Subject: Inline help text was incorrectly telling user to uncomment line to use default local time. User should comment the line to use default local time. Signed-off-by: Michael Koziarski [#960 state:committed] --- railties/environments/environment.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'railties') diff --git a/railties/environments/environment.rb b/railties/environments/environment.rb index a85ade371b..2140dfe971 100644 --- a/railties/environments/environment.rb +++ b/railties/environments/environment.rb @@ -40,7 +40,7 @@ Rails::Initializer.run do |config| # Make Time.zone default to the specified zone, and make Active Record store time values # in the database in UTC, and return them converted to the specified local zone. - # Run "rake -D time" for a list of tasks for finding time zone names. Uncomment to use default local time. + # Run "rake -D time" for a list of tasks for finding time zone names. Comment line to use default local time. config.time_zone = 'UTC' # Your secret key for verifying cookie session data integrity. -- cgit v1.2.3