aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport
diff options
context:
space:
mode:
Diffstat (limited to 'activesupport')
-rw-r--r--activesupport/CHANGELOG26
-rw-r--r--activesupport/Rakefile12
-rw-r--r--activesupport/lib/active_support.rb1
-rw-r--r--activesupport/lib/active_support/cache.rb16
-rw-r--r--activesupport/lib/active_support/core_ext/array/conversions.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/array/grouping.rb47
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/module/introspection.rb25
-rw-r--r--activesupport/lib/active_support/core_ext/module/model_naming.rb5
-rw-r--r--activesupport/lib/active_support/core_ext/object/instance_variables.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/test.rb1
-rw-r--r--activesupport/lib/active_support/dependencies.rb6
-rw-r--r--activesupport/lib/active_support/inflector.rb27
-rw-r--r--activesupport/lib/active_support/json/encoders/date_time.rb2
-rw-r--r--activesupport/lib/active_support/memoizable.rb35
-rw-r--r--activesupport/lib/active_support/test_case.rb6
-rw-r--r--activesupport/lib/active_support/testing/core_ext/test.rb6
-rw-r--r--activesupport/lib/active_support/testing/core_ext/test/unit/assertions.rb (renamed from activesupport/lib/active_support/core_ext/test/unit/assertions.rb)7
-rw-r--r--activesupport/lib/active_support/testing/performance.rb92
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb47
-rw-r--r--activesupport/lib/active_support/values/time_zone.rb6
-rw-r--r--activesupport/test/caching_test.rb6
-rw-r--r--activesupport/test/core_ext/array_ext_test.rb50
-rw-r--r--activesupport/test/core_ext/enumerable_test.rb9
-rw-r--r--activesupport/test/core_ext/time_with_zone_test.rb174
-rw-r--r--activesupport/test/inflector_test.rb29
-rw-r--r--activesupport/test/memoizable_test.rb49
-rw-r--r--activesupport/test/time_zone_test.rb7
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, '&nbsp;') {|g| p g}
+ # ["1", "2", "3"]
+ # ["4", "5", "&nbsp;"]
+ # ["6", "7", "&nbsp;"]
+ #
+ # %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