提交 9e065c6b 编写于 作者: X Xavier Noria

Merge remote branch 'rails/master'

......@@ -22,7 +22,7 @@
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')
......
......@@ -139,14 +139,16 @@ def assign_parameters(routes, controller_path, action, parameters = {})
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 @@ def recycle!
@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 @@ def recycle!
@block = nil
@length = 0
@body = []
@charset = nil
@content_type = nil
@charset = @content_type = nil
@request = @template = nil
end
end
......
......@@ -52,9 +52,11 @@ def key?(key)
# 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 @@ def request_method_symbol
# 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 @@ def headers
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 @@ def xml_http_request?
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 @@ def xml_http_request?
# 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.
......
......@@ -176,7 +176,7 @@ def call(env)
@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
......
......@@ -51,11 +51,11 @@ def inspect
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)
......
......@@ -72,7 +72,7 @@ def render_exception(env, exception)
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
......
......@@ -5,13 +5,13 @@ class MiddlewareStack < Array
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 @@ def ==(middleware)
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 @@ def build(app)
private
def lazy_compare?(object)
object.is_a?(String) || object.is_a?(Symbol)
end
def normalize(object)
object.to_s.strip.sub(/^::/, '')
end
......
......@@ -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
%>
......
......@@ -622,12 +622,19 @@ def collection
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
......
......@@ -12,6 +12,7 @@ class Dispatcher #:nodoc:
def initialize(options={})
@defaults = options[:defaults]
@glob_param = options.delete(:glob)
@controllers = {}
end
def call(env)
......@@ -29,19 +30,18 @@ def call(env)
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 @@ def recognize_path(path, environment = {})
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)
......
......@@ -184,7 +184,7 @@ class << self
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 @@ def self.xss_safe? #:nodoc:
end
def self.process_view_paths(value)
return value.dup if value.is_a?(PathSet)
ActionView::PathSet.new(Array.wrap(value))
end
......
......@@ -13,8 +13,13 @@ class LookupContext #:nodoc:
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 @@ def #{name}
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 @@ def initialize
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 @@ def normalize_name(name, prefix) #:nodoc:
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 @@ def freeze_formats(formats, unless_frozen=false) #:nodoc:
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 @@ def locale=(value)
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
......
......@@ -57,15 +57,11 @@ def _layout_for(name = nil, &block) #:nodoc:
# 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 @@ def find_layout(layout)
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) }
......
......@@ -19,7 +19,7 @@ def self.extended(base)
@@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
......
......@@ -28,6 +28,10 @@ def self.matches?(request)
post :reset
resource :info
member do
get :crush
end
end
match 'account/logout' => redirect("/logout"), :as => :logout_redirect
......@@ -352,6 +356,14 @@ def test_session_info_nested_singleton_resource
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'
......
......@@ -26,18 +26,6 @@ def teardown
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 @@ def teardown
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 @@ def teardown
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
......
......@@ -21,5 +21,5 @@
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
......@@ -2,6 +2,7 @@
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
......
......@@ -2,6 +2,7 @@
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:
......
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 @@ def associated_records_to_validate_or_save(association, new_record, autosave)
# 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
......
......@@ -211,7 +211,7 @@ def checkout
# 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
......
......@@ -3,48 +3,6 @@
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 @@ def self.mysql_connection(config) # :nodoc:
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 @@ def configure_connection
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
......
......@@ -1149,7 +1149,7 @@ def setup
include AutosaveAssociationOnACollectionAssociationTests
end
class TestAutosaveAssociationValidationsOnAHasManyAssocication < ActiveRecord::TestCase
class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
......@@ -1165,7 +1165,7 @@ def setup
end
end
class TestAutosaveAssociationValidationsOnAHasOneAssocication < ActiveRecord::TestCase
class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
......@@ -1186,7 +1186,7 @@ def setup
end
end
class TestAutosaveAssociationValidationsOnABelongsToAssocication < ActiveRecord::TestCase
class TestAutosaveAssociationValidationsOnABelongsToAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
......@@ -1206,7 +1206,7 @@ def setup
end
end
class TestAutosaveAssociationValidationsOnAHABTMAssocication < ActiveRecord::TestCase
class TestAutosaveAssociationValidationsOnAHABTMAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
......
......@@ -734,7 +734,7 @@ def test_should_update_existing_records_with_non_standard_primary_key
end
end
class TestHasOneAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
class TestHasOneAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
......@@ -774,7 +774,7 @@ def setup
end
end
class TestHasManyAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
self.use_transactional_fixtures = false
def setup
......
......@@ -390,9 +390,12 @@ def __define_runner(symbol) #:nodoc:
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
......
......@@ -47,4 +47,15 @@ def to_datetime
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
......@@ -146,15 +146,4 @@ def humanize
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
......@@ -6,11 +6,15 @@
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
......
......@@ -47,6 +47,9 @@ module Dependencies #: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 @@ def load_missing_constant(from_mod, const_name)
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 @@ def remove_constant(const) #:nodoc:
log "removing constant #{const}"
parent.instance_eval { remove_const to_remove }
return true
end
......
......@@ -47,11 +47,11 @@ def generate(value)
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)
......
......@@ -198,6 +198,16 @@ def test_advance
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)
......
......@@ -431,6 +431,27 @@ def test_removal_from_tree_should_be_detected
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
......
......@@ -52,6 +52,7 @@ class LogSubscriber
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 @@ def self.log_subscribers
@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.
......
require 'rails/log_subscriber'
require 'active_support/core_ext/time/conversions'
module Rails
module Rack
......@@ -19,10 +20,10 @@ def call(env)
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)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册