From bd9805871b576984e13c3d99558eda27d22c06c5 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 29 May 2010 19:26:02 -0700 Subject: Include backtrace in failsafe log. Rescue possible exceptions in failsafe response. --- actionpack/lib/action_dispatch/middleware/show_exceptions.rb | 2 +- .../middleware/templates/rescues/_request_and_response.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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/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 %> -- cgit v1.2.3 From ab1407cc5bbfe67f9e59335fe0a4e8ac82d513c6 Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 09:47:25 -0700 Subject: Improve performance of commonly used request methods --- actionpack/lib/action_dispatch/http/request.rb | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) 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. -- cgit v1.2.3 From a87b62729715fb286ea613e6e3ec59135a82529d Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 09:47:37 -0700 Subject: Use faster form of running callbacks --- .../lib/active_record/connection_adapters/abstract/connection_pool.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..dfc76fcae7 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._run_checkin_callbacks do @checked_out.delete conn @queue.signal end -- cgit v1.2.3 From 220603ee70d1655cf97a1e9f09fbc5d019c99856 Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 09:48:03 -0700 Subject: Eliminate the need to check for superclass changes to the callback stack each time through the callbacks --- activesupport/lib/active_support/callbacks.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 -- cgit v1.2.3 From 5fa3a2d12395ce1d7188c3b7dcb5d616e77eb5dd Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 09:48:29 -0700 Subject: Improve performance of the log subscriber by remembering the list of all loggers instead of trying to extract them each time --- railties/lib/rails/log_subscriber.rb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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. -- cgit v1.2.3 From 8b05c5207dd5757d55d0c384740db289e6bd5415 Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 09:48:57 -0700 Subject: Improve performance of MessageVerifier while keeping it constant time --- activesupport/lib/active_support/message_verifier.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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) -- cgit v1.2.3 From 67a2d648d88f8520612fd7b01fcc7223290aac75 Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 09:49:25 -0700 Subject: Improve performance of the Logger middleware by using simpler versions of methods --- railties/lib/rails/rack/logger.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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) -- cgit v1.2.3 From a260e02fd3e967d2d6b602aa3bd15f952d4e45cc Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 10:03:28 -0700 Subject: Whoops. _run_*_callbacks is private --- .../lib/active_record/connection_adapters/abstract/connection_pool.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 dfc76fcae7..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_checkin_callbacks do + conn.send(:_run_checkin_callbacks) do @checked_out.delete conn @queue.signal end -- cgit v1.2.3 From cb1b2a719ac2ba31dc8d66195c0be32798255be0 Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 10:03:53 -0700 Subject: Stop the flash middleware from forcibly loading sessions even if the user doesn't use sessions at all --- actionpack/lib/action_dispatch/middleware/flash.rb | 4 ++-- .../lib/action_dispatch/middleware/session/abstract_store.rb | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index adde183cdb..043966a585 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -170,13 +170,13 @@ module ActionDispatch end def call(env) - if (session = env['rack.session']) && (flash = session['flash']) + if (session = env['rack.session']) && session.key?('flash') flash.sweep end @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) -- cgit v1.2.3 From ff4c218095687ef9925baaca78d644579831d3c1 Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 10:27:21 -0700 Subject: Memoizing methods on request means we need to clear them out on recycle! --- actionpack/lib/action_controller/test_case.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 34499fa784..37906b79f6 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -155,6 +155,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 +169,7 @@ module ActionController @block = nil @length = 0 @body = [] - @charset = nil - @content_type = nil - + @charset = @content_type = nil @request = @template = nil end end -- cgit v1.2.3 From b8af484476d1dda685f058a0f185608cd18a862e Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 11:49:55 -0700 Subject: No need to unescape params twice if we came from Rack::Mount --- actionpack/lib/action_dispatch/routing/route_set.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 0d071dd7fe..5c4c96d9fc 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -29,13 +29,6 @@ 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) @@ -466,6 +459,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) -- cgit v1.2.3 From 16ee4b4d1b125bd3edb5c191d58c7afdf6d3232e Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 11:50:34 -0700 Subject: Small optimization of 1.9 unescape. We should make sure that inbound ASCII always means UTF-8. It seems so based on a quick survey of common browsers, but let's be sure --- activesupport/lib/active_support/core_ext/uri.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 -- cgit v1.2.3 From a6b39428431abeaa0251bbf4b6582e578f81783f Mon Sep 17 00:00:00 2001 From: wycats Date: Fri, 4 Jun 2010 17:28:04 -0700 Subject: Optimize LookupContext --- .../lib/action_dispatch/routing/route_set.rb | 13 ++++++-- actionpack/lib/action_view/base.rb | 1 + actionpack/lib/action_view/lookup_context.rb | 35 +++++++++++++++------- actionpack/lib/action_view/template/handlers.rb | 2 +- activesupport/lib/active_support/dependencies.rb | 23 ++++++++++++++ activesupport/test/dependencies_test.rb | 15 ++++++++++ 6 files changed, 75 insertions(+), 14 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 5c4c96d9fc..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) @@ -32,9 +33,15 @@ module ActionDispatch 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 diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index f4af763afe..16d7d25279 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -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..801b08b19d 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -13,8 +13,12 @@ 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} @@ -60,7 +64,7 @@ module ActionView @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.initialize_details(details) end module ViewPaths @@ -116,11 +120,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 +145,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,14 +177,22 @@ module ActionView super(@skip_default_locale ? I18n.locale : _locale_defaults) end + def initialize_details(details) + details = details.dup + + registered_detail_setters.each do |key, setter| + send(setter, details[key]) + 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? 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/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index e14e225596..e0d2b70306 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,28 @@ module ActiveSupport #:nodoc: def remove_unloadable_constants! autoloaded_constants.each { |const| remove_constant const } autoloaded_constants.clear + references.each {|k,v| v.clear! } explicitly_unloadable_constants.each { |const| remove_constant const } end + class Reference + def initialize(constant, name) + @constant, @name = constant, name + end + + def get + @constant ||= Inflector.constantize(@name) + end + + def clear! + @constant = nil + end + end + + def ref(name) + references[name] ||= Reference.new(Inflector.constantize(name), name) + end + # Determine if the given constant has been automatically loaded. def autoloaded?(desc) # No name => anonymous module. @@ -572,6 +594,7 @@ module ActiveSupport #:nodoc: log "removing constant #{const}" parent.instance_eval { remove_const to_remove } + return true end diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 75ff88505d..15543e07c3 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -431,6 +431,21 @@ 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_nested_load_error_isnt_rescued with_loading 'dependencies' do assert_raise(MissingSourceFile) do -- cgit v1.2.3 From a5f3f3ef7a10bcc3771910a63018cfd558485965 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 5 Jun 2010 14:50:34 -0700 Subject: MySQL: require 2.7 or later so we can rely on result.each_hash --- .../connection_adapters/mysql_adapter.rb | 57 +++------------------- 1 file changed, 8 insertions(+), 49 deletions(-) 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 -- cgit v1.2.3 From 7ace23abacfecf6d34630bb5141faf80a8b88634 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 5 Jun 2010 21:31:16 -0700 Subject: Restore flash sweep --- actionpack/lib/action_dispatch/middleware/flash.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb index 043966a585..18771fe782 100644 --- a/actionpack/lib/action_dispatch/middleware/flash.rb +++ b/actionpack/lib/action_dispatch/middleware/flash.rb @@ -170,7 +170,7 @@ module ActionDispatch end def call(env) - if (session = env['rack.session']) && session.key?('flash') + if (session = env['rack.session']) && (flash = session['flash']) flash.sweep end -- cgit v1.2.3 From 35ae42be4f9e23ae954e4705276b460998cb401b Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 5 Jun 2010 21:31:30 -0700 Subject: Bump i18n to 0.4.1 --- actionpack/actionpack.gemspec | 2 +- activemodel/activemodel.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 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/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 -- cgit v1.2.3 From fd1a5041362f5e65b813b7938d477143bd82de40 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 5 Jun 2010 20:33:53 -0700 Subject: ActiveSupport::Dependencies.constantize shortcut for caching named constant lookups --- .../lib/active_support/core_ext/string/conversions.rb | 11 +++++++++++ .../lib/active_support/core_ext/string/inflections.rb | 11 ----------- activesupport/lib/active_support/dependencies.rb | 12 +++++++++--- activesupport/test/dependencies_test.rb | 6 ++++++ 4 files changed, 26 insertions(+), 14 deletions(-) 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/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index e0d2b70306..5091abc3a4 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -484,8 +484,10 @@ module ActiveSupport #:nodoc: end class Reference - def initialize(constant, name) - @constant, @name = constant, name + attr_reader :name + + def initialize(name, constant = nil) + @name, @constant = name, constant end def get @@ -498,7 +500,11 @@ module ActiveSupport #:nodoc: end def ref(name) - references[name] ||= Reference.new(Inflector.constantize(name), name) + references[name] ||= Reference.new(name) + end + + def constantize(name) + ref(name).get end # Determine if the given constant has been automatically loaded. diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 15543e07c3..5422c75f5f 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -446,6 +446,12 @@ class DependenciesTest < Test::Unit::TestCase 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 -- cgit v1.2.3 From 9d0d6f7d261a14def6afd17568b8c8aa7c33d0b3 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 5 Jun 2010 22:02:49 -0700 Subject: Clear const references all at once --- activesupport/lib/active_support/dependencies.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 5091abc3a4..16c3bc1142 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -479,23 +479,26 @@ module ActiveSupport #:nodoc: def remove_unloadable_constants! autoloaded_constants.each { |const| remove_constant const } autoloaded_constants.clear - references.each {|k,v| v.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, constant = nil) - @name, @constant = name, constant + def initialize(name) + @name = name.to_s + @@constants[@name] = name if name.respond_to?(:name) end def get - @constant ||= Inflector.constantize(@name) + @@constants[@name] end - def clear! - @constant = nil + def self.clear! + @@constants.clear end end -- cgit v1.2.3 From 509f3d7d2f346b83dfd22aec681feffbd2d25803 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 5 Jun 2010 20:34:30 -0700 Subject: Simplify middleware stack lazy compares using named const references --- actionpack/lib/action_dispatch/middleware/stack.rb | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) 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 -- cgit v1.2.3 From 9f93de9d3dd7db8de67cb0ee10ea03cdba9b6e5c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 6 Jun 2010 12:07:40 -0700 Subject: Reset request.parameters after assigning params for functional tests --- actionpack/lib/action_controller/test_case.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 37906b79f6..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 -- cgit v1.2.3 From 83729e2fe36a0a629c2a5a52a7e2970287d57036 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sun, 6 Jun 2010 19:14:53 -0400 Subject: Formats should always be an array. --- actionpack/lib/action_view/render/layouts.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/render/layouts.rb b/actionpack/lib/action_view/render/layouts.rb index 31b09d9f0a..255c44450d 100644 --- a/actionpack/lib/action_view/render/layouts.rb +++ b/actionpack/lib/action_view/render/layouts.rb @@ -65,7 +65,7 @@ module ActionView if formats.size == 1 _find_layout(layout) else - update_details(:formats => self.formats.first){ _find_layout(layout) } + update_details(:formats => [self.formats.first]) { _find_layout(layout) } end rescue ActionView::MissingTemplate => e update_details(:formats => nil) do -- cgit v1.2.3 From ac9f8e1b7bbbfa13ff75c86ef72fe6641ba26eb3 Mon Sep 17 00:00:00 2001 From: Rizwan Reza Date: Sun, 6 Jun 2010 14:00:09 +0430 Subject: Router accepts member routes on resource. [#4624 state:resolved] --- actionpack/lib/action_dispatch/routing/mapper.rb | 19 ++++++++++++++----- actionpack/test/dispatch/routing_test.rb | 13 +++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 8a8d21c434..63c553dd07 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -622,13 +622,22 @@ 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 - yield + 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 + scope(':id', :as => "") do + yield + end end end end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 180889ddf2..b2b8aca9b2 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,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_member_on_resource + with_test_routes do + get '/session/1/crush' + assert_equal 'sessions#crush', @response.body + + assert_equal '/session/1/crush', crush_session_path(1) + end + end + def test_redirect_modulo with_test_routes do get '/account/modulo/name' -- cgit v1.2.3 From d96efe63681cdf63e7c1a1448387a39af736bab9 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Sun, 6 Jun 2010 18:42:21 +0200 Subject: the order in which we apply deltas in Date#advance matters, add test coverage for that --- activesupport/test/core_ext/date_ext_test.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index a3a2f13436..55cd566738 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, 28), Date.new(2010, 2, 28).advance(:months => 1, :day => 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) -- cgit v1.2.3 From 8c7730db02b8a26c318caf98e726ee6e9f39df24 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Mon, 7 Jun 2010 07:27:51 +0200 Subject: oops, two cancelling errors made a previous test pass, fixing it --- activesupport/test/core_ext/date_ext_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 55cd566738..3e003b3ed4 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -204,7 +204,7 @@ class DateExtCalculationsTest < ActiveSupport::TestCase end def test_advance_does_first_months_and_then_days - assert_equal Date.new(2010, 3, 28), Date.new(2010, 2, 28).advance(:months => 1, :day => 1) + 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 -- cgit v1.2.3 From b3d2080278fc2b17d64010c3bfb149a08583b667 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 7 Jun 2010 02:39:24 -0300 Subject: Observing module is using constantize --- activemodel/lib/active_model/observing.rb | 1 + 1 file changed, 1 insertion(+) 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 -- cgit v1.2.3 From 5273bd97e6746bd5bdc2450ad5a2d60f6a6eb7ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 7 Jun 2010 10:13:41 +0200 Subject: Make AP test suite green once again and speed up performance in layouts lookup for some cases. --- actionpack/lib/action_view/base.rb | 2 +- actionpack/lib/action_view/lookup_context.rb | 51 ++++++++++++++++--------- actionpack/lib/action_view/render/layouts.rb | 15 ++------ actionpack/test/template/lookup_context_test.rb | 16 +------- 4 files changed, 39 insertions(+), 45 deletions(-) diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 16d7d25279..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 diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb index 801b08b19d..3aaa5e401c 100644 --- a/actionpack/lib/action_view/lookup_context.rb +++ b/actionpack/lib/action_view/lookup_context.rb @@ -19,6 +19,7 @@ module ActionView 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} @@ -27,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 @@ -63,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.initialize_details(details) + self.registered_detail_setters.each do |key, setter| + send(setter, details[key]) + end end module ViewPaths @@ -177,11 +176,20 @@ module ActionView super(@skip_default_locale ? I18n.locale : _locale_defaults) end - def initialize_details(details) - details = details.dup - - registered_detail_setters.each do |key, setter| - send(setter, details[key]) + # 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 @@ -195,14 +203,21 @@ module ActionView 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 255c44450d..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/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 -- cgit v1.2.3 From 3f1cdb85b80b935791018e59161e527617af6f1f Mon Sep 17 00:00:00 2001 From: Tom Meier Date: Mon, 7 Jun 2010 14:18:56 +1000 Subject: Require active support/string/conversions so constantize can be used in associations.rb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/associations.rb | 1 + 1 file changed, 1 insertion(+) 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: -- cgit v1.2.3 From 7eedc3f3975cca2b7e729fcb67e310977b5e5dcd Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Tue, 18 May 2010 22:49:19 -0300 Subject: Fixing test class names and refactor line in autosave association MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activerecord/lib/active_record/autosave_association.rb | 2 +- activerecord/test/cases/autosave_association_test.rb | 8 ++++---- activerecord/test/cases/nested_attributes_test.rb | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index c553e95bad..59a7bc1da1 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -243,7 +243,7 @@ module ActiveRecord 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? } + return true if association.target.detect { |record| record.changed_for_autosave? } end end 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 -- cgit v1.2.3 From 634c9310e302aba0c113363ccec748460113b523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 7 Jun 2010 11:00:39 +0200 Subject: Make the logic for nested_records_changed_for_autosave? simpler. [#4648 state:resolved] --- activerecord/lib/active_record/autosave_association.rb | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 59a7bc1da1..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 - return true if association.target.detect { |record| 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 :validate or :autosave is -- cgit v1.2.3 From 1a8f784a236058101c6e8b6387aefc96e86a1e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 7 Jun 2010 11:20:54 +0200 Subject: member on resource should not expect an ID. --- actionpack/lib/action_dispatch/routing/mapper.rb | 4 +--- actionpack/test/dispatch/routing_test.rb | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 63c553dd07..ae4417b56c 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -635,9 +635,7 @@ module ActionDispatch end when :resource with_scope_level(:member) do - scope(':id', :as => "") do - yield - end + yield end end end diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index b2b8aca9b2..ffa4f50b00 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -358,10 +358,9 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest def test_member_on_resource with_test_routes do - get '/session/1/crush' + get '/session/crush' assert_equal 'sessions#crush', @response.body - - assert_equal '/session/1/crush', crush_session_path(1) + assert_equal '/session/crush', crush_session_path end end -- cgit v1.2.3