diff options
Diffstat (limited to 'activesupport')
21 files changed, 410 insertions, 164 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 13ec3c3775..abd0664118 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,9 @@ *Rails 3.0.0 [Release Candidate] (unreleased)* +* Added ActiveSupport::FileUpdateChecker to execute a block only if a set of files changed, used by Router and I18n locale files. [José Valim] + +* Added ActiveSupport::DescendantsTracker to track descendants with support to constants reloading. [José Valim] + * ActiveSupport::OrderedHash#merge and #merge! accept a block. #4838 [Paul Mucur, fxn] * Date#since, #ago, #beginning_of_day, #end_of_day, and #xmlschema honor now the user time zone if set. [Geoff Buesing] diff --git a/activesupport/Rakefile b/activesupport/Rakefile index 43f4722dbc..2aebe05de2 100644 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile @@ -1,3 +1,5 @@ +gem 'rdoc', '= 2.2' +require 'rdoc' require 'rake/testtask' require 'rake/rdoctask' require 'rake/gempackagetask' @@ -47,5 +49,5 @@ end desc "Publish the API documentation" task :pdoc => [:rdoc] do require 'rake/contrib/sshpublisher' - Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/as", "doc").upload + Rake::SshDirPublisher.new("rails@api.rubyonrails.org", "public_html/as", "doc").upload end diff --git a/activesupport/bin/generate_tables b/activesupport/bin/generate_tables index 51edb59c77..51edb59c77 100755..100644 --- a/activesupport/bin/generate_tables +++ b/activesupport/bin/generate_tables diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index e34e46b4cf..3ce5476bbd 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -39,6 +39,9 @@ require "active_support/dependencies/autoload" module ActiveSupport extend ActiveSupport::Autoload + autoload :DescendantsTracker + autoload :FileUpdateChecker + # TODO: Narrow this list down eager_autoload do autoload :BacktraceCleaner diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 3ff33eea72..c4e1eb2c04 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -1,6 +1,6 @@ +require 'active_support/descendants_tracker' require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/class/inheritable_attributes' -require 'active_support/core_ext/class/subclasses' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/singleton_class' @@ -85,6 +85,10 @@ module ActiveSupport module Callbacks extend Concern + included do + extend ActiveSupport::DescendantsTracker + end + def run_callbacks(kind, *args, &block) send("_run_#{kind}_callbacks", *args, &block) end @@ -428,7 +432,7 @@ module ActiveSupport options = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block - ([self] + self.descendents).each do |target| + ([self] + self.descendants).each do |target| chain = target.send("_#{name}_callbacks") yield chain, type, filters, options target.__define_runner(name) @@ -502,7 +506,7 @@ module ActiveSupport def reset_callbacks(symbol) callbacks = send("_#{symbol}_callbacks") - self.descendents.each do |target| + self.descendants.each do |target| chain = target.send("_#{symbol}_callbacks") callbacks.each { |c| chain.delete(c) } target.__define_runner(symbol) diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb index bbd8f5aef6..7d58a8b56a 100644 --- a/activesupport/lib/active_support/core_ext/class/subclasses.rb +++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb @@ -11,9 +11,9 @@ class Class #:nodoc: # Rubinius if defined?(Class.__subclasses__) - def descendents + def descendants subclasses = [] - __subclasses__.each {|k| subclasses << k; subclasses.concat k.descendents } + __subclasses__.each {|k| subclasses << k; subclasses.concat k.descendants } subclasses end else @@ -21,7 +21,7 @@ class Class #:nodoc: begin ObjectSpace.each_object(Class.new) {} - def descendents + def descendants subclasses = [] ObjectSpace.each_object(class << self; self; end) do |k| subclasses << k unless k == self @@ -30,7 +30,7 @@ class Class #:nodoc: end # JRuby rescue StandardError - def descendents + def descendants subclasses = [] ObjectSpace.each_object(Class) do |k| subclasses << k if k < self @@ -48,7 +48,7 @@ class Class #:nodoc: def self.subclasses_of(*superclasses) #:nodoc: subclasses = [] superclasses.each do |klass| - subclasses.concat klass.descendents.select {|k| k.anonymous? || k.reachable?} + subclasses.concat klass.descendants.select {|k| k.anonymous? || k.reachable?} end subclasses end diff --git a/activesupport/lib/active_support/core_ext/date_time/zones.rb b/activesupport/lib/active_support/core_ext/date_time/zones.rb index 98565e6750..6002d4ad2a 100644 --- a/activesupport/lib/active_support/core_ext/date_time/zones.rb +++ b/activesupport/lib/active_support/core_ext/date_time/zones.rb @@ -12,6 +12,8 @@ class DateTime # # DateTime.new(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00 def in_time_zone(zone = ::Time.zone) + return self unless zone + ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone)) end end diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb index 06f077e920..06f077e920 100755..100644 --- a/activesupport/lib/active_support/core_ext/object/to_param.rb +++ b/activesupport/lib/active_support/core_ext/object/to_param.rb diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb index adc9fe3824..a02402aa3f 100644 --- a/activesupport/lib/active_support/core_ext/time/zones.rb +++ b/activesupport/lib/active_support/core_ext/time/zones.rb @@ -73,6 +73,8 @@ class Time # # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00 def in_time_zone(zone = ::Time.zone) + return self unless zone + ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone)) end end diff --git a/activesupport/lib/active_support/descendants_tracker.rb b/activesupport/lib/active_support/descendants_tracker.rb new file mode 100644 index 0000000000..a587d7770c --- /dev/null +++ b/activesupport/lib/active_support/descendants_tracker.rb @@ -0,0 +1,39 @@ +require 'active_support/dependencies' + +module ActiveSupport + # This module provides an internal implementation to track descendants + # which is faster than iterating through ObjectSpace. + module DescendantsTracker + @@descendants = Hash.new { |h, k| h[k] = [] } + + def self.descendants + @@descendants + end + + def self.clear + @@descendants.each do |klass, descendants| + if ActiveSupport::Dependencies.autoloaded?(klass) + @@descendants.delete(klass) + else + descendants.reject! { |v| ActiveSupport::Dependencies.autoloaded?(v) } + end + end + end + + def inherited(base) + self.direct_descendants << base + super + end + + def direct_descendants + @@descendants[self] + end + + def descendants + @@descendants[self].inject([]) do |descendants, klass| + descendants << klass + descendants.concat klass.descendants + end + end + end +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb new file mode 100644 index 0000000000..c0b5ca4deb --- /dev/null +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -0,0 +1,37 @@ +module ActiveSupport + # This class is responsible to track files and invoke the given block + # whenever one of these files are changed. For example, this class + # is used by Rails to reload routes whenever they are changed upon + # a new request. + # + # routes_reloader = ActiveSupport::FileUpdateChecker.new(paths) do + # paths.each { |p| load(p) } + # Rails::Application.routes.reload! + # end + # + # ActionDispatch::Callbacks.to_prepare do + # routes_reloader.execute_if_updated + # end + # + class FileUpdateChecker + attr_reader :paths, :last_update_at + + def initialize(paths, calculate=false, &block) + @paths = paths + @block = block + @last_update_at = updated_at if calculate + end + + def updated_at + paths.map { |path| File.stat(path).mtime }.max + end + + def execute_if_updated + current_update_at = self.updated_at + if @last_update_at != current_update_at + @last_update_at = current_update_at + @block.call + end + end + end +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index 0ffdd904fd..45b9d20c01 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -4,5 +4,6 @@ rescue LoadError => e $stderr.puts "You don't have i18n installed in your application. Please add it to your Gemfile and run bundle install" raise e end + I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" ActiveSupport.run_load_hooks(:i18n) diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb new file mode 100644 index 0000000000..d82e54f1d4 --- /dev/null +++ b/activesupport/lib/active_support/i18n_railtie.rb @@ -0,0 +1,80 @@ +require "active_support" +require "rails" +require "active_support/file_update_checker" + +module I18n + class Railtie < Rails::Railtie + config.i18n = ActiveSupport::OrderedOptions.new + config.i18n.railties_load_path = [] + config.i18n.load_path = [] + config.i18n.fallbacks = ActiveSupport::OrderedOptions.new + + def self.reloader + @reloader ||= ActiveSupport::FileUpdateChecker.new([]){ I18n.reload! } + end + + # Add I18n::Railtie.reloader to ActionDispatch callbacks. Since, at this + # point, no path was added to the reloader, I18n.reload! is not triggered + # on to_prepare callbacks. This will only happen on the config.after_initialize + # callback below. + initializer "i18n.callbacks" do + ActionDispatch::Callbacks.to_prepare do + I18n::Railtie.reloader.execute_if_updated + end + end + + # Set the i18n configuration only after initialization since a lot of + # configuration is still usually done in application initializers. + config.after_initialize do |app| + fallbacks = app.config.i18n.delete(:fallbacks) + + app.config.i18n.each do |setting, value| + case setting + when :railties_load_path + app.config.i18n.load_path.unshift(*value) + when :load_path + I18n.load_path += value + else + I18n.send("#{setting}=", value) + end + end + + init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks) + + reloader.paths.concat I18n.load_path + reloader.execute_if_updated + end + + protected + + def self.include_fallbacks_module + I18n.backend.class.send(:include, I18n::Backend::Fallbacks) + end + + def self.init_fallbacks(fallbacks) + include_fallbacks_module + + args = case fallbacks + when ActiveSupport::OrderedOptions + [*(fallbacks[:defaults] || []) << fallbacks[:map]].compact + when Hash, Array + Array.wrap(fallbacks) + else # TrueClass + [] + end + + I18n.fallbacks = I18n::Locale::Fallbacks.new(*args) + end + + def self.validate_fallbacks(fallbacks) + case fallbacks + when ActiveSupport::OrderedOptions + !fallbacks.empty? + when TrueClass, Array, Hash + true + else + raise "Unexpected fallback type #{fallbacks.inspect}" + end + end + end +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/json/backends/yajl.rb b/activesupport/lib/active_support/json/backends/yajl.rb index d76f8b03e4..64e50e0d87 100644 --- a/activesupport/lib/active_support/json/backends/yajl.rb +++ b/activesupport/lib/active_support/json/backends/yajl.rb @@ -1,4 +1,4 @@ -require 'yajl-ruby' unless defined?(Yajl) +require 'yajl' unless defined?(Yajl) module ActiveSupport module JSON diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index d6ccb4bac1..04193bfa65 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -50,10 +50,6 @@ module ActiveSupport #:nodoc: end end - def <=>(other) - @wrapped_string <=> other - end - # Forward all undefined methods to the wrapped string. def method_missing(method, *args, &block) if method.to_s =~ /!$/ @@ -87,6 +83,16 @@ module ActiveSupport #:nodoc: include Comparable + # Returns <tt>-1</tt>, <tt>0</tt> or <tt>+1</tt> depending on whether the Chars object is to be sorted before, + # equal or after the object on the right side of the operation. It accepts any object that implements +to_s+. + # See <tt>String#<=></tt> for more details. + # + # Example: + # 'é'.mb_chars <=> 'ü'.mb_chars #=> -1 + def <=>(other) + @wrapped_string <=> other.to_s + end + if RUBY_VERSION < "1.9" # Returns +true+ if the Chars class can and should act as a proxy for the string _string_. Returns # +false+ otherwise. @@ -94,16 +100,6 @@ module ActiveSupport #:nodoc: $KCODE == 'UTF8' && consumes?(string) end - # Returns <tt>-1</tt>, <tt>0</tt> or <tt>+1</tt> depending on whether the Chars object is to be sorted before, - # equal or after the object on the right side of the operation. It accepts any object that implements +to_s+. - # See <tt>String#<=></tt> for more details. - # - # Example: - # 'é'.mb_chars <=> 'ü'.mb_chars #=> -1 - def <=>(other) - @wrapped_string <=> other.to_s - end - # Returns a new Chars object containing the _other_ object concatenated to the string. # # Example: @@ -375,6 +371,16 @@ module ActiveSupport #:nodoc: (slice(0) || chars('')).upcase + (slice(1..-1) || chars('')).downcase end + # Capitalizes the first letter of every word, when possible. + # + # Example: + # "ÉL QUE SE ENTERÓ".mb_chars.titleize # => "Él Que Se Enteró" + # "日本語".mb_chars.titleize # => "日本語" + def titleize + chars(downcase.to_s.gsub(/\b('?[\S])/u) { Unicode.apply_mapping $1, :uppercase_mapping }) + end + alias_method :titlecase, :titleize + # Returns the KC normalization of the string by default. NFKC is considered the best normalization form for # passing strings to databases and validations. # diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb index 59f9ab18b1..1f32f8718f 100644 --- a/activesupport/lib/active_support/railtie.rb +++ b/activesupport/lib/active_support/railtie.rb @@ -1,5 +1,6 @@ require "active_support" require "rails" +require "active_support/i18n_railtie" module ActiveSupport class Railtie < Rails::Railtie @@ -26,75 +27,4 @@ module ActiveSupport Time.zone_default = zone_default end end -end - -module I18n - class Railtie < Rails::Railtie - config.i18n = ActiveSupport::OrderedOptions.new - config.i18n.railties_load_path = [] - config.i18n.load_path = [] - config.i18n.fallbacks = ActiveSupport::OrderedOptions.new - - initializer "i18n.initialize" do - ActiveSupport.on_load(:i18n) do - I18n.reload! - - ActionDispatch::Callbacks.to_prepare do - I18n.reload! - end - end - end - - # Set the i18n configuration from config.i18n but special-case for - # the load_path which should be appended to what's already set instead of overwritten. - config.after_initialize do |app| - fallbacks = app.config.i18n.delete(:fallbacks) - - app.config.i18n.each do |setting, value| - case setting - when :railties_load_path - app.config.i18n.load_path.unshift(*value) - when :load_path - I18n.load_path += value - else - I18n.send("#{setting}=", value) - end - end - - init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks) - I18n.reload! - end - - class << self - protected - - def init_fallbacks(fallbacks) - include_fallbacks_module - args = case fallbacks - when ActiveSupport::OrderedOptions - [*(fallbacks[:defaults] || []) << fallbacks[:map]].compact - when Hash, Array - Array.wrap(fallbacks) - else # TrueClass - [] - end - I18n.fallbacks = I18n::Locale::Fallbacks.new(*args) - end - - def include_fallbacks_module - I18n.backend.class.send(:include, I18n::Backend::Fallbacks) - end - - def validate_fallbacks(fallbacks) - case fallbacks - when ActiveSupport::OrderedOptions - !fallbacks.empty? - when TrueClass, Array, Hash - true - else - raise "Unexpected fallback type #{fallbacks.inspect}" - end - end - end - end end
\ No newline at end of file diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 24eea1e40b..cd628a956d 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -260,14 +260,8 @@ begin end protected - if GC.respond_to?(:enable_stats) - def with_gc_stats - GC.enable_stats - yield - ensure - GC.disable_stats - end - elsif defined?(GC::Profiler) + # Ruby 1.9 + extented GC profiler patch + if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data) def with_gc_stats GC.start GC.disable @@ -277,6 +271,16 @@ begin GC::Profiler.disable GC.enable end + + # Ruby 1.8 + ruby-prof wrapper (enable/disable stats for Benchmarker) + elsif GC.respond_to?(:enable_stats) + def with_gc_stats + GC.enable_stats + yield + ensure + GC.disable_stats + end + else def with_gc_stats yield @@ -319,7 +323,7 @@ begin def initialize(*args) # FIXME: yeah my CPU is 2.33 GHz - RubyProf.cpu_frequency = 2.33e9 + RubyProf.cpu_frequency = 2.33e9 unless RubyProf.cpu_frequency > 0 super end @@ -331,38 +335,8 @@ begin class Memory < Base Mode = RubyProf::MEMORY if RubyProf.const_defined?(:MEMORY) - # ruby-prof wrapper - if RubyProf.respond_to?(:measure_memory) - def measure - RubyProf.measure_memory / 1024.0 - end - - # Ruby 1.8 + railsbench patch - elsif GC.respond_to?(:allocated_size) - def measure - GC.allocated_size / 1024.0 - end - - # Ruby 1.8 + lloyd patch - elsif GC.respond_to?(:heap_info) - def measure - GC.heap_info['heap_current_memory'] / 1024.0 - end - - # Ruby 1.9 with total_malloc_allocated_size patch - elsif GC.respond_to?(:malloc_total_allocated_size) - def measure - GC.total_malloc_allocated_size / 1024.0 - end - - # Ruby 1.9 unpatched - elsif GC.respond_to?(:malloc_allocated_size) - def measure - GC.malloc_allocated_size / 1024.0 - end - - # Ruby 1.9 + GC profiler patch - elsif defined?(GC::Profiler) + # Ruby 1.9 + extended GC profiler patch + if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data) def measure GC.enable GC.start @@ -370,6 +344,12 @@ begin GC.disable kb end + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_memory) + def measure + RubyProf.measure_memory / 1024.0 + end end def format(measurement) @@ -380,27 +360,21 @@ begin class Objects < Base Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) - if RubyProf.respond_to?(:measure_allocations) - def measure - RubyProf.measure_allocations - end - - # Ruby 1.8 + railsbench patch - elsif ObjectSpace.respond_to?(:allocated_objects) - def measure - ObjectSpace.allocated_objects - end - - # Ruby 1.9 + GC profiler patch - elsif defined?(GC::Profiler) + # Ruby 1.9 + extented GC profiler patch + if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data) def measure GC.enable GC.start - last = GC::Profiler.data.last - count = last[:HEAP_LIVE_OBJECTS] + last[:HEAP_FREE_OBJECTS] + count = GC::Profiler.data.last[:HEAP_TOTAL_OBJECTS] GC.disable count end + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_allocations) + def measure + RubyProf.measure_allocations + end end def format(measurement) @@ -411,17 +385,20 @@ begin class GcRuns < Base Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) - if RubyProf.respond_to?(:measure_gc_runs) + # Ruby 1.9 + extented GC profiler patch + if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data) def measure - RubyProf.measure_gc_runs - end - elsif GC.respond_to?(:collections) - def measure - GC.collections + GC.enable + GC.start + count = GC::Profiler.data.last[:GC_RUNS] + GC.disable + count end - elsif GC.respond_to?(:heap_info) + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_gc_runs) def measure - GC.heap_info['num_gc_passes'] + RubyProf.measure_gc_runs end end @@ -433,13 +410,20 @@ begin class GcTime < Base Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) - if RubyProf.respond_to?(:measure_gc_time) + # Ruby 1.9 + extented GC profiler patch + if defined?(GC::Profiler) and GC::Profiler.respond_to?(:data) def measure - RubyProf.measure_gc_time + GC.enable + GC.start + sec = GC::Profiler.data.inject(0) { |total, run| total += run[:GC_TIME] } + GC.disable + sec end - elsif GC.respond_to?(:time) + + # Ruby 1.8 + ruby-prof wrapper + elsif RubyProf.respond_to?(:measure_gc_time) def measure - GC.time + RubyProf.measure_gc_time end end @@ -452,4 +436,4 @@ begin end end rescue LoadError -end
\ No newline at end of file +end diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 2cf5bd6ea9..cf11f4d28f 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -737,6 +737,13 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase end end + def test_nil_time_zone + Time.use_zone nil do + assert !@t.in_time_zone.respond_to?(:period), 'no period method' + assert !@dt.in_time_zone.respond_to?(:period), 'no period method' + end + end + def test_in_time_zone_with_argument Time.use_zone 'Eastern Time (US & Canada)' do # Time.zone will not affect #in_time_zone(zone) assert_equal 'Fri, 31 Dec 1999 15:00:00 AKST -09:00', @t.in_time_zone('Alaska').inspect diff --git a/activesupport/test/descendants_tracker_test.rb b/activesupport/test/descendants_tracker_test.rb new file mode 100644 index 0000000000..3a424180de --- /dev/null +++ b/activesupport/test/descendants_tracker_test.rb @@ -0,0 +1,75 @@ +require 'abstract_unit' +require 'test/unit' +require 'active_support' +require 'active_support/core_ext/hash/slice' + +class DescendantsTrackerTest < Test::Unit::TestCase + class Parent + extend ActiveSupport::DescendantsTracker + end + + class Child1 < Parent + end + + class Child2 < Parent + end + + class Grandchild1 < Child1 + end + + class Grandchild2 < Child1 + end + + ALL = [Parent, Child1, Child2, Grandchild1, Grandchild2] + + def test_descendants + assert_equal [Child1, Grandchild1, Grandchild2, Child2], Parent.descendants + assert_equal [Grandchild1, Grandchild2], Child1.descendants + assert_equal [], Child2.descendants + end + + def test_direct_descendants + assert_equal [Child1, Child2], Parent.direct_descendants + assert_equal [Grandchild1, Grandchild2], Child1.direct_descendants + assert_equal [], Child2.direct_descendants + end + + def test_clear_with_autoloaded_parent_children_and_granchildren + mark_as_autoloaded *ALL do + ActiveSupport::DescendantsTracker.clear + assert ActiveSupport::DescendantsTracker.descendants.slice(*ALL).empty? + end + end + + def test_clear_with_autoloaded_children_and_granchildren + mark_as_autoloaded Child1, Grandchild1, Grandchild2 do + ActiveSupport::DescendantsTracker.clear + assert_equal [Child2], Parent.descendants + assert_equal [], Child2.descendants + end + end + + def test_clear_with_autoloaded_granchildren + mark_as_autoloaded Grandchild1, Grandchild2 do + ActiveSupport::DescendantsTracker.clear + assert_equal [Child1, Child2], Parent.descendants + assert_equal [], Child1.descendants + assert_equal [], Child2.descendants + end + end + + protected + + def mark_as_autoloaded(*klasses) + old_autoloaded = ActiveSupport::Dependencies.autoloaded_constants.dup + ActiveSupport::Dependencies.autoloaded_constants = klasses.map(&:name) + + old_descendants = ActiveSupport::DescendantsTracker.descendants.dup + old_descendants.each { |k, v| old_descendants[k] = v.dup } + + yield + ensure + ActiveSupport::Dependencies.autoloaded_constants = old_autoloaded + ActiveSupport::DescendantsTracker.descendants.replace(old_descendants) + end +end
\ No newline at end of file diff --git a/activesupport/test/file_update_checker_test.rb b/activesupport/test/file_update_checker_test.rb new file mode 100644 index 0000000000..baf29cc337 --- /dev/null +++ b/activesupport/test/file_update_checker_test.rb @@ -0,0 +1,56 @@ +require 'abstract_unit' +require 'test/unit' +require 'active_support' +require 'fileutils' + +MTIME_FIXTURES_PATH = File.expand_path("../fixtures", __FILE__) + +class FileUpdateCheckerTest < Test::Unit::TestCase + FILES = %w(1.txt 2.txt 3.txt) + + def setup + FileUtils.touch(FILES) + end + + def teardown + FileUtils.rm(FILES) + end + + def test_should_not_execute_the_block_if_no_paths_are_given + i = 0 + checker = ActiveSupport::FileUpdateChecker.new([]){ i += 1 } + checker.execute_if_updated + assert_equal 0, i + end + + def test_should_invoke_the_block_on_first_call_if_it_does_not_calculate_last_updated_at_on_load + i = 0 + checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 } + checker.execute_if_updated + assert_equal 1, i + end + + def test_should_not_invoke_the_block_on_first_call_if_it_calculates_last_updated_at_on_load + i = 0 + checker = ActiveSupport::FileUpdateChecker.new(FILES, true){ i += 1 } + checker.execute_if_updated + assert_equal 0, i + end + + def test_should_not_invoke_the_block_if_no_file_has_changed + i = 0 + checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 } + 5.times { checker.execute_if_updated } + assert_equal 1, i + end + + def test_should_invoke_the_block_if_a_file_has_changed + i = 0 + checker = ActiveSupport::FileUpdateChecker.new(FILES){ i += 1 } + checker.execute_if_updated + sleep(1) + FileUtils.touch(FILES) + checker.execute_if_updated + assert_equal 2, i + end +end diff --git a/activesupport/test/multibyte_chars_test.rb b/activesupport/test/multibyte_chars_test.rb index f7a5834527..602828ef5f 100644 --- a/activesupport/test/multibyte_chars_test.rb +++ b/activesupport/test/multibyte_chars_test.rb @@ -443,6 +443,11 @@ class MultibyteCharsUTF8BehaviourTest < Test::Unit::TestCase assert_equal 'Abc', 'abc'.mb_chars.capitalize end + def test_titleize_should_work_on_ascii_characters + assert_equal '', ''.mb_chars.titleize + assert_equal 'Abc Abc', 'abc abc'.mb_chars.titleize + end + def test_respond_to_knows_which_methods_the_proxy_responds_to assert ''.mb_chars.respond_to?(:slice) # Defined on Chars assert ''.mb_chars.respond_to?(:capitalize!) # Defined on Chars @@ -480,6 +485,15 @@ class MultibyteCharsExtrasTest < Test::Unit::TestCase end end + def test_titleize_should_be_unicode_aware + assert_equal "Él Que Se Enteró", chars("ÉL QUE SE ENTERÓ").titleize + assert_equal "Абвг Абвг", chars("аБвг аБвг").titleize + end + + def test_titleize_should_not_affect_characters_that_do_not_case_fold + assert_equal "日本語", chars("日本語").titleize + end + def test_limit_should_not_break_on_blank_strings example = chars('') assert_equal example, example.limit(0) |