diff options
Diffstat (limited to 'activesupport')
28 files changed, 620 insertions, 82 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index d87558aba9..8d3b136d80 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,12 +1,36 @@ *Edge* +* Add Array#in_groups which splits or iterates over the array in specified number of groups. #579. [Adrian Mugnolo] Example: + + a = (1..10).to_a + a.in_groups(3) #=> [[1, 2, 3, 4], [5, 6, 7, nil], [8, 9, 10, nil]] + a.in_groups(3, false) #=> [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]] + +* Fix TimeWithZone unmarshaling: coerce unmarshaled Time instances to utc, because Ruby's marshaling of Time instances doesn't respect the zone [Geoff Buesing] + +* Added Memoizable mixin for caching simple lazy loaded attributes [Josh Peek] + +* Move the test related core_ext stuff out of core_ext so it's only loaded by the test helpers. [Michael Koziarski] + +* Add Inflection rules for String#humanize. #535 [dcmanges] + + ActiveSupport::Inflector.inflections do |inflect| + inflect.human(/_cnt$/i, '\1_count') + end + + 'jargon_cnt'.humanize # => 'Jargon count' + +* TimeWithZone: when crossing DST boundary, treat Durations of days, months or years as variable-length, and all other values as absolute length. A time + 24.hours will advance exactly 24 hours, but a time + 1.day will advance 23-25 hours, depending on the day. Ensure consistent behavior across all advancing methods [Geoff Buesing] + +* Added TimeZone #=~, to support matching zones by regex in time_zone_select. #195 [Ernie Miller] + * Added Array#second through Array#tenth as aliases for Array#[1] through Array#[9] [DHH] * Added test/do declaration style testing to ActiveSupport::TestCase [DHH via Jay Fields] * Added Object#present? which is equivalent to !Object#blank? [DHH] -* Added Enumberable#many? to encapsulate collection.size > 1 [DHH] +* Added Enumberable#many? to encapsulate collection.size > 1 [DHH/Damian Janowski] * Add more standard Hash methods to ActiveSupport::OrderedHash [Steve Purcell] diff --git a/activesupport/Rakefile b/activesupport/Rakefile index e51bf08f8d..f2885c69a4 100644 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile @@ -33,11 +33,11 @@ Rake::RDocTask.new { |rdoc| rdoc.title = "Active Support -- Utility classes and standard library extensions from Rails" rdoc.options << '--line-numbers' << '--inline-source' rdoc.options << '--charset' << 'utf-8' - rdoc.template = "#{ENV['template']}.rb" if ENV['template'] + rdoc.template = ENV['template'] ? "#{ENV['template']}.rb" : '../doc/template/horo' rdoc.rdoc_files.include('README', 'CHANGELOG') rdoc.rdoc_files.include('lib/active_support.rb') - rdoc.rdoc_files.include('lib/active_support/*.rb') rdoc.rdoc_files.include('lib/active_support/**/*.rb') + rdoc.rdoc_files.exclude('lib/active_support/vendor/*') } spec = Gem::Specification.new do |s| @@ -65,13 +65,13 @@ end desc "Publish the beta gem" task :pgem => [:package] do - Rake::SshFilePublisher.new("davidhh@wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload - `ssh davidhh@wrath.rubyonrails.org './gemupdate.sh'` + Rake::SshFilePublisher.new("wrath.rubyonrails.org", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload + `ssh wrath.rubyonrails.org './gemupdate.sh'` end desc "Publish the API documentation" task :pdoc => [:rdoc] do - Rake::SshDirPublisher.new("davidhh@wrath.rubyonrails.org", "public_html/as", "doc").upload + Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/as", "doc").upload end desc "Publish the release files to RubyForge." @@ -172,4 +172,4 @@ namespace :tzinfo do def excluded_classes %w(country country_index_definition country_info country_timezone timezone_index_definition timezone_proxy tzdataparser) end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index de50aafe16..1df911a3f2 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -43,6 +43,7 @@ require 'active_support/ordered_hash' require 'active_support/ordered_options' require 'active_support/option_merger' +require 'active_support/memoizable' require 'active_support/string_inquirer' require 'active_support/values/time_zone' diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 2f1143e610..3e3dc18263 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -19,19 +19,19 @@ module ActiveSupport def self.expand_cache_key(key, namespace = nil) expanded_cache_key = namespace ? "#{namespace}/" : "" - + if ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"] expanded_cache_key << "#{ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]}/" end expanded_cache_key << case - when key.respond_to?(:cache_key) - key.cache_key - when key.is_a?(Array) - key.collect { |element| expand_cache_key(element) }.to_param - when key.respond_to?(:to_param) - key.to_param - end + when key.respond_to?(:cache_key) + key.cache_key + when key.is_a?(Array) + key.collect { |element| expand_cache_key(element) }.to_param + when key + key.to_param + end.to_s expanded_cache_key end diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 59dc96754f..e67b719ddb 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -30,7 +30,7 @@ module ActiveSupport #:nodoc: # Calls <tt>to_param</tt> on all its elements and joins the result with # slashes. This is used by <tt>url_for</tt> in Action Pack. def to_param - map(&:to_param).join '/' + collect { |e| e.to_param }.join '/' end # Converts an array into a string suitable for use as a URL query string, @@ -38,7 +38,8 @@ module ActiveSupport #:nodoc: # # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding" def to_query(key) - collect { |value| value.to_query("#{key}[]") } * '&' + prefix = "#{key}[]" + collect { |value| value.to_query(prefix) }.join '&' end def self.included(base) #:nodoc: diff --git a/activesupport/lib/active_support/core_ext/array/grouping.rb b/activesupport/lib/active_support/core_ext/array/grouping.rb index 767acc4e07..df37afb053 100644 --- a/activesupport/lib/active_support/core_ext/array/grouping.rb +++ b/activesupport/lib/active_support/core_ext/array/grouping.rb @@ -4,8 +4,8 @@ module ActiveSupport #:nodoc: module CoreExtensions #:nodoc: module Array #:nodoc: module Grouping - # Iterates over the array in groups of size +number+, padding any remaining - # slots with +fill_with+ unless it is +false+. + # Splits or iterates over the array in groups of size +number+, + # padding any remaining slots with +fill_with+ unless it is +false+. # # %w(1 2 3 4 5 6 7).in_groups_of(3) {|g| p g} # ["1", "2", "3"] @@ -39,6 +39,49 @@ module ActiveSupport #:nodoc: end end + # Splits or iterates over the array in +number+ of groups, padding any + # remaining slots with +fill_with+ unless it is +false+. + # + # %w(1 2 3 4 5 6 7 8 9 10).in_groups(3) {|g| p g} + # ["1", "2", "3", "4"] + # ["5", "6", "7", nil] + # ["8", "9", "10", nil] + # + # %w(1 2 3 4 5 6 7).in_groups(3, ' ') {|g| p g} + # ["1", "2", "3"] + # ["4", "5", " "] + # ["6", "7", " "] + # + # %w(1 2 3 4 5 6 7).in_groups(3, false) {|g| p g} + # ["1", "2", "3"] + # ["4", "5"] + # ["6", "7"] + def in_groups(number, fill_with = nil) + # size / number gives minor group size; + # size % number gives how many objects need extra accomodation; + # each group hold either division or division + 1 items. + division = size / number + modulo = size % number + + # create a new array avoiding dup + groups = [] + start = 0 + + number.times do |index| + length = division + (modulo > 0 && modulo > index ? 1 : 0) + padding = fill_with != false && + modulo > 0 && length == division ? 1 : 0 + groups << slice(start, length).concat([fill_with] * padding) + start += length + end + + if block_given? + groups.each{|g| yield(g) } + else + groups + end + end + # Divides the array into one or more subarrays based on a delimiting +value+ # or the result of an optional block. # diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb index 9647797ec2..e451e9933a 100644 --- a/activesupport/lib/active_support/core_ext/enumerable.rb +++ b/activesupport/lib/active_support/core_ext/enumerable.rb @@ -79,7 +79,9 @@ module Enumerable end # Returns true if the collection has more than 1 element. Functionally equivalent to collection.size > 1. - def many? + # Works with a block too ala any?, so people.many? { |p| p.age > 26 } # => returns true if more than 1 person is over 26. + def many?(&block) + size = block_given? ? select(&block).size : self.size size > 1 end end diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb index 40bbebb7c4..45f3e4bf5c 100644 --- a/activesupport/lib/active_support/core_ext/module/introspection.rb +++ b/activesupport/lib/active_support/core_ext/module/introspection.rb @@ -1,4 +1,14 @@ class Module + # Returns the name of the module containing this one. + # + # p M::N.parent_name # => "M" + def parent_name + unless defined? @parent_name + @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil + end + @parent_name + end + # Returns the module which contains this one according to its name. # # module M @@ -16,8 +26,7 @@ class Module # p Module.new.parent # => Object # def parent - parent_name = name.split('::')[0..-2] * '::' - parent_name.empty? ? Object : parent_name.constantize + parent_name ? parent_name.constantize : Object end # Returns all the parents of this module according to its name, ordered from @@ -35,10 +44,12 @@ class Module # def parents parents = [] - parts = name.split('::')[0..-2] - until parts.empty? - parents << (parts * '::').constantize - parts.pop + if parent_name + parts = parent_name.split('::') + until parts.empty? + parents << (parts * '::').constantize + parts.pop + end end parents << Object unless parents.include? Object parents @@ -70,6 +81,6 @@ class Module # Returns the names of the constants defined locally rather than the # constants themselves. See <tt>local_constants</tt>. def local_constant_names - local_constants.map(&:to_s) + local_constants.map { |c| c.to_s } end end diff --git a/activesupport/lib/active_support/core_ext/module/model_naming.rb b/activesupport/lib/active_support/core_ext/module/model_naming.rb index 26e76ab556..5518f5417b 100644 --- a/activesupport/lib/active_support/core_ext/module/model_naming.rb +++ b/activesupport/lib/active_support/core_ext/module/model_naming.rb @@ -1,12 +1,13 @@ module ActiveSupport class ModelName < String - attr_reader :singular, :plural, :partial_path + attr_reader :singular, :plural, :cache_key, :partial_path def initialize(name) super @singular = underscore.tr('/', '_').freeze @plural = @singular.pluralize.freeze - @partial_path = "#{tableize}/#{demodulize.underscore}".freeze + @cache_key = tableize.freeze + @partial_path = "#{@cache_key}/#{demodulize.underscore}".freeze end end diff --git a/activesupport/lib/active_support/core_ext/object/instance_variables.rb b/activesupport/lib/active_support/core_ext/object/instance_variables.rb index 9f1d4ed2aa..4ecaab3bbb 100644 --- a/activesupport/lib/active_support/core_ext/object/instance_variables.rb +++ b/activesupport/lib/active_support/core_ext/object/instance_variables.rb @@ -35,7 +35,7 @@ class Object # C.new(0, 1).instance_variable_names # => ["@y", "@x"] if RUBY_VERSION >= '1.9' def instance_variable_names - instance_variables.map(&:to_s) + instance_variables.map { |var| var.to_s } end else alias_method :instance_variable_names, :instance_variables diff --git a/activesupport/lib/active_support/core_ext/test.rb b/activesupport/lib/active_support/core_ext/test.rb deleted file mode 100644 index c0b19bdc58..0000000000 --- a/activesupport/lib/active_support/core_ext/test.rb +++ /dev/null @@ -1 +0,0 @@ -require 'active_support/core_ext/test/unit/assertions' diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 7a8c4d0326..2f3fa72bb4 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -299,7 +299,7 @@ module ActiveSupport #:nodoc: # Will the provided constant descriptor be unloaded? def will_unload?(const_desc) - autoloaded?(desc) || + autoloaded?(const_desc) || explicitly_unloadable_constants.include?(to_constant_name(const_desc)) end @@ -387,7 +387,7 @@ module ActiveSupport #:nodoc: ensure # Remove the stack frames that we added. if defined?(watch_frames) && ! watch_frames.blank? - frame_ids = watch_frames.collect(&:object_id) + frame_ids = watch_frames.collect { |frame| frame.object_id } constant_watch_stack.delete_if do |watch_frame| frame_ids.include? watch_frame.object_id end @@ -437,7 +437,7 @@ module ActiveSupport #:nodoc: protected def log_call(*args) if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER && log_activity - arg_str = args.collect(&:inspect) * ', ' + arg_str = args.collect { |arg| arg.inspect } * ', ' /in `([a-z_\?\!]+)'/ =~ caller(1).first selector = $1 || '<unknown>' log "called #{selector}(#{arg_str})" diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index 47bd6e1767..6651569d33 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -30,10 +30,10 @@ module ActiveSupport class Inflections include Singleton - attr_reader :plurals, :singulars, :uncountables + attr_reader :plurals, :singulars, :uncountables, :humans def initialize - @plurals, @singulars, @uncountables = [], [], [] + @plurals, @singulars, @uncountables, @humans = [], [], [], [] end # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression. @@ -76,9 +76,20 @@ module ActiveSupport (@uncountables << words).flatten! end + # Specifies a humanized form of a string by a regular expression rule or by a string mapping. + # When using a regular expression based replacement, the normal humanize formatting is called after the replacement. + # When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name') + # + # Examples: + # human /_cnt$/i, '\1_count' + # human "legacy_col_person_name", "Name" + def human(rule, replacement) + @humans.insert(0, [rule, replacement]) + end + # Clears the loaded inflections within a given scope (default is <tt>:all</tt>). # Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>, - # <tt>:singulars</tt>, <tt>:uncountables</tt>. + # <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>. # # Examples: # clear :all @@ -209,7 +220,10 @@ module ActiveSupport # "employee_salary" # => "Employee salary" # "author_id" # => "Author" def humanize(lower_case_and_underscored_word) - lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize + result = lower_case_and_underscored_word.to_s.dup + + inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } + result.gsub(/_id$/, "").gsub(/_/, " ").capitalize end # Removes the module part from the expression in the string. @@ -307,4 +321,9 @@ module ActiveSupport end end +# in case active_support/inflector is required without the rest of active_support require 'active_support/inflections' +require 'active_support/core_ext/string/inflections' +unless String.included_modules.include?(ActiveSupport::CoreExtensions::String::Inflections) + String.send :include, ActiveSupport::CoreExtensions::String::Inflections +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/json/encoders/date_time.rb b/activesupport/lib/active_support/json/encoders/date_time.rb index d41c3e9786..a4a5efbfb1 100644 --- a/activesupport/lib/active_support/json/encoders/date_time.rb +++ b/activesupport/lib/active_support/json/encoders/date_time.rb @@ -8,7 +8,7 @@ class DateTime if ActiveSupport.use_standard_json_time_format xmlschema.inspect else - %("#{strftime("%Y/%m/%d %H:%M:%S %z")}") + strftime('"%Y/%m/%d %H:%M:%S %z"') end end end diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb new file mode 100644 index 0000000000..d06250171a --- /dev/null +++ b/activesupport/lib/active_support/memoizable.rb @@ -0,0 +1,35 @@ +module ActiveSupport + module Memoizable + def self.included(base) #:nodoc: + base.extend(ClassMethods) + end + + module ClassMethods + def memoize(symbol) + original_method = "_unmemoized_#{symbol}" + memoized_ivar = "@_memoized_#{symbol}" + raise "Already memoized #{symbol}" if instance_methods.map(&:to_s).include?(original_method) + + alias_method original_method, symbol + class_eval <<-EOS, __FILE__, __LINE__ + def #{symbol} + if defined? #{memoized_ivar} + #{memoized_ivar} + else + #{memoized_ivar} = #{original_method} + end + end + EOS + end + end + + def freeze + methods.each do |method| + if m = method.to_s.match(/\A_unmemoized_(.*)/) + send(m[1]).freeze + end + end + super + end + end +end diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index 2fd02d5313..0f531b0c79 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -1,11 +1,7 @@ require 'test/unit/testcase' -require 'active_support/testing/setup_and_teardown' require 'active_support/testing/default' +require 'active_support/testing/core_ext/test' -# TODO: move to core_ext -class Test::Unit::TestCase #:nodoc: - include ActiveSupport::Testing::SetupAndTeardown -end module ActiveSupport class TestCase < Test::Unit::TestCase diff --git a/activesupport/lib/active_support/testing/core_ext/test.rb b/activesupport/lib/active_support/testing/core_ext/test.rb new file mode 100644 index 0000000000..d3f38f0bc7 --- /dev/null +++ b/activesupport/lib/active_support/testing/core_ext/test.rb @@ -0,0 +1,6 @@ +require 'active_support/testing/core_ext/test/unit/assertions' +require 'active_support/testing/setup_and_teardown' + +class Test::Unit::TestCase #:nodoc: + include ActiveSupport::Testing::SetupAndTeardown +end
\ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/test/unit/assertions.rb b/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb index 77fe325fb4..70a44eab8c 100644 --- a/activesupport/lib/active_support/core_ext/test/unit/assertions.rb +++ b/activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb @@ -1,9 +1,10 @@ -module Test - module Unit +require 'test/unit/assertions' +module Test + module Unit #-- # FIXME: no Proc#binding in Ruby 2, must change this API #++ - module Assertions + module Assertions # Test numeric difference between the return value of an expression as a result of what is evaluated # in the yielded block. # diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 24936029a2..5f2027eb3b 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -12,13 +12,13 @@ module ActiveSupport if benchmark = ARGV.include?('--benchmark') # HAX for rake test { :benchmark => true, :runs => 10, - :metrics => [:process_time, :memory, :objects], + :metrics => [:process_time, :memory, :objects, :gc_runs, :gc_time], :output => 'tmp/performance' } else { :benchmark => false, :runs => 1, :min_percent => 0.02, - :metrics => [:wall_time, :memory, :objects], + :metrics => [:process_time, :memory, :objects], :formats => [:flat, :graph_html, :call_tree], :output => 'tmp/performance' } end @@ -44,7 +44,7 @@ module ActiveSupport run_profile(klass.new) result.add_run else - $stderr.puts "Skipping unknown metric #{metric_name.inspect}. Expected :process_time, :wall_time, :cpu_time, :memory, or :objects." + $stderr.puts '%20s: unsupported' % metric_name.to_s end end @@ -72,9 +72,13 @@ module ActiveSupport protected def run_warmup + 5.times { GC.start } + time = Metrics::Time.new run_test(time, :benchmark) puts "%s (%s warmup)" % [full_test_name, time.format(time.total)] + + 5.times { GC.start } end def run_profile(metric) @@ -95,8 +99,13 @@ module ActiveSupport def report rate = @total / profile_options[:runs] - '%20s: %s/run' % [@metric.name, @metric.format(rate)] + '%20s: %s' % [@metric.name, @metric.format(rate)] end + + protected + def output_filename + "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}" + end end class Benchmarker < Performer @@ -107,9 +116,9 @@ module ActiveSupport def record avg = @metric.total / profile_options[:runs].to_i - data = [full_test_name, @metric.name, avg, Time.now.utc.xmlschema] * ',' + now = Time.now.utc.xmlschema with_output_file do |file| - file.puts "#{data},#{environment}" + file.puts "#{avg},#{now},#{environment}" end end @@ -134,10 +143,10 @@ module ActiveSupport end protected - HEADER = 'test,metric,measurement,created_at,app,rails,ruby,platform' + HEADER = 'measurement,created_at,app,rails,ruby,platform' def with_output_file - fname = "#{profile_options[:output]}/benchmarks.csv" + fname = output_filename if new = !File.exist?(fname) FileUtils.mkdir_p(File.dirname(fname)) @@ -148,6 +157,10 @@ module ActiveSupport yield file end end + + def output_filename + "#{super}.csv" + end end class Profiler < Performer @@ -183,7 +196,7 @@ module ActiveSupport else printer_class.name.sub(/Printer$/, '').underscore end - "#{profile_options[:output]}/#{full_test_name}_#{@metric.name}_#{suffix}" + "#{super()}_#{suffix}" end end @@ -210,6 +223,10 @@ module ActiveSupport self.class::Mode end + def measure + 0 + end + def benchmark with_gc_stats do before = measure @@ -271,7 +288,7 @@ module ActiveSupport end class CpuTime < Time - Mode = RubyProf::CPU_TIME + Mode = RubyProf::CPU_TIME if RubyProf.const_defined?(:CPU_TIME) def initialize(*args) # FIXME: yeah my CPU is 2.33 GHz @@ -285,16 +302,27 @@ module ActiveSupport end class Memory < Base - Mode = RubyProf::MEMORY + 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 + adymo 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 unpatched elsif GC.respond_to?(:malloc_allocated_size) def measure GC.malloc_allocated_size / 1024.0 @@ -307,7 +335,7 @@ module ActiveSupport end class Objects < Base - Mode = RubyProf::ALLOCATIONS + Mode = RubyProf::ALLOCATIONS if RubyProf.const_defined?(:ALLOCATIONS) if RubyProf.respond_to?(:measure_allocations) def measure @@ -323,6 +351,46 @@ module ActiveSupport measurement.to_i.to_s end end + + class GcRuns < Base + Mode = RubyProf::GC_RUNS if RubyProf.const_defined?(:GC_RUNS) + + if RubyProf.respond_to?(:measure_gc_runs) + def measure + RubyProf.measure_gc_runs + end + elsif GC.respond_to?(:collections) + def measure + GC.collections + end + elsif GC.respond_to?(:heap_info) + def measure + GC.heap_info['num_gc_passes'] + end + end + + def format(measurement) + measurement.to_i.to_s + end + end + + class GcTime < Base + Mode = RubyProf::GC_TIME if RubyProf.const_defined?(:GC_TIME) + + if RubyProf.respond_to?(:measure_gc_time) + def measure + RubyProf.measure_gc_time + end + elsif GC.respond_to?(:time) + def measure + GC.time + end + end + + def format(measurement) + '%d ms' % (measurement / 1000) + end + end end end end diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index bfc5b16039..e85bfe9b2e 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -159,19 +159,24 @@ module ActiveSupport utc == other end - # If wrapped +time+ is a DateTime, use DateTime#since instead of <tt>+</tt>. - # Otherwise, just pass on to +method_missing+. def +(other) - result = utc.acts_like?(:date) ? utc.since(other) : utc + other rescue utc.since(other) - result.in_time_zone(time_zone) + # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time, + # otherwise move forward from #utc, for accuracy when moving across DST boundaries + if duration_of_variable_length?(other) + method_missing(:+, other) + else + result = utc.acts_like?(:date) ? utc.since(other) : utc + other rescue utc.since(other) + result.in_time_zone(time_zone) + end end - - # If a time-like object is passed in, compare it with +utc+. - # Else if wrapped +time+ is a DateTime, use DateTime#ago instead of DateTime#-. - # Otherwise, just pass on to +method_missing+. + def -(other) + # If we're subtracting a Duration of variable length (i.e., years, months, days), move backwards from #time, + # otherwise move backwards #utc, for accuracy when moving across DST boundaries if other.acts_like?(:time) utc - other + elsif duration_of_variable_length?(other) + method_missing(:-, other) else result = utc.acts_like?(:date) ? utc.ago(other) : utc - other rescue utc.ago(other) result.in_time_zone(time_zone) @@ -179,15 +184,27 @@ module ActiveSupport end def since(other) - utc.since(other).in_time_zone(time_zone) + # If we're adding a Duration of variable length (i.e., years, months, days), move forward from #time, + # otherwise move forward from #utc, for accuracy when moving across DST boundaries + if duration_of_variable_length?(other) + method_missing(:since, other) + else + utc.since(other).in_time_zone(time_zone) + end end def ago(other) - utc.ago(other).in_time_zone(time_zone) + since(-other) end - + def advance(options) - utc.advance(options).in_time_zone(time_zone) + # If we're advancing a value of variable length (i.e., years, weeks, months, days), advance from #time, + # otherwise advance from #utc, for accuracy when moving across DST boundaries + if options.detect {|k,v| [:years, :weeks, :months, :days].include? k} + method_missing(:advance, options) + else + utc.advance(options).in_time_zone(time_zone) + end end %w(year mon month day mday hour min sec).each do |method_name| @@ -246,7 +263,7 @@ module ActiveSupport end def marshal_load(variables) - initialize(variables[0], ::Time.send!(:get_zone, variables[1]), variables[2]) + initialize(variables[0].utc, ::Time.send!(:get_zone, variables[1]), variables[2].utc) end # Ensure proxy class responds to all methods that underlying time instance responds to. @@ -279,5 +296,9 @@ module ActiveSupport def transfer_time_values_to_utc_constructor(time) ::Time.utc_time(time.year, time.month, time.day, time.hour, time.min, time.sec, time.respond_to?(:usec) ? time.usec : 0) end + + def duration_of_variable_length?(obj) + ActiveSupport::Duration === obj && obj.parts.flatten.detect {|p| [:years, :months, :days].include? p } + end end end diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 5b2d42aa3c..788d40bfa8 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -201,6 +201,12 @@ module ActiveSupport result end + # Compare #name and TZInfo identifier to a supplied regexp, returning true + # if a match is found. + def =~(re) + return true if name =~ re || MAPPING[name] =~ re + end + # Returns a textual representation of this time zone. def to_s "(GMT#{formatted_offset}) #{name}" diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 09b56525e0..f3220d27aa 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -1,5 +1,11 @@ require 'abstract_unit' +class CacheKeyTest < Test::Unit::TestCase + def test_expand_cache_key + assert_equal 'name/1/2/true', ActiveSupport::Cache.expand_cache_key([1, '2', true], :name) + end +end + class CacheStoreSettingTest < Test::Unit::TestCase def test_file_fragment_cache_store store = ActiveSupport::Cache.lookup_store :file_store, "/path/to/cache/directory" diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index 7563be44f8..62a1f61d53 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -99,7 +99,7 @@ class ArrayExtToSTests < Test::Unit::TestCase end class ArrayExtGroupingTests < Test::Unit::TestCase - def test_group_by_with_perfect_fit + def test_in_groups_of_with_perfect_fit groups = [] ('a'..'i').to_a.in_groups_of(3) do |group| groups << group @@ -109,7 +109,7 @@ class ArrayExtGroupingTests < Test::Unit::TestCase assert_equal [%w(a b c), %w(d e f), %w(g h i)], ('a'..'i').to_a.in_groups_of(3) end - def test_group_by_with_padding + def test_in_groups_of_with_padding groups = [] ('a'..'g').to_a.in_groups_of(3) do |group| groups << group @@ -118,7 +118,7 @@ class ArrayExtGroupingTests < Test::Unit::TestCase assert_equal [%w(a b c), %w(d e f), ['g', nil, nil]], groups end - def test_group_by_pads_with_specified_values + def test_in_groups_of_pads_with_specified_values groups = [] ('a'..'g').to_a.in_groups_of(3, 'foo') do |group| @@ -128,7 +128,7 @@ class ArrayExtGroupingTests < Test::Unit::TestCase assert_equal [%w(a b c), %w(d e f), ['g', 'foo', 'foo']], groups end - def test_group_without_padding + def test_in_groups_of_without_padding groups = [] ('a'..'g').to_a.in_groups_of(3, false) do |group| @@ -137,6 +137,48 @@ class ArrayExtGroupingTests < Test::Unit::TestCase assert_equal [%w(a b c), %w(d e f), ['g']], groups end + + def test_in_groups_returned_array_size + array = (1..7).to_a + + 1.upto(array.size + 1) do |number| + assert_equal number, array.in_groups(number).size + end + end + + def test_in_groups_with_empty_array + assert_equal [[], [], []], [].in_groups(3) + end + + def test_in_groups_with_block + array = (1..9).to_a + groups = [] + + array.in_groups(3) do |group| + groups << group + end + + assert_equal array.in_groups(3), groups + end + + def test_in_groups_with_perfect_fit + assert_equal [[1, 2, 3], [4, 5, 6], [7, 8, 9]], + (1..9).to_a.in_groups(3) + end + + def test_in_groups_with_padding + array = (1..7).to_a + + assert_equal [[1, 2, 3], [4, 5, nil], [6, 7, nil]], + array.in_groups(3) + assert_equal [[1, 2, 3], [4, 5, 'foo'], [6, 7, 'foo']], + array.in_groups(3, 'foo') + end + + def test_in_groups_without_padding + assert_equal [[1, 2, 3], [4, 5], [6, 7]], + (1..7).to_a.in_groups(3, false) + end end class ArraySplitTests < Test::Unit::TestCase diff --git a/activesupport/test/core_ext/enumerable_test.rb b/activesupport/test/core_ext/enumerable_test.rb index fb9b9b8916..2315d8f3db 100644 --- a/activesupport/test/core_ext/enumerable_test.rb +++ b/activesupport/test/core_ext/enumerable_test.rb @@ -63,10 +63,15 @@ class EnumerableTests < Test::Unit::TestCase assert_equal({ 5 => payments[0], 15 => payments[1], 10 => payments[2] }, payments.index_by { |p| p.price }) end - - def test_several + + def test_many assert ![].many? assert ![ 1 ].many? assert [ 1, 2 ].many? + + assert ![].many? {|x| x > 1 } + assert ![ 2 ].many? {|x| x > 1 } + assert ![ 1, 2 ].many? {|x| x > 1 } + assert [ 1, 2, 2 ].many? {|x| x > 1 } end end diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 0977cd8e50..dfe04485be 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -320,8 +320,11 @@ class TimeWithZoneTest < Test::Unit::TestCase marshal_str = Marshal.dump(@twz) mtime = Marshal.load(marshal_str) assert_equal Time.utc(2000, 1, 1, 0), mtime.utc + assert mtime.utc.utc? assert_equal ActiveSupport::TimeZone['Eastern Time (US & Canada)'], mtime.time_zone assert_equal Time.utc(1999, 12, 31, 19), mtime.time + assert mtime.time.utc? + assert_equal @twz.inspect, mtime.inspect end end @@ -331,8 +334,11 @@ class TimeWithZoneTest < Test::Unit::TestCase marshal_str = Marshal.dump(twz) mtime = Marshal.load(marshal_str) assert_equal Time.utc(2000, 1, 1, 0), mtime.utc + assert mtime.utc.utc? assert_equal 'America/New_York', mtime.time_zone.name assert_equal Time.utc(1999, 12, 31, 19), mtime.time + assert mtime.time.utc? + assert_equal @twz.inspect, mtime.inspect end end @@ -485,6 +491,7 @@ class TimeWithZoneTest < Test::Unit::TestCase twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2004,2,29)) assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.advance(:years => 1).inspect assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.years_since(1).inspect + assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.since(1.year).inspect assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", (twz + 1.year).inspect end @@ -492,6 +499,7 @@ class TimeWithZoneTest < Test::Unit::TestCase twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2005,1,31)) assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.advance(:months => 1).inspect assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.months_since(1).inspect + assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.since(1.month).inspect assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", (twz + 1.month).inspect end @@ -499,6 +507,7 @@ class TimeWithZoneTest < Test::Unit::TestCase twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000,1,31)) assert_equal "Tue, 29 Feb 2000 00:00:00 EST -05:00", twz.advance(:months => 1).inspect assert_equal "Tue, 29 Feb 2000 00:00:00 EST -05:00", twz.months_since(1).inspect + assert_equal "Tue, 29 Feb 2000 00:00:00 EST -05:00", twz.since(1.month).inspect assert_equal "Tue, 29 Feb 2000 00:00:00 EST -05:00", (twz + 1.month).inspect end @@ -506,6 +515,7 @@ class TimeWithZoneTest < Test::Unit::TestCase twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,3,2,2)) assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.advance(:months => 1).inspect assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.months_since(1).inspect + assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.since(1.month).inspect assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", (twz + 1.month).inspect end @@ -513,8 +523,172 @@ class TimeWithZoneTest < Test::Unit::TestCase twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,1,59,59)) assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.advance(:seconds => 1).inspect assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", (twz + 1).inspect + assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.since(1).inspect + assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.since(1.second).inspect assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", (twz + 1.second).inspect end + + def test_advance_1_day_across_spring_dst_transition + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,1,10,30)) + # In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long + # When we advance 1 day, we want to end up at the same time on the next day + assert_equal "Sun, 02 Apr 2006 10:30:00 EDT -04:00", twz.advance(:days => 1).inspect + assert_equal "Sun, 02 Apr 2006 10:30:00 EDT -04:00", twz.since(1.days).inspect + assert_equal "Sun, 02 Apr 2006 10:30:00 EDT -04:00", (twz + 1.days).inspect + assert_equal "Sun, 02 Apr 2006 10:30:01 EDT -04:00", twz.since(1.days + 1.second).inspect + assert_equal "Sun, 02 Apr 2006 10:30:01 EDT -04:00", (twz + 1.days + 1.second).inspect + end + + def test_advance_1_day_across_spring_dst_transition_backwards + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,10,30)) + # In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long + # When we advance back 1 day, we want to end up at the same time on the previous day + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.advance(:days => -1).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.ago(1.days).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", (twz - 1.days).inspect + assert_equal "Sat, 01 Apr 2006 10:30:01 EST -05:00", twz.ago(1.days - 1.second).inspect + end + + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_spring_dst_transition + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,1,10,30)) + # In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long + # When we advance a specific number of hours, minutes or seconds, we want to advance exactly that amount + assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", (twz + 86400).inspect + assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", (twz + 86400.seconds).inspect + assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", twz.since(86400).inspect + assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", twz.since(86400.seconds).inspect + assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", twz.advance(:seconds => 86400).inspect + assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", (twz + 1440.minutes).inspect + assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", twz.since(1440.minutes).inspect + assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", twz.advance(:minutes => 1440).inspect + assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", (twz + 24.hours).inspect + assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", twz.since(24.hours).inspect + assert_equal "Sun, 02 Apr 2006 11:30:00 EDT -04:00", twz.advance(:hours => 24).inspect + end + + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_spring_dst_transition_backwards + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,11,30)) + # In 2006, spring DST transition occurred Apr 2 at 2AM; this day was only 23 hours long + # When we advance a specific number of hours, minutes or seconds, we want to advance exactly that amount + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", (twz - 86400).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", (twz - 86400.seconds).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.ago(86400).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.ago(86400.seconds).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.advance(:seconds => -86400).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", (twz - 1440.minutes).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.ago(1440.minutes).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.advance(:minutes => -1440).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", (twz - 24.hours).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.ago(24.hours).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.advance(:hours => -24).inspect + end + + def test_advance_1_day_across_fall_dst_transition + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,28,10,30)) + # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long + # When we advance 1 day, we want to end up at the same time on the next day + assert_equal "Sun, 29 Oct 2006 10:30:00 EST -05:00", twz.advance(:days => 1).inspect + assert_equal "Sun, 29 Oct 2006 10:30:00 EST -05:00", twz.since(1.days).inspect + assert_equal "Sun, 29 Oct 2006 10:30:00 EST -05:00", (twz + 1.days).inspect + assert_equal "Sun, 29 Oct 2006 10:30:01 EST -05:00", twz.since(1.days + 1.second).inspect + assert_equal "Sun, 29 Oct 2006 10:30:01 EST -05:00", (twz + 1.days + 1.second).inspect + end + + def test_advance_1_day_across_fall_dst_transition_backwards + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,29,10,30)) + # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long + # When we advance backwards 1 day, we want to end up at the same time on the previous day + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.advance(:days => -1).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.ago(1.days).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", (twz - 1.days).inspect + assert_equal "Sat, 28 Oct 2006 10:30:01 EDT -04:00", twz.ago(1.days - 1.second).inspect + end + + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_fall_dst_transition + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,28,10,30)) + # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long + # When we advance a specific number of hours, minutes or seconds, we want to advance exactly that amount + assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", (twz + 86400).inspect + assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", (twz + 86400.seconds).inspect + assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", twz.since(86400).inspect + assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", twz.since(86400.seconds).inspect + assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", twz.advance(:seconds => 86400).inspect + assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", (twz + 1440.minutes).inspect + assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", twz.since(1440.minutes).inspect + assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", twz.advance(:minutes => 1440).inspect + assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", (twz + 24.hours).inspect + assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", twz.since(24.hours).inspect + assert_equal "Sun, 29 Oct 2006 09:30:00 EST -05:00", twz.advance(:hours => 24).inspect + end + + def test_advance_1_day_expressed_as_number_of_seconds_minutes_or_hours_across_fall_dst_transition_backwards + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,29,9,30)) + # In 2006, fall DST transition occurred Oct 29 at 2AM; this day was 25 hours long + # When we advance a specific number of hours, minutes or seconds, we want to advance exactly that amount + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", (twz - 86400).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", (twz - 86400.seconds).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.ago(86400).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.ago(86400.seconds).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.advance(:seconds => -86400).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", (twz - 1440.minutes).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.ago(1440.minutes).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.advance(:minutes => -1440).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", (twz - 24.hours).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.ago(24.hours).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.advance(:hours => -24).inspect + end + + def test_advance_1_month_across_spring_dst_transition + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,1,10,30)) + assert_equal "Mon, 01 May 2006 10:30:00 EDT -04:00", twz.advance(:months => 1).inspect + assert_equal "Mon, 01 May 2006 10:30:00 EDT -04:00", twz.months_since(1).inspect + assert_equal "Mon, 01 May 2006 10:30:00 EDT -04:00", twz.since(1.month).inspect + assert_equal "Mon, 01 May 2006 10:30:00 EDT -04:00", (twz + 1.month).inspect + end + + def test_advance_1_month_across_spring_dst_transition_backwards + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,5,1,10,30)) + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.advance(:months => -1).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.months_ago(1).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", twz.ago(1.month).inspect + assert_equal "Sat, 01 Apr 2006 10:30:00 EST -05:00", (twz - 1.month).inspect + end + + def test_advance_1_month_across_fall_dst_transition + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,28,10,30)) + assert_equal "Tue, 28 Nov 2006 10:30:00 EST -05:00", twz.advance(:months => 1).inspect + assert_equal "Tue, 28 Nov 2006 10:30:00 EST -05:00", twz.months_since(1).inspect + assert_equal "Tue, 28 Nov 2006 10:30:00 EST -05:00", twz.since(1.month).inspect + assert_equal "Tue, 28 Nov 2006 10:30:00 EST -05:00", (twz + 1.month).inspect + end + + def test_advance_1_month_across_fall_dst_transition_backwards + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,11,28,10,30)) + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.advance(:months => -1).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.months_ago(1).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", twz.ago(1.month).inspect + assert_equal "Sat, 28 Oct 2006 10:30:00 EDT -04:00", (twz - 1.month).inspect + end + + def test_advance_1_year + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2008,2,15,10,30)) + assert_equal "Sun, 15 Feb 2009 10:30:00 EST -05:00", twz.advance(:years => 1).inspect + assert_equal "Sun, 15 Feb 2009 10:30:00 EST -05:00", twz.years_since(1).inspect + assert_equal "Sun, 15 Feb 2009 10:30:00 EST -05:00", (twz + 1.year).inspect + assert_equal "Thu, 15 Feb 2007 10:30:00 EST -05:00", twz.advance(:years => -1).inspect + assert_equal "Thu, 15 Feb 2007 10:30:00 EST -05:00", twz.years_ago(1).inspect + assert_equal "Thu, 15 Feb 2007 10:30:00 EST -05:00", (twz - 1.year).inspect + end + + def test_advance_1_year_during_dst + twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2008,7,15,10,30)) + assert_equal "Wed, 15 Jul 2009 10:30:00 EDT -04:00", twz.advance(:years => 1).inspect + assert_equal "Wed, 15 Jul 2009 10:30:00 EDT -04:00", twz.years_since(1).inspect + assert_equal "Wed, 15 Jul 2009 10:30:00 EDT -04:00", (twz + 1.year).inspect + assert_equal "Sun, 15 Jul 2007 10:30:00 EDT -04:00", twz.advance(:years => -1).inspect + assert_equal "Sun, 15 Jul 2007 10:30:00 EDT -04:00", twz.years_ago(1).inspect + assert_equal "Sun, 15 Jul 2007 10:30:00 EDT -04:00", (twz - 1.year).inspect + end end class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index 4ce9cbb705..6c0c14e866 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -110,6 +110,23 @@ class InflectorTest < Test::Unit::TestCase end end + def test_humanize_by_rule + ActiveSupport::Inflector.inflections do |inflect| + inflect.human(/_cnt$/i, '\1_count') + inflect.human(/^prefx_/i, '\1') + end + assert_equal("Jargon count", ActiveSupport::Inflector.humanize("jargon_cnt")) + assert_equal("Request", ActiveSupport::Inflector.humanize("prefx_request")) + end + + def test_humanize_by_string + ActiveSupport::Inflector.inflections do |inflect| + inflect.human("col_rpted_bugs", "Reported bugs") + end + assert_equal("Reported bugs", ActiveSupport::Inflector.humanize("col_rpted_bugs")) + assert_equal("Col rpted bugs", ActiveSupport::Inflector.humanize("COL_rpted_bugs")) + end + def test_constantize assert_nothing_raised { assert_equal Ace::Base::Case, ActiveSupport::Inflector.constantize("Ace::Base::Case") } assert_nothing_raised { assert_equal Ace::Base::Case, ActiveSupport::Inflector.constantize("::Ace::Base::Case") } @@ -148,7 +165,7 @@ class InflectorTest < Test::Unit::TestCase end end - %w{plurals singulars uncountables}.each do |inflection_type| + %w{plurals singulars uncountables humans}.each do |inflection_type| class_eval " def test_clear_#{inflection_type} cached_values = ActiveSupport::Inflector.inflections.#{inflection_type} @@ -160,25 +177,29 @@ class InflectorTest < Test::Unit::TestCase end def test_clear_all - cached_values = ActiveSupport::Inflector.inflections.plurals, ActiveSupport::Inflector.inflections.singulars, ActiveSupport::Inflector.inflections.uncountables + cached_values = ActiveSupport::Inflector.inflections.plurals, ActiveSupport::Inflector.inflections.singulars, ActiveSupport::Inflector.inflections.uncountables, ActiveSupport::Inflector.inflections.humans ActiveSupport::Inflector.inflections.clear :all assert ActiveSupport::Inflector.inflections.plurals.empty? assert ActiveSupport::Inflector.inflections.singulars.empty? assert ActiveSupport::Inflector.inflections.uncountables.empty? + assert ActiveSupport::Inflector.inflections.humans.empty? ActiveSupport::Inflector.inflections.instance_variable_set :@plurals, cached_values[0] ActiveSupport::Inflector.inflections.instance_variable_set :@singulars, cached_values[1] ActiveSupport::Inflector.inflections.instance_variable_set :@uncountables, cached_values[2] + ActiveSupport::Inflector.inflections.instance_variable_set :@humans, cached_values[3] end def test_clear_with_default - cached_values = ActiveSupport::Inflector.inflections.plurals, ActiveSupport::Inflector.inflections.singulars, ActiveSupport::Inflector.inflections.uncountables + cached_values = ActiveSupport::Inflector.inflections.plurals, ActiveSupport::Inflector.inflections.singulars, ActiveSupport::Inflector.inflections.uncountables, ActiveSupport::Inflector.inflections.humans ActiveSupport::Inflector.inflections.clear assert ActiveSupport::Inflector.inflections.plurals.empty? assert ActiveSupport::Inflector.inflections.singulars.empty? assert ActiveSupport::Inflector.inflections.uncountables.empty? + assert ActiveSupport::Inflector.inflections.humans.empty? ActiveSupport::Inflector.inflections.instance_variable_set :@plurals, cached_values[0] ActiveSupport::Inflector.inflections.instance_variable_set :@singulars, cached_values[1] ActiveSupport::Inflector.inflections.instance_variable_set :@uncountables, cached_values[2] + ActiveSupport::Inflector.inflections.instance_variable_set :@humans, cached_values[3] end Irregularities.each do |irregularity| @@ -217,7 +238,7 @@ class InflectorTest < Test::Unit::TestCase end end - { :singulars => :singular, :plurals => :plural, :uncountables => :uncountable }.each do |scope, method| + { :singulars => :singular, :plurals => :plural, :uncountables => :uncountable, :humans => :human }.each do |scope, method| ActiveSupport::Inflector.inflections do |inflect| define_method("test_clear_inflections_with_#{scope}") do # save the inflections diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb new file mode 100644 index 0000000000..fc24a2942d --- /dev/null +++ b/activesupport/test/memoizable_test.rb @@ -0,0 +1,49 @@ +require 'abstract_unit' + +uses_mocha 'Memoizable' do + class MemoizableTest < Test::Unit::TestCase + class Person + include ActiveSupport::Memoizable + + def name + fetch_name_from_floppy + end + memoize :name + + def age + nil + end + memoize :age + + private + def fetch_name_from_floppy + "Josh" + end + end + + def test_memoization + person = Person.new + assert_equal "Josh", person.name + + person.expects(:fetch_name_from_floppy).never + 2.times { assert_equal "Josh", person.name } + end + + def test_memoized_methods_are_frozen + person = Person.new + person.freeze + assert_equal "Josh", person.name + assert_equal true, person.name.frozen? + end + + def test_memoization_frozen_with_nil_value + person = Person.new + person.freeze + assert_equal nil, person.age + end + + def test_double_memoization + assert_raise(RuntimeError) { Person.memoize :name } + end + end +end diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index b42dcd17f8..515ffcf0bf 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -250,6 +250,13 @@ class TimeZoneTest < Test::Unit::TestCase assert zone1 == zone1 end + def test_zone_match + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + assert zone =~ /Eastern/ + assert zone =~ /New_York/ + assert zone !~ /Nonexistent_Place/ + end + def test_to_s assert_equal "(GMT+03:00) Moscow", ActiveSupport::TimeZone['Moscow'].to_s end |