aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/action_controller/test_case.rb12
-rwxr-xr-xactionpack/lib/action_dispatch/http/request.rb26
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb8
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb2
-rw-r--r--actionpack/lib/action_dispatch/middleware/stack.rb18
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb15
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb27
-rw-r--r--actionpack/lib/action_view/base.rb3
-rw-r--r--actionpack/lib/action_view/lookup_context.rb74
-rw-r--r--actionpack/lib/action_view/render/layouts.rb15
-rw-r--r--actionpack/lib/action_view/template/handlers.rb2
-rw-r--r--actionpack/test/dispatch/routing_test.rb12
-rw-r--r--actionpack/test/template/lookup_context_test.rb16
-rw-r--r--activemodel/activemodel.gemspec2
-rw-r--r--activemodel/lib/active_model/observing.rb1
-rwxr-xr-xactiverecord/lib/active_record/associations.rb1
-rw-r--r--activerecord/lib/active_record/autosave_association.rb14
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb57
-rw-r--r--activerecord/test/cases/autosave_association_test.rb8
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb4
-rw-r--r--activesupport/lib/active_support/callbacks.rb9
-rw-r--r--activesupport/lib/active_support/core_ext/string/conversions.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/string/inflections.rb11
-rw-r--r--activesupport/lib/active_support/core_ext/uri.rb8
-rw-r--r--activesupport/lib/active_support/dependencies.rb32
-rw-r--r--activesupport/lib/active_support/message_verifier.rb8
-rw-r--r--activesupport/test/core_ext/date_ext_test.rb10
-rw-r--r--activesupport/test/dependencies_test.rb21
-rw-r--r--railties/lib/rails/log_subscriber.rb13
-rw-r--r--railties/lib/rails/rack/logger.rb7
34 files changed, 266 insertions, 189 deletions
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index 0f45cb5a4a..02ff908c1c 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
s.add_dependency('activesupport', version)
s.add_dependency('activemodel', version)
s.add_dependency('builder', '~> 2.1.2')
- s.add_dependency('i18n', '~> 0.4.0')
+ s.add_dependency('i18n', '~> 0.4.1')
s.add_dependency('rack', '~> 1.1.0')
s.add_dependency('rack-test', '~> 0.5.4')
s.add_dependency('rack-mount', '~> 0.6.3')
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 34499fa784..21281b606e 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -139,14 +139,16 @@ module ActionController
end
end
- params = self.request_parameters.dup
+ # Clear the combined params hash in case it was already referenced.
+ @env.delete("action_dispatch.request.parameters")
+ params = self.request_parameters.dup
%w(controller action only_path).each do |k|
params.delete(k)
params.delete(k.to_sym)
end
-
data = params.to_query
+
@env['CONTENT_LENGTH'] = data.length.to_s
@env['rack.input'] = StringIO.new(data)
end
@@ -155,6 +157,8 @@ module ActionController
@formats = nil
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
+ @method = @request_method = nil
+ @fullpath = @ip = @remote_ip = nil
@env['action_dispatch.request.query_parameters'] = {}
end
end
@@ -167,9 +171,7 @@ module ActionController
@block = nil
@length = 0
@body = []
- @charset = nil
- @content_type = nil
-
+ @charset = @content_type = nil
@request = @template = nil
end
end
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 8560a6fc9c..98f4f5ae18 100755
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -52,9 +52,11 @@ module ActionDispatch
# the application should use), this \method returns the overridden
# value, not the original.
def request_method
- method = env["REQUEST_METHOD"]
- HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
- method
+ @request_method ||= begin
+ method = env["REQUEST_METHOD"]
+ HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
+ method
+ end
end
# Returns a symbol form of the #request_method
@@ -66,9 +68,11 @@ module ActionDispatch
# even if it was overridden by middleware. See #request_method for
# more information.
def method
- method = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
- HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
- method
+ @method ||= begin
+ method = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
+ HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
+ method
+ end
end
# Returns a symbol form of the #method
@@ -113,6 +117,10 @@ module ActionDispatch
Http::Headers.new(@env)
end
+ def fullpath
+ @fullpath ||= super
+ end
+
def forgery_whitelisted?
get? || xhr? || content_mime_type.nil? || !content_mime_type.verify_request?
end
@@ -134,6 +142,10 @@ module ActionDispatch
end
alias :xhr? :xml_http_request?
+ def ip
+ @ip ||= super
+ end
+
# Which IP addresses are "trusted proxies" that can be stripped from
# the right-hand-side of X-Forwarded-For
TRUSTED_PROXIES = /^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\./i
@@ -145,7 +157,7 @@ module ActionDispatch
# delimited list in the case of multiple chained proxies; the last
# address which is not trusted is the originating IP.
def remote_ip
- (@env["action_dispatch.remote_ip"] || ip).to_s
+ @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s
end
# Returns the lowercase name of the HTTP server software.
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index adde183cdb..18771fe782 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -176,7 +176,7 @@ module ActionDispatch
@app.call(env)
ensure
- if (session = env['rack.session']) && (flash = session['flash']) && flash.empty?
+ if (session = env['rack.session']) && session.key?('flash') && session['flash'].empty?
session.delete('flash')
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index 15493cd2eb..3e8d64b0c6 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -51,11 +51,11 @@ module ActionDispatch
super
end
- private
- def loaded?
- @loaded
- end
+ def loaded?
+ @loaded
+ end
+ private
def load!
stale_session_check! do
id, session = @by.send(:load_session, @env)
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 2dd2ec9fe9..f9e81a02d3 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -72,7 +72,7 @@ module ActionDispatch
rescue_action_in_public(exception)
end
rescue Exception => failsafe_error
- $stderr.puts "Error during failsafe response: #{failsafe_error}"
+ $stderr.puts "Error during failsafe response: #{failsafe_error}\n #{failsafe_error.backtrace * "\n "}"
FAILSAFE_RESPONSE
end
diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb
index e3180dba77..4240e7a5d5 100644
--- a/actionpack/lib/action_dispatch/middleware/stack.rb
+++ b/actionpack/lib/action_dispatch/middleware/stack.rb
@@ -5,13 +5,13 @@ module ActionDispatch
class Middleware
attr_reader :args, :block
- def initialize(klass, *args, &block)
- @klass, @args, @block = klass, args, block
+ def initialize(klass_or_name, *args, &block)
+ @ref = ActiveSupport::Dependencies::Reference.new(klass_or_name)
+ @args, @block = args, block
end
def klass
- return @klass if @klass.respond_to?(:new)
- @klass = ActiveSupport::Inflector.constantize(@klass.to_s)
+ @ref.get
end
def ==(middleware)
@@ -21,11 +21,7 @@ module ActionDispatch
when Class
klass == middleware
else
- if lazy_compare?(@klass) && lazy_compare?(middleware)
- normalize(@klass) == normalize(middleware)
- else
- klass.name == normalize(middleware.to_s)
- end
+ normalize(@ref.name) == normalize(middleware)
end
end
@@ -39,10 +35,6 @@ module ActionDispatch
private
- def lazy_compare?(object)
- object.is_a?(String) || object.is_a?(Symbol)
- end
-
def normalize(object)
object.to_s.strip.sub(/^::/, '')
end
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
index 09ff052fd0..e963b04524 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb
@@ -13,7 +13,7 @@
request_dump = clean_params.empty? ? 'None' : clean_params.inspect.gsub(',', ",\n")
def debug_hash(hash)
- hash.sort_by { |k, v| k.to_s }.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
+ hash.sort_by { |k, v| k.to_s }.map { |k, v| "#{k}: #{v.inspect rescue $!.message}" }.join("\n")
end
%>
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 8a8d21c434..ae4417b56c 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -622,12 +622,19 @@ module ActionDispatch
end
def member
- unless @scope[:scope_level] == :resources
- raise ArgumentError, "can't use member outside resources scope"
+ unless [:resources, :resource].include?(@scope[:scope_level])
+ raise ArgumentError, "You can't use member action outside resources and resource scope."
end
- with_scope_level(:member) do
- scope(':id', :name_prefix => parent_resource.member_name, :as => "") do
+ case @scope[:scope_level]
+ when :resources
+ with_scope_level(:member) do
+ scope(':id', :name_prefix => parent_resource.member_name, :as => "") do
+ yield
+ end
+ end
+ when :resource
+ with_scope_level(:member) do
yield
end
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 0d071dd7fe..750912b251 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -12,6 +12,7 @@ module ActionDispatch
def initialize(options={})
@defaults = options[:defaults]
@glob_param = options.delete(:glob)
+ @controllers = {}
end
def call(env)
@@ -29,19 +30,18 @@ module ActionDispatch
def prepare_params!(params)
merge_default_action!(params)
split_glob_param!(params) if @glob_param
-
- params.each do |key, value|
- if value.is_a?(String)
- value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding)
- params[key] = URI.unescape(value)
- end
- end
end
def controller(params, raise_error=true)
- if params && params.has_key?(:controller)
- controller = "#{params[:controller].camelize}Controller"
- ActiveSupport::Inflector.constantize(controller)
+ 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
end
rescue NameError => e
raise ActionController::RoutingError, e.message, e.backtrace if raise_error
@@ -466,6 +466,13 @@ module ActionDispatch
req = Rack::Request.new(env)
@set.recognize(req) do |route, matches, params|
+ params.each do |key, value|
+ if value.is_a?(String)
+ value = value.dup.force_encoding(Encoding::BINARY) if value.encoding_aware?
+ params[key] = URI.unescape(value)
+ end
+ end
+
dispatcher = route.app
if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
dispatcher.prepare_params!(params)
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index f4af763afe..7dd9dea358 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -184,7 +184,7 @@ module ActionView #:nodoc:
attr_internal :captures, :request, :controller, :template, :config
delegate :find_template, :template_exists?, :formats, :formats=, :locale, :locale=,
- :view_paths, :view_paths=, :with_fallbacks, :update_details, :to => :lookup_context
+ :view_paths, :view_paths=, :with_fallbacks, :update_details, :with_layout_format, :to => :lookup_context
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
:flash, :action_name, :controller_name, :to => :controller
@@ -201,6 +201,7 @@ module ActionView #:nodoc:
end
def self.process_view_paths(value)
+ return value.dup if value.is_a?(PathSet)
ActionView::PathSet.new(Array.wrap(value))
end
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index 7021342b4a..3aaa5e401c 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -13,8 +13,13 @@ module ActionView
mattr_accessor :registered_details
self.registered_details = []
+ mattr_accessor :registered_detail_setters
+ self.registered_detail_setters = []
+
def self.register_detail(name, options = {}, &block)
self.registered_details << name
+ self.registered_detail_setters << [name, "#{name}="]
+
Accessors.send :define_method, :"_#{name}_defaults", &block
Accessors.module_eval <<-METHOD, __FILE__, __LINE__ + 1
def #{name}
@@ -23,12 +28,7 @@ module ActionView
def #{name}=(value)
value = Array.wrap(value.presence || _#{name}_defaults)
-
- if value != @details[:#{name}]
- @details_key = nil
- @details = @details.dup if @details.frozen?
- @details[:#{name}] = value.freeze
- end
+ _set_detail(:#{name}, value) if value != @details[:#{name}]
end
METHOD
end
@@ -59,8 +59,11 @@ module ActionView
def initialize(view_paths, details = {})
@details, @details_key = { :handlers => default_handlers }, nil
@frozen_formats, @skip_default_locale = false, false
+
self.view_paths = view_paths
- self.update_details(details, true)
+ self.registered_detail_setters.each do |key, setter|
+ send(setter, details[key])
+ end
end
module ViewPaths
@@ -116,11 +119,11 @@ module ActionView
end
def default_handlers #:nodoc:
- @default_handlers ||= Template::Handlers.extensions
+ @@default_handlers ||= Template::Handlers.extensions
end
def handlers_regexp #:nodoc:
- @handlers_regexp ||= /\.(?:#{default_handlers.join('|')})$/
+ @@handlers_regexp ||= /\.(?:#{default_handlers.join('|')})$/
end
end
@@ -141,10 +144,13 @@ module ActionView
end
# Overload formats= to reject [:"*/*"] values.
- def formats=(value)
- value = nil if value == [:"*/*"]
- value << :html if value == [:js]
- super(value)
+ def formats=(values)
+ if values && values.size == 1
+ value = values.first
+ values = nil if value == :"*/*"
+ values << :html if value == :js
+ end
+ super(values)
end
# Do not use the default locale on template lookup.
@@ -170,24 +176,48 @@ module ActionView
super(@skip_default_locale ? I18n.locale : _locale_defaults)
end
+ # A method which only uses the first format in the formats array for layout lookup.
+ # This method plays straight with instance variables for performance reasons.
+ def with_layout_format
+ if formats.size == 1
+ yield
+ else
+ old_formats = formats
+ _set_detail(:formats, formats[0,1])
+
+ begin
+ yield
+ ensure
+ _set_detail(:formats, formats)
+ end
+ end
+ end
+
# Update the details keys by merging the given hash into the current
# details hash. If a block is given, the details are modified just during
# the execution of the block and reverted to the previous value after.
- def update_details(new_details, force=false)
+ def update_details(new_details)
old_details = @details.dup
- registered_details.each do |key|
- send(:"#{key}=", new_details[key]) if force || new_details.key?(key)
+ registered_detail_setters.each do |key, setter|
+ send(setter, new_details[key]) if new_details.key?(key)
end
- if block_given?
- begin
- yield
- ensure
- @details = old_details
- end
+ begin
+ yield
+ ensure
+ @details_key = nil
+ @details = old_details
end
end
+
+ protected
+
+ def _set_detail(key, value)
+ @details_key = nil
+ @details = @details.dup if @details.frozen?
+ @details[key] = value.freeze
+ end
end
include Accessors
diff --git a/actionpack/lib/action_view/render/layouts.rb b/actionpack/lib/action_view/render/layouts.rb
index 31b09d9f0a..a9dfc0cced 100644
--- a/actionpack/lib/action_view/render/layouts.rb
+++ b/actionpack/lib/action_view/render/layouts.rb
@@ -57,15 +57,11 @@ module ActionView
# This is the method which actually finds the layout using details in the lookup
# context object. If no layout is found, it checkes if at least a layout with
# the given name exists across all details before raising the error.
- #
- # If self.formats contains several formats, just the first one is considered in
- # the layout lookup.
def find_layout(layout)
begin
- if formats.size == 1
- _find_layout(layout)
- else
- update_details(:formats => self.formats.first){ _find_layout(layout) }
+ with_layout_format do
+ layout =~ /^\// ?
+ with_fallbacks { find_template(layout) } : find_template(layout)
end
rescue ActionView::MissingTemplate => e
update_details(:formats => nil) do
@@ -74,11 +70,6 @@ module ActionView
end
end
- def _find_layout(layout) #:nodoc:
- layout =~ /^\// ?
- with_fallbacks { find_template(layout) } : find_template(layout)
- end
-
# Contains the logic that actually renders the layout.
def _render_layout(layout, locals, &block) #:nodoc:
layout.render(self, locals){ |*name| _layout_for(*name, &block) }
diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb
index 35488c0391..6228d7ac39 100644
--- a/actionpack/lib/action_view/template/handlers.rb
+++ b/actionpack/lib/action_view/template/handlers.rb
@@ -19,7 +19,7 @@ module ActionView #:nodoc:
@@default_template_handlers = nil
def self.extensions
- @@template_handlers.keys
+ @@template_extensions ||= @@template_handlers.keys
end
# Register a class that knows how to handle template files with the given
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index 180889ddf2..ffa4f50b00 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -28,6 +28,10 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
post :reset
resource :info
+
+ member do
+ get :crush
+ end
end
match 'account/logout' => redirect("/logout"), :as => :logout_redirect
@@ -352,6 +356,14 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
end
end
+ def test_member_on_resource
+ with_test_routes do
+ get '/session/crush'
+ assert_equal 'sessions#crush', @response.body
+ assert_equal '/session/crush', crush_session_path
+ end
+ end
+
def test_redirect_modulo
with_test_routes do
get '/account/modulo/name'
diff --git a/actionpack/test/template/lookup_context_test.rb b/actionpack/test/template/lookup_context_test.rb
index df1aa2edb2..cc71cb42d0 100644
--- a/actionpack/test/template/lookup_context_test.rb
+++ b/actionpack/test/template/lookup_context_test.rb
@@ -26,18 +26,6 @@ class LookupContextTest < ActiveSupport::TestCase
assert_equal :en, @lookup_context.locale
end
- test "allows me to update details" do
- @lookup_context.update_details(:formats => [:html], :locale => :pt)
- assert_equal [:html], @lookup_context.formats
- assert_equal :pt, @lookup_context.locale
- end
-
- test "allows me to update an specific detail" do
- @lookup_context.update_details(:locale => :pt)
- assert_equal :pt, I18n.locale
- assert_equal :pt, @lookup_context.locale
- end
-
test "allows me to freeze and retrieve frozen formats" do
@lookup_context.formats.freeze
assert @lookup_context.formats.frozen?
@@ -54,7 +42,7 @@ class LookupContextTest < ActiveSupport::TestCase
end
test "provides getters and setters for formats" do
- @lookup_context.formats = :html
+ @lookup_context.formats = [:html]
assert_equal [:html], @lookup_context.formats
end
@@ -138,7 +126,7 @@ class LookupContextTest < ActiveSupport::TestCase
keys << @lookup_context.details_key
assert_equal 2, keys.uniq.size
- @lookup_context.formats = :html
+ @lookup_context.formats = [:html]
keys << @lookup_context.details_key
assert_equal 3, keys.uniq.size
diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec
index 678007c0ef..6bd6fe49ff 100644
--- a/activemodel/activemodel.gemspec
+++ b/activemodel/activemodel.gemspec
@@ -21,5 +21,5 @@ Gem::Specification.new do |s|
s.add_dependency('activesupport', version)
s.add_dependency('builder', '~> 2.1.2')
- s.add_dependency('i18n', '~> 0.4.0')
+ s.add_dependency('i18n', '~> 0.4.1')
end
diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb
index 74a33d45ab..d7e3ca100e 100644
--- a/activemodel/lib/active_model/observing.rb
+++ b/activemodel/lib/active_model/observing.rb
@@ -2,6 +2,7 @@ require 'singleton'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/module/aliasing'
require 'active_support/core_ext/string/inflections'
+require 'active_support/core_ext/string/conversions'
module ActiveModel
module Observing
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 5b0ba86308..284ae6695b 100755
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -2,6 +2,7 @@ require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/string/conversions'
module ActiveRecord
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb
index c553e95bad..0dcadfab5f 100644
--- a/activerecord/lib/active_record/autosave_association.rb
+++ b/activerecord/lib/active_record/autosave_association.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/array/wrap'
+
module ActiveRecord
# AutosaveAssociation is a module that takes care of automatically saving
# your associations when the parent is saved. In addition to saving, it
@@ -238,16 +240,10 @@ module ActiveRecord
# go through nested autosave associations that are loaded in memory (without loading
# any new ones), and return true if is changed for autosave
def nested_records_changed_for_autosave?
- self.class.reflect_on_all_autosave_associations.each do |reflection|
- if association = association_instance_get(reflection.name)
- if [:belongs_to, :has_one].include?(reflection.macro)
- return true if association.target && association.target.changed_for_autosave?
- else
- association.target.each {|record| return true if record.changed_for_autosave? }
- end
- end
+ self.class.reflect_on_all_autosave_associations.any? do |reflection|
+ association = association_instance_get(reflection.name)
+ association && Array.wrap(association.target).any?(&:changed_for_autosave?)
end
- false
end
# Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index bf8c546d2e..454d3e60e3 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -211,7 +211,7 @@ module ActiveRecord
# calling +checkout+ on this pool.
def checkin(conn)
@connection_mutex.synchronize do
- conn.run_callbacks :checkin do
+ conn.send(:_run_checkin_callbacks) do
@checked_out.delete conn
@queue.signal
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index ec25bbf18e..7c7bc5e292 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -3,48 +3,6 @@ require 'active_support/core_ext/kernel/requires'
require 'active_support/core_ext/object/blank'
require 'set'
-module MysqlCompat #:nodoc:
- # add all_hashes method to standard mysql-c bindings or pure ruby version
- def self.define_all_hashes_method!
- raise 'Mysql not loaded' unless defined?(::Mysql)
-
- target = defined?(Mysql::Result) ? Mysql::Result : MysqlRes
- return if target.instance_methods.include?('all_hashes') ||
- target.instance_methods.include?(:all_hashes)
-
- # Ruby driver has a version string and returns null values in each_hash
- # C driver >= 2.7 returns null values in each_hash
- if Mysql.const_defined?(:VERSION) && (Mysql::VERSION.is_a?(String) || Mysql::VERSION >= 20700)
- target.class_eval <<-'end_eval', __FILE__, __LINE__ + 1
- def all_hashes # def all_hashes
- rows = [] # rows = []
- each_hash { |row| rows << row } # each_hash { |row| rows << row }
- rows # rows
- end # end
- end_eval
-
- # adapters before 2.7 don't have a version constant
- # and don't return null values in each_hash
- else
- target.class_eval <<-'end_eval', __FILE__, __LINE__ + 1
- def all_hashes # def all_hashes
- rows = [] # rows = []
- all_fields = fetch_fields.inject({}) { |fields, f| # all_fields = fetch_fields.inject({}) { |fields, f|
- fields[f.name] = nil; fields # fields[f.name] = nil; fields
- } # }
- each_hash { |row| rows << all_fields.dup.update(row) } # each_hash { |row| rows << all_fields.dup.update(row) }
- rows # rows
- end # end
- end_eval
- end
-
- unless target.instance_methods.include?('all_hashes') ||
- target.instance_methods.include?(:all_hashes)
- raise "Failed to defined #{target.name}#all_hashes method. Mysql::VERSION = #{Mysql::VERSION.inspect}"
- end
- end
-end
-
module ActiveRecord
class Base
# Establishes a connection to the database that's used by all Active Record objects.
@@ -57,17 +15,17 @@ module ActiveRecord
password = config[:password].to_s
database = config[:database]
- # Require the MySQL driver and define Mysql::Result.all_hashes
unless defined? Mysql
begin
- require_library_or_gem('mysql')
+ require 'mysql'
rescue LoadError
- $stderr.puts '!!! Please install the mysql gem and try again: gem install mysql.'
- raise
+ raise "!!! Missing the mysql gem. Add it to your Gemfile: gem 'mysql', '2.8.1'"
end
- end
- MysqlCompat.define_all_hashes_method!
+ unless defined?(Mysql::Result) && Mysql::Result.method_defined?(:each_hash)
+ raise "!!! Outdated mysql gem. Upgrade to 2.8.1 or later. In your Gemfile: gem 'mysql', '2.8.1'"
+ end
+ end
mysql = Mysql.init
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey]
@@ -656,7 +614,8 @@ module ActiveRecord
def select(sql, name = nil)
@connection.query_with_result = true
result = execute(sql, name)
- rows = result.all_hashes
+ rows = []
+ result.each_hash { |row| rows << row }
result.free
rows
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 063f0f0fb2..4e4f9c385c 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -1149,7 +1149,7 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T
include AutosaveAssociationOnACollectionAssociationTests
end
-class TestAutosaveAssociationValidationsOnAHasManyAssocication < ActiveRecord::TestCase
+class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
@@ -1165,7 +1165,7 @@ class TestAutosaveAssociationValidationsOnAHasManyAssocication < ActiveRecord::T
end
end
-class TestAutosaveAssociationValidationsOnAHasOneAssocication < ActiveRecord::TestCase
+class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
@@ -1186,7 +1186,7 @@ class TestAutosaveAssociationValidationsOnAHasOneAssocication < ActiveRecord::Te
end
end
-class TestAutosaveAssociationValidationsOnABelongsToAssocication < ActiveRecord::TestCase
+class TestAutosaveAssociationValidationsOnABelongsToAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
@@ -1206,7 +1206,7 @@ class TestAutosaveAssociationValidationsOnABelongsToAssocication < ActiveRecord:
end
end
-class TestAutosaveAssociationValidationsOnAHABTMAssocication < ActiveRecord::TestCase
+class TestAutosaveAssociationValidationsOnAHABTMAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 57b66fb312..685b11cb03 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -734,7 +734,7 @@ class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase
end
end
-class TestHasOneAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
+class TestHasOneAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
@@ -774,7 +774,7 @@ class TestHasOneAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveRe
end
end
-class TestHasManyAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
+class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 933667c909..ba15043bde 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -390,9 +390,12 @@ module ActiveSupport
undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def _run_#{symbol}_callbacks(key = nil, &blk)
- if self.class.send("_update_#{symbol}_superclass_callbacks")
- self.class.__define_runner(#{symbol.inspect})
- return _run_#{symbol}_callbacks(key, &blk)
+ @_initialized_#{symbol}_callbacks ||= begin
+ if self.class.send("_update_#{symbol}_superclass_callbacks")
+ self.class.__define_runner(#{symbol.inspect})
+ return _run_#{symbol}_callbacks(key, &blk)
+ end
+ true
end
if key
diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb
index 6a243fe982..cd7a42ed2b 100644
--- a/activesupport/lib/active_support/core_ext/string/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/string/conversions.rb
@@ -47,4 +47,15 @@ class String
d[5] += d.pop
::DateTime.civil(*d)
end
+
+ # +constantize+ tries to find a declared constant with the name specified
+ # in the string. It raises a NameError when the name is not in CamelCase
+ # or is not initialized.
+ #
+ # Examples
+ # "Module".constantize # => Module
+ # "Class".constantize # => Class
+ def constantize
+ ActiveSupport::Inflector.constantize(self)
+ end
end
diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb
index 48b028bb64..ef479d559e 100644
--- a/activesupport/lib/active_support/core_ext/string/inflections.rb
+++ b/activesupport/lib/active_support/core_ext/string/inflections.rb
@@ -146,15 +146,4 @@ class String
def foreign_key(separate_class_name_and_id_with_underscore = true)
ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
end
-
- # +constantize+ tries to find a declared constant with the name specified
- # in the string. It raises a NameError when the name is not in CamelCase
- # or is not initialized.
- #
- # Examples
- # "Module".constantize # => Module
- # "Class".constantize # => Class
- def constantize
- ActiveSupport::Inflector.constantize(self)
- end
end
diff --git a/activesupport/lib/active_support/core_ext/uri.rb b/activesupport/lib/active_support/core_ext/uri.rb
index 28eabd2111..b7fe0a6209 100644
--- a/activesupport/lib/active_support/core_ext/uri.rb
+++ b/activesupport/lib/active_support/core_ext/uri.rb
@@ -6,11 +6,15 @@ if RUBY_VERSION >= '1.9'
str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
parser = URI::Parser.new
+
unless str == parser.unescape(parser.escape(str))
URI::Parser.class_eval do
remove_method :unescape
- def unescape(str, escaped = @regexp[:ESCAPED])
- enc = (str.encoding == Encoding::US_ASCII) ? Encoding::UTF_8 : str.encoding
+ def unescape(str, escaped = /%[a-fA-F\d]{2}/)
+ # TODO: Are we actually sure that ASCII == UTF-8?
+ # YK: My initial experiments say yes, but let's be sure please
+ enc = str.encoding
+ enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
end
end
diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb
index e14e225596..16c3bc1142 100644
--- a/activesupport/lib/active_support/dependencies.rb
+++ b/activesupport/lib/active_support/dependencies.rb
@@ -47,6 +47,9 @@ module ActiveSupport #:nodoc:
mattr_accessor :autoloaded_constants
self.autoloaded_constants = []
+ mattr_accessor :references
+ self.references = {}
+
# An array of constant names that need to be unloaded on every request. Used
# to allow arbitrary constants to be marked for unloading.
mattr_accessor :explicitly_unloadable_constants
@@ -476,9 +479,37 @@ module ActiveSupport #:nodoc:
def remove_unloadable_constants!
autoloaded_constants.each { |const| remove_constant const }
autoloaded_constants.clear
+ Reference.clear!
explicitly_unloadable_constants.each { |const| remove_constant const }
end
+ class Reference
+ @@constants = Hash.new { |h, k| h[k] = Inflector.constantize(k) }
+
+ attr_reader :name
+
+ def initialize(name)
+ @name = name.to_s
+ @@constants[@name] = name if name.respond_to?(:name)
+ end
+
+ def get
+ @@constants[@name]
+ end
+
+ def self.clear!
+ @@constants.clear
+ end
+ end
+
+ def ref(name)
+ references[name] ||= Reference.new(name)
+ end
+
+ def constantize(name)
+ ref(name).get
+ end
+
# Determine if the given constant has been automatically loaded.
def autoloaded?(desc)
# No name => anonymous module.
@@ -572,6 +603,7 @@ module ActiveSupport #:nodoc:
log "removing constant #{const}"
parent.instance_eval { remove_const to_remove }
+
return true
end
diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb
index 6c46b68eaf..1031662293 100644
--- a/activesupport/lib/active_support/message_verifier.rb
+++ b/activesupport/lib/active_support/message_verifier.rb
@@ -47,11 +47,11 @@ module ActiveSupport
def secure_compare(a, b)
return false unless a.bytesize == b.bytesize
- l = a.unpack "C#{a.bytesize}"
+ l = a.unpack "C*"
- res = 0
- b.each_byte { |byte| res |= byte ^ l.shift }
- res == 0
+ res = true
+ b.each_byte { |byte| res = (byte == l.shift) && res }
+ res
end
def generate_digest(data)
diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb
index a3a2f13436..3e003b3ed4 100644
--- a/activesupport/test/core_ext/date_ext_test.rb
+++ b/activesupport/test/core_ext/date_ext_test.rb
@@ -198,6 +198,16 @@ class DateExtCalculationsTest < ActiveSupport::TestCase
assert_equal Date.new(2005,2,28), Date.new(2004,2,29).advance(:years => 1) #leap day plus one year
end
+ def test_advance_does_first_years_and_then_days
+ assert_equal Date.new(2012, 2, 29), Date.new(2011, 2, 28).advance(:years => 1, :days => 1)
+ # If day was done first we would jump to 2012-03-01 instead.
+ end
+
+ def test_advance_does_first_months_and_then_days
+ assert_equal Date.new(2010, 3, 29), Date.new(2010, 2, 28).advance(:months => 1, :days => 1)
+ # If day was done first we would jump to 2010-04-01 instead.
+ end
+
def test_advance_in_calendar_reform
assert_equal Date.new(1582,10,15), Date.new(1582,10,4).advance(:days => 1)
assert_equal Date.new(1582,10,4), Date.new(1582,10,15).advance(:days => -1)
diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb
index 75ff88505d..5422c75f5f 100644
--- a/activesupport/test/dependencies_test.rb
+++ b/activesupport/test/dependencies_test.rb
@@ -431,6 +431,27 @@ class DependenciesTest < Test::Unit::TestCase
end
end
+ def test_references_should_work
+ with_loading 'dependencies' do
+ root = ActiveSupport::Dependencies.load_paths.first
+ c = ActiveSupport::Dependencies.ref("ServiceOne")
+ service_one_first = ServiceOne
+ assert_equal service_one_first, c.get
+ ActiveSupport::Dependencies.clear
+ assert ! defined?(ServiceOne)
+
+ service_one_second = ServiceOne
+ assert_not_equal service_one_first, c.get
+ assert_equal service_one_second, c.get
+ end
+ end
+
+ def test_constantize_shortcut_for_cached_constant_lookups
+ with_loading 'dependencies' do
+ assert_equal ServiceOne, ActiveSupport::Dependencies.constantize("ServiceOne")
+ end
+ end
+
def test_nested_load_error_isnt_rescued
with_loading 'dependencies' do
assert_raise(MissingSourceFile) do
diff --git a/railties/lib/rails/log_subscriber.rb b/railties/lib/rails/log_subscriber.rb
index 145c7e0ace..9a74fee745 100644
--- a/railties/lib/rails/log_subscriber.rb
+++ b/railties/lib/rails/log_subscriber.rb
@@ -52,6 +52,7 @@ module Rails
def self.add(namespace, log_subscriber, notifier = ActiveSupport::Notifications)
log_subscribers << log_subscriber
+ @flushable_loggers = nil
log_subscriber.public_methods(false).each do |event|
notifier.subscribe("#{event}.#{namespace}") do |*args|
@@ -70,11 +71,17 @@ module Rails
@log_subscribers ||= []
end
+ def self.flushable_loggers
+ @flushable_loggers ||= begin
+ loggers = log_subscribers.map(&:logger)
+ loggers.uniq!
+ loggers.select { |l| l.respond_to?(:flush) }
+ end
+ end
+
# Flush all log_subscribers' logger.
def self.flush_all!
- loggers = log_subscribers.map(&:logger)
- loggers.uniq!
- loggers.each { |l| l.flush if l.respond_to?(:flush) }
+ flushable_loggers.each(&:flush)
end
# By default, we use the Rails.logger for logging.
diff --git a/railties/lib/rails/rack/logger.rb b/railties/lib/rails/rack/logger.rb
index dd8b342f59..73e9af3b41 100644
--- a/railties/lib/rails/rack/logger.rb
+++ b/railties/lib/rails/rack/logger.rb
@@ -1,4 +1,5 @@
require 'rails/log_subscriber'
+require 'active_support/core_ext/time/conversions'
module Rails
module Rack
@@ -19,10 +20,10 @@ module Rails
def before_dispatch(env)
request = ActionDispatch::Request.new(env)
- path = request.fullpath.inspect rescue "unknown"
+ path = request.fullpath
- info "\n\nStarted #{request.method.to_s.upcase} #{path} " <<
- "for #{request.remote_ip} at #{Time.now.to_s(:db)}"
+ info "\n\nStarted #{env["REQUEST_METHOD"]} \"#{path}\" " \
+ "for #{request.ip} at #{Time.now.to_default_s}"
end
def after_dispatch(env)