diff options
Diffstat (limited to 'activesupport/lib/active_support')
17 files changed, 513 insertions, 298 deletions
diff --git a/activesupport/lib/active_support/all.rb b/activesupport/lib/active_support/all.rb index f537818300..def8eca89f 100644 --- a/activesupport/lib/active_support/all.rb +++ b/activesupport/lib/active_support/all.rb @@ -1,3 +1,4 @@ require 'active_support' require 'active_support/time' require 'active_support/core_ext' +require 'active_support/json' diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index f04544c0c5..bef9c98ecf 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -267,27 +267,39 @@ module ActiveSupport # :bar # end # cache.fetch("foo") # => "bar" - def fetch(name, options = nil, &block) - options = merged_options(options) - key = namespaced_key(name, options) - entry = instrument(:read, name, options) { read_entry(key, options) } unless options[:force] - if entry && entry.expired? - race_ttl = options[:race_condition_ttl].to_f - if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl - entry.expires_at = Time.now + race_ttl - write_entry(key, entry, :expires_in => race_ttl * 2) - else - delete_entry(key, options) + def fetch(name, options = nil) + if block_given? + options = merged_options(options) + key = namespaced_key(name, options) + unless options[:force] + entry = instrument(:read, name, options) do |payload| + payload[:super_operation] = :fetch if payload + read_entry(key, options) + end + end + if entry && entry.expired? + race_ttl = options[:race_condition_ttl].to_f + if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl + entry.expires_at = Time.now + race_ttl + write_entry(key, entry, :expires_in => race_ttl * 2) + else + delete_entry(key, options) + end + entry = nil end - entry = nil - end - if entry - entry.value - elsif block_given? - result = instrument(:generate, name, options, &block) - write(name, result, options) - result + if entry + instrument(:fetch_hit, name, options) { |payload| } + entry.value + else + result = instrument(:generate, name, options) do |payload| + yield + end + write(name, result, options) + result + end + else + read(name, options) end end @@ -299,16 +311,19 @@ module ActiveSupport def read(name, options = nil) options = merged_options(options) key = namespaced_key(name, options) - instrument(:read, name, options) do + instrument(:read, name, options) do |payload| entry = read_entry(key, options) if entry if entry.expired? delete_entry(key, options) + payload[:hit] = false if payload nil else + payload[:hit] = true if payload entry.value end else + payload[:hit] = false if payload nil end end @@ -345,7 +360,7 @@ module ActiveSupport # +options+. def write(name, value, options = nil) options = merged_options(options) - instrument(:write, name, options) do + instrument(:write, name, options) do |payload| entry = Entry.new(value, options) write_entry(namespaced_key(name, options), entry, options) end @@ -356,7 +371,7 @@ module ActiveSupport # Options are passed to the underlying cache implementation. def delete(name, options = nil) options = merged_options(options) - instrument(:delete, name) do + instrument(:delete, name) do |payload| delete_entry(namespaced_key(name, options), options) end end @@ -366,7 +381,7 @@ module ActiveSupport # Options are passed to the underlying cache implementation. def exist?(name, options = nil) options = merged_options(options) - instrument(:exist?, name) do + instrument(:exist?, name) do |payload| entry = read_entry(namespaced_key(name, options), options) if entry && !entry.expired? true @@ -502,9 +517,9 @@ module ActiveSupport if self.class.instrument payload = { :key => key } payload.merge!(options) if options.is_a?(Hash) - ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield } + ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) } else - yield + yield(nil) end end diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index e3a2688e2f..852defeae8 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -35,7 +35,7 @@ module ActiveSupport def self.build_mem_cache(*addresses) addresses = addresses.flatten options = addresses.extract_options! - addresses = ["localhost"] if addresses.empty? + addresses = ["localhost:11211"] if addresses.empty? MemCache.new(addresses, options) end @@ -159,7 +159,7 @@ module ActiveSupport private def escape_key(key) - key = key.to_s.gsub(ESCAPE_KEY_CHARS){|match| "%#{match[0].to_s(16).upcase}"} + key = key.to_s.gsub(ESCAPE_KEY_CHARS){|match| "%#{match.getbyte(0).to_s(16).upcase}"} key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250 key end diff --git a/activesupport/lib/active_support/core_ext/class.rb b/activesupport/lib/active_support/core_ext/class.rb index f2ca9c7cc9..6f308a0d62 100644 --- a/activesupport/lib/active_support/core_ext/class.rb +++ b/activesupport/lib/active_support/core_ext/class.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/class/inheritable_attributes' require 'active_support/core_ext/class/delegating_attributes' diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb index 4be7eaf476..7aff05dcdf 100644 --- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb @@ -128,7 +128,7 @@ class Class # :nodoc: private # Prevent this constant from being created multiple times - EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES) + EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze def inherited_with_inheritable_attributes(child) inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes) diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb index c4994ca2ee..d023b4bcff 100644 --- a/activesupport/lib/active_support/core_ext/logger.rb +++ b/activesupport/lib/active_support/core_ext/logger.rb @@ -72,44 +72,6 @@ class Logger @formatter ||= SimpleFormatter.new end - unless const_defined? :Formatter - class Formatter - Format = "%s, [%s#%d] %5s -- %s: %s\n" - - attr_accessor :datetime_format - - def initialize - @datetime_format = nil - end - - def call(severity, time, progname, msg) - Format % [severity[0..0], format_datetime(time), $$, severity, progname, - msg2str(msg)] - end - - private - def format_datetime(time) - if @datetime_format.nil? - time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec - else - time.strftime(@datetime_format) - end - end - - def msg2str(msg) - case msg - when ::String - msg - when ::Exception - "#{ msg.message } (#{ msg.class })\n" << - (msg.backtrace || []).join("\n") - else - msg.inspect - end - end - end - end - # Simple formatter which only displays the message. class SimpleFormatter < Logger::Formatter # This method is invoked when a log event occurs diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index cd7a42ed2b..af277f9995 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -28,6 +28,10 @@ class String self[0] end unless method_defined?(:ord) + def getbyte(index) + self[index] + end if RUBY_VERSION < '1.9' + # Form can be either :utc (default) or :local. def to_time(form = :utc) return nil if self.blank? @@ -47,15 +51,4 @@ 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 ef479d559e..66c4034781 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -28,6 +28,17 @@ class String ActiveSupport::Inflector.singularize(self) 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 + # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize # is set to <tt>:lower</tt> then camelize produces lowerCamelCase. # diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index e8210dfe37..7d5143ba37 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -33,14 +33,14 @@ module ActiveSupport #:nodoc: # The set of directories from which we may automatically load files. Files # under these directories will be reloaded on each request in development mode, - # unless the directory also appears in load_once_paths. - mattr_accessor :load_paths - self.load_paths = [] + # unless the directory also appears in autoload_once_paths. + mattr_accessor :autoload_paths + self.autoload_paths = [] # The set of directories from which automatically loaded constants are loaded - # only once. All directories in this set must also be present in +load_paths+. - mattr_accessor :load_once_paths - self.load_once_paths = [] + # only once. All directories in this set must also be present in +autoload_paths+. + mattr_accessor :autoload_once_paths + self.autoload_once_paths = [] # An array of qualified constant names that have been loaded. Adding a name to # this array will cause it to be unloaded the next time Dependencies are cleared. @@ -352,22 +352,31 @@ module ActiveSupport #:nodoc: # Given +path+, a filesystem path to a ruby file, return an array of constant # paths which would cause Dependencies to attempt to load this file. - def loadable_constants_for_path(path, bases = load_paths) - expanded_path = Pathname.new(path[/\A(.*?)(\.rb)?\Z/, 1]).expand_path + def loadable_constants_for_path(path, bases = autoload_paths) + path = $1 if path =~ /\A(.*)\.rb\Z/ + expanded_path = File.expand_path(path) + paths = [] + + bases.each do |root| + expanded_root = File.expand_path(root) + next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path + + nesting = expanded_path[(expanded_root.size)..-1] + nesting = nesting[1..-1] if nesting && nesting[0] == ?/ + next if nesting.blank? - bases.inject([]) do |paths, root| - expanded_root = Pathname.new(root).expand_path - nesting = expanded_path.relative_path_from(expanded_root).to_s - next paths if nesting =~ /\.\./ paths << nesting.camelize - end.uniq + end + + paths.uniq! + paths end - # Search for a file in load_paths matching the provided suffix. + # Search for a file in autoload_paths matching the provided suffix. def search_for_file(path_suffix) path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb") - load_paths.each do |root| + autoload_paths.each do |root| path = File.join(root, path_suffix) return path if File.file? path end @@ -377,14 +386,14 @@ module ActiveSupport #:nodoc: # Does the provided path_suffix correspond to an autoloadable module? # Instead of returning a boolean, the autoload base for this module is returned. def autoloadable_module?(path_suffix) - load_paths.each do |load_path| + autoload_paths.each do |load_path| return load_path if File.directory? File.join(load_path, path_suffix) end nil end def load_once_path?(path) - load_once_paths.any? { |base| path.starts_with? base } + autoload_once_paths.any? { |base| path.starts_with? base } end # Attempt to autoload the provided module name by searching for a directory @@ -396,7 +405,7 @@ module ActiveSupport #:nodoc: return nil unless base_path = autoloadable_module?(path_suffix) mod = Module.new into.const_set const_name, mod - autoloaded_constants << qualified_name unless load_once_paths.include?(base_path) + autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path) return mod end diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb index c0b5ca4deb..5f5b264eb9 100644 --- a/activesupport/lib/active_support/file_update_checker.rb +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -19,7 +19,7 @@ module ActiveSupport def initialize(paths, calculate=false, &block) @paths = paths @block = block - @last_update_at = updated_at if calculate + @last_update_at = calculate ? updated_at : nil end def updated_at @@ -34,4 +34,4 @@ module ActiveSupport end end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 02c233595d..dd94315111 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -128,12 +128,21 @@ module ActiveSupport end end -class Object - # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. - def to_json(options = nil) - ActiveSupport::JSON.encode(self, options) - end +# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting +# their default behavior. That said, we need to define the basic to_json method in all of them, +# otherwise they will always use to_json gem implementation, which is backwards incompatible in +# several cases (for instance, the JSON implementation for Hash does not work) with inheritance +# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json. +[Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass| + klass.class_eval <<-RUBY, __FILE__, __LINE__ + # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. + def to_json(options = nil) + ActiveSupport::JSON.encode(self, options) + end + RUBY +end +class Object def as_json(options = nil) #:nodoc: if respond_to?(:to_hash) to_hash diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb new file mode 100644 index 0000000000..891d718af3 --- /dev/null +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -0,0 +1,118 @@ +require 'active_support/core_ext/module/attribute_accessors' +require 'active_support/core_ext/class/attribute' + +module ActiveSupport + # ActiveSupport::LogSubscriber is an object set to consume ActiveSupport::Notifications + # with solely purpose of logging. The log subscriber dispatches notifications to a + # regirested object based on its given namespace. + # + # An example would be Active Record log subscriber responsible for logging queries: + # + # module ActiveRecord + # class LogSubscriber < ActiveSupport::LogSubscriber + # def sql(event) + # "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}" + # end + # end + # end + # + # And it's finally registed as: + # + # ActiveRecord::LogSubscriber.attach_to :active_record + # + # Since we need to know all instance methods before attaching the log subscriber, + # the line above shuold be called after your ActiveRecord::LogSubscriber definition. + # + # After configured, whenever a "sql.active_record" notification is published, + # it will properly dispatch the event (ActiveSupport::Notifications::Event) to + # the sql method. + # + # Log subscriber also has some helpers to deal with logging and automatically flushes + # all logs when the request finishes (via action_dispatch.callback notification) in + # a Rails environment. + class LogSubscriber + mattr_accessor :colorize_logging + self.colorize_logging = true + + class_attribute :logger + + class << self + remove_method :logger + end + + def self.logger + @logger ||= Rails.logger if defined?(Rails) + end + + # Embed in a String to clear all previous ANSI sequences. + CLEAR = "\e[0m" + BOLD = "\e[1m" + + # Colors + BLACK = "\e[30m" + RED = "\e[31m" + GREEN = "\e[32m" + YELLOW = "\e[33m" + BLUE = "\e[34m" + MAGENTA = "\e[35m" + CYAN = "\e[36m" + WHITE = "\e[37m" + + def self.attach_to(namespace, log_subscriber=new, notifier=ActiveSupport::Notifications) + log_subscribers << log_subscriber + @@flushable_loggers = nil + + log_subscriber.public_methods(false).each do |event| + notifier.subscribe("#{event}.#{namespace}") do |*args| + next if log_subscriber.logger.nil? + + begin + log_subscriber.send(event, ActiveSupport::Notifications::Event.new(*args)) + rescue Exception => e + log_subscriber.logger.error "Could not log #{args[0].inspect} event. #{e.class}: #{e.message}" + end + end + end + end + + 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! + flushable_loggers.each(&:flush) + end + + protected + + %w(info debug warn error fatal unknown).each do |level| + class_eval <<-METHOD, __FILE__, __LINE__ + 1 + def #{level}(*args, &block) + return unless logger + logger.#{level}(*args, &block) + end + METHOD + end + + # Set color by using a string or one of the defined constants. If a third + # option is set to true, it also adds bold to the string. This is based + # on Highline implementation and it automatically appends CLEAR to the end + # of the returned String. + # + def color(text, color, bold=false) + return text unless colorize_logging + color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol) + bold = bold ? BOLD : "" + "#{bold}#{color}#{text}#{CLEAR}" + end + end +end diff --git a/activesupport/lib/active_support/log_subscriber/test_helper.rb b/activesupport/lib/active_support/log_subscriber/test_helper.rb new file mode 100644 index 0000000000..96506a4b2b --- /dev/null +++ b/activesupport/lib/active_support/log_subscriber/test_helper.rb @@ -0,0 +1,90 @@ +require 'active_support/log_subscriber' + +module ActiveSupport + class LogSubscriber + # Provides some helpers to deal with testing log subscribers by setting up + # notifications. Take for instance Active Record subscriber tests: + # + # class SyncLogSubscriberTest < ActiveSupport::TestCase + # include ActiveSupport::LogSubscriber::TestHelper + # + # def setup + # ActiveRecord::LogSubscriber.attach_to(:active_record) + # end + # + # def test_basic_query_logging + # Developer.all + # wait + # assert_equal 1, @logger.logged(:debug).size + # assert_match /Developer Load/, @logger.logged(:debug).last + # assert_match /SELECT \* FROM "developers"/, @logger.logged(:debug).last + # end + # end + # + # All you need to do is to ensure that your log subscriber is added to Rails::Subscriber, + # as in the second line of the code above. The test helpers is reponsible for setting + # up the queue, subscriptions and turning colors in logs off. + # + # The messages are available in the @logger instance, which is a logger with limited + # powers (it actually do not send anything to your output), and you can collect them + # doing @logger.logged(level), where level is the level used in logging, like info, + # debug, warn and so on. + # + module TestHelper + def setup + @logger = MockLogger.new + @notifier = ActiveSupport::Notifications::Notifier.new(queue) + + ActiveSupport::LogSubscriber.colorize_logging = false + + set_logger(@logger) + ActiveSupport::Notifications.notifier = @notifier + end + + def teardown + set_logger(nil) + ActiveSupport::Notifications.notifier = nil + end + + class MockLogger + attr_reader :flush_count + + def initialize + @flush_count = 0 + @logged = Hash.new { |h,k| h[k] = [] } + end + + def method_missing(level, message) + @logged[level] << message + end + + def logged(level) + @logged[level].compact.map { |l| l.to_s.strip } + end + + def flush + @flush_count += 1 + end + end + + # Wait notifications to be published. + def wait + @notifier.wait + end + + # Overwrite if you use another logger in your log subscriber: + # + # def logger + # ActiveRecord::Base.logger = @logger + # end + # + def set_logger(logger) + ActiveSupport::LogSubscriber.logger = logger + end + + def queue + ActiveSupport::Notifications::Fanout.new + end + end + end +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 04193bfa65..c107aad6bb 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -195,6 +195,45 @@ module ActiveSupport #:nodoc: Unicode.u_unpack(@wrapped_string)[0] end + # Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes. + # + # Example: + # + # "¾ cup".mb_chars.rjust(8).to_s + # #=> " ¾ cup" + # + # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace + # #=> " ¾ cup" + def rjust(integer, padstr=' ') + justify(integer, :right, padstr) + end + + # Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes. + # + # Example: + # + # "¾ cup".mb_chars.rjust(8).to_s + # #=> "¾ cup " + # + # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace + # #=> "¾ cup " + def ljust(integer, padstr=' ') + justify(integer, :left, padstr) + end + + # Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes. + # + # Example: + # + # "¾ cup".mb_chars.center(8).to_s + # #=> " ¾ cup " + # + # "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace + # #=> " ¾ cup " + def center(integer, padstr=' ') + justify(integer, :center, padstr) + end + else def =~(other) @wrapped_string =~ other @@ -250,46 +289,6 @@ module ActiveSupport #:nodoc: end end - # Works just like <tt>String#rjust</tt>, only integer specifies characters instead of bytes. - # - # Example: - # - # "¾ cup".mb_chars.rjust(8).to_s - # #=> " ¾ cup" - # - # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace - # #=> " ¾ cup" - def rjust(integer, padstr=' ') - justify(integer, :right, padstr) - end - - # Works just like <tt>String#ljust</tt>, only integer specifies characters instead of bytes. - # - # Example: - # - # "¾ cup".mb_chars.rjust(8).to_s - # #=> "¾ cup " - # - # "¾ cup".mb_chars.rjust(8, " ").to_s # Use non-breaking whitespace - # #=> "¾ cup " - def ljust(integer, padstr=' ') - justify(integer, :left, padstr) - end - - # Works just like <tt>String#center</tt>, only integer specifies characters instead of bytes. - # - # Example: - # - # "¾ cup".mb_chars.center(8).to_s - # #=> " ¾ cup " - # - # "¾ cup".mb_chars.center(8, " ").to_s # Use non-breaking whitespace - # #=> " ¾ cup " - def center(integer, padstr=' ') - justify(integer, :center, padstr) - end - - # Reverses all characters in the string. # # Example: diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 91de722748..6b563b9063 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -149,6 +149,8 @@ module ActiveSupport self end + alias_method :update, :merge! + def merge(other_hash, &block) dup.merge!(other_hash, &block) end @@ -160,6 +162,10 @@ module ActiveSupport self end + def invert + OrderedHash[self.to_a.map!{|key_value_pair| key_value_pair.reverse}] + end + def inspect "#<OrderedHash #{super}>" end diff --git a/activesupport/lib/active_support/testing/isolation.rb b/activesupport/lib/active_support/testing/isolation.rb index 69df399cde..d629f6f2b7 100644 --- a/activesupport/lib/active_support/testing/isolation.rb +++ b/activesupport/lib/active_support/testing/isolation.rb @@ -45,12 +45,16 @@ module ActiveSupport end end + def _run_class_setup # class setup method should only happen in parent + unless defined?(@@ran_class_setup) || ENV['ISOLATION_TEST'] + self.class.setup if self.class.respond_to?(:setup) + @@ran_class_setup = true + end + end + module TestUnit def run(result) - unless defined?(@@ran_class_setup) - self.class.setup if self.class.respond_to?(:setup) - @@ran_class_setup = true - end + _run_class_setup yield(Test::Unit::TestCase::STARTED, name) @@ -74,10 +78,7 @@ module ActiveSupport module MiniTest def run(runner) - unless defined?(@@ran_class_setup) - self.class.setup if self.class.respond_to?(:setup) - @@ran_class_setup = true - end + _run_class_setup serialized = run_in_isolation do |isolated_runner| super(isolated_runner) @@ -109,6 +110,8 @@ module ActiveSupport end module Subprocess + ORIG_ARGV = ARGV.dup unless defined?(ORIG_ARGV) + # Crazy H4X to get this working in windows / jruby with # no forking. def run_in_isolation(&blk) diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 7550d8909f..0a08016522 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -30,154 +30,152 @@ end # (if a recent version of the gem is installed locally, this will be used instead of the bundled version.) module ActiveSupport class TimeZone - unless const_defined?(:MAPPING) - # Keys are Rails TimeZone names, values are TZInfo identifiers - MAPPING = { - "International Date Line West" => "Pacific/Midway", - "Midway Island" => "Pacific/Midway", - "Samoa" => "Pacific/Pago_Pago", - "Hawaii" => "Pacific/Honolulu", - "Alaska" => "America/Juneau", - "Pacific Time (US & Canada)" => "America/Los_Angeles", - "Tijuana" => "America/Tijuana", - "Mountain Time (US & Canada)" => "America/Denver", - "Arizona" => "America/Phoenix", - "Chihuahua" => "America/Chihuahua", - "Mazatlan" => "America/Mazatlan", - "Central Time (US & Canada)" => "America/Chicago", - "Saskatchewan" => "America/Regina", - "Guadalajara" => "America/Mexico_City", - "Mexico City" => "America/Mexico_City", - "Monterrey" => "America/Monterrey", - "Central America" => "America/Guatemala", - "Eastern Time (US & Canada)" => "America/New_York", - "Indiana (East)" => "America/Indiana/Indianapolis", - "Bogota" => "America/Bogota", - "Lima" => "America/Lima", - "Quito" => "America/Lima", - "Atlantic Time (Canada)" => "America/Halifax", - "Caracas" => "America/Caracas", - "La Paz" => "America/La_Paz", - "Santiago" => "America/Santiago", - "Newfoundland" => "America/St_Johns", - "Brasilia" => "America/Sao_Paulo", - "Buenos Aires" => "America/Argentina/Buenos_Aires", - "Georgetown" => "America/Guyana", - "Greenland" => "America/Godthab", - "Mid-Atlantic" => "Atlantic/South_Georgia", - "Azores" => "Atlantic/Azores", - "Cape Verde Is." => "Atlantic/Cape_Verde", - "Dublin" => "Europe/Dublin", - "Edinburgh" => "Europe/London", - "Lisbon" => "Europe/Lisbon", - "London" => "Europe/London", - "Casablanca" => "Africa/Casablanca", - "Monrovia" => "Africa/Monrovia", - "UTC" => "Etc/UTC", - "Belgrade" => "Europe/Belgrade", - "Bratislava" => "Europe/Bratislava", - "Budapest" => "Europe/Budapest", - "Ljubljana" => "Europe/Ljubljana", - "Prague" => "Europe/Prague", - "Sarajevo" => "Europe/Sarajevo", - "Skopje" => "Europe/Skopje", - "Warsaw" => "Europe/Warsaw", - "Zagreb" => "Europe/Zagreb", - "Brussels" => "Europe/Brussels", - "Copenhagen" => "Europe/Copenhagen", - "Madrid" => "Europe/Madrid", - "Paris" => "Europe/Paris", - "Amsterdam" => "Europe/Amsterdam", - "Berlin" => "Europe/Berlin", - "Bern" => "Europe/Berlin", - "Rome" => "Europe/Rome", - "Stockholm" => "Europe/Stockholm", - "Vienna" => "Europe/Vienna", - "West Central Africa" => "Africa/Algiers", - "Bucharest" => "Europe/Bucharest", - "Cairo" => "Africa/Cairo", - "Helsinki" => "Europe/Helsinki", - "Kyiv" => "Europe/Kiev", - "Riga" => "Europe/Riga", - "Sofia" => "Europe/Sofia", - "Tallinn" => "Europe/Tallinn", - "Vilnius" => "Europe/Vilnius", - "Athens" => "Europe/Athens", - "Istanbul" => "Europe/Istanbul", - "Minsk" => "Europe/Minsk", - "Jerusalem" => "Asia/Jerusalem", - "Harare" => "Africa/Harare", - "Pretoria" => "Africa/Johannesburg", - "Moscow" => "Europe/Moscow", - "St. Petersburg" => "Europe/Moscow", - "Volgograd" => "Europe/Moscow", - "Kuwait" => "Asia/Kuwait", - "Riyadh" => "Asia/Riyadh", - "Nairobi" => "Africa/Nairobi", - "Baghdad" => "Asia/Baghdad", - "Tehran" => "Asia/Tehran", - "Abu Dhabi" => "Asia/Muscat", - "Muscat" => "Asia/Muscat", - "Baku" => "Asia/Baku", - "Tbilisi" => "Asia/Tbilisi", - "Yerevan" => "Asia/Yerevan", - "Kabul" => "Asia/Kabul", - "Ekaterinburg" => "Asia/Yekaterinburg", - "Islamabad" => "Asia/Karachi", - "Karachi" => "Asia/Karachi", - "Tashkent" => "Asia/Tashkent", - "Chennai" => "Asia/Kolkata", - "Kolkata" => "Asia/Kolkata", - "Mumbai" => "Asia/Kolkata", - "New Delhi" => "Asia/Kolkata", - "Kathmandu" => "Asia/Kathmandu", - "Astana" => "Asia/Dhaka", - "Dhaka" => "Asia/Dhaka", - "Sri Jayawardenepura" => "Asia/Colombo", - "Almaty" => "Asia/Almaty", - "Novosibirsk" => "Asia/Novosibirsk", - "Rangoon" => "Asia/Rangoon", - "Bangkok" => "Asia/Bangkok", - "Hanoi" => "Asia/Bangkok", - "Jakarta" => "Asia/Jakarta", - "Krasnoyarsk" => "Asia/Krasnoyarsk", - "Beijing" => "Asia/Shanghai", - "Chongqing" => "Asia/Chongqing", - "Hong Kong" => "Asia/Hong_Kong", - "Urumqi" => "Asia/Urumqi", - "Kuala Lumpur" => "Asia/Kuala_Lumpur", - "Singapore" => "Asia/Singapore", - "Taipei" => "Asia/Taipei", - "Perth" => "Australia/Perth", - "Irkutsk" => "Asia/Irkutsk", - "Ulaan Bataar" => "Asia/Ulaanbaatar", - "Seoul" => "Asia/Seoul", - "Osaka" => "Asia/Tokyo", - "Sapporo" => "Asia/Tokyo", - "Tokyo" => "Asia/Tokyo", - "Yakutsk" => "Asia/Yakutsk", - "Darwin" => "Australia/Darwin", - "Adelaide" => "Australia/Adelaide", - "Canberra" => "Australia/Melbourne", - "Melbourne" => "Australia/Melbourne", - "Sydney" => "Australia/Sydney", - "Brisbane" => "Australia/Brisbane", - "Hobart" => "Australia/Hobart", - "Vladivostok" => "Asia/Vladivostok", - "Guam" => "Pacific/Guam", - "Port Moresby" => "Pacific/Port_Moresby", - "Magadan" => "Asia/Magadan", - "Solomon Is." => "Asia/Magadan", - "New Caledonia" => "Pacific/Noumea", - "Fiji" => "Pacific/Fiji", - "Kamchatka" => "Asia/Kamchatka", - "Marshall Is." => "Pacific/Majuro", - "Auckland" => "Pacific/Auckland", - "Wellington" => "Pacific/Auckland", - "Nuku'alofa" => "Pacific/Tongatapu" - }.each { |name, zone| name.freeze; zone.freeze } - MAPPING.freeze - end + # Keys are Rails TimeZone names, values are TZInfo identifiers + MAPPING = { + "International Date Line West" => "Pacific/Midway", + "Midway Island" => "Pacific/Midway", + "Samoa" => "Pacific/Pago_Pago", + "Hawaii" => "Pacific/Honolulu", + "Alaska" => "America/Juneau", + "Pacific Time (US & Canada)" => "America/Los_Angeles", + "Tijuana" => "America/Tijuana", + "Mountain Time (US & Canada)" => "America/Denver", + "Arizona" => "America/Phoenix", + "Chihuahua" => "America/Chihuahua", + "Mazatlan" => "America/Mazatlan", + "Central Time (US & Canada)" => "America/Chicago", + "Saskatchewan" => "America/Regina", + "Guadalajara" => "America/Mexico_City", + "Mexico City" => "America/Mexico_City", + "Monterrey" => "America/Monterrey", + "Central America" => "America/Guatemala", + "Eastern Time (US & Canada)" => "America/New_York", + "Indiana (East)" => "America/Indiana/Indianapolis", + "Bogota" => "America/Bogota", + "Lima" => "America/Lima", + "Quito" => "America/Lima", + "Atlantic Time (Canada)" => "America/Halifax", + "Caracas" => "America/Caracas", + "La Paz" => "America/La_Paz", + "Santiago" => "America/Santiago", + "Newfoundland" => "America/St_Johns", + "Brasilia" => "America/Sao_Paulo", + "Buenos Aires" => "America/Argentina/Buenos_Aires", + "Georgetown" => "America/Guyana", + "Greenland" => "America/Godthab", + "Mid-Atlantic" => "Atlantic/South_Georgia", + "Azores" => "Atlantic/Azores", + "Cape Verde Is." => "Atlantic/Cape_Verde", + "Dublin" => "Europe/Dublin", + "Edinburgh" => "Europe/London", + "Lisbon" => "Europe/Lisbon", + "London" => "Europe/London", + "Casablanca" => "Africa/Casablanca", + "Monrovia" => "Africa/Monrovia", + "UTC" => "Etc/UTC", + "Belgrade" => "Europe/Belgrade", + "Bratislava" => "Europe/Bratislava", + "Budapest" => "Europe/Budapest", + "Ljubljana" => "Europe/Ljubljana", + "Prague" => "Europe/Prague", + "Sarajevo" => "Europe/Sarajevo", + "Skopje" => "Europe/Skopje", + "Warsaw" => "Europe/Warsaw", + "Zagreb" => "Europe/Zagreb", + "Brussels" => "Europe/Brussels", + "Copenhagen" => "Europe/Copenhagen", + "Madrid" => "Europe/Madrid", + "Paris" => "Europe/Paris", + "Amsterdam" => "Europe/Amsterdam", + "Berlin" => "Europe/Berlin", + "Bern" => "Europe/Berlin", + "Rome" => "Europe/Rome", + "Stockholm" => "Europe/Stockholm", + "Vienna" => "Europe/Vienna", + "West Central Africa" => "Africa/Algiers", + "Bucharest" => "Europe/Bucharest", + "Cairo" => "Africa/Cairo", + "Helsinki" => "Europe/Helsinki", + "Kyiv" => "Europe/Kiev", + "Riga" => "Europe/Riga", + "Sofia" => "Europe/Sofia", + "Tallinn" => "Europe/Tallinn", + "Vilnius" => "Europe/Vilnius", + "Athens" => "Europe/Athens", + "Istanbul" => "Europe/Istanbul", + "Minsk" => "Europe/Minsk", + "Jerusalem" => "Asia/Jerusalem", + "Harare" => "Africa/Harare", + "Pretoria" => "Africa/Johannesburg", + "Moscow" => "Europe/Moscow", + "St. Petersburg" => "Europe/Moscow", + "Volgograd" => "Europe/Moscow", + "Kuwait" => "Asia/Kuwait", + "Riyadh" => "Asia/Riyadh", + "Nairobi" => "Africa/Nairobi", + "Baghdad" => "Asia/Baghdad", + "Tehran" => "Asia/Tehran", + "Abu Dhabi" => "Asia/Muscat", + "Muscat" => "Asia/Muscat", + "Baku" => "Asia/Baku", + "Tbilisi" => "Asia/Tbilisi", + "Yerevan" => "Asia/Yerevan", + "Kabul" => "Asia/Kabul", + "Ekaterinburg" => "Asia/Yekaterinburg", + "Islamabad" => "Asia/Karachi", + "Karachi" => "Asia/Karachi", + "Tashkent" => "Asia/Tashkent", + "Chennai" => "Asia/Kolkata", + "Kolkata" => "Asia/Kolkata", + "Mumbai" => "Asia/Kolkata", + "New Delhi" => "Asia/Kolkata", + "Kathmandu" => "Asia/Kathmandu", + "Astana" => "Asia/Dhaka", + "Dhaka" => "Asia/Dhaka", + "Sri Jayawardenepura" => "Asia/Colombo", + "Almaty" => "Asia/Almaty", + "Novosibirsk" => "Asia/Novosibirsk", + "Rangoon" => "Asia/Rangoon", + "Bangkok" => "Asia/Bangkok", + "Hanoi" => "Asia/Bangkok", + "Jakarta" => "Asia/Jakarta", + "Krasnoyarsk" => "Asia/Krasnoyarsk", + "Beijing" => "Asia/Shanghai", + "Chongqing" => "Asia/Chongqing", + "Hong Kong" => "Asia/Hong_Kong", + "Urumqi" => "Asia/Urumqi", + "Kuala Lumpur" => "Asia/Kuala_Lumpur", + "Singapore" => "Asia/Singapore", + "Taipei" => "Asia/Taipei", + "Perth" => "Australia/Perth", + "Irkutsk" => "Asia/Irkutsk", + "Ulaan Bataar" => "Asia/Ulaanbaatar", + "Seoul" => "Asia/Seoul", + "Osaka" => "Asia/Tokyo", + "Sapporo" => "Asia/Tokyo", + "Tokyo" => "Asia/Tokyo", + "Yakutsk" => "Asia/Yakutsk", + "Darwin" => "Australia/Darwin", + "Adelaide" => "Australia/Adelaide", + "Canberra" => "Australia/Melbourne", + "Melbourne" => "Australia/Melbourne", + "Sydney" => "Australia/Sydney", + "Brisbane" => "Australia/Brisbane", + "Hobart" => "Australia/Hobart", + "Vladivostok" => "Asia/Vladivostok", + "Guam" => "Pacific/Guam", + "Port Moresby" => "Pacific/Port_Moresby", + "Magadan" => "Asia/Magadan", + "Solomon Is." => "Asia/Magadan", + "New Caledonia" => "Pacific/Noumea", + "Fiji" => "Pacific/Fiji", + "Kamchatka" => "Asia/Kamchatka", + "Marshall Is." => "Pacific/Majuro", + "Auckland" => "Pacific/Auckland", + "Wellington" => "Pacific/Auckland", + "Nuku'alofa" => "Pacific/Tongatapu" + }.each { |name, zone| name.freeze; zone.freeze } + MAPPING.freeze UTC_OFFSET_WITH_COLON = '%s%02d:%02d' UTC_OFFSET_WITHOUT_COLON = UTC_OFFSET_WITH_COLON.sub(':', '') |