Merge branch 'master' of github.com:lifo/docrails

This commit is contained in:
Neeraj Singh
2010-07-08 10:09:39 -04:00
83 changed files with 861 additions and 551 deletions

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
*.gem
pkg
.bundle
Gemfile.lock
debug.log
doc/rdoc
activemodel/doc

View File

@@ -737,13 +737,13 @@ module ActionMailer #:nodoc:
raise "You can no longer call ActionMailer::Base.default_url_options " \
"directly. You need to set config.action_mailer.default_url_options. " \
"If you are using ActionMailer standalone, you need to include the " \
"url_helpers of a router directly."
"routing url_helpers directly."
end
end
# This module will complain if the user tries to set default_url_options
# directly instead of through the config object. In Action Mailer's Railtie,
# we include the url_helpers of the router, which will override this module
# we include the router's url_helpers, which will override this module.
extend DeprecatedUrlOptions
ActiveSupport.run_load_hooks(:action_mailer, self)

View File

@@ -1,5 +1,6 @@
# encoding: utf-8
require 'abstract_unit'
require 'active_support/time'
class BaseTest < ActiveSupport::TestCase
# TODO Add some tests for implicity layout render and url helpers

View File

@@ -28,7 +28,7 @@ class UrlTestMailer < ActionMailer::Base
end
end
class ActionMailerUrlTest < Test::Unit::TestCase
class ActionMailerUrlTest < ActionMailer::TestCase
def encode( text, charset="UTF-8" )
quoted_printable( text, charset )
@@ -57,10 +57,12 @@ class ActionMailerUrlTest < Test::Unit::TestCase
def test_signed_up_with_url
UrlTestMailer.delivery_method = :test
AppRoutes.draw do |map|
map.connect ':controller/:action/:id'
map.welcome 'welcome', :controller=>"foo", :action=>"bar"
assert_deprecated do
AppRoutes.draw do |map|
map.connect ':controller/:action/:id'
map.welcome 'welcome', :controller=>"foo", :action=>"bar"
end
end
expected = new_mail

View File

@@ -27,5 +27,5 @@ Gem::Specification.new do |s|
s.add_dependency('rack-test', '~> 0.5.4')
#s.add_dependency('rack-mount', '~> 0.6.6')
s.add_dependency('tzinfo', '~> 0.3.16')
s.add_dependency('erubis', '~> 2.6.5')
s.add_dependency('erubis', '~> 2.6.6')
end

View File

@@ -50,8 +50,8 @@ module AbstractController
if controller.respond_to?(:_helpers)
include controller._helpers
if controller.respond_to?(:_router)
include controller._router.url_helpers
if controller.respond_to?(:_routes)
include controller._routes.url_helpers
end
# TODO: Fix RJS to not require this

View File

@@ -60,17 +60,11 @@ module ActionController
include ActionController::Compatibility
def self.inherited(klass)
::ActionController::Base.subclasses << klass.to_s
super
klass.helper :all
end
def self.subclasses
@subclasses ||= []
end
config_accessor :asset_host, :asset_path
ActiveSupport.run_load_hooks(:action_controller, self)
end
end

View File

@@ -12,17 +12,17 @@ module ActionController
).merge(:script_name => request.script_name)
end
def _router
raise "In order to use #url_for, you must include the helpers of a particular " \
"router. For instance, `include Rails.application.routes.url_helpers"
def _routes
raise "In order to use #url_for, you must include routing helpers explicitly. " \
"For instance, `include Rails.application.routes.url_helpers"
end
module ClassMethods
def action_methods
@action_methods ||= begin
super - _router.named_routes.helper_names
super - _routes.named_routes.helper_names
end
end
end
end
end
end

View File

@@ -2,7 +2,6 @@ require "rails"
require "action_controller"
require "action_dispatch/railtie"
require "action_view/railtie"
require "active_support/core_ext/class/subclasses"
require "active_support/deprecation/proxy_wrappers"
require "active_support/deprecation"

View File

@@ -64,6 +64,11 @@ module ActionDispatch
super(key.to_s, value)
end
def clear
load_for_write!
super
end
def to_hash
load_for_read!
h = {}.replace(self)

View File

@@ -31,7 +31,7 @@ module ActionDispatch
# Think of creating routes as drawing a map for your requests. The map tells
# them where to go based on some predefined pattern:
#
# AppName::Applications.routes.draw do |map|
# AppName::Application.routes.draw do |map|
# Pattern 1 tells some request to go to one place
# Pattern 2 tell them to go to another
# ...
@@ -62,7 +62,7 @@ module ActionDispatch
#
# redirect_to show_item_path(:id => 25)
#
# Use <tt>root</tt> as a shorthand to name a route for the root path "".
# Use <tt>root</tt> as a shorthand to name a route for the root path "/".
#
# # In routes.rb
# root :to => 'blogs#index'
@@ -72,7 +72,7 @@ module ActionDispatch
#
# # and provide these named routes
# root_url # => 'http://www.example.com/'
# root_path # => ''
# root_path # => '/'
#
# Note: when using +controller+, the route is simply named after the
# method you call on the block parameter rather than map.
@@ -91,9 +91,7 @@ module ActionDispatch
#
# Routes can generate pretty URLs. For example:
#
# match '/articles/:year/:month/:day', :constraints => {
# :controller => 'articles',
# :action => 'find_by_date',
# match '/articles/:year/:month/:day' => 'articles#find_by_id', :constraints => {
# :year => /\d{4}/,
# :month => /\d{1,2}/,
# :day => /\d{1,2}/
@@ -167,7 +165,7 @@ module ActionDispatch
#
# You can reload routes if you feel you must:
#
# Rails::Application.reload_routes!
# Rails.application.reload_routes!
#
# This will clear all named routes and reload routes.rb if the file has been modified from
# last load. To absolutely force reloading, use <tt>reload!</tt>.

View File

@@ -19,9 +19,9 @@ module ActionDispatch
def in_memory_controller_namespaces
namespaces = Set.new
ActionController::Base.subclasses.each do |klass|
controller_name = klass.underscore
namespaces << controller_name.split('/')[0...-1].join('/')
ActionController::Base.descendants.each do |klass|
next if klass.anonymous?
namespaces << klass.name.underscore.split('/')[0...-1].join('/')
end
namespaces.delete('')
namespaces
@@ -31,7 +31,7 @@ module ActionDispatch
class DeprecatedMapper #:nodoc:
def initialize(set) #:nodoc:
ActiveSupport::Deprecation.warn "You are using the old router DSL which will be removed in Rails 3.1. " <<
"Please check how to update your router file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
"Please check how to update your routes file at: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/"
@set = set
end

View File

@@ -13,6 +13,8 @@ module ActionDispatch
end
end
attr_reader :app
def initialize(app, constraints, request)
@app, @constraints, @request = app, constraints, request
end
@@ -63,6 +65,16 @@ module ActionDispatch
end
end
if path.match(':controller')
raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module]
# Add a default constraint for :controller path segments that matches namespaced
# controllers with default routes like :controller/:action/:id(.:format), e.g:
# GET /admin/products/show/1
# => { :controller => 'admin/products', :action => 'show', :id => '1' }
options.reverse_merge!(:controller => /.+?/)
end
path = normalize_path(path)
path_without_format = path.sub(/\(\.:format\)$/, '')
@@ -133,8 +145,11 @@ module ActionDispatch
defaults[:controller] ||= default_controller
defaults[:action] ||= default_action
defaults.delete(:controller) if defaults[:controller].blank?
defaults.delete(:action) if defaults[:action].blank?
defaults.delete(:controller) if defaults[:controller].blank? || defaults[:controller].is_a?(Regexp)
defaults.delete(:action) if defaults[:action].blank? || defaults[:action].is_a?(Regexp)
defaults[:controller] = defaults[:controller].to_s if defaults.key?(:controller)
defaults[:action] = defaults[:action].to_s if defaults.key?(:action)
if defaults[:controller].blank? && segment_keys.exclude?("controller")
raise ArgumentError, "missing :controller"
@@ -183,15 +198,15 @@ module ActionDispatch
def default_controller
if @options[:controller]
@options[:controller].to_s
@options[:controller]
elsif @scope[:controller]
@scope[:controller].to_s
@scope[:controller]
end
end
def default_action
if @options[:action]
@options[:action].to_s
@options[:action]
end
end
end
@@ -431,12 +446,16 @@ module ActionDispatch
end
def merge_options_scope(parent, child)
(parent || {}).merge(child)
(parent || {}).except(*override_keys(child)).merge(child)
end
def merge_shallow_scope(parent, child)
child ? true : false
end
def override_keys(child)
child.key?(:only) || child.key?(:except) ? [:only, :except] : []
end
end
module Resources
@@ -444,7 +463,7 @@ module ActionDispatch
# a path appended since they fit properly in their scope level.
VALID_ON_OPTIONS = [:new, :collection, :member]
CANONICAL_ACTIONS = [:index, :create, :new, :show, :update, :destroy]
MERGE_FROM_SCOPE_OPTIONS = [:shallow, :constraints]
RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except]
class Resource #:nodoc:
DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit]
@@ -464,9 +483,9 @@ module ActionDispatch
end
def actions
if only = options[:only]
if only = @options[:only]
Array(only).map(&:to_sym)
elsif except = options[:except]
elsif except = @options[:except]
default_actions - Array(except).map(&:to_sym)
else
default_actions
@@ -489,68 +508,31 @@ module ActionDispatch
singular
end
alias_method :nested_name, :member_name
# Checks for uncountable plurals, and appends "_index" if they're.
def collection_name
singular == plural ? "#{plural}_index" : plural
end
def shallow?
options[:shallow] ? true : false
end
def constraints
options[:constraints] || {}
end
def id_constraint?
options[:id] && options[:id].is_a?(Regexp) || constraints[:id] && constraints[:id].is_a?(Regexp)
end
def id_constraint
options[:id] || constraints[:id]
end
def collection_options
(options || {}).dup.tap do |opts|
opts.delete(:id)
opts[:constraints] = options[:constraints].dup if options[:constraints]
opts[:constraints].delete(:id) if options[:constraints].is_a?(Hash)
end
end
def nested_path
"#{path}/:#{singular}_id"
end
def nested_options
{}.tap do |opts|
opts[:as] = member_name
opts["#{singular}_id".to_sym] = id_constraint if id_constraint?
opts[:options] = { :shallow => shallow? } unless options[:shallow].nil?
end
end
def resource_scope
[{ :controller => controller }]
{ :controller => controller }
end
def collection_scope
[path, collection_options]
path
end
def member_scope
["#{path}/:id", options]
"#{path}/:id"
end
def new_scope(new_path)
["#{path}/#{new_path}"]
"#{path}/#{new_path}"
end
def nested_scope
[nested_path, nested_options]
"#{path}/:#{singular}_id"
end
end
class SingletonResource < Resource #:nodoc:
@@ -559,7 +541,7 @@ module ActionDispatch
def initialize(entities, options)
@name = entities.to_s
@path = options.delete(:path) || @name
@controller = (options.delete(:controller) || @name.to_s.pluralize).to_s
@controller = (options.delete(:controller) || plural).to_s
@as = options.delete(:as)
@options = options
end
@@ -569,24 +551,10 @@ module ActionDispatch
end
alias :collection_name :member_name
def nested_path
def member_scope
path
end
def nested_options
{}.tap do |opts|
opts[:as] = member_name
opts[:options] = { :shallow => shallow? } unless @options[:shallow].nil?
end
end
def shallow?
false
end
def member_scope
[path, options]
end
alias :nested_scope :member_scope
end
def initialize(*args) #:nodoc:
@@ -693,18 +661,18 @@ module ActionDispatch
end
with_scope_level(:nested) do
if parent_resource.shallow?
if shallow?
with_exclusive_scope do
if @scope[:shallow_path].blank?
scope(*parent_resource.nested_scope) { yield }
scope(parent_resource.nested_scope, nested_options) { yield }
else
scope(@scope[:shallow_path], :as => @scope[:shallow_prefix]) do
scope(*parent_resource.nested_scope) { yield }
scope(parent_resource.nested_scope, nested_options) { yield }
end
end
end
else
scope(*parent_resource.nested_scope) { yield }
scope(parent_resource.nested_scope, nested_options) { yield }
end
end
end
@@ -723,6 +691,10 @@ module ActionDispatch
end
end
def shallow?
parent_resource.instance_of?(Resource) && @scope[:shallow]
end
def match(*args)
options = args.extract_options!.dup
options[:anchor] = true unless options.key?(:anchor)
@@ -800,16 +772,17 @@ module ActionDispatch
return true
end
if path_names = options.delete(:path_names)
scope(:path_names => path_names) do
scope_options = options.slice!(*RESOURCE_OPTIONS)
unless scope_options.empty?
scope(scope_options) do
send(method, resources.pop, options, &block)
end
return true
end
scope_options = @scope.slice(*MERGE_FROM_SCOPE_OPTIONS).delete_if{ |k,v| v.blank? }
options.reverse_merge!(scope_options) unless scope_options.empty?
options.reverse_merge!(@scope[:options]) unless @scope[:options].blank?
unless action_options?(options)
options.merge!(scope_action_options) if scope_action_options?
end
if resource_scope?
nested do
@@ -821,6 +794,18 @@ module ActionDispatch
false
end
def action_options?(options)
options[:only] || options[:except]
end
def scope_action_options?
@scope[:options].is_a?(Hash) && (@scope[:options][:only] || @scope[:options][:except])
end
def scope_action_options
@scope[:options].slice(:only, :except)
end
def resource_scope?
[:resource, :resources].include?(@scope[:scope_level])
end
@@ -853,7 +838,7 @@ module ActionDispatch
def resource_scope(resource)
with_scope_level(resource.is_a?(SingletonResource) ? :resource : :resources, resource) do
scope(*parent_resource.resource_scope) do
scope(parent_resource.resource_scope) do
yield
end
end
@@ -861,7 +846,7 @@ module ActionDispatch
def new_scope
with_scope_level(:new) do
scope(*parent_resource.new_scope(action_path(:new))) do
scope(parent_resource.new_scope(action_path(:new))) do
yield
end
end
@@ -869,7 +854,7 @@ module ActionDispatch
def collection_scope
with_scope_level(:collection) do
scope(*parent_resource.collection_scope) do
scope(parent_resource.collection_scope) do
yield
end
end
@@ -877,18 +862,33 @@ module ActionDispatch
def member_scope
with_scope_level(:member) do
scope(*parent_resource.member_scope) do
scope(parent_resource.member_scope) do
yield
end
end
end
def nested_options
{}.tap do |options|
options[:as] = parent_resource.member_name
options[:constraints] = { "#{parent_resource.singular}_id".to_sym => id_constraint } if id_constraint?
end
end
def id_constraint?
@scope[:id].is_a?(Regexp) || (@scope[:constraints] && @scope[:constraints][:id].is_a?(Regexp))
end
def id_constraint
@scope[:id] || @scope[:constraints][:id]
end
def canonical_action?(action, flag)
flag && CANONICAL_ACTIONS.include?(action)
end
def shallow_scoping?
parent_resource && parent_resource.shallow? && @scope[:scope_level] == :member
shallow? && @scope[:scope_level] == :member
end
def path_for_action(action, path)
@@ -932,7 +932,7 @@ module ActionDispatch
collection_name = parent_resource.collection_name
member_name = parent_resource.member_name
name_prefix = "#{name_prefix}_" if name_prefix.present?
end
end
case @scope[:scope_level]
when :collection

View File

@@ -26,7 +26,7 @@ module ActionDispatch
return [404, {'X-Cascade' => 'pass'}, []]
end
controller.action(params[:action]).call(env)
dispatch(controller, params[:action], env)
end
def prepare_params!(params)
@@ -34,29 +34,43 @@ module ActionDispatch
split_glob_param!(params) if @glob_param
end
def controller(params, raise_error=true)
# If this is a default_controller (i.e. a controller specified by the user)
# we should raise an error in case it's not found, because it usually means
# an user error. However, if the controller was retrieved through a dynamic
# segment, as in :controller(/:action), we should simply return nil and
# delegate the control back to Rack cascade. Besides, if this is not a default
# controller, it means we should respect the @scope[:module] parameter.
def controller(params, default_controller=true)
if params && params.key?(:controller)
controller_param = params[:controller]
unless controller = @controllers[controller_param]
controller_name = "#{controller_param.camelize}Controller"
controller = @controllers[controller_param] =
ActiveSupport::Dependencies.ref(controller_name)
end
controller.get
controller_reference(controller_param)
end
rescue NameError => e
raise ActionController::RoutingError, e.message, e.backtrace if raise_error
raise ActionController::RoutingError, e.message, e.backtrace if default_controller
end
private
def merge_default_action!(params)
params[:action] ||= 'index'
end
private
def split_glob_param!(params)
params[@glob_param] = params[@glob_param].split('/').map { |v| URI.unescape(v) }
def controller_reference(controller_param)
unless controller = @controllers[controller_param]
controller_name = "#{controller_param.camelize}Controller"
controller = @controllers[controller_param] =
ActiveSupport::Dependencies.ref(controller_name)
end
controller.get
end
def dispatch(controller, action, env)
controller.action(action).call(env)
end
def merge_default_action!(params)
params[:action] ||= 'index'
end
def split_glob_param!(params)
params[@glob_param] = params[@glob_param].split('/').map { |v| URI.unescape(v) }
end
end
# A NamedRouteCollection instance is a collection of named routes, and also
@@ -268,10 +282,10 @@ module ActionDispatch
# Yes plz - JP
included do
routes.install_helpers(self)
singleton_class.send(:define_method, :_router) { routes }
singleton_class.send(:define_method, :_routes) { routes }
end
define_method(:_router) { routes }
define_method(:_routes) { routes }
end
helpers
@@ -302,7 +316,6 @@ module ActionDispatch
@extras = extras
normalize_options!
normalize_recall!
normalize_controller_action_id!
use_relative_controller!
controller.sub!(%r{^/}, '') if controller
@@ -319,7 +332,11 @@ module ActionDispatch
def use_recall_for(key)
if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
@options[key] = @recall.delete(key)
if named_route_exists?
@options[key] = @recall.delete(key) if segment_keys.include?(key)
else
@options[key] = @recall.delete(key)
end
end
end
@@ -343,15 +360,6 @@ module ActionDispatch
end
end
def normalize_recall!
# If the target route is not a standard route then remove controller and action
# from the options otherwise they will appear in the url parameters
if block_or_proc_route_target?
recall.delete(:controller) unless segment_keys.include?(:controller)
recall.delete(:action) unless segment_keys.include?(:action)
end
end
# This pulls :controller, :action, and :id out of the recall.
# The recall key is only used if there is no key in the options
# or if the key in the options is identical. If any of
@@ -424,12 +432,8 @@ module ActionDispatch
named_route && set.named_routes[named_route]
end
def block_or_proc_route_target?
named_route_exists? && !set.named_routes[named_route].app.is_a?(Dispatcher)
end
def segment_keys
named_route_exists? ? set.named_routes[named_route].segment_keys : []
set.named_routes[named_route].segment_keys
end
end
@@ -474,7 +478,7 @@ module ActionDispatch
path_options = yield(path_options) if block_given?
path = generate(path_options, path_segments || {})
# ROUTES TODO: This can be called directly, so script_name should probably be set in the router
# ROUTES TODO: This can be called directly, so script_name should probably be set in the routes
rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
rewritten_url << "##{Rack::Mount::Utils.escape_uri(options[:anchor].to_param.to_s)}" if options[:anchor]
@@ -506,6 +510,8 @@ module ActionDispatch
end
dispatcher = route.app
dispatcher = dispatcher.app while dispatcher.is_a?(Mapper::Constraints)
if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
dispatcher.prepare_params!(params)
return params

View File

@@ -128,7 +128,7 @@ module ActionDispatch
when String
options
when nil, Hash
_router.url_for(url_options.merge((options || {}).symbolize_keys))
_routes.url_for(url_options.merge((options || {}).symbolize_keys))
else
polymorphic_url(options)
end

View File

@@ -173,7 +173,7 @@ module ActionView #:nodoc:
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
class_attribute :helpers
class_attribute :_router
class_attribute :_routes
class << self
delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'

View File

@@ -12,7 +12,7 @@ module ActionView
# and controllers.
module UrlHelper
# This helper may be included in any class that includes the
# URL helpers of a router (router.url_helpers). Some methods
# URL helpers of a routes (routes.url_helpers). Some methods
# provided here will only work in the4 context of a request
# (link_to_unless_current, for instance), which must be provided
# as a method called #request on the context.

View File

@@ -151,7 +151,7 @@ module ActionView
@view ||= begin
view = ActionView::Base.new(ActionController::Base.view_paths, {}, @controller)
view.singleton_class.send :include, _helpers
view.singleton_class.send :include, @controller._router.url_helpers
view.singleton_class.send :include, @controller._routes.url_helpers
view.singleton_class.send :delegate, :alert, :notice, :to => "request.flash"
view.extend(Locals)
view.locals = self.locals
@@ -192,13 +192,13 @@ module ActionView
end
end
def _router
@controller._router if @controller.respond_to?(:_router)
def _routes
@controller._routes if @controller.respond_to?(:_routes)
end
def method_missing(selector, *args)
if @controller.respond_to?(:_router) &&
@controller._router.named_routes.helpers.include?(selector)
if @controller.respond_to?(:_routes) &&
@controller._routes.named_routes.helpers.include?(selector)
@controller.__send__(selector, *args)
else
super

View File

@@ -41,7 +41,7 @@ require 'pp' # require 'pp' early to prevent hidden_methods from not picking up
module Rails
end
# Monkey patch the old router initialization to be silenced.
# Monkey patch the old routes initialization to be silenced.
class ActionDispatch::Routing::DeprecatedMapper
def initialize_with_silencer(*args)
ActiveSupport::Deprecation.silence { initialize_without_silencer(*args) }
@@ -182,13 +182,16 @@ class ActionController::IntegrationTest < ActiveSupport::TestCase
self.app = build_app
class StubDispatcher
def self.new(*args)
lambda { |env|
params = env['action_dispatch.request.path_parameters']
controller, action = params[:controller], params[:action]
[200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]]
}
# Stub Rails dispatcher so it does not get controller references and
# simply return the controller#action as Rack::Body.
class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher
protected
def controller_reference(controller_param)
controller_param
end
def dispatch(controller, action, env)
[200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]]
end
end
@@ -275,9 +278,9 @@ end
class ActionController::Base
def self.test_routes(&block)
router = ActionDispatch::Routing::RouteSet.new
router.draw(&block)
include router.url_helpers
routes = ActionDispatch::Routing::RouteSet.new
routes.draw(&block)
include routes.url_helpers
end
end

View File

@@ -36,6 +36,15 @@ class RespondToController < ActionController::Base
type.all { render :text => "Nothing" }
end
end
def json_xml_or_html
respond_to do |type|
type.json { render :text => 'JSON' }
type.xml { render :xml => 'XML' }
type.html { render :text => 'HTML' }
end
end
def forced_xml
request.format = :xml
@@ -364,6 +373,17 @@ class RespondToControllerTest < ActionController::TestCase
get :handle_any_any
assert_equal 'Whatever you ask for, I got it', @response.body
end
def test_browser_check_with_any_any
@request.accept = "application/json, application/xml"
get :json_xml_or_html
assert_equal 'JSON', @response.body
@request.accept = "application/json, application/xml, */*"
get :json_xml_or_html
assert_equal 'HTML', @response.body
end
def test_rjs_type_skips_layout
@request.accept = "text/javascript"

View File

@@ -120,7 +120,7 @@ module AbstractController
def test_relative_url_root_is_respected
# ROUTES TODO: Tests should not have to pass :relative_url_root directly. This
# should probably come from the router.
# should probably come from routes.
add_host!
assert_equal('https://www.basecamphq.com/subdir/c/a/i',

View File

@@ -45,7 +45,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
match 'account/logout' => redirect("/logout"), :as => :logout_redirect
match 'account/login', :to => redirect("/login")
match 'account/overview'
constraints(lambda { |req| true }) do
match 'account/overview'
end
match '/account/nested/overview'
match 'sign_in' => "sessions#new"
@@ -298,8 +301,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resource :dashboard, :constraints => { :ip => /192\.168\.1\.\d{1,3}/ }
resource :token, :module => :api
scope :module => :api do
resource :token
resources :errors, :shallow => true do
resources :notices
end
@@ -310,11 +313,6 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
match '/' => 'mes#index'
end
namespace :private do
root :to => redirect('/private/index')
match "index", :to => 'private#index'
end
get "(/:username)/followers" => "followers#index"
get "/groups(/user/:username)" => "groups#index"
get "(/user/:username)/photos" => "photos#index"
@@ -325,7 +323,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
match "whatever/:controller(/:action(/:id))"
match "whatever/:controller(/:action(/:id))", :id => /\d+/
resource :profile do
get :settings
@@ -348,6 +346,63 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
namespace :private do
root :to => redirect('/private/index')
match "index", :to => 'private#index'
end
scope :only => [:index, :show] do
namespace :only do
resources :clubs do
resources :players
resource :chairman
end
end
end
scope :except => [:new, :create, :edit, :update, :destroy] do
namespace :except do
resources :clubs do
resources :players
resource :chairman
end
end
end
scope :only => :show do
namespace :only do
resources :sectors, :only => :index do
resources :companies do
scope :only => :index do
resources :divisions
end
scope :except => [:show, :update, :destroy] do
resources :departments
end
end
resource :leader
resources :managers, :except => [:show, :update, :destroy]
end
end
end
scope :except => :index do
namespace :except do
resources :sectors, :except => [:show, :update, :destroy] do
resources :companies do
scope :except => [:show, :update, :destroy] do
resources :divisions
end
scope :only => :index do
resources :departments
end
end
resource :leader
resources :managers, :only => :index
end
end
end
match '/:locale/*file.:format', :to => 'files#show', :file => /path\/to\/existing\/file/
end
end
@@ -470,6 +525,18 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
def test_namespace_with_controller_segment
assert_raise(ArgumentError) do
self.class.stub_controllers do |routes|
routes.draw do
namespace :admin do
match '/:controller(/:action(/:id(.:format)))'
end
end
end
end
end
def test_session_singleton_resource
with_test_routes do
get '/session'
@@ -1240,7 +1307,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
assert_equal 'pass', @response.headers['X-Cascade']
get '/products/0001/images'
assert_equal 'images#index', @response.body
get '/products/0001/images/1'
get '/products/0001/images/0001'
assert_equal 'images#show', @response.body
get '/dashboard', {}, {'REMOTE_ADDR' => '10.0.0.100'}
@@ -1285,6 +1352,22 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
def test_url_generator_for_namespaced_generic_route
with_test_routes do
get 'whatever/foo/bar/show'
assert_equal 'foo/bar#show', @response.body
get 'whatever/foo/bar/show/1'
assert_equal 'foo/bar#show', @response.body
assert_equal 'http://www.example.com/whatever/foo/bar/show',
url_for(:controller => "foo/bar", :action => "show")
assert_equal 'http://www.example.com/whatever/foo/bar/show/1',
url_for(:controller => "foo/bar", :action => "show", :id => '1')
end
end
def test_assert_recognizes_account_overview
with_test_routes do
assert_recognizes({:controller => "account", :action => "overview"}, "/account/overview")
@@ -1628,6 +1711,182 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
def test_only_should_be_read_from_scope
with_test_routes do
get '/only/clubs'
assert_equal 'only/clubs#index', @response.body
assert_equal '/only/clubs', only_clubs_path
get '/only/clubs/1/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_only_club_path(:id => '1') }
get '/only/clubs/1/players'
assert_equal 'only/players#index', @response.body
assert_equal '/only/clubs/1/players', only_club_players_path(:club_id => '1')
get '/only/clubs/1/players/2/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_only_club_player_path(:club_id => '1', :id => '2') }
get '/only/clubs/1/chairman'
assert_equal 'only/chairmen#show', @response.body
assert_equal '/only/clubs/1/chairman', only_club_chairman_path(:club_id => '1')
get '/only/clubs/1/chairman/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_only_club_chairman_path(:club_id => '1') }
end
end
def test_except_should_be_read_from_scope
with_test_routes do
get '/except/clubs'
assert_equal 'except/clubs#index', @response.body
assert_equal '/except/clubs', except_clubs_path
get '/except/clubs/1/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_except_club_path(:id => '1') }
get '/except/clubs/1/players'
assert_equal 'except/players#index', @response.body
assert_equal '/except/clubs/1/players', except_club_players_path(:club_id => '1')
get '/except/clubs/1/players/2/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_except_club_player_path(:club_id => '1', :id => '2') }
get '/except/clubs/1/chairman'
assert_equal 'except/chairmen#show', @response.body
assert_equal '/except/clubs/1/chairman', except_club_chairman_path(:club_id => '1')
get '/except/clubs/1/chairman/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_except_club_chairman_path(:club_id => '1') }
end
end
def test_only_option_should_override_scope
with_test_routes do
get '/only/sectors'
assert_equal 'only/sectors#index', @response.body
assert_equal '/only/sectors', only_sectors_path
get '/only/sectors/1'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { only_sector_path(:id => '1') }
end
end
def test_only_option_should_not_inherit
with_test_routes do
get '/only/sectors/1/companies/2'
assert_equal 'only/companies#show', @response.body
assert_equal '/only/sectors/1/companies/2', only_sector_company_path(:sector_id => '1', :id => '2')
get '/only/sectors/1/leader'
assert_equal 'only/leaders#show', @response.body
assert_equal '/only/sectors/1/leader', only_sector_leader_path(:sector_id => '1')
end
end
def test_except_option_should_override_scope
with_test_routes do
get '/except/sectors'
assert_equal 'except/sectors#index', @response.body
assert_equal '/except/sectors', except_sectors_path
get '/except/sectors/1'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { except_sector_path(:id => '1') }
end
end
def test_except_option_should_not_inherit
with_test_routes do
get '/except/sectors/1/companies/2'
assert_equal 'except/companies#show', @response.body
assert_equal '/except/sectors/1/companies/2', except_sector_company_path(:sector_id => '1', :id => '2')
get '/except/sectors/1/leader'
assert_equal 'except/leaders#show', @response.body
assert_equal '/except/sectors/1/leader', except_sector_leader_path(:sector_id => '1')
end
end
def test_except_option_should_override_scoped_only
with_test_routes do
get '/only/sectors/1/managers'
assert_equal 'only/managers#index', @response.body
assert_equal '/only/sectors/1/managers', only_sector_managers_path(:sector_id => '1')
get '/only/sectors/1/managers/2'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { only_sector_manager_path(:sector_id => '1', :id => '2') }
end
end
def test_only_option_should_override_scoped_except
with_test_routes do
get '/except/sectors/1/managers'
assert_equal 'except/managers#index', @response.body
assert_equal '/except/sectors/1/managers', except_sector_managers_path(:sector_id => '1')
get '/except/sectors/1/managers/2'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { except_sector_manager_path(:sector_id => '1', :id => '2') }
end
end
def test_only_scope_should_override_parent_scope
with_test_routes do
get '/only/sectors/1/companies/2/divisions'
assert_equal 'only/divisions#index', @response.body
assert_equal '/only/sectors/1/companies/2/divisions', only_sector_company_divisions_path(:sector_id => '1', :company_id => '2')
get '/only/sectors/1/companies/2/divisions/3'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { only_sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
end
def test_except_scope_should_override_parent_scope
with_test_routes do
get '/except/sectors/1/companies/2/divisions'
assert_equal 'except/divisions#index', @response.body
assert_equal '/except/sectors/1/companies/2/divisions', except_sector_company_divisions_path(:sector_id => '1', :company_id => '2')
get '/except/sectors/1/companies/2/divisions/3'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { except_sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
end
def test_except_scope_should_override_parent_only_scope
with_test_routes do
get '/only/sectors/1/companies/2/departments'
assert_equal 'only/departments#index', @response.body
assert_equal '/only/sectors/1/companies/2/departments', only_sector_company_departments_path(:sector_id => '1', :company_id => '2')
get '/only/sectors/1/companies/2/departments/3'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { only_sector_company_department_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
end
def test_only_scope_should_override_parent_except_scope
with_test_routes do
get '/except/sectors/1/companies/2/departments'
assert_equal 'except/departments#index', @response.body
assert_equal '/except/sectors/1/companies/2/departments', except_sector_company_departments_path(:sector_id => '1', :company_id => '2')
get '/except/sectors/1/companies/2/departments/3'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { except_sector_company_department_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
end
private
def with_test_routes
yield

View File

@@ -30,6 +30,11 @@ class CookieStoreTest < ActionController::IntegrationTest
render :text => "id: #{request.session_options[:id]}"
end
def call_session_clear
session.clear
head :ok
end
def call_reset_session
reset_session
head :ok
@@ -175,6 +180,23 @@ class CookieStoreTest < ActionController::IntegrationTest
end
end
def test_setting_session_value_after_session_clear
with_test_route_set do
get '/set_session_value'
assert_response :success
session_payload = response.body
assert_equal "_myapp_session=#{response.body}; path=/; HttpOnly",
headers['Set-Cookie']
get '/call_session_clear'
assert_response :success
get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body
end
end
def test_persistent_session_id
with_test_route_set do
cookies[SessionKey] = SignedBar

View File

@@ -2,24 +2,24 @@ require 'abstract_unit'
module TestUrlGeneration
class WithMountPoint < ActionDispatch::IntegrationTest
Router = ActionDispatch::Routing::RouteSet.new
Router.draw { match "/foo", :to => "my_route_generating#index", :as => :foo }
Routes = ActionDispatch::Routing::RouteSet.new
Routes.draw { match "/foo", :to => "my_route_generating#index", :as => :foo }
class ::MyRouteGeneratingController < ActionController::Base
include Router.url_helpers
include Routes.url_helpers
def index
render :text => foo_path
end
end
include Router.url_helpers
include Routes.url_helpers
def _router
Router
def _routes
Routes
end
def app
Router
Routes
end
test "generating URLS normally" do
@@ -30,7 +30,7 @@ module TestUrlGeneration
assert_equal "/bar/foo", foo_path(:script_name => "/bar")
end
test "the request's SCRIPT_NAME takes precedence over the router's" do
test "the request's SCRIPT_NAME takes precedence over the routes'" do
get "/foo", {}, 'SCRIPT_NAME' => "/new"
assert_equal "/new/foo", response.body
end

View File

@@ -406,6 +406,14 @@ end
class UrlHelperControllerTest < ActionController::TestCase
class UrlHelperController < ActionController::Base
test_routes do |map|
match 'url_helper_controller_test/url_helper/show/:id',
:to => 'url_helper_controller_test/url_helper#show',
:as => :show
match 'url_helper_controller_test/url_helper/profile/:name',
:to => 'url_helper_controller_test/url_helper#show',
:as => :profile
match 'url_helper_controller_test/url_helper/show_named_route',
:to => 'url_helper_controller_test/url_helper#show_named_route',
:as => :show_named_route
@@ -418,6 +426,14 @@ class UrlHelperControllerTest < ActionController::TestCase
:as => :normalize_recall_params
end
def show
if params[:name]
render :inline => 'ok'
else
redirect_to profile_path(params[:id])
end
end
def show_url_for
render :inline => "<%= url_for :controller => 'url_helper_controller_test/url_helper', :action => 'show_url_for' %>"
end
@@ -484,15 +500,24 @@ class UrlHelperControllerTest < ActionController::TestCase
assert_equal 'http://testtwo.host/url_helper_controller_test/url_helper/show_named_route', @response.body
end
def test_recall_params_should_be_normalized_when_using_block_route
def test_recall_params_should_be_normalized
get :normalize_recall_params
assert_equal '/url_helper_controller_test/url_helper/normalize_recall_params', @response.body
end
def test_recall_params_should_not_be_changed_when_using_normal_route
def test_recall_params_should_not_be_changed
get :recall_params_not_changed
assert_equal '/url_helper_controller_test/url_helper/show_url_for', @response.body
end
def test_recall_params_should_normalize_id
get :show, :id => '123'
assert_equal 302, @response.status
assert_equal 'http://test.host/url_helper_controller_test/url_helper/profile/123', @response.location
get :show, :name => '123'
assert_equal 'ok', @response.body
end
end
class TasksController < ActionController::Base

View File

@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/array/conversions'
require 'active_support/core_ext/string/inflections'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/hash/reverse_merge'

View File

@@ -770,15 +770,20 @@ module ActiveRecord
# Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>,
# and deleted if they're associated with <tt>:dependent => :delete_all</tt>.
# [collection=objects]
# Replaces the collections content by deleting and adding objects as appropriate.
# Replaces the collections content by deleting and adding objects as appropriate. If the <tt>:through</tt>
# option is true callbacks in the join models are triggered except destroy callbacks, since deletion is
# direct.
# [collection_singular_ids]
# Returns an array of the associated objects' ids
# [collection_singular_ids=ids]
# Replace the collection with the objects identified by the primary keys in +ids+
# Replace the collection with the objects identified by the primary keys in +ids+. This
# method loads the models and calls <tt>collection=</tt>. See above.
# [collection.clear]
# Removes every object from the collection. This destroys the associated objects if they
# are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the
# database if <tt>:dependent => :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
# If the <tt>:through</tt> option is true no destroy callbacks are invoked on the join models.
# Join models are directly deleted.
# [collection.empty?]
# Returns +true+ if there are no associated objects.
# [collection.size]
@@ -871,9 +876,11 @@ module ActiveRecord
# [:as]
# Specifies a polymorphic interface (See <tt>belongs_to</tt>).
# [:through]
# Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
# Specifies a join model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
# are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
# <tt>has_one</tt> or <tt>has_many</tt> association on the join model.
# <tt>has_one</tt> or <tt>has_many</tt> association on the join model. The collection of join models can be managed via the collection
# API. For example, new join models are created for newly associated objects, and if some are gone their rows are deleted (directly,
# no destroy callbacks are triggered).
# [:source]
# Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
# inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or

View File

@@ -393,7 +393,12 @@ module ActiveRecord
@target = find_target.map do |f|
i = @target.index(f)
t = @target.delete_at(i) if i
(t && t.changed?) ? t : f
if t && t.changed?
t
else
f.mark_for_destruction if t && t.marked_for_destruction?
f
end
end + @target
else
@target = find_target

View File

@@ -898,11 +898,6 @@ module ActiveRecord #:nodoc:
store_full_sti_class ? name : name.demodulize
end
def relation
@relation ||= Relation.new(self, arel_table)
finder_needs_type_condition? ? @relation.where(type_condition) : @relation
end
def arel_table
@arel_table ||= Arel::Table.new(table_name, :engine => arel_engine)
end
@@ -943,6 +938,12 @@ module ActiveRecord #:nodoc:
end
private
def relation #:nodoc:
@relation ||= Relation.new(self, arel_table)
finder_needs_type_condition? ? @relation.where(type_condition) : @relation
end
# Finder methods must instantiate through this method to work with the
# single-table inheritance model that makes it possible to create
# objects of different types from the same table.

View File

@@ -29,7 +29,7 @@ module ActiveRecord
if options.present?
scoped.apply_finder_options(options)
else
current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone
current_scoped_methods ? relation.merge(current_scoped_methods) : relation.clone
end
end

View File

@@ -94,7 +94,7 @@ module ActiveRecord
def initialize
super
observed_subclasses.each { |klass| add_observer!(klass) }
observed_descendants.each { |klass| add_observer!(klass) }
end
def self.method_added(method)
@@ -108,8 +108,8 @@ module ActiveRecord
protected
def observed_subclasses
observed_classes.sum([]) { |klass| klass.send(:descendants) }
def observed_descendants
observed_classes.sum([]) { |klass| klass.descendants }
end
def observe_callbacks?

View File

@@ -1,7 +1,7 @@
namespace :db do
task :load_config => :rails_env do
require 'active_record'
ActiveRecord::Base.configurations = Rails::Application.config.database_configuration
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
end
namespace :create do

View File

@@ -489,6 +489,12 @@ module NestedAttributesOnACollectionAssociationTests
assert_equal 'Polly', @pirate.send(@association_name).send(:load_target).last.name
end
def test_should_not_remove_scheduled_destroys_when_loading_association
@pirate.reload
@pirate.send(association_setter, [{ :id => @child_1.id, :_destroy => '1' }])
assert @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.marked_for_destruction?
end
def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
@child_1.stubs(:id).returns('ABC1X')
@child_2.stubs(:id).returns('ABC2X')

View File

@@ -1,4 +1,3 @@
require 'active_support'
require 'active_support/time'
require 'active_support/core_ext'
require 'active_support/json'

View File

@@ -432,7 +432,7 @@ module ActiveSupport
options = filters.last.is_a?(Hash) ? filters.pop : {}
filters.unshift(block) if block
([self] + self.descendants).each do |target|
([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target|
chain = target.send("_#{name}_callbacks")
yield chain, type, filters, options
target.__define_runner(name)
@@ -506,7 +506,7 @@ module ActiveSupport
def reset_callbacks(symbol)
callbacks = send("_#{symbol}_callbacks")
self.descendants.each do |target|
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
chain = target.send("_#{symbol}_callbacks")
callbacks.each { |c| chain.delete(c) }
target.__define_runner(symbol)

View File

@@ -2,54 +2,49 @@ require 'active_support/core_ext/module/anonymous'
require 'active_support/core_ext/module/reachable'
class Class #:nodoc:
# Returns an array with the names of the subclasses of +self+ as strings.
#
# Integer.subclasses # => ["Bignum", "Fixnum"]
def subclasses
Class.subclasses_of(self).map { |o| o.to_s }
end
# Rubinius
if defined?(Class.__subclasses__)
alias :subclasses :__subclasses__
def descendants
subclasses = []
__subclasses__.each {|k| subclasses << k; subclasses.concat k.descendants }
subclasses
descendants = []
__subclasses__.each do |k|
descendants << k
descendants.concat k.descendants
end
descendants
end
else
# MRI
else # MRI
begin
ObjectSpace.each_object(Class.new) {}
def descendants
subclasses = []
descendants = []
ObjectSpace.each_object(class << self; self; end) do |k|
subclasses << k unless k == self
descendants.unshift k unless k == self
end
subclasses
descendants
end
# JRuby
rescue StandardError
rescue StandardError # JRuby
def descendants
subclasses = []
descendants = []
ObjectSpace.each_object(Class) do |k|
subclasses << k if k < self
descendants.unshift k if k < self
end
subclasses.uniq!
subclasses
descendants.uniq!
descendants
end
end
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 self.subclasses_of(*superclasses) #:nodoc:
subclasses = []
superclasses.each do |klass|
subclasses.concat klass.descendants.select {|k| k.anonymous? || k.reachable?}
# Returns an array with the direct children of +self+.
#
# Integer.subclasses # => [Bignum, Fixnum]
def subclasses
subclasses, chain = [], descendants
chain.each do |k|
subclasses << k unless chain.any? { |c| c > k }
end
subclasses
end
subclasses
end
end

View File

@@ -1,7 +1,8 @@
require 'date'
require 'active_support/duration'
require 'active_support/core_ext/time/zones'
require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/date/zones'
require 'active_support/core_ext/time/zones'
class Date
if RUBY_VERSION < '1.9'

View File

@@ -1,6 +1,6 @@
require 'date'
require 'active_support/inflector'
require 'active_support/core_ext/time/calculations'
require 'active_support/core_ext/date/zones'
class Date
DATE_FORMATS = {
@@ -13,10 +13,10 @@ class Date
}
# Ruby 1.9 has Date#to_time which converts to localtime only.
remove_method :to_time if instance_methods.include?(:to_time)
remove_method :to_time if method_defined?(:to_time)
# Ruby 1.9 has Date#xmlschema which converts to a string without the time component.
remove_method :xmlschema if instance_methods.include?(:xmlschema)
remove_method :xmlschema if method_defined?(:xmlschema)
# Convert to a formatted string. See DATE_FORMATS for predefined formats.
#
@@ -81,16 +81,6 @@ class Date
def to_time(form = :local)
::Time.send("#{form}_time", year, month, day)
end
# Converts Date to a TimeWithZone in the current zone if Time.zone_default is set,
# otherwise converts Date to a Time via Date#to_time
def to_time_in_current_zone
if ::Time.zone_default
::Time.zone.local(year, month, day)
else
to_time
end
end
# Converts a Date instance to a DateTime, where the time is set to the beginning of the day
# and UTC offset is set to 0.

View File

@@ -0,0 +1,14 @@
require 'date'
require 'active_support/core_ext/time/zones'
class Date
# Converts Date to a TimeWithZone in the current zone if Time.zone_default is set,
# otherwise converts Date to a Time via Date#to_time
def to_time_in_current_zone
if ::Time.zone_default
::Time.zone.local(year, month, day)
else
to_time
end
end
end

View File

@@ -1,5 +1,6 @@
require 'rational' unless RUBY_VERSION >= '1.9.2'
require 'active_support/core_ext/object/acts_like'
require 'active_support/core_ext/time/zones'
class DateTime
class << self

View File

@@ -1,6 +1,7 @@
require 'active_support/inflector'
require 'active_support/core_ext/time/conversions'
require 'active_support/core_ext/date_time/calculations'
require 'active_support/values/time_zone'
class DateTime
# Ruby 1.9 has DateTime#to_time which internally relies on Time. We define our own #to_time which allows

View File

@@ -1,3 +1,5 @@
require 'active_support/core_ext/time/zones'
class DateTime
# Returns the simultaneous time in <tt>Time.zone</tt>.
#

View File

@@ -6,9 +6,9 @@ require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/object/instance_variables'
require 'active_support/core_ext/object/misc'
require 'active_support/core_ext/object/extending'
require 'active_support/core_ext/object/returning'
require 'active_support/core_ext/object/to_json'
require 'active_support/core_ext/object/to_param'
require 'active_support/core_ext/object/to_query'
require 'active_support/core_ext/object/with_options'

View File

@@ -1,11 +0,0 @@
require 'active_support/core_ext/class/subclasses'
class Object
# 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.subclasses_of(*superclasses)
end
end

View File

@@ -0,0 +1,19 @@
# Hack to load json gem first so we can overwrite its to_json.
begin
require 'json'
rescue LoadError
end
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
# their default behavior. That said, we need to define the basic to_json method in all of them,
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
klass.class_eval <<-RUBY, __FILE__, __LINE__
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def to_json(options = nil)
ActiveSupport::JSON.encode(self, options)
end
RUBY
end

View File

@@ -40,6 +40,11 @@ class Time
def local_time(*args)
time_with_datetime_fallback(:local, *args)
end
# Returns <tt>Time.zone.now</tt> when <tt>config.time_zone</tt> is set, otherwise just returns <tt>Time.now</tt>.
def current
::Time.zone_default ? ::Time.zone.now : ::Time.now
end
end
# Tells whether the Time object's time lies in the past

View File

@@ -41,11 +41,6 @@ class Time
::Time.zone = old_zone
end
# Returns <tt>Time.zone.now</tt> when <tt>config.time_zone</tt> is set, otherwise just returns <tt>Time.now</tt>.
def current
::Time.zone_default ? ::Time.zone.now : ::Time.now
end
private
def get_zone(time_zone)
return time_zone if time_zone.nil? || time_zone.is_a?(ActiveSupport::TimeZone)

View File

@@ -1,4 +1,5 @@
require "active_support/notifications"
require "active_support/core_ext/array/wrap"
module ActiveSupport
module Deprecation
@@ -7,11 +8,11 @@ module ActiveSupport
attr_accessor :debug
def behavior
@behavior ||= DEFAULT_BEHAVIORS[:stderr]
@behavior ||= [DEFAULT_BEHAVIORS[:stderr]]
end
def behavior=(behavior)
@behavior = DEFAULT_BEHAVIORS[behavior] || behavior
@behavior = Array.wrap(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b }
end
end

View File

@@ -4,8 +4,9 @@ module ActiveSupport
attr_accessor :silenced
def warn(message = nil, callstack = caller)
if behavior && !silenced
behavior.call(deprecation_message(callstack, message), callstack)
return if silenced
deprecation_message(callstack, message).tap do |m|
behavior.each { |b| b.call(m, callstack) }
end
end

View File

@@ -4,16 +4,23 @@ module ActiveSupport
# This module provides an internal implementation to track descendants
# which is faster than iterating through ObjectSpace.
module DescendantsTracker
@@descendants = Hash.new { |h, k| h[k] = [] }
@@direct_descendants = Hash.new { |h, k| h[k] = [] }
def self.descendants
@@descendants
def self.direct_descendants(klass)
@@direct_descendants[klass]
end
def self.descendants(klass)
@@direct_descendants[klass].inject([]) do |descendants, klass|
descendants << klass
descendants.concat klass.descendants
end
end
def self.clear
@@descendants.each do |klass, descendants|
@@direct_descendants.each do |klass, descendants|
if ActiveSupport::Dependencies.autoloaded?(klass)
@@descendants.delete(klass)
@@direct_descendants.delete(klass)
else
descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) }
end
@@ -26,14 +33,11 @@ module ActiveSupport
end
def direct_descendants
@@descendants[self]
DescendantsTracker.direct_descendants(self)
end
def descendants
@@descendants[self].inject([]) do |descendants, klass|
descendants << klass
descendants.concat klass.descendants
end
DescendantsTracker.descendants(self)
end
end
end

View File

@@ -1,16 +1,15 @@
module ActiveSupport
# This class is responsible to track files and invoke the given block
# whenever one of these files are changed. For example, this class
# is used by Rails to reload routes whenever they are changed upon
# a new request.
# is used by Rails to reload the I18n framework whenever they are
# changed upon a new request.
#
# routes_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
# paths.each { |p| load(p) }
# Rails::Application.routes.reload!
# i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
# I18n.reload!
# end
#
# ActionDispatch::Callbacks.to_prepare do
# routes_reloader.execute_if_updated
# i18n_reloader.execute_if_updated
# end
#
class FileUpdateChecker

View File

@@ -1,6 +1,7 @@
require "active_support"
require "rails"
require "active_support/file_update_checker"
require "active_support/core_ext/array/wrap"
module I18n
class Railtie < Rails::Railtie

View File

@@ -1,20 +1,18 @@
# encoding: utf-8
require 'active_support/core_ext/object/to_json'
require 'active_support/core_ext/module/delegation'
require 'active_support/deprecation'
require 'active_support/json/variable'
require 'bigdecimal'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/big_decimal/conversions' # for #to_s
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/object/instance_variables'
require 'active_support/deprecation'
require 'active_support/time'
# Hack to load json gem first so we can overwrite its to_json.
begin
require 'json'
rescue LoadError
end
require 'time'
require 'active_support/core_ext/time/conversions'
require 'active_support/core_ext/date_time/conversions'
require 'active_support/core_ext/date/conversions'
module ActiveSupport
class << self
@@ -128,20 +126,6 @@ module ActiveSupport
end
end
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
# their default behavior. That said, we need to define the basic to_json method in all of them,
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
klass.class_eval <<-RUBY, __FILE__, __LINE__
# Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
def to_json(options = nil)
ActiveSupport::JSON.encode(self, options)
end
RUBY
end
class Object
def as_json(options = nil) #:nodoc:
if respond_to?(:to_hash)
@@ -152,12 +136,6 @@ class Object
end
end
# A string that returns itself as its JSON-encoded form.
class ActiveSupport::JSON::Variable < String
def as_json(options = nil) self end #:nodoc:
def encode_json(encoder) self end #:nodoc:
end
class TrueClass
AS_JSON = ActiveSupport::JSON::Variable.new('true').freeze
def as_json(options = nil) AS_JSON end #:nodoc:

View File

@@ -2,10 +2,8 @@ module ActiveSupport
module JSON
# A string that returns itself as its JSON-encoded form.
class Variable < String
private
def rails_to_json(*)
self
end
def as_json(options = nil) self end #:nodoc:
def encode_json(encoder) self end #:nodoc:
end
end
end

View File

@@ -24,6 +24,7 @@ require 'active_support/core_ext/date/acts_like'
require 'active_support/core_ext/date/freeze'
require 'active_support/core_ext/date/calculations'
require 'active_support/core_ext/date/conversions'
require 'active_support/core_ext/date/zones'
require 'active_support/core_ext/date_time/acts_like'
require 'active_support/core_ext/date_time/calculations'

View File

@@ -1,11 +1,5 @@
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/try'
begin
require 'tzinfo'
rescue LoadError => e
$stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install"
raise e
end
# The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following:
#
@@ -201,6 +195,12 @@ module ActiveSupport
# (GMT). Seconds were chosen as the offset unit because that is the unit that
# Ruby uses to represent time zone offsets (see Time#utc_offset).
def initialize(name, utc_offset = nil, tzinfo = nil)
begin
require 'tzinfo'
rescue LoadError => e
$stderr.puts "You don't have tzinfo installed in your application. Please add it to your Gemfile and run bundle install"
raise e
end
@name = name
@utc_offset = utc_offset
@tzinfo = tzinfo || TimeZone.find_tzinfo(name)

View File

@@ -1,29 +1,27 @@
require 'abstract_unit'
require 'active_support/core_ext/class'
class A
end
module X
class B
end
end
module Y
module Z
class C
end
end
end
class ClassTest < Test::Unit::TestCase
def test_retrieving_subclasses
@parent = eval("class D; end; D")
@sub = eval("class E < D; end; E")
@subofsub = eval("class F < E; end; F")
assert_equal 2, @parent.subclasses.size
assert_equal [@subofsub.to_s], @sub.subclasses
assert_equal [], @subofsub.subclasses
assert_equal [@sub.to_s, @subofsub.to_s].sort, @parent.subclasses.sort
class Parent; end
class Foo < Parent; end
class Bar < Foo; end
class Baz < Bar; end
class A < Parent; end
class B < A; end
class C < B; end
def test_descendants
assert_equal [Foo, Bar, Baz, A, B, C], Parent.descendants
assert_equal [Bar, Baz], Foo.descendants
assert_equal [Baz], Bar.descendants
assert_equal [], Baz.descendants
end
end
def test_subclasses
assert_equal [Foo, A], Parent.subclasses
assert_equal [Bar], Foo.subclasses
assert_equal [Baz], Bar.subclasses
assert_equal [], Baz.subclasses
end
end

View File

@@ -40,55 +40,6 @@ class Foo
include Bar
end
class ClassExtTest < Test::Unit::TestCase
def test_subclasses_of_should_find_nested_classes
assert Class.subclasses_of(ClassK).include?(Nested::ClassL)
end
def test_subclasses_of_should_not_return_removed_classes
# First create the removed class
old_class = Nested.class_eval { remove_const :ClassL }
new_class = Class.new(ClassK)
Nested.const_set :ClassL, new_class
assert_equal "Nested::ClassL", new_class.name # Sanity check
subclasses = Class.subclasses_of(ClassK)
assert subclasses.include?(new_class)
assert ! subclasses.include?(old_class)
ensure
Nested.const_set :ClassL, old_class unless defined?(Nested::ClassL)
end
def test_subclasses_of_should_not_trigger_const_missing
const_missing = false
Nested.on_const_missing { const_missing = true }
subclasses = Class.subclasses_of ClassK
assert !const_missing
assert_equal [ Nested::ClassL ], subclasses
removed = Nested.class_eval { remove_const :ClassL } # keep it in memory
subclasses = Class.subclasses_of ClassK
assert !const_missing
assert subclasses.empty?
ensure
Nested.const_set :ClassL, removed unless defined?(Nested::ClassL)
end
def test_subclasses_of_with_multiple_roots
classes = Class.subclasses_of(ClassI, ClassK)
assert_equal %w(ClassJ Nested::ClassL), classes.collect(&:to_s).sort
end
def test_subclasses_of_doesnt_find_anonymous_classes
assert_equal [], Class.subclasses_of(Foo)
bar = Class.new(Foo)
assert_nothing_raised do
assert_equal [bar], Class.subclasses_of(Foo)
end
end
end
class ObjectTests < ActiveSupport::TestCase
class DuckTime
def acts_like_time?

View File

@@ -80,6 +80,19 @@ class DeprecationTest < ActiveSupport::TestCase
assert_deprecated(/foo=nil/) { @dtc.partially }
end
def test_several_behaviors
@a, @b = nil, nil
ActiveSupport::Deprecation.behavior = [
Proc.new { |msg, callstack| @a = msg },
Proc.new { |msg, callstack| @b = msg }
]
@dtc.partially
assert_match(/foo=nil/, @a)
assert_match(/foo=nil/, @b)
end
def test_deprecated_instance_variable_proxy
assert_not_deprecated { @dtc.request.size }
@@ -123,6 +136,13 @@ class DeprecationTest < ActiveSupport::TestCase
assert_equal 123, result
end
def test_assert_deprecated_warn_work_with_default_behavior
ActiveSupport::Deprecation.instance_variable_set('@behavior' , nil)
assert_deprecated('abc') do
ActiveSupport::Deprecation.warn 'abc'
end
end
def test_silence
ActiveSupport::Deprecation.silence do
assert_not_deprecated { @dtc.partially }

View File

@@ -37,7 +37,9 @@ class DescendantsTrackerTest < Test::Unit::TestCase
def test_clear_with_autoloaded_parent_children_and_granchildren
mark_as_autoloaded(*ALL) do
ActiveSupport::DescendantsTracker.clear
assert ActiveSupport::DescendantsTracker.descendants.slice(*ALL).empty?
ALL.each do |k|
assert ActiveSupport::DescendantsTracker.descendants(k).empty?
end
end
end
@@ -64,12 +66,12 @@ class DescendantsTrackerTest < Test::Unit::TestCase
old_autoloaded = ActiveSupport::Dependencies.autoloaded_constants.dup
ActiveSupport::Dependencies.autoloaded_constants = klasses.map(&:name)
old_descendants = ActiveSupport::DescendantsTracker.descendants.dup
old_descendants = ActiveSupport::DescendantsTracker.class_eval("@@direct_descendants").dup
old_descendants.each { |k, v| old_descendants[k] = v.dup }
yield
ensure
ActiveSupport::Dependencies.autoloaded_constants = old_autoloaded
ActiveSupport::DescendantsTracker.descendants.replace(old_descendants)
ActiveSupport::DescendantsTracker.class_eval("@@direct_descendants").replace(old_descendants)
end
end

View File

@@ -1,7 +1,5 @@
# encoding: utf-8
require 'abstract_unit'
require 'bigdecimal'
require 'active_support/core_ext/big_decimal/conversions'
require 'active_support/json'
class TestJSONEncoding < Test::Unit::TestCase
@@ -138,6 +136,10 @@ class TestJSONEncoding < Test::Unit::TestCase
ActiveSupport.use_standard_json_time_format = false
end
def test_hash_with_time_to_json
assert_equal '{"time":"2009/01/01 00:00:00 +0000"}', { :time => Time.utc(2009) }.to_json
end
def test_nested_hash_with_float
assert_nothing_raised do
hash = {

View File

@@ -1,5 +1,6 @@
require 'abstract_unit'
require 'active_support/json'
require 'active_support/core_ext/object/to_json'
class OrderedHashTest < Test::Unit::TestCase
def setup

View File

@@ -25,5 +25,5 @@ Gem::Specification.new do |s|
s.add_dependency('activeresource', version)
s.add_dependency('actionmailer', version)
s.add_dependency('railties', version)
s.add_dependency('bundler', '>= 0.9.26')
s.add_dependency('bundler', '>= 1.0.0.beta.2')
end

View File

@@ -387,40 +387,6 @@ TIP: Since +with_options+ forwards calls to its receiver they can be nested. Eac
NOTE: Defined in +active_support/core_ext/object/with_options.rb+.
h5. +subclasses_of+
The method +subclasses_of+ receives an arbitrary number of class objects and returns all their anonymous or reachable descendants as a single array:
<ruby>
class C; end
subclasses_of(C) # => []
subclasses_of(Integer) # => [Bignum, Fixnum]
module M
class A; end
class B1 < A; end
class B2 < A; end
end
module N
class C < M::B1; end
end
subclasses_of(M::A) # => [N::C, M::B2, M::B1]
</ruby>
The order in which these classes are returned is unspecified. The returned collection may have duplicates:
<ruby>
subclasses_of(Numeric, Integer)
# => [Bignum, Float, Fixnum, Integer, Date::Infinity, Rational, BigDecimal, Bignum, Fixnum]
</ruby>
See also +Class#subclasses+ in "Extensions to +Class+ FIX THIS LINK":FIXME.
NOTE: Defined in +active_support/core_ext/object/extending.rb+.
h4. Instance Variables
Active Support provides several methods to ease access to instance variables.
@@ -1141,36 +1107,47 @@ If for whatever reason an application loads the definition of a mailer class and
NOTE: Defined in +active_support/core_ext/class/delegating_attributes.rb+.
h4. Descendants
h4. Descendants & Subclasses
h5. +subclasses+
h5. +descendants+
The +subclasses+ method returns the names of all the anonymous or reachable descendants of its receiver as an array of strings:
The +descendants+ method returns all classes, including its children, that inherits from self.
<ruby>
class C; end
C.subclasses # => []
C.descendants #=> []
Integer.subclasses # => ["Bignum", "Fixnum"]
class B < C; end
C.descendants #=> [B]
module M
class A; end
class B1 < A; end
class B2 < A; end
end
class A < B; end
C.descendants #=> [B, A]
module N
class C < M::B1; end
end
M::A.subclasses # => ["N::C", "M::B2", "M::B1"]
class D < C; end
C.descendants #=> [B, A, D]
</ruby>
The order in which these class names are returned is unspecified.
h5. +subclasses+
See also +Object#subclasses_of+ in "Extensions to All Objects FIX THIS LINK":FIXME.
The +subclasses+ method returns all direct classes that inherits from self.
WARNING: This method is redefined in some Rails core classes.
<ruby>
class C; end
C.subclasses #=> []
class B < C; end
C.subclasses #=> [B]
class A < B; end
C.subclasses #=> [B]
class D < C; end
C.subclasses #=> [B, D]
</ruby>
The order in which these class are returned is unspecified.
WARNING: This method is redefined in some Rails core classes but should be all compatible in Rails 3.1.
NOTE: Defined in +active_support/core_ext/class/subclasses.rb+.

View File

@@ -137,6 +137,16 @@ end
!images/has_many_through.png(has_many :through Association Diagram)!
The collection of join models can be managed via the API. For example, if you assign
<ruby>
physician.patients = patients
</ruby>
new join models are created for newly associated objects, and if some are gone their rows are deleted.
WARNING: Automatic deletion of join models is direct, no destroy callbacks are triggered.
The +has_many :through+ association is also useful for setting up "shortcuts" through nested +has_many+ associations. For example, if a document has many sections, and a section has many paragraphs, you may sometimes want to get a simple collection of all paragraphs in the document. You could set that up this way:
<ruby>

View File

@@ -11,7 +11,7 @@ This guide first describes the process of +rails server+ then explains the Passe
h3. Launch!
As of Rails 3, +script/server+ has become +rails server+. This was done to centralise all rails related commands to one common file.
As of Rails 3, +script/server+ has become +rails server+. This was done to centralize all rails related commands to one common file.
The actual +rails+ command is kept in _railties/bin/rails_ and goes like this:
@@ -58,11 +58,8 @@ In +script/rails+ we see the following:
#!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
ENV_PATH = File.expand_path('../../config/environment', __FILE__)
BOOT_PATH = File.expand_path('../../config/boot', __FILE__)
APP_PATH = File.expand_path('../../config/application', __FILE__)
require BOOT_PATH
APP_PATH = File.expand_path('../../config/application', __FILE__)
require File.expand_path('../../config/boot', __FILE__)
require 'rails/commands'
</ruby>
@@ -79,15 +76,19 @@ h3. _config/boot.rb_
_config/boot.rb_ is the first stop for everything for initializing your application. This boot process does quite a bit of work for you and so this section attempts to go in-depth enough to explain what each of the pieces does.
<ruby>
# Use Bundler (preferred)
require 'rubygems'
# Set up gems listed in the Gemfile.
gemfile = File.expand_path('../../Gemfile', __FILE__)
begin
require File.expand_path('../../.bundle/environment', __FILE__)
rescue LoadError
require 'rubygems'
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
Bundler.setup
end
rescue Bundler::GemNotFound => e
STDERR.puts e.message
STDERR.puts "Try running `bundle install`."
exit!
end if File.exist?(gemfile)
</ruby>
h3. Bundled Rails (3.x)
@@ -140,8 +141,8 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This
* activesupport-3.0.0.beta4.gem
* arel-0.4.0.gem
* builder-2.1.2.gem
* bundler-0.9.26.gem
* erubis-2.6.5.gem
* bundler-1.0.0.beta.2.gem
* erubis-2.6.6.gem
* i18n-0.4.1.gem
* mail-2.2.4.gem
* memcache-client-1.8.3.gem
@@ -156,7 +157,7 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This
* sqlite3-ruby-1.3.0.gem
* text-format-1.0.0.gem
* text-hyphen-1.0.0.gem
* thor-0.13.6.gem
* thor-0.13.7.gem
* treetop-1.4.8.gem
* tzinfo-0.3.22.gem
@@ -164,33 +165,7 @@ TODO: Prettify when it becomes more stable.
I won't go into what each of these gems are, as that is really something that needs covering on a case-by-case basis. We will however just dig a little under the surface of Bundler.
Back in _config/boot.rb_, the first line will try to include _.bundle/environment.rb_, which doesn't exist in a bare-bones Rails application and because this file does not exist Ruby will raise a +LoadError+ which will be rescued and run the following code:
<ruby>
require 'rubygems'
require 'bundler'
Bundler.setup
</ruby>
+Bundler.setup+ here will load and parse the +Gemfile+ and add the _lib_ directory of the gems mentioned **and** their dependencies (**and** their dependencies' dependencies, and so on) to the +$LOAD_PATH+.
Now we will go down the alternate timeline where we generate a _.bundle/environment.rb_ file using the +bundle lock+ command. This command also creates a _Gemfile.lock_ file which is actually a YAML file loaded by this method in Bundler before it moves on to check for _Gemfile_:
<ruby>
def definition(gemfile = default_gemfile)
configure
root = Pathname.new(gemfile).dirname
lockfile = root.join("Gemfile.lock")
if lockfile.exist?
Definition.from_lock(lockfile)
else
Definition.from_gemfile(gemfile)
end
end
</ruby>
The _.bundle/environment.rb_ file adds the _lib_ directory of all the gems specified in +Gemfile.lock+ to +$LOAD_PATH+.
Back in _config/boot.rb_, we call +Bundler.setup+ which will load and parse the +Gemfile+ and add the _lib_ directory of the gems mentioned **and** their dependencies (**and** their dependencies' dependencies, and so on) to the +$LOAD_PATH+.
h3. Requiring Rails
@@ -326,6 +301,11 @@ As you can see for the duration of the +eager_autoload+ block the class variable
module ActiveSupport
extend ActiveSupport::Autoload
autoload :DescendantsTracker
autoload :FileUpdateChecker
autoload :LogSubscriber
autoload :Notifications
# TODO: Narrow this list down
eager_autoload do
autoload :BacktraceCleaner
@@ -348,7 +328,6 @@ As you can see for the duration of the +eager_autoload+ block the class variable
autoload :OptionMerger
autoload :OrderedHash
autoload :OrderedOptions
autoload :Notifications
autoload :Rescuable
autoload :SecureRandom
autoload :StringInquirer
@@ -589,19 +568,20 @@ This file (_railties/lib/rails.rb_) requires the very, very basics that Rails ne
require 'action_dispatch/railtie'
</ruby>
+require 'pathname'+ requires the Pathname class which is used for returning a Pathname object for +Rails.root+ so that instead of doing:
<ruby>
File.join(Rails.root, "app/controllers")
</ruby>
You may do:
+require 'pathname'+ requires the Pathname class which is used for returning a Pathname object for +Rails.root+. Although is coming to use this path name to generate paths as below:
<ruby>
Rails.root.join("app/controllers")
</ruby>
Although this is not new to Rails 3 (it was available in 2.3.5), it is something worthwhile pointing out.
Pathname can also be converted to string, so the following syntax is preferred:
<ruby>
"#{Rails.root}/app/controllers"
</ruby>
This works because Ruby automatically handles file path conversions. Although this is not new to Rails 3 (it was available in 2.3.5), it is something worthwhile pointing out.
Inside this file there are other helpful helper methods defined, such as +Rails.root+, +Rails.env+, +Rails.logger+ and +Rails.application+.
@@ -1833,7 +1813,7 @@ We do not already have a +Rails.application+, so instead this resorts to calling
end
</ruby>
This +called_from+ setting looks a little overwhelming to begin with, but the short end of it is that it returns the route to your application's config directory, something like: _/home/you/yourapp/config_. After +called_from+ has been set, +super+ is again called and this means the +Rails::Railtie#inherited+ method (in _railties/lib/rails/railtie.rb_):
This +called_from+ setting looks a little overwhelming to begin with, but the short end of it is that it returns your application's root, something like: _/home/you/yourapp_. After +called_from+ has been set, +super+ is again called and this means the +Rails::Railtie#inherited+ method (in _railties/lib/rails/railtie.rb_):
<ruby>
def inherited(base)

View File

@@ -382,6 +382,12 @@ match ':controller/:action/:id/:user_id'
An incoming URL of +/photos/show/1/2+ will be dispatched to the +show+ action of the +PhotosController+. +params[:id]+ will be +"1"+, and +params[:user_id]+ will be +"2"+.
NOTE: You can't use +namespace+ or +:module+ with a +:controller+ path segment. If you need to do this then use a constraint on :controller that matches the namespace you require. e.g:
<ruby>
match ':controller(/:action(/:id))', :controller => /admin\/[^\/]+/
</ruby>
h4. Static Segments
You can specify static segments when creating a route:

View File

@@ -8,14 +8,6 @@ module Rails
# In Rails 3.0, a Rails::Application object was introduced which is nothing more than
# an Engine but with the responsibility of coordinating the whole boot process.
#
# Opposite to Rails::Engine, you can only have one Rails::Application instance
# in your process and both Rails::Application and YourApplication::Application
# points to it.
#
# In other words, Rails::Application is Singleton and whenever you are accessing
# Rails::Application.config or YourApplication::Application.config, you are actually
# accessing YourApplication::Application.instance.config.
#
# == Initialization
#
# Rails::Application is responsible for executing all railties, engines and plugin
@@ -57,6 +49,10 @@ module Rails
def instance
if self == Rails::Application
if Rails.application
ActiveSupport::Deprecation.warn "Calling a method in Rails::Application is deprecated, " <<
"please call it directly in your application constant #{Rails.application.class.name}.", caller
end
Rails.application
else
@@instance ||= new
@@ -125,14 +121,13 @@ module Rails
end
def reload_routes!
routes = Rails::Application.routes
routes.disable_clear_and_finalize = true
routes.clear!
_routes = self.routes
_routes.disable_clear_and_finalize = true
_routes.clear!
routes_reloader.paths.each { |path| load(path) }
ActiveSupport.on_load(:action_controller) { routes.finalize! }
ActiveSupport.on_load(:action_controller) { _routes.finalize! }
ensure
routes.disable_clear_and_finalize = false
_routes.disable_clear_and_finalize = false
end
def initialize!

View File

@@ -13,27 +13,27 @@ command = aliases[command] || command
case command
when 'generate', 'destroy', 'plugin', 'benchmarker', 'profiler'
require APP_PATH
Rails::Application.require_environment!
Rails.application.require_environment!
require "rails/commands/#{command}"
when 'console'
require 'rails/commands/console'
require APP_PATH
Rails::Application.require_environment!
Rails::Console.start(Rails::Application)
Rails.application.require_environment!
Rails::Console.start(Rails.application)
when 'server'
require 'rails/commands/server'
Rails::Server.new.tap { |server|
require APP_PATH
Dir.chdir(Rails::Application.root)
Dir.chdir(Rails.application.root)
server.start
}
when 'dbconsole'
require 'rails/commands/dbconsole'
require APP_PATH
Rails::DBConsole.start(Rails::Application)
Rails::DBConsole.start(Rails.application)
when 'application', 'runner'
require "rails/commands/#{command}"

View File

@@ -4,7 +4,7 @@ if %w(--version -v).include? ARGV.first
exit(0)
end
if ARGV.first != "new" || ARGV.empty?
if ARGV.first != "new"
ARGV[0] = "--help"
else
ARGV.shift

View File

@@ -37,7 +37,7 @@ ARGV.delete(code_or_file)
ENV["RAILS_ENV"] = options[:environment]
require APP_PATH
Rails::Application.require_environment!
Rails.application.require_environment!
begin
if code_or_file.nil?

View File

@@ -4,4 +4,4 @@
require File.expand_path('../config/application', __FILE__)
require 'rake'
Rails::Application.load_tasks
<%= app_const %>.load_tasks

View File

@@ -4,4 +4,4 @@
# If you change this key, all old signed cookies will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
Rails.application.config.secret_token = '<%= app_secret %>'
<%= app_const %>.config.secret_token = '<%= app_secret %>'

View File

@@ -1,8 +1,8 @@
# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, :key => '_<%= app_name %>_session'
<%= app_const %>.config.session_store :cookie_store, :key => '_<%= app_name %>_session'
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rake db:sessions:create")
# Rails.application.config.session_store :active_record_store
# <%= app_const %>.config.session_store :active_record_store

View File

@@ -1,3 +1,3 @@
Rails.application.routes.draw do |map|
Rails.application.routes.draw do
match '/rails/info/properties' => "rails/info#properties"
end

View File

@@ -3,5 +3,5 @@ task :middleware => :environment do
Rails.configuration.middleware.each do |middleware|
puts "use #{middleware.inspect}"
end
puts "run #{Rails::Application.instance.class.name}.routes"
puts "run #{Rails.application.class.name}.routes"
end

View File

@@ -1,6 +1,6 @@
desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
task :routes => :environment do
Rails::Application.reload_routes!
Rails.application.reload_routes!
all_routes = ENV['CONTROLLER'] ? Rails.application.routes.routes.select { |route| route.defaults[:controller] == ENV['CONTROLLER'] } : Rails.application.routes.routes
routes = all_routes.collect do |route|
# TODO: The :index method is deprecated in 1.9 in favor of :key

View File

@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
s.has_rdoc = false
s.add_dependency('rake', '>= 0.8.3')
s.add_dependency('thor', '~> 0.13.6')
s.add_dependency('thor', '~> 0.13.7')
s.add_dependency('activesupport', version)
s.add_dependency('actionpack', version)
end

View File

@@ -36,7 +36,7 @@ module ApplicationTests
test "allows me to configure default url options for ActionMailer" do
app_file "config/environments/development.rb", <<-RUBY
Rails::Application.configure do
AppTemplate::Application.configure do
config.action_mailer.default_url_options = { :host => "test.rails" }
end
RUBY
@@ -45,6 +45,25 @@ module ApplicationTests
assert_equal "test.rails", ActionMailer::Base.default_url_options[:host]
end
test "does not include url helpers as action methods" do
app_file "config/routes.rb", <<-RUBY
AppTemplate::Application.routes.draw do
get "/foo", :to => lambda { |env| [200, {}, []] }, :as => :foo
end
RUBY
app_file "app/mailers/foo.rb", <<-RUBY
class Foo < ActionMailer::Base
def notify
end
end
RUBY
require "#{app_path}/config/environment"
assert Foo.method_defined?(:foo_path)
assert_equal ["notify"], Foo.action_methods
end
# AS
test "if there's no config.active_support.bare, all of ActiveSupport is required" do
use_frameworks []

View File

@@ -16,7 +16,7 @@ module ApplicationTests
end
def app
@app ||= Rails::Application
@app ||= Rails.application
end
def assert_fallbacks(fallbacks)

View File

@@ -24,11 +24,11 @@ module ApplicationTests
app_file "config/environment.rb", <<-RUBY
SuperMiddleware = Struct.new(:app)
Rails::Application.configure do
AppTemplate::Application.configure do
config.middleware.use SuperMiddleware
end
Rails::Application.initialize!
AppTemplate::Application.initialize!
RUBY
assert_match "SuperMiddleware", Dir.chdir(app_path){ `rake middleware` }

View File

@@ -19,7 +19,7 @@ module ApplicationTests
end
def test_should_run_ruby_statement
assert_match "42", Dir.chdir(app_path) { `bundle exec rails runner "puts User.count"` }
assert_match "42", Dir.chdir(app_path) { `bundle exec rails runner "puts User.count"` }
end
def test_should_run_file

View File

@@ -11,7 +11,7 @@ class InfoControllerTest < ActionController::TestCase
tests Rails::InfoController
def setup
Rails.application.routes.draw do |map|
Rails.application.routes.draw do
match '/rails/info/properties' => "rails/info#properties"
end
@controller.stubs(:consider_all_requests_local? => false, :local_request? => true)