From 45d41f0dadd9fa171f306ff356770c4492726f30 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Thu, 19 Jun 2008 16:25:27 +0200 Subject: integrating I18n into Rails --- .../lib/active_support/core_ext/array/conversions.rb | 14 ++++++++++---- activesupport/lib/active_support/vendor.rb | 7 +++++++ activesupport/lib/active_support/vendor/i18n-0.0.1 | 1 + 3 files changed, 18 insertions(+), 4 deletions(-) create mode 160000 activesupport/lib/active_support/vendor/i18n-0.0.1 (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index a9882828ca..08e608d346 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -6,10 +6,15 @@ module ActiveSupport #:nodoc: module Conversions # Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options: # * :connector - The word used to join the last element in arrays with two or more elements (default: "and") - # * :skip_last_comma - Set to true to return "a, b and c" instead of "a, b, and c". - def to_sentence(options = {}) - options.assert_valid_keys(:connector, :skip_last_comma) - options.reverse_merge! :connector => 'and', :skip_last_comma => false + # * :skip_last_comma - Set to true to return "a, b and c" instead of "a, b, and c". + def to_sentence(options = {}) + options.assert_valid_keys(:connector, :skip_last_comma, :locale) + + locale = options[:locale] + locale ||= request.locale if respond_to?(:request) + + default = :'support.array.sentence_connector'.t(locale) + options.reverse_merge! :connector => default, :skip_last_comma => false options[:connector] = "#{options[:connector]} " unless options[:connector].nil? || options[:connector].strip == '' case length @@ -23,6 +28,7 @@ module ActiveSupport #:nodoc: "#{self[0...-1].join(', ')}#{options[:skip_last_comma] ? '' : ','} #{options[:connector]}#{self[-1]}" end end + # Calls to_param on all its elements and joins the result with # slashes. This is used by url_for in Action Pack. diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index a02e42f791..bca2664fa4 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -23,4 +23,11 @@ begin gem 'tzinfo', '~> 0.3.9' rescue Gem::LoadError $:.unshift "#{File.dirname(__FILE__)}/vendor/tzinfo-0.3.9" +end + +begin + gem 'i18n', '~> 0.3.9' +rescue Gem::LoadError + $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.0.1/lib" # TODO + require 'i18n' end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 new file mode 160000 index 0000000000..70ab0f3cc5 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -0,0 +1 @@ +Subproject commit 70ab0f3cc5921cc67e09741939a08b2582d707cb -- cgit v1.2.3 From 4a8486a1b1aa28d4cab5571b55301917221870e9 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Thu, 19 Jun 2008 16:43:30 +0200 Subject: add lang file for active_support --- activesupport/lib/active_support.rb | 1 + activesupport/lib/active_support/lang/en-US.rb | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 activesupport/lib/active_support/lang/en-US.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 1a8603e892..acdb3056d2 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -55,6 +55,7 @@ require 'active_support/multibyte' require 'active_support/base64' require 'active_support/time_with_zone' +require 'active_support/lang/en-US.rb' Inflector = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Inflector', 'ActiveSupport::Inflector') Dependencies = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Dependencies', 'ActiveSupport::Dependencies') diff --git a/activesupport/lib/active_support/lang/en-US.rb b/activesupport/lib/active_support/lang/en-US.rb new file mode 100644 index 0000000000..8732927f48 --- /dev/null +++ b/activesupport/lib/active_support/lang/en-US.rb @@ -0,0 +1,7 @@ +I18n.backend.add_translations :'en-US', { + :support => { + :array => { + :sentence_connector => 'and' + } + } +} \ No newline at end of file -- cgit v1.2.3 From fdb5f810dc41f54f8cdb9c51154ff8987362c13a Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Thu, 19 Jun 2008 16:55:47 +0200 Subject: I18n has not been released as a gem, yet --- activesupport/lib/active_support/vendor.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/vendor.rb b/activesupport/lib/active_support/vendor.rb index bca2664fa4..381471b833 100644 --- a/activesupport/lib/active_support/vendor.rb +++ b/activesupport/lib/active_support/vendor.rb @@ -25,9 +25,10 @@ rescue Gem::LoadError $:.unshift "#{File.dirname(__FILE__)}/vendor/tzinfo-0.3.9" end -begin - gem 'i18n', '~> 0.3.9' -rescue Gem::LoadError - $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.0.1/lib" # TODO +# TODO I18n gem has not been released yet +# begin +# gem 'i18n', '~> 0.0.1' +# rescue Gem::LoadError + $:.unshift "#{File.dirname(__FILE__)}/vendor/i18n-0.0.1/lib" require 'i18n' -end \ No newline at end of file +# end \ No newline at end of file -- cgit v1.2.3 From 585c8c17c303fc46fcf014a644a541eae6cb5ffd Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Fri, 20 Jun 2008 09:13:20 +0200 Subject: rename Backend::Simple#add_translations to set_translations because it overwrites existing translations --- activesupport/lib/active_support/lang/en-US.rb | 2 +- activesupport/lib/active_support/vendor/i18n-0.0.1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/lang/en-US.rb b/activesupport/lib/active_support/lang/en-US.rb index 8732927f48..5b8e04363e 100644 --- a/activesupport/lib/active_support/lang/en-US.rb +++ b/activesupport/lib/active_support/lang/en-US.rb @@ -1,4 +1,4 @@ -I18n.backend.add_translations :'en-US', { +I18n.backend.set_translations :'en-US', { :support => { :array => { :sentence_connector => 'and' diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 index 70ab0f3cc5..1af3435539 160000 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1 +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -1 +1 @@ -Subproject commit 70ab0f3cc5921cc67e09741939a08b2582d707cb +Subproject commit 1af3435539b4a0729c13d21c5df037a635fe98c1 -- cgit v1.2.3 From c1e2506494107892a0962b8491cd234f77949c08 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sat, 21 Jun 2008 11:27:19 +0200 Subject: Changed process of storing translations from the client libraries to the backend: clients now can pass a block to backend#populate which can contain code to load and register translations. This makes sense for backends that persist their translations (e.g. to db) so the repeated loading and passing of translations throughout the server startup would be wasted resources. --- activesupport/lib/active_support.rb | 5 ++++- activesupport/lib/active_support/lang/en-US.rb | 2 +- activesupport/lib/active_support/vendor/i18n-0.0.1 | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index acdb3056d2..0de948dda9 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -55,7 +55,10 @@ require 'active_support/multibyte' require 'active_support/base64' require 'active_support/time_with_zone' -require 'active_support/lang/en-US.rb' + +I18n.backend.populate do + require 'active_support/lang/en-US.rb' +end Inflector = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Inflector', 'ActiveSupport::Inflector') Dependencies = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Dependencies', 'ActiveSupport::Dependencies') diff --git a/activesupport/lib/active_support/lang/en-US.rb b/activesupport/lib/active_support/lang/en-US.rb index 5b8e04363e..aa06fe14bd 100644 --- a/activesupport/lib/active_support/lang/en-US.rb +++ b/activesupport/lib/active_support/lang/en-US.rb @@ -1,4 +1,4 @@ -I18n.backend.set_translations :'en-US', { +I18n.backend.store_translations :'en-US', { :support => { :array => { :sentence_connector => 'and' diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 index 1af3435539..8e43afa38a 160000 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1 +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -1 +1 @@ -Subproject commit 1af3435539b4a0729c13d21c5df037a635fe98c1 +Subproject commit 8e43afa38aa007d1de6d6acf44d43143c403d13f -- cgit v1.2.3 From 428aa24d24032d382dc3d9ccf131e0c874043dbd Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sat, 21 Jun 2008 11:35:02 +0200 Subject: Renamed lang/ to locale/ because that's what we seem to standarize on. Also, in future this place can be used for data/code that's not literally translations but conceptually belongs to the locale (like custom pluralization algorithms etc.). --- activesupport/lib/active_support.rb | 2 +- activesupport/lib/active_support/lang/en-US.rb | 7 ------- activesupport/lib/active_support/locale/en-US.rb | 7 +++++++ 3 files changed, 8 insertions(+), 8 deletions(-) delete mode 100644 activesupport/lib/active_support/lang/en-US.rb create mode 100644 activesupport/lib/active_support/locale/en-US.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 0de948dda9..de50aafe16 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -57,7 +57,7 @@ require 'active_support/base64' require 'active_support/time_with_zone' I18n.backend.populate do - require 'active_support/lang/en-US.rb' + require 'active_support/locale/en-US.rb' end Inflector = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Inflector', 'ActiveSupport::Inflector') diff --git a/activesupport/lib/active_support/lang/en-US.rb b/activesupport/lib/active_support/lang/en-US.rb deleted file mode 100644 index aa06fe14bd..0000000000 --- a/activesupport/lib/active_support/lang/en-US.rb +++ /dev/null @@ -1,7 +0,0 @@ -I18n.backend.store_translations :'en-US', { - :support => { - :array => { - :sentence_connector => 'and' - } - } -} \ No newline at end of file diff --git a/activesupport/lib/active_support/locale/en-US.rb b/activesupport/lib/active_support/locale/en-US.rb new file mode 100644 index 0000000000..aa06fe14bd --- /dev/null +++ b/activesupport/lib/active_support/locale/en-US.rb @@ -0,0 +1,7 @@ +I18n.backend.store_translations :'en-US', { + :support => { + :array => { + :sentence_connector => 'and' + } + } +} \ No newline at end of file -- cgit v1.2.3 From 8bfdabbd8b5137f91d8bcddc8c3d18961c8e316b Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sat, 21 Jun 2008 17:50:37 +0200 Subject: incorporate #translate usage with several default keys (use first default key that resolves to a translation). this might, depending on the backend implementation save some expensive lookups (like db lookups) --- activesupport/lib/active_support/vendor/i18n-0.0.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 index 8e43afa38a..20c331666b 160000 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1 +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -1 +1 @@ -Subproject commit 8e43afa38aa007d1de6d6acf44d43143c403d13f +Subproject commit 20c331666b3b6a21791d4cded53c3d8654fba714 -- cgit v1.2.3 From 55e2e2e8b4efbe6fdb0a921c19cd8be5650eab0a Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sat, 21 Jun 2008 18:12:59 +0200 Subject: experimental DeprecatedConstantToMethodProxy --- activesupport/lib/active_support/deprecation.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index ebdaf86146..36933b007d 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -194,6 +194,23 @@ module ActiveSupport ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack) end end + + class DeprecatedConstantToMethodProxy < DeprecationProxy #:nodoc: + def initialize(old_const, new_target, new_method) + @old_const = old_const + @new_target = new_target + @new_method = new_method + end + + private + def target + @new_target.__send__(@new_method) + end + + def warn(callstack, called, args) + ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_target.inspect}.#{@new_method} instead.", callstack) + end + end end end -- cgit v1.2.3 From d897acfbb11aaa2d7f3138e1f9772546ecb6f981 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 22 Jun 2008 11:39:10 +0200 Subject: remove DeprecatedConstantToMethodProxy again --- activesupport/lib/active_support/deprecation.rb | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 36933b007d..ebdaf86146 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -194,23 +194,6 @@ module ActiveSupport ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack) end end - - class DeprecatedConstantToMethodProxy < DeprecationProxy #:nodoc: - def initialize(old_const, new_target, new_method) - @old_const = old_const - @new_target = new_target - @new_method = new_method - end - - private - def target - @new_target.__send__(@new_method) - end - - def warn(callstack, called, args) - ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_target.inspect}.#{@new_method} instead.", callstack) - end - end end end -- cgit v1.2.3 From 67fce4671e8bcfe2aa670a89195b20837546183a Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 22 Jun 2008 13:49:08 +0200 Subject: crap, an array never has a request, stupid. --- activesupport/lib/active_support/core_ext/array/conversions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 08e608d346..80d91a6cbd 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -11,7 +11,7 @@ module ActiveSupport #:nodoc: options.assert_valid_keys(:connector, :skip_last_comma, :locale) locale = options[:locale] - locale ||= request.locale if respond_to?(:request) + locale ||= self.locale if respond_to?(:locale) default = :'support.array.sentence_connector'.t(locale) options.reverse_merge! :connector => default, :skip_last_comma => false -- cgit v1.2.3 From 7403c825a05af320e20f1b7e20b0c565081ede89 Mon Sep 17 00:00:00 2001 From: Luca Guidi Date: Wed, 2 Jul 2008 17:51:34 +0200 Subject: Fixed Date and Time localization for ActiveSupport --- activesupport/lib/active_support/locale/en-US.rb | 21 +++++++ activesupport/test/core_ext/i18n_test.rb | 75 ++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 activesupport/test/core_ext/i18n_test.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support/locale/en-US.rb b/activesupport/lib/active_support/locale/en-US.rb index aa06fe14bd..51324a90bf 100644 --- a/activesupport/lib/active_support/locale/en-US.rb +++ b/activesupport/lib/active_support/locale/en-US.rb @@ -3,5 +3,26 @@ I18n.backend.store_translations :'en-US', { :array => { :sentence_connector => 'and' } + }, + :date => { + :formats => { + :default => "%Y-%m-%d", + :short => "%b %d", + :long => "%B %d, %Y", + }, + :day_names => Date::DAYNAMES, + :abbr_day_names => Date::ABBR_DAYNAMES, + :month_names => Date::MONTHNAMES, + :abbr_month_names => Date::ABBR_MONTHNAMES, + :order => [:year, :month, :day] + }, + :time => { + :formats => { + :default => "%a, %d %b %Y %H:%M:%S %z", + :short => "%d %b %H:%M", + :long => "%B %d, %Y %H:%M", + }, + :am => 'am', + :pm => 'pm' } } \ No newline at end of file diff --git a/activesupport/test/core_ext/i18n_test.rb b/activesupport/test/core_ext/i18n_test.rb new file mode 100644 index 0000000000..a67b6a5d8c --- /dev/null +++ b/activesupport/test/core_ext/i18n_test.rb @@ -0,0 +1,75 @@ +require 'abstract_unit' + +class I18nTest < Test::Unit::TestCase + def setup + @date = Date.parse("2008-7-2") + @time = Time.utc(2008, 7, 2, 16, 47, 1) + end + + uses_mocha 'I18nTimeZoneTest' do + def test_time_zone_localization_with_default_format + Time.zone.stubs(:now).returns Time.local(2000) + assert_equal "Sat, 01 Jan 2000 00:00:00 +0100", Time.zone.now.l + end + end + + def test_date_localization_should_use_default_format + assert_equal "2008-07-02", @date.l + end + + def test_date_localization_with_default_format + assert_equal "2008-07-02", @date.l(nil, :default) + end + + def test_date_localization_with_short_format + assert_equal "Jul 02", @date.l(nil, :short) + end + + def test_date_localization_with_long_format + assert_equal "July 02, 2008", @date.l(nil, :long) + end + + def test_time_localization_should_use_default_format + assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", @time.l + end + + def test_time_localization_with_default_format + assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", @time.l(nil, :default) + end + + def test_time_localization_with_short_format + assert_equal "02 Jul 16:47", @time.l(nil, :short) + end + + def test_time_localization_with_long_format + assert_equal "July 02, 2008 16:47", @time.l(nil, :long) + end + + def test_day_names + assert_equal Date::DAYNAMES, :'date.day_names'.t + end + + def test_abbr_day_names + assert_equal Date::ABBR_DAYNAMES, :'date.abbr_day_names'.t + end + + def test_month_names + assert_equal Date::MONTHNAMES, :'date.month_names'.t + end + + def test_abbr_month_names + assert_equal Date::ABBR_MONTHNAMES, :'date.abbr_month_names'.t + end + + def test_date_order + assert_equal [:year, :month, :day], :'date.order'.t + end + + def test_time_am + assert_equal 'am', :'time.am'.t + end + + def test_time_pm + assert_equal 'pm', :'time.pm'.t + end +end -- cgit v1.2.3 From 8f74ba96c47e77e18ce363c8e7cd2fc9196faf7a Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 2 Jul 2008 19:21:07 +0200 Subject: remove core extensions in favor of I18n#translate and I18n#localize --- .../active_support/core_ext/array/conversions.rb | 2 +- activesupport/lib/active_support/vendor/i18n-0.0.1 | 2 +- activesupport/test/core_ext/i18n_test.rb | 75 ---------------------- activesupport/test/i18n_test.rb | 75 ++++++++++++++++++++++ 4 files changed, 77 insertions(+), 77 deletions(-) delete mode 100644 activesupport/test/core_ext/i18n_test.rb create mode 100644 activesupport/test/i18n_test.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 80d91a6cbd..80bf1404de 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -13,7 +13,7 @@ module ActiveSupport #:nodoc: locale = options[:locale] locale ||= self.locale if respond_to?(:locale) - default = :'support.array.sentence_connector'.t(locale) + default = I18n.translate(:'support.array.sentence_connector', locale) options.reverse_merge! :connector => default, :skip_last_comma => false options[:connector] = "#{options[:connector]} " unless options[:connector].nil? || options[:connector].strip == '' diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 index 20c331666b..970bc7ab5f 160000 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1 +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -1 +1 @@ -Subproject commit 20c331666b3b6a21791d4cded53c3d8654fba714 +Subproject commit 970bc7ab5faa94e41ee4a56bc8913c144c1cdd19 diff --git a/activesupport/test/core_ext/i18n_test.rb b/activesupport/test/core_ext/i18n_test.rb deleted file mode 100644 index a67b6a5d8c..0000000000 --- a/activesupport/test/core_ext/i18n_test.rb +++ /dev/null @@ -1,75 +0,0 @@ -require 'abstract_unit' - -class I18nTest < Test::Unit::TestCase - def setup - @date = Date.parse("2008-7-2") - @time = Time.utc(2008, 7, 2, 16, 47, 1) - end - - uses_mocha 'I18nTimeZoneTest' do - def test_time_zone_localization_with_default_format - Time.zone.stubs(:now).returns Time.local(2000) - assert_equal "Sat, 01 Jan 2000 00:00:00 +0100", Time.zone.now.l - end - end - - def test_date_localization_should_use_default_format - assert_equal "2008-07-02", @date.l - end - - def test_date_localization_with_default_format - assert_equal "2008-07-02", @date.l(nil, :default) - end - - def test_date_localization_with_short_format - assert_equal "Jul 02", @date.l(nil, :short) - end - - def test_date_localization_with_long_format - assert_equal "July 02, 2008", @date.l(nil, :long) - end - - def test_time_localization_should_use_default_format - assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", @time.l - end - - def test_time_localization_with_default_format - assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", @time.l(nil, :default) - end - - def test_time_localization_with_short_format - assert_equal "02 Jul 16:47", @time.l(nil, :short) - end - - def test_time_localization_with_long_format - assert_equal "July 02, 2008 16:47", @time.l(nil, :long) - end - - def test_day_names - assert_equal Date::DAYNAMES, :'date.day_names'.t - end - - def test_abbr_day_names - assert_equal Date::ABBR_DAYNAMES, :'date.abbr_day_names'.t - end - - def test_month_names - assert_equal Date::MONTHNAMES, :'date.month_names'.t - end - - def test_abbr_month_names - assert_equal Date::ABBR_MONTHNAMES, :'date.abbr_month_names'.t - end - - def test_date_order - assert_equal [:year, :month, :day], :'date.order'.t - end - - def test_time_am - assert_equal 'am', :'time.am'.t - end - - def test_time_pm - assert_equal 'pm', :'time.pm'.t - end -end diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb new file mode 100644 index 0000000000..e6d90f09c1 --- /dev/null +++ b/activesupport/test/i18n_test.rb @@ -0,0 +1,75 @@ +require 'abstract_unit' + +class I18nTest < Test::Unit::TestCase + def setup + @date = Date.parse("2008-7-2") + @time = Time.utc(2008, 7, 2, 16, 47, 1) + end + + uses_mocha 'I18nTimeZoneTest' do + def test_time_zone_localization_with_default_format + Time.zone.stubs(:now).returns Time.local(2000) + assert_equal "Sat, 01 Jan 2000 00:00:00 +0100", I18n.localize(Time.zone.now) + end + end + + def test_date_localization_should_use_default_format + assert_equal "2008-07-02", I18n.localize(@date) + end + + def test_date_localization_with_default_format + assert_equal "2008-07-02", I18n.localize(@date, nil, :default) + end + + def test_date_localization_with_short_format + assert_equal "Jul 02", I18n.localize(@date, nil, :short) + end + + def test_date_localization_with_long_format + assert_equal "July 02, 2008", I18n.localize(@date, nil, :long) + end + + def test_time_localization_should_use_default_format + assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", I18n.localize(@time) + end + + def test_time_localization_with_default_format + assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", I18n.localize(@time, nil, :default) + end + + def test_time_localization_with_short_format + assert_equal "02 Jul 16:47", I18n.localize(@time, nil, :short) + end + + def test_time_localization_with_long_format + assert_equal "July 02, 2008 16:47", I18n.localize(@time, nil, :long) + end + + def test_day_names + assert_equal Date::DAYNAMES, I18n.translate(:'date.day_names') + end + + def test_abbr_day_names + assert_equal Date::ABBR_DAYNAMES, I18n.translate(:'date.abbr_day_names') + end + + def test_month_names + assert_equal Date::MONTHNAMES, I18n.translate(:'date.month_names') + end + + def test_abbr_month_names + assert_equal Date::ABBR_MONTHNAMES, I18n.translate(:'date.abbr_month_names') + end + + def test_date_order + assert_equal [:year, :month, :day], I18n.translate(:'date.order') + end + + def test_time_am + assert_equal 'am', I18n.translate(:'time.am') + end + + def test_time_pm + assert_equal 'pm', I18n.translate(:'time.pm') + end +end -- cgit v1.2.3 From 84816ae981a8598e5e401eb1b9b805de840fefc9 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Sun, 6 Jul 2008 21:20:02 +0200 Subject: align with changes in i18n --- .../lib/active_support/core_ext/array/conversions.rb | 5 +---- activesupport/lib/active_support/vendor/i18n-0.0.1 | 2 +- activesupport/test/i18n_test.rb | 12 ++++++------ 3 files changed, 8 insertions(+), 11 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 80bf1404de..59dc96754f 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -10,10 +10,7 @@ module ActiveSupport #:nodoc: def to_sentence(options = {}) options.assert_valid_keys(:connector, :skip_last_comma, :locale) - locale = options[:locale] - locale ||= self.locale if respond_to?(:locale) - - default = I18n.translate(:'support.array.sentence_connector', locale) + default = I18n.translate(:'support.array.sentence_connector', :locale => options[:locale]) options.reverse_merge! :connector => default, :skip_last_comma => false options[:connector] = "#{options[:connector]} " unless options[:connector].nil? || options[:connector].strip == '' diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 index 970bc7ab5f..46aad28993 160000 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1 +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1 @@ -1 +1 @@ -Subproject commit 970bc7ab5faa94e41ee4a56bc8913c144c1cdd19 +Subproject commit 46aad289935eaf059c429acb5f3bfa0946f2d99f diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb index e6d90f09c1..17074b6cc6 100644 --- a/activesupport/test/i18n_test.rb +++ b/activesupport/test/i18n_test.rb @@ -18,15 +18,15 @@ class I18nTest < Test::Unit::TestCase end def test_date_localization_with_default_format - assert_equal "2008-07-02", I18n.localize(@date, nil, :default) + assert_equal "2008-07-02", I18n.localize(@date, :format => :default) end def test_date_localization_with_short_format - assert_equal "Jul 02", I18n.localize(@date, nil, :short) + assert_equal "Jul 02", I18n.localize(@date, :format => :short) end def test_date_localization_with_long_format - assert_equal "July 02, 2008", I18n.localize(@date, nil, :long) + assert_equal "July 02, 2008", I18n.localize(@date, :format => :long) end def test_time_localization_should_use_default_format @@ -34,15 +34,15 @@ class I18nTest < Test::Unit::TestCase end def test_time_localization_with_default_format - assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", I18n.localize(@time, nil, :default) + assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", I18n.localize(@time, :format => :default) end def test_time_localization_with_short_format - assert_equal "02 Jul 16:47", I18n.localize(@time, nil, :short) + assert_equal "02 Jul 16:47", I18n.localize(@time, :format => :short) end def test_time_localization_with_long_format - assert_equal "July 02, 2008 16:47", I18n.localize(@time, nil, :long) + assert_equal "July 02, 2008 16:47", I18n.localize(@time, :format => :long) end def test_day_names -- cgit v1.2.3 From 9783e66cade4d145389cca18fab822f44d03161a Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 14 Jul 2008 01:02:07 +0100 Subject: Slightly faster DateTime#to_json. [#598 state:resolved] [Alex Zepeda] --- activesupport/lib/active_support/json/encoders/date_time.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activesupport') 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 -- cgit v1.2.3 From 269c6c6bcfec342cf830e0b20c44bb2c888e8fdf Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 9 Jul 2008 19:43:38 -0700 Subject: Cache Module#parent_name --- .../core_ext/module/introspection.rb | 23 ++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb index bb894ec080..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 -- cgit v1.2.3 From 8a9934a9d9fc98b56c4566ae2e3fd4d83e505d3e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Jul 2008 19:50:32 -0500 Subject: Added Memoizable mixin for caching simple lazy loaded attributes --- activesupport/CHANGELOG | 2 ++ activesupport/lib/active_support.rb | 1 + activesupport/lib/active_support/memoizable.rb | 32 ++++++++++++++++++ activesupport/test/memoizable_test.rb | 45 ++++++++++++++++++++++++++ 4 files changed, 80 insertions(+) create mode 100644 activesupport/lib/active_support/memoizable.rb create mode 100644 activesupport/test/memoizable_test.rb (limited to 'activesupport') diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 983e7d0dac..0c308a1cfe 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* 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] diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 1a8603e892..0526057b15 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/memoizable.rb b/activesupport/lib/active_support/memoizable.rb new file mode 100644 index 0000000000..c78fb0a793 --- /dev/null +++ b/activesupport/lib/active_support/memoizable.rb @@ -0,0 +1,32 @@ +module ActiveSupport + module Memoizable + def self.included(base) #:nodoc: + base.extend(ClassMethods) + end + + module ClassMethods + def memorize(symbol) + original_method = "_unmemorized_#{symbol}" + alias_method original_method, symbol + class_eval <<-EOS, __FILE__, __LINE__ + def #{symbol} + if instance_variable_defined?(:@#{symbol}) + @#{symbol} + else + @#{symbol} = #{original_method} + end + end + EOS + end + end + + def freeze + methods.each do |method| + if m = method.to_s.match(/^_unmemorized_(.*)/) + send(m[1]).freeze + end + end + super + end + end +end diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb new file mode 100644 index 0000000000..40a02cf253 --- /dev/null +++ b/activesupport/test/memoizable_test.rb @@ -0,0 +1,45 @@ +require 'abstract_unit' + +uses_mocha 'Memoizable' do + class MemoizableTest < Test::Unit::TestCase + class Person + include ActiveSupport::Memoizable + + def name + fetch_name_from_floppy + end + memorize :name + + def age + nil + end + memorize :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 + end +end -- cgit v1.2.3 From 001c8beb4d0999a858a8b52ad511ee1251cc3517 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Jul 2008 20:02:59 -0500 Subject: memorize typo --- activesupport/lib/active_support/memoizable.rb | 6 +++--- activesupport/test/memoizable_test.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index c78fb0a793..65feca363a 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -5,8 +5,8 @@ module ActiveSupport end module ClassMethods - def memorize(symbol) - original_method = "_unmemorized_#{symbol}" + def memoize(symbol) + original_method = "_unmemoized_#{symbol}" alias_method original_method, symbol class_eval <<-EOS, __FILE__, __LINE__ def #{symbol} @@ -22,7 +22,7 @@ module ActiveSupport def freeze methods.each do |method| - if m = method.to_s.match(/^_unmemorized_(.*)/) + if m = method.to_s.match(/^_unmemoized_(.*)/) send(m[1]).freeze end end diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index 40a02cf253..1b6cec2b2f 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -8,12 +8,12 @@ uses_mocha 'Memoizable' do def name fetch_name_from_floppy end - memorize :name + memoize :name def age nil end - memorize :age + memoize :age private def fetch_name_from_floppy -- cgit v1.2.3 From 911c2c381347ffb04615896ee6afe45277eeb103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarmo=20T=C3=A4nav?= Date: Mon, 14 Jul 2008 20:23:23 -0500 Subject: Some performance tweaks to ActiveSupport::Memoizable Signed-off-by: Joshua Peek --- activesupport/lib/active_support/memoizable.rb | 6 ++++-- activesupport/test/memoizable_test.rb | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 65feca363a..5af50df023 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -7,10 +7,12 @@ module ActiveSupport module ClassMethods def memoize(symbol) original_method = "_unmemoized_#{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 instance_variable_defined?(:@#{symbol}) + if defined? @#{symbol} @#{symbol} else @#{symbol} = #{original_method} @@ -22,7 +24,7 @@ module ActiveSupport def freeze methods.each do |method| - if m = method.to_s.match(/^_unmemoized_(.*)/) + if m = method.to_s.match(/\A_unmemoized_(.*)/) send(m[1]).freeze end end diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index 1b6cec2b2f..fc24a2942d 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -41,5 +41,9 @@ uses_mocha 'Memoizable' do person.freeze assert_equal nil, person.age end + + def test_double_memoization + assert_raise(RuntimeError) { Person.memoize :name } + end end end -- cgit v1.2.3 From 7f0346237e30e55d6cd16a8b4a9dfe4193f61804 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 14 Jul 2008 20:25:09 -0500 Subject: Append a "_" to memoized instance variables --- activesupport/lib/active_support/memoizable.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 5af50df023..d06250171a 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -7,15 +7,16 @@ module ActiveSupport 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? @#{symbol} - @#{symbol} + if defined? #{memoized_ivar} + #{memoized_ivar} else - @#{symbol} = #{original_method} + #{memoized_ivar} = #{original_method} end end EOS -- cgit v1.2.3 From 34510456585216004e483b79beeea3ddc3eb4de6 Mon Sep 17 00:00:00 2001 From: gbuesing Date: Mon, 14 Jul 2008 23:16:39 -0500 Subject: Fix TimeWithZone unmarshaling: coerce unmarshaled Time instances to utc, because Ruby's marshaling of Time instances doesn't respect the zone --- activesupport/CHANGELOG | 2 ++ activesupport/lib/active_support/time_with_zone.rb | 2 +- activesupport/test/core_ext/time_with_zone_test.rb | 6 ++++++ 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'activesupport') diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 0c308a1cfe..a7b33c09ad 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* 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] diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 88593eb92d..e85bfe9b2e 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -263,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. diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index ac52a1be0b..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 -- cgit v1.2.3 From fc89a951933638b051bb1f9e1339ee6ae7c94cda Mon Sep 17 00:00:00 2001 From: Adrian Mugnolo Date: Tue, 15 Jul 2008 01:17:03 -0300 Subject: Add in_groups to ActiveSupport::CoreExtensions::Array::Grouping. [#579 state:resolved] Signed-off-by: Pratik Naik --- activesupport/CHANGELOG | 6 +++ .../lib/active_support/core_ext/array/grouping.rb | 47 +++++++++++++++++++- activesupport/test/core_ext/array_ext_test.rb | 50 ++++++++++++++++++++-- 3 files changed, 97 insertions(+), 6 deletions(-) (limited to 'activesupport') diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index a7b33c09ad..8d3b136d80 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,11 @@ *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] 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/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 -- cgit v1.2.3 From 731fca8cf68ddb6d6b9670538a4686ada6b14b26 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 16 Jul 2008 04:15:31 +0200 Subject: remove i18n lib subproject --- activesupport/lib/active_support/vendor/i18n-0.0.1 | 1 - 1 file changed, 1 deletion(-) delete mode 160000 activesupport/lib/active_support/vendor/i18n-0.0.1 (limited to 'activesupport') diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1 b/activesupport/lib/active_support/vendor/i18n-0.0.1 deleted file mode 160000 index 46aad28993..0000000000 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 46aad289935eaf059c429acb5f3bfa0946f2d99f -- cgit v1.2.3 From fbe1823117c6170e26b328ce192a2fc0945af533 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 16 Jul 2008 04:16:08 +0200 Subject: add i18n lib files --- .../active_support/vendor/i18n-0.0.1/MIT-LICENSE | 20 ++ .../vendor/i18n-0.0.1/README.textile | 18 + .../active_support/vendor/i18n-0.0.1/i18n.gemspec | 24 ++ .../active_support/vendor/i18n-0.0.1/lib/i18n.rb | 182 ++++++++++ .../vendor/i18n-0.0.1/lib/i18n/backend/simple.rb | 154 +++++++++ .../vendor/i18n-0.0.1/lib/i18n/exceptions.rb | 45 +++ .../active_support/vendor/i18n-0.0.1/test/all.rb | 4 + .../vendor/i18n-0.0.1/test/i18n_exceptions_test.rb | 100 ++++++ .../vendor/i18n-0.0.1/test/i18n_test.rb | 141 ++++++++ .../vendor/i18n-0.0.1/test/simple_backend_test.rb | 376 +++++++++++++++++++++ 10 files changed, 1064 insertions(+) create mode 100755 activesupport/lib/active_support/vendor/i18n-0.0.1/MIT-LICENSE create mode 100644 activesupport/lib/active_support/vendor/i18n-0.0.1/README.textile create mode 100644 activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.gemspec create mode 100755 activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/exceptions.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.0.1/test/all.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.0.1/test/i18n_exceptions_test.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.0.1/test/i18n_test.rb create mode 100644 activesupport/lib/active_support/vendor/i18n-0.0.1/test/simple_backend_test.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/MIT-LICENSE b/activesupport/lib/active_support/vendor/i18n-0.0.1/MIT-LICENSE new file mode 100755 index 0000000000..ed8e9ee66d --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008 The Ruby I18n team + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/README.textile b/activesupport/lib/active_support/vendor/i18n-0.0.1/README.textile new file mode 100644 index 0000000000..2b14a99c0f --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/README.textile @@ -0,0 +1,18 @@ +h1. Ruby I18n gem + +I18n and localization solution for Ruby. + +h2. Authors + +* "Matt Aimonetti":http://railsontherun.com +* "Sven Fuchs":http://www.artweb-design.de +* "Joshua Harvey":http://www.workingwithrails.com/person/759-joshua-harvey +* "Saimon Moore":http://saimonmoore.net +* "Stephan Soller":http://www.arkanis-development.de + +h2. License + +MIT License. See the included MIT-LICENCE file. + + + diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.gemspec b/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.gemspec new file mode 100644 index 0000000000..81ad0b598d --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/i18n.gemspec @@ -0,0 +1,24 @@ +Gem::Specification.new do |s| + s.name = "i18n" + s.version = "0.0.1" + s.date = "2008-06-13" + s.summary = "Internationalization for Ruby" + s.email = "rails-patch-i18n@googlegroups.com" + s.homepage = "http://groups.google.com/group/rails-patch-i18n" + s.description = "Add Internationalization to your Ruby application." + s.has_rdoc = false + s.authors = ['Sven Fuchs', 'Matt Aimonetti', 'Stephan Soller', 'Saimon Moore'] + s.files = [ + "lib/i18n/backend/minimal.rb", + "lib/i18n/core_ext.rb", + "lib/i18n/localization.rb", + "lib/i18n/translation.rb", + "lib/i18n.rb", + "LICENSE", + "README", + "spec/core_ext_spec.rb", + "spec/i18n_spec.rb", + "spec/spec.opts", + "spec/spec_helper.rb" + ] +end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb new file mode 100755 index 0000000000..2185194da9 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb @@ -0,0 +1,182 @@ +# Authors:: Matt Aimonetti (http://railsontherun.com/), +# Sven Fuchs (http://www.artweb-design.de), +# Joshua Harvey (http://www.workingwithrails.com/person/759-joshua-harvey), +# Saimon Moore (http://saimonmoore.net), +# Stephan Soller (http://www.arkanis-development.de/) +# Copyright:: Copyright (c) 2008 The Ruby i18n Team +# License:: MIT +require 'i18n/backend/simple' +require 'i18n/exceptions' + +module I18n + @@backend = Backend::Simple + @@default_locale = 'en-US' + @@exception_handler = :default_exception_handler + + class << self + # Returns the current backend. Defaults to +Backend::Simple+. + def backend + @@backend + end + + # Sets the current backend. Used to set a custom backend. + def backend=(backend) + @@backend = backend + end + + # Returns the current default locale. Defaults to 'en-US' + def default_locale + @@default_locale + end + + # Sets the current default locale. Used to set a custom default locale. + def default_locale=(locale) + @@default_locale = locale + end + + # Returns the current locale. Defaults to I18n.default_locale. + def locale + Thread.current[:locale] ||= default_locale + end + + # Sets the current locale pseudo-globally, i.e. in the Thread.current hash. + def locale=(locale) + Thread.current[:locale] = locale + end + + # Sets the exception handler. + def exception_handler=(exception_handler) + @@exception_handler = exception_handler + end + + # Allow client libraries to pass a block that populates the translation + # storage. Decoupled for backends like a db backend that persist their + # translations, so the backend can decide whether/when to yield or not. + def populate(&block) + backend.populate &block + end + + # Stores translations for the given locale in the backend. + def store_translations(locale, data) + backend.store_translations locale, data + end + + # Translates, pluralizes and interpolates a given key using a given locale, + # scope, and default, as well as interpolation values. + # + # *LOOKUP* + # + # Translation data is organized as a nested hash using the upper-level keys + # as namespaces. E.g., ActionView ships with the translation: + # :date => {:formats => {:short => "%b %d"}}. + # + # Translations can be looked up at any level of this hash using the key argument + # and the scope option. E.g., in this example I18n.t :date + # returns the whole translations hash {:formats => {:short => "%b %d"}}. + # + # Key can be either a single key or a dot-separated key (both Strings and Symbols + # work). E.g., the short format can be looked up using both: + # I18n.t 'date.formats.short' + # I18n.t :'date.formats.short' + # + # Scope can be either a single key, a dot-separated key or an array of keys + # or dot-separated keys. Keys and scopes can be combined freely. So these + # examples will all look up the same short date format: + # I18n.t 'date.formats.short' + # I18n.t 'formats.short', :scope => 'date' + # I18n.t 'short', :scope => 'date.formats' + # I18n.t 'short', :scope => %w(date formats) + # + # *INTERPOLATION* + # + # Translations can contain interpolation variables which will be replaced by + # values passed to #translate as part of the options hash, with the keys matching + # the interpolation variable names. + # + # E.g., with a translation :foo => "foo {{bar}}" the option + # value for the key +bar+ will be interpolated into the translation: + # I18n.t :foo, :bar => 'baz' # => 'foo baz' + # + # *PLURALIZATION* + # + # Translation data can contain pluralized translations. Pluralized translations + # are arrays of singluar/plural versions of translations like ['Foo', 'Foos']. + # + # Note that I18n::Backend::Simple only supports an algorithm for English + # pluralization rules. Other algorithms can be supported by custom backends. + # + # This returns the singular version of a pluralized translation: + # I18n.t :foo, :count => 1 # => 'Foo' + # + # These both return the plural version of a pluralized translation: + # I18n.t :foo, :count => 0 # => 'Foos' + # I18n.t :foo, :count => 2 # => 'Foos' + # + # The :count option can be used both for pluralization and interpolation. + # E.g., with the translation + # :foo => ['{{count}} foo', '{{count}} foos'], count will + # be interpolated to the pluralized translation: + # I18n.t :foo, :count => 1 # => '1 foo' + # + # *DEFAULTS* + # + # This returns the translation for :foo or default if no translation was found: + # I18n.t :foo, :default => 'default' + # + # This returns the translation for :foo or the translation for :bar if no + # translation for :foo was found: + # I18n.t :foo, :default => :bar + # + # Returns the translation for :foo or the translation for :bar + # or default if no translations for :foo and :bar were found. + # I18n.t :foo, :default => [:bar, 'default'] + # + # BULK LOOKUP + # + # This returns an array with the translations for :foo and :bar. + # I18n.t [:foo, :bar] + # + # Can be used with dot-separated nested keys: + # I18n.t [:'baz.foo', :'baz.bar'] + # + # Which is the same as using a scope option: + # I18n.t [:foo, :bar], :scope => :baz + def translate(key, options = {}) + locale = options.delete(:locale) || I18n.locale + backend.translate locale, key, options + rescue I18n::ArgumentError => e + raise e if options[:raise] + send @@exception_handler, e, locale, key, options + end + alias :t :translate + + # Localizes certain objects, such as dates and numbers to local formatting. + def localize(object, options = {}) + locale = options[:locale] || I18n.locale + format = options[:format] || :default + backend.localize(locale, object, format) + end + alias :l :localize + + protected + # Handles exceptions raised in the backend. All exceptions except for + # MissingTranslationData exceptions are re-raised. When a MissingTranslationData + # was caught and the option :raise is not set the handler returns an error + # message string containing the key/scope. + def default_exception_handler(exception, locale, key, options) + return exception.message if MissingTranslationData === exception + raise exception + end + + # Merges the given locale, key and scope into a single array of keys. + # Splits keys that contain dots into multiple keys. Makes sure all + # keys are Symbols. + def normalize_translation_keys(locale, key, scope) + keys = [locale] + Array(scope) + [key] + keys = keys.map{|key| key.to_s.split(/\./) } + keys.flatten.map{|key| key.to_sym} + end + end +end + + diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb new file mode 100644 index 0000000000..284f2bfcbd --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb @@ -0,0 +1,154 @@ +require 'strscan' + +module I18n + module Backend + module Simple + @@translations = {} + + class << self + # Allow client libraries to pass a block that populates the translation + # storage. Decoupled for backends like a db backend that persist their + # translations, so the backend can decide whether/when to yield or not. + def populate(&block) + yield + end + + # Stores translations for the given locale in memory. + # This uses a deep merge for the translations hash, so existing + # translations will be overwritten by new ones only at the deepest + # level of the hash. + def store_translations(locale, data) + merge_translations(locale, data) + end + + def translate(locale, key, options = {}) + raise InvalidLocale.new(locale) if locale.nil? + return key.map{|key| translate locale, key, options } if key.is_a? Array + + reserved = :scope, :default + count, scope, default = options.values_at(:count, *reserved) + options.delete(:default) + values = options.reject{|name, value| reserved.include? name } + + entry = lookup(locale, key, scope) || default(locale, default, options) || raise(I18n::MissingTranslationData.new(locale, key, options)) + entry = pluralize entry, count + entry = interpolate entry, values + entry + end + + # Acts the same as +strftime+, but returns a localized version of the + # formatted date string. Takes a key from the date/time formats + # translations as a format argument (e.g., :short in :'date.formats'). + def localize(locale, object, format = :default) + raise ArgumentError, "Object must be a Date, DateTime or Time object. #{object.inspect} given." unless object.respond_to?(:strftime) + + type = object.respond_to?(:sec) ? 'time' : 'date' + formats = translate(locale, :"#{type}.formats") + format = formats[format.to_sym] if formats && formats[format.to_sym] + # TODO raise exception unless format found? + format = format.to_s.dup + + format.gsub!(/%a/, translate(locale, :"date.abbr_day_names")[object.wday]) + format.gsub!(/%A/, translate(locale, :"date.day_names")[object.wday]) + format.gsub!(/%b/, translate(locale, :"date.abbr_month_names")[object.mon]) + format.gsub!(/%B/, translate(locale, :"date.month_names")[object.mon]) + format.gsub!(/%p/, translate(locale, :"time.#{object.hour < 12 ? :am : :pm}")) if object.respond_to? :hour + object.strftime(format) + end + + protected + + # Looks up a translation from the translations hash. Returns nil if + # eiher key is nil, or locale, scope or key do not exist as a key in the + # nested translations hash. Splits keys or scopes containing dots + # into multiple keys, i.e. currency.format is regarded the same as + # %w(currency format). + def lookup(locale, key, scope = []) + return unless key + keys = I18n.send :normalize_translation_keys, locale, key, scope + keys.inject(@@translations){|result, key| result[key.to_sym] or return nil } + end + + # Evaluates a default translation. + # If the given default is a String it is used literally. If it is a Symbol + # it will be translated with the given options. If it is an Array the first + # translation yielded will be returned. + # + # I.e., default(locale, [:foo, 'default']) will return +default+ if + # translate(locale, :foo) does not yield a result. + def default(locale, default, options = {}) + case default + when String then default + when Symbol then translate locale, default, options + when Array then default.each do |obj| + result = default(locale, obj, options.dup) and return result + end + end + rescue MissingTranslationData + nil + end + + # Picks a translation from an array according to English pluralization + # rules. It will pick the first translation if count is not equal to 1 + # and the second translation if it is equal to 1. Other backends can + # implement more flexible or complex pluralization rules. + def pluralize(entry, count) + return entry unless entry.is_a?(Array) and count + raise InvalidPluralizationData.new(entry, count) unless entry.size == 2 + entry[count == 1 ? 0 : 1] + end + + # Interpolates values into a given string. + # + # interpolate "file {{file}} opened by \\{{user}}", :file => 'test.txt', :user => 'Mr. X' + # # => "file test.txt opened by {{user}}" + # + # Note that you have to double escape the \\ when you want to escape + # the {{...}} key in a string (once for the string and once for the + # interpolation). + def interpolate(string, values = {}) + return string if !string.is_a?(String) + + map = {'%d' => '{{count}}', '%s' => '{{value}}'} # TODO deprecate this? + string.gsub!(/#{map.keys.join('|')}/){|key| map[key]} + + s = StringScanner.new string.dup + while s.skip_until(/\{\{/) + s.string[s.pos - 3, 1] = '' and next if s.pre_match[-1, 1] == '\\' + start_pos = s.pos - 2 + key = s.scan_until(/\}\}/)[0..-3] + end_pos = s.pos - 1 + + raise ReservedInterpolationKey.new(key, string) if %w(scope default).include?(key) + raise MissingInterpolationArgument.new(key, string) unless values.has_key? key.to_sym + + s.string[start_pos..end_pos] = values[key.to_sym].to_s + s.unscan + end + s.string + end + + # Deep merges the given translations hash with the existing translations + # for the given locale + def merge_translations(locale, data) + locale = locale.to_sym + @@translations[locale] ||= {} + data = deep_symbolize_keys data + + # deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809 + merger = proc{|key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 } + @@translations[locale].merge! data, &merger + end + + # Return a new hash with all keys and nested keys converted to symbols. + def deep_symbolize_keys(hash) + hash.inject({}){|result, (key, value)| + value = deep_symbolize_keys(value) if value.is_a? Hash + result[(key.to_sym rescue key) || key] = value + result + } + end + end + end + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/exceptions.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/exceptions.rb new file mode 100644 index 0000000000..6c1fc6d9b6 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/exceptions.rb @@ -0,0 +1,45 @@ +module I18n + class ArgumentError < ::ArgumentError; end + + class InvalidLocale < ArgumentError + attr_reader :locale + def initialize(locale) + @locale = locale + super "#{locale.inspect} is not a valid locale" + end + end + + class MissingTranslationData < ArgumentError + attr_reader :locale, :key, :options + def initialize(locale, key, options) + @key, @locale, @options = key, locale, options + keys = I18n.send(:normalize_translation_keys, locale, key, options[:scope]) + keys << 'no key' if keys.size < 2 + super "translation missing: #{keys.join(', ')}" + end + end + + class InvalidPluralizationData < ArgumentError + attr_reader :entry, :count + def initialize(entry, count) + @entry, @count = entry, count + super "translation data #{entry.inspect} can not be used with :count => #{count}" + end + end + + class MissingInterpolationArgument < ArgumentError + attr_reader :key, :string + def initialize(key, string) + @key, @string = key, string + super "interpolation argument #{key} missing in #{string.inspect}" + end + end + + class ReservedInterpolationKey < ArgumentError + attr_reader :key, :string + def initialize(key, string) + @key, @string = key, string + super "reserved key #{key.inspect} used in #{string.inspect}" + end + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/test/all.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/test/all.rb new file mode 100644 index 0000000000..048b62f8c3 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/test/all.rb @@ -0,0 +1,4 @@ +dir = File.dirname(__FILE__) +require dir + '/i18n_test.rb' +require dir + '/simple_backend_test.rb' +require dir + '/i18n_exceptions_test.rb' diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/test/i18n_exceptions_test.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/test/i18n_exceptions_test.rb new file mode 100644 index 0000000000..1ea1601681 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/test/i18n_exceptions_test.rb @@ -0,0 +1,100 @@ +$:.unshift "lib" + +require 'rubygems' +require 'test/unit' +require 'mocha' +require 'i18n' +require 'active_support' + +class I18nExceptionsTest < Test::Unit::TestCase + def test_invalid_locale_stores_locale + force_invalid_locale + rescue I18n::ArgumentError => e + assert_nil e.locale + end + + def test_invalid_locale_message + force_invalid_locale + rescue I18n::ArgumentError => e + assert_equal 'nil is not a valid locale', e.message + end + + def test_missing_translation_data_stores_locale_key_and_options + force_missing_translation_data + rescue I18n::ArgumentError => e + options = {:scope => :bar} + assert_equal 'de-DE', e.locale + assert_equal :foo, e.key + assert_equal options, e.options + end + + def test_missing_translation_data_message + force_missing_translation_data + rescue I18n::ArgumentError => e + assert_equal 'translation missing: de-DE, bar, foo', e.message + end + + def test_invalid_pluralization_data_stores_entry_and_count + force_invalid_pluralization_data + rescue I18n::ArgumentError => e + assert_equal [:bar], e.entry + assert_equal 1, e.count + end + + def test_invalid_pluralization_data_message + force_invalid_pluralization_data + rescue I18n::ArgumentError => e + assert_equal 'translation data [:bar] can not be used with :count => 1', e.message + end + + def test_missing_interpolation_argument_stores_key_and_string + force_missing_interpolation_argument + rescue I18n::ArgumentError => e + assert_equal 'bar', e.key + assert_equal "{{bar}}", e.string + end + + def test_missing_interpolation_argument_message + force_missing_interpolation_argument + rescue I18n::ArgumentError => e + assert_equal 'interpolation argument bar missing in "{{bar}}"', e.message + end + + def test_reserved_interpolation_key_stores_key_and_string + force_reserved_interpolation_key + rescue I18n::ArgumentError => e + assert_equal 'scope', e.key + assert_equal "{{scope}}", e.string + end + + def test_reserved_interpolation_key_message + force_reserved_interpolation_key + rescue I18n::ArgumentError => e + assert_equal 'reserved key "scope" used in "{{scope}}"', e.message + end + + private + def force_invalid_locale + I18n.backend.translate nil, :foo + end + + def force_missing_translation_data + I18n.store_translations 'de-DE', :bar => nil + I18n.backend.translate 'de-DE', :foo, :scope => :bar + end + + def force_invalid_pluralization_data + I18n.store_translations 'de-DE', :foo => [:bar] + I18n.backend.translate 'de-DE', :foo, :count => 1 + end + + def force_missing_interpolation_argument + I18n.store_translations 'de-DE', :foo => "{{bar}}" + I18n.backend.translate 'de-DE', :foo, :baz => 'baz' + end + + def force_reserved_interpolation_key + I18n.store_translations 'de-DE', :foo => "{{scope}}" + I18n.backend.translate 'de-DE', :foo, :baz => 'baz' + end +end \ No newline at end of file diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/test/i18n_test.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/test/i18n_test.rb new file mode 100644 index 0000000000..bbb1316b49 --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/test/i18n_test.rb @@ -0,0 +1,141 @@ +$:.unshift "lib" + +require 'rubygems' +require 'test/unit' +require 'mocha' +require 'i18n' +require 'active_support' + +class I18nTest < Test::Unit::TestCase + def setup + I18n.store_translations :'en-US', { + :currency => { + :format => { + :separator => '.', + :delimiter => ',', + } + } + } + end + + def test_uses_simple_backend_set_by_default + assert_equal I18n::Backend::Simple, I18n.backend + end + + def test_can_set_backend + assert_nothing_raised{ I18n.backend = self } + assert_equal self, I18n.backend + I18n.backend = I18n::Backend::Simple + end + + def test_uses_en_us_as_default_locale_by_default + assert_equal 'en-US', I18n.default_locale + end + + def test_can_set_default_locale + assert_nothing_raised{ I18n.default_locale = 'de-DE' } + assert_equal 'de-DE', I18n.default_locale + I18n.default_locale = 'en-US' + end + + def test_uses_default_locale_as_locale_by_default + assert_equal I18n.default_locale, I18n.locale + end + + def test_can_set_locale_to_thread_current + assert_nothing_raised{ I18n.locale = 'de-DE' } + assert_equal 'de-DE', I18n.locale + assert_equal 'de-DE', Thread.current[:locale] + I18n.locale = 'en-US' + end + + def test_can_set_exception_handler + assert_nothing_raised{ I18n.exception_handler = :custom_exception_handler } + I18n.exception_handler = :default_exception_handler # revert it + end + + def test_uses_custom_exception_handler + I18n.exception_handler = :custom_exception_handler + I18n.expects(:custom_exception_handler) + I18n.translate :bogus + I18n.exception_handler = :default_exception_handler # revert it + end + + def test_delegates_translate_to_backend + I18n.backend.expects(:translate).with 'de-DE', :foo, {} + I18n.translate :foo, :locale => 'de-DE' + end + + def test_delegates_localize_to_backend + I18n.backend.expects(:localize).with 'de-DE', :whatever, :default + I18n.localize :whatever, :locale => 'de-DE' + end + + def test_delegates_store_translations_to_backend + I18n.backend.expects(:store_translations).with 'de-DE', {:foo => :bar} + I18n.store_translations 'de-DE', {:foo => :bar} + end + + def test_delegates_populate_to_backend + I18n.backend.expects(:populate) # can't specify a block here as an expected argument + I18n.populate{ } + end + + def test_populate_yields_the_block + tmp = nil + I18n.populate do tmp = 'yielded' end + assert_equal 'yielded', tmp + end + + def test_translate_given_no_locale_uses_i18n_locale + I18n.backend.expects(:translate).with 'en-US', :foo, {} + I18n.translate :foo + end + + def test_translate_on_nested_symbol_keys_works + assert_equal ".", I18n.t(:'currency.format.separator') + end + + def test_translate_with_nested_string_keys_works + assert_equal ".", I18n.t('currency.format.separator') + end + + def test_translate_with_array_as_scope_works + assert_equal ".", I18n.t(:separator, :scope => ['currency.format']) + end + + def test_translate_with_array_containing_dot_separated_strings_as_scope_works + assert_equal ".", I18n.t(:separator, :scope => ['currency.format']) + end + + def test_translate_with_key_array_and_dot_separated_scope_works + assert_equal [".", ","], I18n.t(%w(separator delimiter), :scope => 'currency.format') + end + + def test_translate_with_dot_separated_key_array_and_scope_works + assert_equal [".", ","], I18n.t(%w(format.separator format.delimiter), :scope => 'currency') + end + + def test_translate_with_options_using_scope_works + I18n.backend.expects(:translate).with('de-DE', :precision, :scope => :"currency.format") + I18n.with_options :locale => 'de-DE', :scope => :'currency.format' do |locale| + locale.t :precision + end + end + + # def test_translate_given_no_args_raises_missing_translation_data + # assert_equal "translation missing: en-US, no key", I18n.t + # end + + def test_translate_given_a_bogus_key_raises_missing_translation_data + assert_equal "translation missing: en-US, bogus", I18n.t(:bogus) + end + + def test_localize_nil_raises_argument_error + assert_raises(I18n::ArgumentError) { I18n.l nil } + end + + def test_localize_object_raises_argument_error + assert_raises(I18n::ArgumentError) { I18n.l Object.new } + end +end diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/test/simple_backend_test.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/test/simple_backend_test.rb new file mode 100644 index 0000000000..c94d742e2d --- /dev/null +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/test/simple_backend_test.rb @@ -0,0 +1,376 @@ +$:.unshift "lib" + +require 'rubygems' +require 'test/unit' +require 'mocha' +require 'i18n' + +module I18nSimpleBackendTestSetup + def setup_backend + @backend = I18n::Backend::Simple + @backend.send :class_variable_set, :@@translations, {} + @backend.store_translations 'en-US', :foo => {:bar => 'bar', :baz => 'baz'} + end + alias :setup :setup_backend + + def add_datetime_translations + @backend.store_translations :'de-DE', { + :date => { + :formats => { + :default => "%d.%m.%Y", + :short => "%d. %b", + :long => "%d. %B %Y", + }, + :day_names => %w(Sonntag Montag Dienstag Mittwoch Donnerstag Freitag Samstag), + :abbr_day_names => %w(So Mo Di Mi Do Fr Sa), + :month_names => %w(Januar Februar März April Mai Juni Juli August September Oktober November Dezember).unshift(nil), + :abbr_month_names => %w(Jan Feb Mar Apr Mai Jun Jul Aug Sep Okt Nov Dez).unshift(nil), + :order => [:day, :month, :year] + }, + :time => { + :formats => { + :default => "%a, %d. %b %Y %H:%M:%S %z", + :short => "%d. %b %H:%M", + :long => "%d. %B %Y %H:%M", + }, + :am => 'am', + :pm => 'pm' + }, + :datetime => { + :distance_in_words => { + :half_a_minute => 'half a minute', + :less_than_x_seconds => ['less than 1 second', 'less than {{count}} seconds'], + :x_seconds => ['1 second', '{{count}} seconds'], + :less_than_x_minutes => ['less than a minute', 'less than {{count}} minutes'], + :x_minutes => ['1 minute', '{{count}} minutes'], + :about_x_hours => ['about 1 hour', 'about {{count}} hours'], + :x_days => ['1 day', '{{count}} days'], + :about_x_months => ['about 1 month', 'about {{count}} months'], + :x_months => ['1 month', '{{count}} months'], + :about_x_years => ['about 1 year', 'about {{count}} year'], + :over_x_years => ['over 1 year', 'over {{count}} years'] + } + } + } + end +end + +class I18nSimpleBackendTranslationsTest < Test::Unit::TestCase + include I18nSimpleBackendTestSetup + + def test_store_translations_adds_translations # no, really :-) + @backend.store_translations :'en-US', :foo => 'bar' + assert_equal Hash[:'en-US', {:foo => 'bar'}], @backend.send(:class_variable_get, :@@translations) + end + + def test_store_translations_deep_merges_translations + @backend.store_translations :'en-US', :foo => {:bar => 'bar'} + @backend.store_translations :'en-US', :foo => {:baz => 'baz'} + assert_equal Hash[:'en-US', {:foo => {:bar => 'bar', :baz => 'baz'}}], @backend.send(:class_variable_get, :@@translations) + end + + def test_store_translations_forces_locale_to_sym + @backend.store_translations 'en-US', :foo => 'bar' + assert_equal Hash[:'en-US', {:foo => 'bar'}], @backend.send(:class_variable_get, :@@translations) + end + + def test_store_translations_covert_key_symbols + @backend.send :class_variable_set, :@@translations, {} # reset translations + @backend.store_translations :'en-US', 'foo' => {'bar' => 'baz'} + assert_equal Hash[:'en-US', {:foo => {:bar => 'baz'}}], + @backend.send(:class_variable_get, :@@translations) + end +end + +class I18nSimpleBackendTranslateTest < Test::Unit::TestCase + include I18nSimpleBackendTestSetup + + def test_translate_calls_lookup_with_locale_given + @backend.expects(:lookup).with('de-DE', :bar, [:foo]).returns 'bar' + @backend.translate 'de-DE', :bar, :scope => [:foo] + end + + def test_translate_given_a_symbol_as_a_default_translates_the_symbol + assert_equal 'bar', @backend.translate('en-US', nil, :scope => [:foo], :default => :bar) + end + + def test_translate_given_an_array_as_default_uses_the_first_match + assert_equal 'bar', @backend.translate('en-US', :does_not_exist, :scope => [:foo], :default => [:does_not_exist_2, :bar]) + end + + def test_translate_an_array_of_keys_translates_all_of_them + assert_equal %w(bar baz), @backend.translate('en-US', [:bar, :baz], :scope => [:foo]) + end + + def test_translate_calls_pluralize + @backend.expects(:pluralize).with 'bar', 1 + @backend.translate 'en-US', :bar, :scope => [:foo], :count => 1 + end + + def test_translate_calls_interpolate + @backend.expects(:interpolate).with 'bar', {} + @backend.translate 'en-US', :bar, :scope => [:foo] + end + + def test_translate_calls_interpolate_including_count_as_a_value + @backend.expects(:interpolate).with 'bar', {:count => 1} + @backend.translate 'en-US', :bar, :scope => [:foo], :count => 1 + end + + def test_given_no_keys_it_returns_the_default + assert_equal 'default', @backend.translate('en-US', nil, :default => 'default') + end + + def test_translate_given_nil_as_a_locale_raises_an_argument_error + assert_raises(I18n::InvalidLocale){ @backend.translate nil, :bar } + end + + def test_translate_with_a_bogus_key_and_no_default_raises_missing_translation_data + assert_raises(I18n::MissingTranslationData){ @backend.translate 'de-DE', :bogus } + end +end + +class I18nSimpleBackendLookupTest < Test::Unit::TestCase + include I18nSimpleBackendTestSetup + + # useful because this way we can use the backend with no key for interpolation/pluralization + def test_lookup_given_nil_as_a_key_returns_nil + assert_nil @backend.send(:lookup, 'en-US', nil) + end + + def test_lookup_given_nested_keys_looks_up_a_nested_hash_value + assert_equal 'bar', @backend.send(:lookup, 'en-US', :bar, [:foo]) + end +end + +class I18nSimpleBackendPluralizeTest < Test::Unit::TestCase + include I18nSimpleBackendTestSetup + + def test_pluralize_given_nil_returns_the_given_entry + assert_equal ['bar', 'bars'], @backend.send(:pluralize, ['bar', 'bars'], nil) + end + + def test_pluralize_given_0_returns_plural_string + assert_equal 'bars', @backend.send(:pluralize, ['bar', 'bars'], 0) + end + + def test_pluralize_given_1_returns_singular_string + assert_equal 'bar', @backend.send(:pluralize, ['bar', 'bars'], 1) + end + + def test_pluralize_given_2_returns_plural_string + assert_equal 'bars', @backend.send(:pluralize, ['bar', 'bars'], 2) + end + + def test_pluralize_given_3_returns_plural_string + assert_equal 'bars', @backend.send(:pluralize, ['bar', 'bars'], 3) + end + + def test_interpolate_given_invalid_pluralization_data_raises_invalid_pluralization_data + assert_raises(I18n::InvalidPluralizationData){ @backend.send(:pluralize, ['bar'], 2) } + end +end + +class I18nSimpleBackendInterpolateTest < Test::Unit::TestCase + include I18nSimpleBackendTestSetup + + def test_interpolate_given_a_value_hash_interpolates_the_values_to_the_string + assert_equal 'Hi David!', @backend.send(:interpolate, 'Hi {{name}}!', :name => 'David') + end + + def test_interpolate_given_nil_as_a_string_returns_nil + assert_nil @backend.send(:interpolate, nil, :name => 'David') + end + + def test_interpolate_given_an_non_string_as_a_string_returns_nil + assert_equal [], @backend.send(:interpolate, [], :name => 'David') + end + + def test_interpolate_given_a_values_hash_with_nil_values_interpolates_the_string + assert_equal 'Hi !', @backend.send(:interpolate, 'Hi {{name}}!', {:name => nil}) + end + + def test_interpolate_given_an_empty_values_hash_raises_missing_interpolation_argument + assert_raises(I18n::MissingInterpolationArgument) { @backend.send(:interpolate, 'Hi {{name}}!', {}) } + end + + def test_interpolate_given_a_string_containing_a_reserved_key_raises_reserved_interpolation_key + assert_raises(I18n::ReservedInterpolationKey) { @backend.send(:interpolate, '{{default}}', {:default => nil}) } + end +end + +class I18nSimpleBackendLocalizeDateTest < Test::Unit::TestCase + include I18nSimpleBackendTestSetup + + def setup + @backend = I18n::Backend::Simple + add_datetime_translations + @date = Date.new 2008, 1, 1 + end + + def test_translate_given_the_short_format_it_uses_it + assert_equal '01. Jan', @backend.localize('de-DE', @date, :short) + end + + def test_translate_given_the_long_format_it_uses_it + assert_equal '01. Januar 2008', @backend.localize('de-DE', @date, :long) + end + + def test_translate_given_the_default_format_it_uses_it + assert_equal '01.01.2008', @backend.localize('de-DE', @date, :default) + end + + def test_translate_given_a_day_name_format_it_returns_a_day_name + assert_equal 'Dienstag', @backend.localize('de-DE', @date, '%A') + end + + def test_translate_given_an_abbr_day_name_format_it_returns_an_abbrevated_day_name + assert_equal 'Di', @backend.localize('de-DE', @date, '%a') + end + + def test_translate_given_a_month_name_format_it_returns_a_month_name + assert_equal 'Januar', @backend.localize('de-DE', @date, '%B') + end + + def test_translate_given_an_abbr_month_name_format_it_returns_an_abbrevated_month_name + assert_equal 'Jan', @backend.localize('de-DE', @date, '%b') + end + + def test_translate_given_no_format_it_does_not_fail + assert_nothing_raised{ @backend.localize 'de-DE', @date } + end + + def test_translate_given_an_unknown_format_it_does_not_fail + assert_nothing_raised{ @backend.localize 'de-DE', @date, '%x' } + end + + def test_localize_nil_raises_argument_error + assert_raises(I18n::ArgumentError) { @backend.localize 'de-DE', nil } + end + + def test_localize_object_raises_argument_error + assert_raises(I18n::ArgumentError) { @backend.localize 'de-DE', Object.new } + end +end + +class I18nSimpleBackendLocalizeDateTimeTest < Test::Unit::TestCase + include I18nSimpleBackendTestSetup + + def setup + @backend = I18n::Backend::Simple + add_datetime_translations + @morning = DateTime.new 2008, 1, 1, 6 + @evening = DateTime.new 2008, 1, 1, 18 + end + + def test_translate_given_the_short_format_it_uses_it + assert_equal '01. Jan 06:00', @backend.localize('de-DE', @morning, :short) + end + + def test_translate_given_the_long_format_it_uses_it + assert_equal '01. Januar 2008 06:00', @backend.localize('de-DE', @morning, :long) + end + + def test_translate_given_the_default_format_it_uses_it + assert_equal 'Di, 01. Jan 2008 06:00:00 +0000', @backend.localize('de-DE', @morning, :default) + end + + def test_translate_given_a_day_name_format_it_returns_the_correct_day_name + assert_equal 'Dienstag', @backend.localize('de-DE', @morning, '%A') + end + + def test_translate_given_an_abbr_day_name_format_it_returns_the_correct_abbrevated_day_name + assert_equal 'Di', @backend.localize('de-DE', @morning, '%a') + end + + def test_translate_given_a_month_name_format_it_returns_the_correct_month_name + assert_equal 'Januar', @backend.localize('de-DE', @morning, '%B') + end + + def test_translate_given_an_abbr_month_name_format_it_returns_the_correct_abbrevated_month_name + assert_equal 'Jan', @backend.localize('de-DE', @morning, '%b') + end + + def test_translate_given_a_meridian_indicator_format_it_returns_the_correct_meridian_indicator + assert_equal 'am', @backend.localize('de-DE', @morning, '%p') + assert_equal 'pm', @backend.localize('de-DE', @evening, '%p') + end + + def test_translate_given_no_format_it_does_not_fail + assert_nothing_raised{ @backend.localize 'de-DE', @morning } + end + + def test_translate_given_an_unknown_format_it_does_not_fail + assert_nothing_raised{ @backend.localize 'de-DE', @morning, '%x' } + end +end + +class I18nSimpleBackendLocalizeTimeTest < Test::Unit::TestCase + include I18nSimpleBackendTestSetup + + def setup + @old_timezone, ENV['TZ'] = ENV['TZ'], 'UTC' + @backend = I18n::Backend::Simple + add_datetime_translations + @morning = Time.parse '2008-01-01 6:00 UTC' + @evening = Time.parse '2008-01-01 18:00 UTC' + end + + def teardown + ENV['TZ'] = @old_timezone + end + + def test_translate_given_the_short_format_it_uses_it + assert_equal '01. Jan 06:00', @backend.localize('de-DE', @morning, :short) + end + + def test_translate_given_the_long_format_it_uses_it + assert_equal '01. Januar 2008 06:00', @backend.localize('de-DE', @morning, :long) + end + + # TODO Seems to break on Windows because ENV['TZ'] is ignored. What's a better way to do this? + # def test_translate_given_the_default_format_it_uses_it + # assert_equal 'Di, 01. Jan 2008 06:00:00 +0000', @backend.localize('de-DE', @morning, :default) + # end + + def test_translate_given_a_day_name_format_it_returns_the_correct_day_name + assert_equal 'Dienstag', @backend.localize('de-DE', @morning, '%A') + end + + def test_translate_given_an_abbr_day_name_format_it_returns_the_correct_abbrevated_day_name + assert_equal 'Di', @backend.localize('de-DE', @morning, '%a') + end + + def test_translate_given_a_month_name_format_it_returns_the_correct_month_name + assert_equal 'Januar', @backend.localize('de-DE', @morning, '%B') + end + + def test_translate_given_an_abbr_month_name_format_it_returns_the_correct_abbrevated_month_name + assert_equal 'Jan', @backend.localize('de-DE', @morning, '%b') + end + + def test_translate_given_a_meridian_indicator_format_it_returns_the_correct_meridian_indicator + assert_equal 'am', @backend.localize('de-DE', @morning, '%p') + assert_equal 'pm', @backend.localize('de-DE', @evening, '%p') + end + + def test_translate_given_no_format_it_does_not_fail + assert_nothing_raised{ @backend.localize 'de-DE', @morning } + end + + def test_translate_given_an_unknown_format_it_does_not_fail + assert_nothing_raised{ @backend.localize 'de-DE', @morning, '%x' } + end +end + +class I18nSimpleBackendHelperMethodsTest < Test::Unit::TestCase + def setup + @backend = I18n::Backend::Simple + end + + def test_deep_symbolize_keys_works + result = @backend.send :deep_symbolize_keys, 'foo' => {'bar' => {'baz' => 'bar'}} + expected = {:foo => {:bar => {:baz => 'bar'}}} + assert_equal expected, result + end +end -- cgit v1.2.3 From f4f6e57e8c2a446a4a600576f0caf0fb8921ba13 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 15 Jul 2008 21:24:00 -0500 Subject: Added Object#metaclass --- activesupport/lib/active_support/core_ext/object.rb | 1 + activesupport/lib/active_support/core_ext/object/metaclass.rb | 8 ++++++++ activesupport/test/core_ext/object_and_class_ext_test.rb | 8 ++++++++ 3 files changed, 17 insertions(+) create mode 100644 activesupport/lib/active_support/core_ext/object/metaclass.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb index bbc7d81672..0796a7b710 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/object/conversions' require 'active_support/core_ext/object/extending' require 'active_support/core_ext/object/instance_variables' +require 'active_support/core_ext/object/metaclass' require 'active_support/core_ext/object/misc' diff --git a/activesupport/lib/active_support/core_ext/object/metaclass.rb b/activesupport/lib/active_support/core_ext/object/metaclass.rb new file mode 100644 index 0000000000..169a76dfb7 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/metaclass.rb @@ -0,0 +1,8 @@ +class Object + # Get object's meta (ghost, eigenclass, singleton) class + def metaclass + class << self + self + end + end +end diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb index 16f4ab888e..b0a746fdc7 100644 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ b/activesupport/test/core_ext/object_and_class_ext_test.rb @@ -173,6 +173,14 @@ class ObjectTests < Test::Unit::TestCase assert duck.acts_like?(:time) assert !duck.acts_like?(:date) end + + def test_metaclass + string = "Hello" + string.metaclass.instance_eval do + define_method(:foo) { "bar" } + end + assert_equal "bar", string.foo + end end class ObjectInstanceVariableTest < Test::Unit::TestCase -- cgit v1.2.3 From be078ee162fcae883a5621a30929879cd783a238 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 15 Jul 2008 21:55:28 -0500 Subject: Run callbacks from object's metaclass [#575 state:resolved] --- activesupport/CHANGELOG | 2 ++ activesupport/lib/active_support/callbacks.rb | 10 +++++++++- activesupport/test/callbacks_test.rb | 24 ++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) (limited to 'activesupport') diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 8d3b136d80..aa383cd166 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Run callbacks from object's metaclass [Josh Peek] + * 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 diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 9c59b7ac76..f125a56246 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -269,7 +269,15 @@ module ActiveSupport # pass # stop def run_callbacks(kind, options = {}, &block) - self.class.send("#{kind}_callback_chain").run(self, options, &block) + callback_chain_method = "#{kind}_callback_chain" + + # Meta class inherits Class so we don't have to merge it in 1.9 + if RUBY_VERSION >= '1.9' + metaclass.send(callback_chain_method).run(self, options, &block) + else + callbacks = self.class.send(callback_chain_method) | metaclass.send(callback_chain_method) + callbacks.run(self, options, &block) + end end end end diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index 7f71ca2262..c3f683bdb5 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -84,6 +84,30 @@ class CallbacksTest < Test::Unit::TestCase end end +class MetaclassCallbacksTest < Test::Unit::TestCase + module ModuleWithCallbacks + def self.extended(object) + object.metaclass.before_save :raise_metaclass_callback_called + end + + def module_callback_called? + @module_callback_called ||= false + end + + def raise_metaclass_callback_called + @module_callback_called = true + end + end + + def test_metaclass_callbacks + person = Person.new + person.extend(ModuleWithCallbacks) + assert !person.module_callback_called? + person.save + assert person.module_callback_called? + end +end + class ConditionalCallbackTest < Test::Unit::TestCase def test_save_conditional_person person = ConditionalPerson.new -- cgit v1.2.3 From 0432d151647f2178ddee79979827d552447c251f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Wed, 16 Jul 2008 13:00:36 +0100 Subject: Merge with docrails. --- .../active_support/core_ext/bigdecimal/conversions.rb | 2 +- .../lib/active_support/core_ext/hash/except.rb | 2 +- .../lib/active_support/core_ext/hash/reverse_merge.rb | 17 ++++++++++++----- .../lib/active_support/core_ext/string/inflections.rb | 4 ++-- .../lib/active_support/core_ext/time/calculations.rb | 2 +- activesupport/lib/active_support/json.rb | 2 +- activesupport/lib/active_support/json/encoders/date.rb | 11 +++++++++-- .../lib/active_support/json/encoders/date_time.rb | 11 +++++++++-- activesupport/lib/active_support/json/encoders/time.rb | 13 ++++++++++--- activesupport/lib/active_support/time_with_zone.rb | 14 +++++++++++++- .../vendor/builder-2.1.2/builder/xmlevents.rb | 2 +- .../vendor/memcache-client-1.5.0/memcache.rb | 4 ++-- .../vendor/tzinfo-0.3.9/tzinfo/data_timezone.rb | 2 +- .../vendor/tzinfo-0.3.9/tzinfo/data_timezone_info.rb | 2 +- .../vendor/tzinfo-0.3.9/tzinfo/linked_timezone.rb | 2 +- .../vendor/tzinfo-0.3.9/tzinfo/timezone.rb | 2 +- .../tzinfo-0.3.9/tzinfo/timezone_transition_info.rb | 2 +- .../vendor/xml-simple-1.0.11/xmlsimple.rb | 6 +++--- 18 files changed, 70 insertions(+), 30 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb b/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb index d2b01b1b8d..94c7c779f7 100644 --- a/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb +++ b/activesupport/lib/active_support/core_ext/bigdecimal/conversions.rb @@ -21,7 +21,7 @@ module ActiveSupport #:nodoc: # This emits the number without any scientific notation. # I prefer it to using self.to_f.to_s, which would lose precision. # - # Note that YAML allows that when reconsituting floats + # Note that YAML allows that when reconstituting floats # to native types, some precision may get lost. # There is no full precision real YAML tag that I am aware of. str = self.to_s diff --git a/activesupport/lib/active_support/core_ext/hash/except.rb b/activesupport/lib/active_support/core_ext/hash/except.rb index bc97fa35a6..f26d01553d 100644 --- a/activesupport/lib/active_support/core_ext/hash/except.rb +++ b/activesupport/lib/active_support/core_ext/hash/except.rb @@ -13,7 +13,7 @@ module ActiveSupport #:nodoc: clone.except!(*keys) end - # Replaces the hash without only the given keys. + # Replaces the hash without the given keys. def except!(*keys) keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) keys.each { |key| delete(key) } diff --git a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb index 7af10846e7..546e261cc9 100644 --- a/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb +++ b/activesupport/lib/active_support/core_ext/hash/reverse_merge.rb @@ -1,21 +1,28 @@ module ActiveSupport #:nodoc: module CoreExtensions #:nodoc: module Hash #:nodoc: - # Allows for reverse merging where its the keys in the calling hash that wins over those in the other_hash. - # This is particularly useful for initializing an incoming option hash with default values: + # Allows for reverse merging two hashes where the keys in the calling hash take precedence over those + # in the other_hash. This is particularly useful for initializing an option hash with default values: # # def setup(options = {}) # options.reverse_merge! :size => 25, :velocity => 10 # end # - # The default :size and :velocity is only set if the +options+ passed in doesn't already have those keys set. + # Using merge, the above example would look as follows: + # + # def setup(options = {}) + # { :size => 25, :velocity => 10 }.merge(options) + # end + # + # The default :size and :velocity are only set if the +options+ hash passed in doesn't already + # have the respective key. module ReverseMerge - # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second. + # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second. def reverse_merge(other_hash) other_hash.merge(self) end - # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second. + # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second. # Modifies the receiver in place. def reverse_merge!(other_hash) replace(reverse_merge(other_hash)) diff --git a/activesupport/lib/active_support/core_ext/string/inflections.rb b/activesupport/lib/active_support/core_ext/string/inflections.rb index a009d7c085..3bbad7dad8 100644 --- a/activesupport/lib/active_support/core_ext/string/inflections.rb +++ b/activesupport/lib/active_support/core_ext/string/inflections.rb @@ -24,8 +24,8 @@ module ActiveSupport #:nodoc: # # "posts".singularize # => "post" # "octopi".singularize # => "octopus" - # "sheep".singluarize # => "sheep" - # "word".singluarize # => "word" + # "sheep".singularize # => "sheep" + # "word".singularize # => "word" # "the blue mailmen".singularize # => "the blue mailman" # "CamelOctopi".singularize # => "CamelOctopus" def singularize diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 2cce782676..cd234c9b89 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -261,7 +261,7 @@ module ActiveSupport #:nodoc: # Layers additional behavior on Time#<=> so that DateTime and ActiveSupport::TimeWithZone instances # can be chronologically compared with a Time def compare_with_coercion(other) - # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do <=> comparision + # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do <=> comparison other = other.comparable_time if other.respond_to?(:comparable_time) if other.acts_like?(:date) # other is a Date/DateTime, so coerce self #to_datetime and hand off to DateTime#<=> diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb index 54a7becd0f..2bdb4a7b11 100644 --- a/activesupport/lib/active_support/json.rb +++ b/activesupport/lib/active_support/json.rb @@ -1,5 +1,5 @@ module ActiveSupport - # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format. + # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format. mattr_accessor :use_standard_json_time_format class << self diff --git a/activesupport/lib/active_support/json/encoders/date.rb b/activesupport/lib/active_support/json/encoders/date.rb index cb9419d29d..1fc99c466f 100644 --- a/activesupport/lib/active_support/json/encoders/date.rb +++ b/activesupport/lib/active_support/json/encoders/date.rb @@ -1,7 +1,14 @@ class Date - # Returns a JSON string representing the date. + # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. # - # ==== Example: + # ==== Examples: + # + # # With ActiveSupport.use_standard_json_time_format = true + # Date.new(2005,2,1).to_json + # # => "2005-02-01" + # + # # With ActiveSupport.use_standard_json_time_format = false # Date.new(2005,2,1).to_json # # => "2005/02/01" def to_json(options = nil) diff --git a/activesupport/lib/active_support/json/encoders/date_time.rb b/activesupport/lib/active_support/json/encoders/date_time.rb index a4a5efbfb1..e259930033 100644 --- a/activesupport/lib/active_support/json/encoders/date_time.rb +++ b/activesupport/lib/active_support/json/encoders/date_time.rb @@ -1,7 +1,14 @@ class DateTime - # Returns a JSON string representing the datetime. + # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. # - # ==== Example: + # ==== Examples: + # + # # With ActiveSupport.use_standard_json_time_format = true + # DateTime.civil(2005,2,1,15,15,10).to_json + # # => "2005-02-01T15:15:10+00:00" + # + # # With ActiveSupport.use_standard_json_time_format = false # DateTime.civil(2005,2,1,15,15,10).to_json # # => "2005/02/01 15:15:10 +0000" def to_json(options = nil) diff --git a/activesupport/lib/active_support/json/encoders/time.rb b/activesupport/lib/active_support/json/encoders/time.rb index 57ed3c9e31..09fc614889 100644 --- a/activesupport/lib/active_support/json/encoders/time.rb +++ b/activesupport/lib/active_support/json/encoders/time.rb @@ -1,9 +1,16 @@ class Time - # Returns a JSON string representing the time. + # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. # - # ==== Example: + # ==== Examples: + # + # # With ActiveSupport.use_standard_json_time_format = true + # Time.utc(2005,2,1,15,15,10).to_json + # # => "2005-02-01T15:15:10Z" + # + # # With ActiveSupport.use_standard_json_time_format = false # Time.utc(2005,2,1,15,15,10).to_json - # # => 2005/02/01 15:15:10 +0000" + # # => "2005/02/01 15:15:10 +0000" def to_json(options = nil) if ActiveSupport.use_standard_json_time_format xmlschema.inspect diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index e85bfe9b2e..4866fa0dc8 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -102,7 +102,19 @@ module ActiveSupport "#{time.strftime("%Y-%m-%dT%H:%M:%S")}#{formatted_offset(true, 'Z')}" end alias_method :iso8601, :xmlschema - + + # Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to + # true, the ISO 8601 format is used. + # + # ==== Examples: + # + # # With ActiveSupport.use_standard_json_time_format = true + # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json + # # => "2005-02-01T15:15:10Z" + # + # # With ActiveSupport.use_standard_json_time_format = false + # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json + # # => "2005/02/01 15:15:10 +0000" def to_json(options = nil) if ActiveSupport.use_standard_json_time_format xmlschema.inspect diff --git a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb b/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb index 91fcd21e13..b373e4da3c 100644 --- a/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb +++ b/activesupport/lib/active_support/vendor/builder-2.1.2/builder/xmlevents.rb @@ -20,7 +20,7 @@ module Builder # markup. # # Usage: - # xe = Builder::XmlEvents.new(hander) + # xe = Builder::XmlEvents.new(handler) # xe.title("HI") # Sends start_tag/end_tag/text messages to the handler. # # Indentation may also be selected by providing value for the diff --git a/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb b/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb index dda7f2c30e..30113201a6 100644 --- a/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb +++ b/activesupport/lib/active_support/vendor/memcache-client-1.5.0/memcache.rb @@ -119,7 +119,7 @@ class MemCache # Valid options for +opts+ are: # # [:namespace] Prepends this value to all keys added or retrieved. - # [:readonly] Raises an exeception on cache writes when true. + # [:readonly] Raises an exception on cache writes when true. # [:multithread] Wraps cache access in a Mutex for thread safety. # # Other options are ignored. @@ -207,7 +207,7 @@ class MemCache end ## - # Deceremets the value for +key+ by +amount+ and returns the new value. + # Decrements the value for +key+ by +amount+ and returns the new value. # +key+ must already exist. If +key+ is not an integer, it is assumed to be # 0. +key+ can not be decremented below 0. diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone.rb index 5eccbdf0db..2510e90b5b 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone.rb @@ -38,7 +38,7 @@ module TZInfo # Returns the set of TimezonePeriod instances that are valid for the given # local time as an array. If you just want a single period, use - # period_for_local instead and specify how abiguities should be resolved. + # period_for_local instead and specify how ambiguities should be resolved. # Raises PeriodNotFound if no periods are found for the given time. def periods_for_local(local) info.periods_for_local(local) diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone_info.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone_info.rb index a45d94554b..8829ba9cc8 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone_info.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/data_timezone_info.rb @@ -66,7 +66,7 @@ module TZInfo # ArgumentError will be raised if a transition is added out of order. # offset_id refers to an id defined with offset. ArgumentError will be # raised if the offset_id cannot be found. numerator_or_time and - # denomiator specify the time the transition occurs as. See + # denominator specify the time the transition occurs as. See # TimezoneTransitionInfo for more detail about specifying times. def transition(year, month, offset_id, numerator_or_time, denominator = nil) offset = @offsets[offset_id] diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/linked_timezone.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/linked_timezone.rb index f8ec4fca87..c757485b84 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/linked_timezone.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/linked_timezone.rb @@ -36,7 +36,7 @@ module TZInfo # Returns the set of TimezonePeriod instances that are valid for the given # local time as an array. If you just want a single period, use - # period_for_local instead and specify how abiguities should be resolved. + # period_for_local instead and specify how ambiguities should be resolved. # Raises PeriodNotFound if no periods are found for the given time. def periods_for_local(local) @linked_timezone.periods_for_local(local) diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone.rb index f87fb6fb70..eeaa772d0f 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone.rb @@ -121,7 +121,7 @@ module TZInfo TimezoneProxy.new(identifier) end - # If identifier is nil calls super(), otherwise calls get. An identfier + # If identifier is nil calls super(), otherwise calls get. An identifier # should always be passed in when called externally. def self.new(identifier = nil) if identifier diff --git a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone_transition_info.rb b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone_transition_info.rb index 3fecb24f0d..781764f0b0 100644 --- a/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone_transition_info.rb +++ b/activesupport/lib/active_support/vendor/tzinfo-0.3.9/tzinfo/timezone_transition_info.rb @@ -37,7 +37,7 @@ module TZInfo attr_reader :numerator_or_time protected :numerator_or_time - # Either the denominotor of the DateTime if the transition time is defined + # Either the denominator of the DateTime if the transition time is defined # as a DateTime, otherwise nil. attr_reader :denominator protected :denominator diff --git a/activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb b/activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb index 0de24c0eff..ec6dab513f 100644 --- a/activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb +++ b/activesupport/lib/active_support/vendor/xml-simple-1.0.11/xmlsimple.rb @@ -121,7 +121,7 @@ class XmlSimple # Create a "global" cache. @@cache = Cache.new - # Creates and intializes a new XmlSimple object. + # Creates and initializes a new XmlSimple object. # # defaults:: # Default values for options. @@ -497,7 +497,7 @@ class XmlSimple } end - # Fold Hases containing a single anonymous Array up into just the Array. + # Fold Hashes containing a single anonymous Array up into just the Array. if count == 1 anonymoustag = @options['anonymoustag'] if result.has_key?(anonymoustag) && result[anonymoustag].instance_of?(Array) @@ -907,7 +907,7 @@ class XmlSimple # Thanks to Norbert Gawor for a bugfix. # # value:: - # Value to be checked for emptyness. + # Value to be checked for emptiness. def empty(value) case value when Hash -- cgit v1.2.3 From 0ac342fbe5727c22dbce58e375e9b42dd2a92ef5 Mon Sep 17 00:00:00 2001 From: Sven Fuchs Date: Wed, 16 Jul 2008 10:51:48 -0700 Subject: make i18n date/time tests timezone independent --- activesupport/test/i18n_test.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'activesupport') diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb index 17074b6cc6..4b17e3c523 100644 --- a/activesupport/test/i18n_test.rb +++ b/activesupport/test/i18n_test.rb @@ -9,40 +9,40 @@ class I18nTest < Test::Unit::TestCase uses_mocha 'I18nTimeZoneTest' do def test_time_zone_localization_with_default_format Time.zone.stubs(:now).returns Time.local(2000) - assert_equal "Sat, 01 Jan 2000 00:00:00 +0100", I18n.localize(Time.zone.now) + assert_equal Time.zone.now.strftime("%a, %d %b %Y %H:%M:%S %z"), I18n.localize(Time.zone.now) end end def test_date_localization_should_use_default_format - assert_equal "2008-07-02", I18n.localize(@date) + assert_equal @date.strftime("%Y-%m-%d"), I18n.localize(@date) end def test_date_localization_with_default_format - assert_equal "2008-07-02", I18n.localize(@date, :format => :default) + assert_equal @date.strftime("%Y-%m-%d"), I18n.localize(@date, :format => :default) end def test_date_localization_with_short_format - assert_equal "Jul 02", I18n.localize(@date, :format => :short) + assert_equal @date.strftime("%b %d"), I18n.localize(@date, :format => :short) end def test_date_localization_with_long_format - assert_equal "July 02, 2008", I18n.localize(@date, :format => :long) + assert_equal @date.strftime("%B %d, %Y"), I18n.localize(@date, :format => :long) end - def test_time_localization_should_use_default_format - assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", I18n.localize(@time) + def test_time_localization_should_use_default_format + assert_equal @time.strftime("%a, %d %b %Y %H:%M:%S %z"), I18n.localize(@time) end def test_time_localization_with_default_format - assert_equal "Wed, 02 Jul 2008 16:47:01 +0100", I18n.localize(@time, :format => :default) + assert_equal @time.strftime("%a, %d %b %Y %H:%M:%S %z"), I18n.localize(@time, :format => :default) end def test_time_localization_with_short_format - assert_equal "02 Jul 16:47", I18n.localize(@time, :format => :short) + assert_equal @time.strftime("%d %b %H:%M"), I18n.localize(@time, :format => :short) end def test_time_localization_with_long_format - assert_equal "July 02, 2008 16:47", I18n.localize(@time, :format => :long) + assert_equal @time.strftime("%B %d, %Y %H:%M"), I18n.localize(@time, :format => :long) end def test_day_names -- cgit v1.2.3 From 7ae2105d57d3c08bebde44bd72093dd43c48d613 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 16 Jul 2008 18:46:04 -0500 Subject: MemCacheStore#decrement should use data instance variable not local variable [#521 state:resolved] --- .../lib/active_support/cache/mem_cache_store.rb | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index b3769b812f..58958dccef 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -29,8 +29,8 @@ module ActiveSupport nil end - # Set key = value. Pass :unless_exist => true if you don't - # want to update the cache if the key is already set. + # Set key = value. Pass :unless_exist => true if you don't + # want to update the cache if the key is already set. def write(key, value, options = nil) super method = options && options[:unless_exist] ? :add : :set @@ -56,33 +56,33 @@ module ActiveSupport !read(key, options).nil? end - def increment(key, amount = 1) + def increment(key, amount = 1) log("incrementing", key, amount) - - response = @data.incr(key, amount) + + response = @data.incr(key, amount) response == Response::NOT_FOUND ? nil : response - rescue MemCache::MemCacheError + rescue MemCache::MemCacheError nil end def decrement(key, amount = 1) log("decrement", key, amount) - - response = data.decr(key, amount) + + response = @data.decr(key, amount) response == Response::NOT_FOUND ? nil : response - rescue MemCache::MemCacheError + rescue MemCache::MemCacheError nil - end - + end + def delete_matched(matcher, options = nil) super raise "Not supported by Memcache" - end - + end + def clear @data.flush_all - end - + end + def stats @data.stats end -- cgit v1.2.3 From 396f9df8916b71f83aad8d56559cf55fc8501679 Mon Sep 17 00:00:00 2001 From: Josh Owens Date: Wed, 16 Jul 2008 19:31:37 -0500 Subject: Hash#slice supports an array of keys [#613 state:resolved] Signed-off-by: Joshua Peek --- .../lib/active_support/core_ext/hash/slice.rb | 1 + activesupport/test/core_ext/hash_ext_test.rb | 56 ++++++++++++++-------- 2 files changed, 36 insertions(+), 21 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index be4dec6e53..d3837d2e54 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -12,6 +12,7 @@ module ActiveSupport #:nodoc: module Slice # Returns a new hash with only the given keys. def slice(*keys) + keys.flatten! keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) hash = {} keys.each { |k| hash[k] = self[k] if has_key?(k) } diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 69028a123f..26e65075eb 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -282,6 +282,20 @@ class HashExtTest < Test::Unit::TestCase assert_equal expected, original end + # This is needed for something like hash.slice!(hash.keys.sort_by {rand} [0..4]) + def test_slice_with_array_keys + original = { :a => 'x', :b => 'y', :c => 10 } + expected = { :a => 'x', :b => 'y' } + + # Should return a new hash with only the given keys, when given an array of keys. + assert_equal expected, original.slice([:a, :b]) + assert_not_equal expected, original + + # Should replace the hash with only the given keys, when given an array of keys. + assert_equal expected, original.slice!([:a, :b]) + assert_equal expected, original + end + def test_indifferent_slice original = { :a => 'x', :b => 'y', :c => 10 }.with_indifferent_access expected = { :a => 'x', :b => 'y' }.with_indifferent_access @@ -469,12 +483,12 @@ class HashToXmlTest < Test::Unit::TestCase EOT expected_topic_hash = { - :title => nil, + :title => nil, :id => nil, :approved => nil, :written_on => nil, :viewed_at => nil, - :content => nil, + :content => nil, :parent_id => nil }.stringify_keys @@ -552,7 +566,7 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal expected_topic_hash, Hash.from_xml(topic_xml)["rsp"]["photos"]["photo"] end - + def test_empty_array_from_xml blog_xml = <<-XML @@ -650,13 +664,13 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal expected_bacon_hash, Hash.from_xml(bacon_xml)["bacon"] end - + def test_type_trickles_through_when_unknown product_xml = <<-EOT 0.5 image.gif - + EOT @@ -665,7 +679,7 @@ class HashToXmlTest < Test::Unit::TestCase :image => {'type' => 'ProductImage', 'filename' => 'image.gif' }, }.stringify_keys - assert_equal expected_product_hash, Hash.from_xml(product_xml)["product"] + assert_equal expected_product_hash, Hash.from_xml(product_xml)["product"] end def test_should_use_default_value_for_unknown_key @@ -699,41 +713,41 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal expected, hash.to_xml(@xml_options) end end - - def test_empty_string_works_for_typecast_xml_value + + def test_empty_string_works_for_typecast_xml_value assert_nothing_raised do Hash.send!(:typecast_xml_value, "") end end - + def test_escaping_to_xml - hash = { - :bare_string => 'First & Last Name', + hash = { + :bare_string => 'First & Last Name', :pre_escaped_string => 'First & Last Name' }.stringify_keys - + expected_xml = 'First & Last NameFirst &amp; Last Name' assert_equal expected_xml, hash.to_xml(@xml_options) end - + def test_unescaping_from_xml xml_string = 'First & Last NameFirst &amp; Last Name' - expected_hash = { - :bare_string => 'First & Last Name', + expected_hash = { + :bare_string => 'First & Last Name', :pre_escaped_string => 'First & Last Name' }.stringify_keys assert_equal expected_hash, Hash.from_xml(xml_string)['person'] end - + def test_roundtrip_to_xml_from_xml - hash = { - :bare_string => 'First & Last Name', + hash = { + :bare_string => 'First & Last Name', :pre_escaped_string => 'First & Last Name' }.stringify_keys assert_equal hash, Hash.from_xml(hash.to_xml(@xml_options))['person'] end - + def test_datetime_xml_type_with_utc_time alert_xml = <<-XML @@ -744,7 +758,7 @@ class HashToXmlTest < Test::Unit::TestCase assert alert_at.utc? assert_equal Time.utc(2008, 2, 10, 15, 30, 45), alert_at end - + def test_datetime_xml_type_with_non_utc_time alert_xml = <<-XML @@ -755,7 +769,7 @@ class HashToXmlTest < Test::Unit::TestCase assert alert_at.utc? assert_equal Time.utc(2008, 2, 10, 15, 30, 45), alert_at end - + def test_datetime_xml_type_with_far_future_date alert_xml = <<-XML -- cgit v1.2.3 From 40dbebba28bfa1c55737da7354542c3bdca4e1a1 Mon Sep 17 00:00:00 2001 From: Lawrence Pit Date: Mon, 14 Jul 2008 11:53:41 +1000 Subject: Allow deep merging of hash values for nested with_options. [#490 state:resolved] Signed-off-by: Pratik Naik --- activesupport/lib/active_support/core_ext/hash.rb | 3 ++- .../lib/active_support/core_ext/hash/deep_merge.rb | 23 ++++++++++++++++++ activesupport/lib/active_support/option_merger.rb | 10 +------- activesupport/test/core_ext/hash_ext_test.rb | 10 ++++++++ activesupport/test/option_merger_test.rb | 27 ++++++++++++++++++++++ 5 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/hash/deep_merge.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/hash.rb b/activesupport/lib/active_support/core_ext/hash.rb index 6cbd9dd378..a6065ab48e 100644 --- a/activesupport/lib/active_support/core_ext/hash.rb +++ b/activesupport/lib/active_support/core_ext/hash.rb @@ -1,10 +1,11 @@ -%w(keys indifferent_access reverse_merge conversions diff slice except).each do |ext| +%w(keys indifferent_access deep_merge reverse_merge conversions diff slice except).each do |ext| require "active_support/core_ext/hash/#{ext}" end class Hash #:nodoc: include ActiveSupport::CoreExtensions::Hash::Keys include ActiveSupport::CoreExtensions::Hash::IndifferentAccess + include ActiveSupport::CoreExtensions::Hash::DeepMerge include ActiveSupport::CoreExtensions::Hash::ReverseMerge include ActiveSupport::CoreExtensions::Hash::Conversions include ActiveSupport::CoreExtensions::Hash::Diff diff --git a/activesupport/lib/active_support/core_ext/hash/deep_merge.rb b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb new file mode 100644 index 0000000000..f8842ba57a --- /dev/null +++ b/activesupport/lib/active_support/core_ext/hash/deep_merge.rb @@ -0,0 +1,23 @@ +module ActiveSupport #:nodoc: + module CoreExtensions #:nodoc: + module Hash #:nodoc: + # Allows for deep merging + module DeepMerge + # Returns a new hash with +self+ and +other_hash+ merged recursively. + def deep_merge(other_hash) + self.merge(other_hash) do |key, oldval, newval| + oldval = oldval.to_hash if oldval.respond_to?(:to_hash) + newval = newval.to_hash if newval.respond_to?(:to_hash) + oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval + end + end + + # Returns a new hash with +self+ and +other_hash+ merged recursively. + # Modifies the receiver in place. + def deep_merge!(other_hash) + replace(deep_merge(other_hash)) + end + end + end + end +end diff --git a/activesupport/lib/active_support/option_merger.rb b/activesupport/lib/active_support/option_merger.rb index 1a4ff9db9a..c77bca1ac9 100644 --- a/activesupport/lib/active_support/option_merger.rb +++ b/activesupport/lib/active_support/option_merger.rb @@ -10,16 +10,8 @@ module ActiveSupport private def method_missing(method, *arguments, &block) - merge_argument_options! arguments + arguments << (arguments.last.respond_to?(:to_hash) ? @options.deep_merge(arguments.pop) : @options.dup) @context.send!(method, *arguments, &block) end - - def merge_argument_options!(arguments) - arguments << if arguments.last.respond_to? :to_hash - @options.merge(arguments.pop) - else - @options.dup - end - end end end diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 26e65075eb..5d2053f106 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -245,6 +245,16 @@ class HashExtTest < Test::Unit::TestCase assert(!indiff.keys.any? {|k| k.kind_of? String}, "A key was converted to a string!") end + def test_deep_merge + hash_1 = { :a => "a", :b => "b", :c => { :c1 => "c1", :c2 => "c2", :c3 => { :d1 => "d1" } } } + hash_2 = { :a => 1, :c => { :c1 => 2, :c3 => { :d2 => "d2" } } } + expected = { :a => 1, :b => "b", :c => { :c1 => 2, :c2 => "c2", :c3 => { :d1 => "d1", :d2 => "d2" } } } + assert_equal expected, hash_1.deep_merge(hash_2) + + hash_1.deep_merge!(hash_2) + assert_equal expected, hash_1 + end + def test_reverse_merge defaults = { :a => "x", :b => "y", :c => 10 }.freeze options = { :a => 1, :b => 2 } diff --git a/activesupport/test/option_merger_test.rb b/activesupport/test/option_merger_test.rb index 509c6d3bad..0d72314880 100644 --- a/activesupport/test/option_merger_test.rb +++ b/activesupport/test/option_merger_test.rb @@ -38,6 +38,33 @@ class OptionMergerTest < Test::Unit::TestCase end end + def test_nested_method_with_options_containing_hashes_merge + with_options :conditions => { :method => :get } do |outer| + outer.with_options :conditions => { :domain => "www" } do |inner| + expected = { :conditions => { :method => :get, :domain => "www" } } + assert_equal expected, inner.method_with_options + end + end + end + + def test_nested_method_with_options_containing_hashes_overwrite + with_options :conditions => { :method => :get, :domain => "www" } do |outer| + outer.with_options :conditions => { :method => :post } do |inner| + expected = { :conditions => { :method => :post, :domain => "www" } } + assert_equal expected, inner.method_with_options + end + end + end + + def test_nested_method_with_options_containing_hashes_going_deep + with_options :html => { :class => "foo", :style => { :margin => 0, :display => "block" } } do |outer| + outer.with_options :html => { :title => "bar", :style => { :margin => "1em", :color => "#fff" } } do |inner| + expected = { :html => { :class => "foo", :title => "bar", :style => { :margin => "1em", :display => "block", :color => "#fff" } } } + assert_equal expected, inner.method_with_options + end + end + end + # Needed when counting objects with the ObjectSpace def test_option_merger_class_method assert_equal ActiveSupport::OptionMerger, ActiveSupport::OptionMerger.new('', '').class -- cgit v1.2.3 From d8a72b32c5b0c32abf257f05b89bad7d21f178ec Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Wed, 16 Jul 2008 20:23:44 -0500 Subject: Revert "Run callbacks from object's metaclass" --- activesupport/CHANGELOG | 2 -- activesupport/lib/active_support/callbacks.rb | 10 +--------- activesupport/test/callbacks_test.rb | 24 ------------------------ 3 files changed, 1 insertion(+), 35 deletions(-) (limited to 'activesupport') diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index aa383cd166..8d3b136d80 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,7 +1,5 @@ *Edge* -* Run callbacks from object's metaclass [Josh Peek] - * 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 diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index f125a56246..9c59b7ac76 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -269,15 +269,7 @@ module ActiveSupport # pass # stop def run_callbacks(kind, options = {}, &block) - callback_chain_method = "#{kind}_callback_chain" - - # Meta class inherits Class so we don't have to merge it in 1.9 - if RUBY_VERSION >= '1.9' - metaclass.send(callback_chain_method).run(self, options, &block) - else - callbacks = self.class.send(callback_chain_method) | metaclass.send(callback_chain_method) - callbacks.run(self, options, &block) - end + self.class.send("#{kind}_callback_chain").run(self, options, &block) end end end diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index c3f683bdb5..7f71ca2262 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -84,30 +84,6 @@ class CallbacksTest < Test::Unit::TestCase end end -class MetaclassCallbacksTest < Test::Unit::TestCase - module ModuleWithCallbacks - def self.extended(object) - object.metaclass.before_save :raise_metaclass_callback_called - end - - def module_callback_called? - @module_callback_called ||= false - end - - def raise_metaclass_callback_called - @module_callback_called = true - end - end - - def test_metaclass_callbacks - person = Person.new - person.extend(ModuleWithCallbacks) - assert !person.module_callback_called? - person.save - assert person.module_callback_called? - end -end - class ConditionalCallbackTest < Test::Unit::TestCase def test_save_conditional_person person = ConditionalPerson.new -- cgit v1.2.3 From b3a2ee7b87a6b2a4c6ff086644f40a472a676b65 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 17 Jul 2008 15:28:02 +0100 Subject: Revert "Hash#slice supports an array of keys [#613 state:resolved]" This reverts commit 396f9df8916b71f83aad8d56559cf55fc8501679. --- .../lib/active_support/core_ext/hash/slice.rb | 1 - activesupport/test/core_ext/hash_ext_test.rb | 56 ++++++++-------------- 2 files changed, 21 insertions(+), 36 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index d3837d2e54..be4dec6e53 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -12,7 +12,6 @@ module ActiveSupport #:nodoc: module Slice # Returns a new hash with only the given keys. def slice(*keys) - keys.flatten! keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) hash = {} keys.each { |k| hash[k] = self[k] if has_key?(k) } diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 5d2053f106..2ab7681a8a 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -292,20 +292,6 @@ class HashExtTest < Test::Unit::TestCase assert_equal expected, original end - # This is needed for something like hash.slice!(hash.keys.sort_by {rand} [0..4]) - def test_slice_with_array_keys - original = { :a => 'x', :b => 'y', :c => 10 } - expected = { :a => 'x', :b => 'y' } - - # Should return a new hash with only the given keys, when given an array of keys. - assert_equal expected, original.slice([:a, :b]) - assert_not_equal expected, original - - # Should replace the hash with only the given keys, when given an array of keys. - assert_equal expected, original.slice!([:a, :b]) - assert_equal expected, original - end - def test_indifferent_slice original = { :a => 'x', :b => 'y', :c => 10 }.with_indifferent_access expected = { :a => 'x', :b => 'y' }.with_indifferent_access @@ -493,12 +479,12 @@ class HashToXmlTest < Test::Unit::TestCase EOT expected_topic_hash = { - :title => nil, + :title => nil, :id => nil, :approved => nil, :written_on => nil, :viewed_at => nil, - :content => nil, + :content => nil, :parent_id => nil }.stringify_keys @@ -576,7 +562,7 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal expected_topic_hash, Hash.from_xml(topic_xml)["rsp"]["photos"]["photo"] end - + def test_empty_array_from_xml blog_xml = <<-XML @@ -674,13 +660,13 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal expected_bacon_hash, Hash.from_xml(bacon_xml)["bacon"] end - + def test_type_trickles_through_when_unknown product_xml = <<-EOT 0.5 image.gif - + EOT @@ -689,7 +675,7 @@ class HashToXmlTest < Test::Unit::TestCase :image => {'type' => 'ProductImage', 'filename' => 'image.gif' }, }.stringify_keys - assert_equal expected_product_hash, Hash.from_xml(product_xml)["product"] + assert_equal expected_product_hash, Hash.from_xml(product_xml)["product"] end def test_should_use_default_value_for_unknown_key @@ -723,41 +709,41 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal expected, hash.to_xml(@xml_options) end end - - def test_empty_string_works_for_typecast_xml_value + + def test_empty_string_works_for_typecast_xml_value assert_nothing_raised do Hash.send!(:typecast_xml_value, "") end end - + def test_escaping_to_xml - hash = { - :bare_string => 'First & Last Name', + hash = { + :bare_string => 'First & Last Name', :pre_escaped_string => 'First & Last Name' }.stringify_keys - + expected_xml = 'First & Last NameFirst &amp; Last Name' assert_equal expected_xml, hash.to_xml(@xml_options) end - + def test_unescaping_from_xml xml_string = 'First & Last NameFirst &amp; Last Name' - expected_hash = { - :bare_string => 'First & Last Name', + expected_hash = { + :bare_string => 'First & Last Name', :pre_escaped_string => 'First & Last Name' }.stringify_keys assert_equal expected_hash, Hash.from_xml(xml_string)['person'] end - + def test_roundtrip_to_xml_from_xml - hash = { - :bare_string => 'First & Last Name', + hash = { + :bare_string => 'First & Last Name', :pre_escaped_string => 'First & Last Name' }.stringify_keys assert_equal hash, Hash.from_xml(hash.to_xml(@xml_options))['person'] end - + def test_datetime_xml_type_with_utc_time alert_xml = <<-XML @@ -768,7 +754,7 @@ class HashToXmlTest < Test::Unit::TestCase assert alert_at.utc? assert_equal Time.utc(2008, 2, 10, 15, 30, 45), alert_at end - + def test_datetime_xml_type_with_non_utc_time alert_xml = <<-XML @@ -779,7 +765,7 @@ class HashToXmlTest < Test::Unit::TestCase assert alert_at.utc? assert_equal Time.utc(2008, 2, 10, 15, 30, 45), alert_at end - + def test_datetime_xml_type_with_far_future_date alert_xml = <<-XML -- cgit v1.2.3 From 7e8aee7e6cbd23c1eb18bec1869465e923915e7a Mon Sep 17 00:00:00 2001 From: MatthewRudy Date: Thu, 17 Jul 2008 13:58:42 +0100 Subject: Add extra tests to ensure Hash#slice works with an array as a key. #613 Signed-off-by: Pratik Naik --- .../lib/active_support/core_ext/hash/slice.rb | 5 +++++ activesupport/test/core_ext/hash_ext_test.rb | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index be4dec6e53..3f14470f36 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -9,6 +9,11 @@ module ActiveSupport #:nodoc: # end # # search(options.slice(:mass, :velocity, :time)) + # + # If you have an array of keys you want to limit to, you should splat them: + # + # valid_keys = [:mass, :velocity, :time] + # search(options.slice(*valid_keys)) module Slice # Returns a new hash with only the given keys. def slice(*keys) diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 2ab7681a8a..fc8ed45358 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -292,6 +292,27 @@ class HashExtTest < Test::Unit::TestCase assert_equal expected, original end + def test_slice_with_an_array_key + original = { :a => 'x', :b => 'y', :c => 10, [:a, :b] => "an array key" } + expected = { [:a, :b] => "an array key", :c => 10 } + + # Should return a new hash with only the given keys when given an array key. + assert_equal expected, original.slice([:a, :b], :c) + assert_not_equal expected, original + + # Should replace the hash with only the given keys when given an array key. + assert_equal expected, original.slice!([:a, :b], :c) + assert_equal expected, original + end + + def test_slice_with_splatted_keys + original = { :a => 'x', :b => 'y', :c => 10, [:a, :b] => "an array key" } + expected = { :a => 'x', :b => "y" } + + # Should grab each of the splatted keys. + assert_equal expected, original.slice(*[:a, :b]) + end + def test_indifferent_slice original = { :a => 'x', :b => 'y', :c => 10 }.with_indifferent_access expected = { :a => 'x', :b => 'y' }.with_indifferent_access -- cgit v1.2.3 From 94cf6675d516a0196f0222694f26dcd4c29c49c6 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 17 Jul 2008 15:29:30 -0500 Subject: Cleanup ActiveSupport::Cache::ThreadSafety module and add test coverage --- activesupport/lib/active_support/cache.rb | 32 +++++---------- activesupport/test/caching_test.rb | 67 +++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 21 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 3e3dc18263..57c6a6331d 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -21,7 +21,7 @@ module ActiveSupport 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"]}/" + expanded_cache_key << "#{ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]}/" end expanded_cache_key << case @@ -40,13 +40,8 @@ module ActiveSupport class Store cattr_accessor :logger - def initialize - end - def threadsafe! - @mutex = Mutex.new - self.class.send :include, ThreadSafety - self + extend ThreadSafety end # Pass :force => true to force a cache miss. @@ -110,29 +105,24 @@ module ActiveSupport nil end end - + private def log(operation, key, options) logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !@logger_off end end - module ThreadSafety #:nodoc: - def read(key, options = nil) #:nodoc: - @mutex.synchronize { super } - end - - def write(key, value, options = nil) #:nodoc: - @mutex.synchronize { super } - end - - def delete(key, options = nil) #:nodoc: - @mutex.synchronize { super } + def self.extended(object) #:nodoc: + object.instance_variable_set(:@mutex, Mutex.new) end - def delete_matched(matcher, options = nil) #:nodoc: - @mutex.synchronize { super } + %w(read write delete delete_matched exist? increment decrement).each do |method| + module_eval <<-EOS, __FILE__, __LINE__ + def #{method}(*args) + @mutex.synchronize { super } + end + EOS end end end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index f3220d27aa..0af4251962 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -70,3 +70,70 @@ uses_mocha 'high-level cache store tests' do end end end + +class ThreadSafetyCacheStoreTest < Test::Unit::TestCase + def setup + @cache = ActiveSupport::Cache.lookup_store(:memory_store).threadsafe! + @cache.write('foo', 'bar') + + # No way to have mocha proxy to the original method + @mutex = @cache.instance_variable_get(:@mutex) + @mutex.instance_eval %( + def calls; @calls; end + def synchronize + @calls ||= 0 + @calls += 1 + yield + end + ) + end + + def test_read_is_synchronized + assert_equal 'bar', @cache.read('foo') + assert_equal 1, @mutex.calls + end + + def test_write_is_synchronized + @cache.write('foo', 'baz') + assert_equal 'baz', @cache.read('foo') + assert_equal 2, @mutex.calls + end + + def test_delete_is_synchronized + assert_equal 'bar', @cache.read('foo') + @cache.delete('foo') + assert_equal nil, @cache.read('foo') + assert_equal 3, @mutex.calls + end + + def test_delete_matched_is_synchronized + assert_equal 'bar', @cache.read('foo') + @cache.delete_matched(/foo/) + assert_equal nil, @cache.read('foo') + assert_equal 3, @mutex.calls + end + + def test_fetch_is_synchronized + assert_equal 'bar', @cache.fetch('foo') { 'baz' } + assert_equal 'fu', @cache.fetch('bar') { 'fu' } + assert_equal 3, @mutex.calls + end + + def test_exist_is_synchronized + assert @cache.exist?('foo') + assert !@cache.exist?('bar') + assert_equal 2, @mutex.calls + end + + def test_increment_is_synchronized + @cache.write('foo_count', 1) + assert_equal 2, @cache.increment('foo_count') + assert_equal 4, @mutex.calls + end + + def test_decrement_is_synchronized + @cache.write('foo_count', 1) + assert_equal 0, @cache.decrement('foo_count') + assert_equal 4, @mutex.calls + end +end -- cgit v1.2.3 From 0eef4e554db9b1af47305f5a8c51c90c15ac6c04 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 17 Jul 2008 16:00:59 -0500 Subject: Allow ActiveSupport::Cache logger to be silenced --- activesupport/lib/active_support/cache.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 57c6a6331d..5a064f8bea 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -36,7 +36,6 @@ module ActiveSupport expanded_cache_key end - class Store cattr_accessor :logger @@ -44,6 +43,11 @@ module ActiveSupport extend ThreadSafety end + def silence! + @silence = true + self + end + # Pass :force => true to force a cache miss. def fetch(key, options = {}) @logger_off = true @@ -108,7 +112,7 @@ module ActiveSupport private def log(operation, key, options) - logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !@logger_off + logger.debug("Cache #{operation}: #{key}#{options ? " (#{options.inspect})" : ""}") if logger && !@silence && !@logger_off end end -- cgit v1.2.3 From 7430c4168fad07b480dbf80c8ac75ba7db8c634f Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 16 Jul 2008 22:27:04 -0700 Subject: Decrease default benchmark runs from 10 to 4 --- activesupport/lib/active_support/testing/performance.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 5f2027eb3b..71d6f4d9c6 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -11,7 +11,7 @@ module ActiveSupport DEFAULTS = if benchmark = ARGV.include?('--benchmark') # HAX for rake test { :benchmark => true, - :runs => 10, + :runs => 4, :metrics => [:process_time, :memory, :objects, :gc_runs, :gc_time], :output => 'tmp/performance' } else -- cgit v1.2.3 From e1f23da53cef20a60e4bf458d959fe2bfe7d52ea Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 18 Jul 2008 11:18:16 -0500 Subject: Allow memoized methods to be reloaded and allow multiple symbols --- activesupport/lib/active_support/memoizable.rb | 32 +++++++++++---------- activesupport/test/memoizable_test.rb | 39 +++++++++++++++++--------- 2 files changed, 43 insertions(+), 28 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index d06250171a..c41feef4c7 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -5,28 +5,30 @@ module ActiveSupport 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) + def memoize(*symbols) + symbols.each do |symbol| + original_method = "unmemoized_#{symbol}" + memoized_ivar = "@#{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} + alias_method original_method, symbol + class_eval <<-EOS, __FILE__, __LINE__ + def #{symbol}(reload = false) + if !reload && defined? #{memoized_ivar} + #{memoized_ivar} + else + #{memoized_ivar} = #{original_method}.freeze + end end - end - EOS + EOS + end end end def freeze methods.each do |method| - if m = method.to_s.match(/\A_unmemoized_(.*)/) - send(m[1]).freeze + if m = method.to_s.match(/\Aunmemoized_(.*)/) + send(m[1]) end end super diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index fc24a2942d..b649b31455 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -8,12 +8,16 @@ uses_mocha 'Memoizable' do def name fetch_name_from_floppy end - memoize :name def age nil end - memoize :age + + def random + rand(0) + end + + memoize :name, :age, :random private def fetch_name_from_floppy @@ -21,25 +25,34 @@ uses_mocha 'Memoizable' do end end + def setup + @person = Person.new + end + def test_memoization - person = Person.new - assert_equal "Josh", person.name + assert_equal "Josh", @person.name + + @person.expects(:fetch_name_from_floppy).never + 2.times { assert_equal "Josh", @person.name } + end - person.expects(:fetch_name_from_floppy).never - 2.times { assert_equal "Josh", person.name } + def test_reloadable + random = @person.random + assert_equal random, @person.random + assert_not_equal random, @person.random(:reload) end def test_memoized_methods_are_frozen - person = Person.new - person.freeze - assert_equal "Josh", person.name - assert_equal true, person.name.frozen? + assert_equal true, @person.name.frozen? + + @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 + @person.freeze + assert_equal nil, @person.age end def test_double_memoization -- cgit v1.2.3 From ef6f6625c91ea789a033799f649e4388e4a71045 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 18 Jul 2008 15:32:28 -0500 Subject: Changed ActiveSupport::Memoizable API to extend since it mainly adds the memoize class method --- activesupport/lib/active_support/memoizable.rb | 55 +++++++++++++------------- activesupport/test/memoizable_test.rb | 42 ++++++++++++++++---- 2 files changed, 63 insertions(+), 34 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index c41feef4c7..59fecbecb1 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -1,37 +1,38 @@ module ActiveSupport - module Memoizable - def self.included(base) #:nodoc: - base.extend(ClassMethods) - end - - module ClassMethods - def memoize(*symbols) - symbols.each do |symbol| - original_method = "unmemoized_#{symbol}" - memoized_ivar = "@#{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}(reload = false) - if !reload && defined? #{memoized_ivar} - #{memoized_ivar} - else - #{memoized_ivar} = #{original_method}.freeze - end + module Memoizable #:nodoc: + def self.extended(obj) + klass = obj.respond_to?(:class_eval) ? obj : obj.metaclass + klass.class_eval do + def freeze + methods.each do |method| + if m = method.to_s.match(/^unmemoized_(.*)/) + send(m[1]) end - EOS + end + super end end end - def freeze - methods.each do |method| - if m = method.to_s.match(/\Aunmemoized_(.*)/) - send(m[1]) - end + def memoize(*symbols) + symbols.each do |symbol| + original_method = "unmemoized_#{symbol}" + memoized_ivar = "@#{symbol}" + + klass = respond_to?(:class_eval) ? self : self.metaclass + raise "Already memoized #{symbol}" if klass.instance_methods.map(&:to_s).include?(original_method) + + klass.class_eval <<-EOS, __FILE__, __LINE__ + alias_method :#{original_method}, :#{symbol} + def #{symbol}(reload = false) + if !reload && defined? #{memoized_ivar} + #{memoized_ivar} + else + #{memoized_ivar} = #{original_method}.freeze + end + end + EOS end - super end end end diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index b649b31455..79769631ad 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -3,21 +3,24 @@ require 'abstract_unit' uses_mocha 'Memoizable' do class MemoizableTest < Test::Unit::TestCase class Person - include ActiveSupport::Memoizable + extend ActiveSupport::Memoizable def name fetch_name_from_floppy end + memoize :name + def age nil end - def random - rand(0) + def counter + @counter ||= 0 + @counter += 1 end - memoize :name, :age, :random + memoize :age, :counter private def fetch_name_from_floppy @@ -37,9 +40,9 @@ uses_mocha 'Memoizable' do end def test_reloadable - random = @person.random - assert_equal random, @person.random - assert_not_equal random, @person.random(:reload) + counter = @person.counter + assert_equal 1, @person.counter + assert_equal 2, @person.counter(:reload) end def test_memoized_methods_are_frozen @@ -58,5 +61,30 @@ uses_mocha 'Memoizable' do def test_double_memoization assert_raise(RuntimeError) { Person.memoize :name } end + + class Company + def name + lookup_name + end + + def lookup_name + "37signals" + end + end + + def test_object_memoization + company = Company.new + company.extend ActiveSupport::Memoizable + company.memoize :name + + assert_equal "37signals", company.name + # Mocha doesn't play well with frozen objects + company.metaclass.instance_eval { define_method(:lookup_name) { b00m } } + assert_equal "37signals", company.name + + assert_equal true, company.name.frozen? + company.freeze + assert_equal true, company.name.frozen? + end end end -- cgit v1.2.3 From c609be45966316bb107e0bad2b0935ac4a0d0c41 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Fri, 18 Jul 2008 23:30:36 -0500 Subject: Ruby 1.9: Ensure Memoizable#freeze is only overriden once to avoid an endless loop --- activesupport/lib/active_support/memoizable.rb | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 59fecbecb1..f7cd73d39c 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -1,19 +1,5 @@ module ActiveSupport module Memoizable #:nodoc: - def self.extended(obj) - klass = obj.respond_to?(:class_eval) ? obj : obj.metaclass - klass.class_eval do - def freeze - methods.each do |method| - if m = method.to_s.match(/^unmemoized_(.*)/) - send(m[1]) - end - end - super - end - end - end - def memoize(*symbols) symbols.each do |symbol| original_method = "unmemoized_#{symbol}" @@ -23,6 +9,18 @@ module ActiveSupport raise "Already memoized #{symbol}" if klass.instance_methods.map(&:to_s).include?(original_method) klass.class_eval <<-EOS, __FILE__, __LINE__ + unless instance_methods.map(&:to_s).include?("freeze_without_memoizable") + alias_method :freeze_without_memoizable, :freeze + def freeze + methods.each do |method| + if m = method.to_s.match(/^unmemoized_(.*)/) + send(m[1]) + end + end + freeze_without_memoizable + end + end + alias_method :#{original_method}, :#{symbol} def #{symbol}(reload = false) if !reload && defined? #{memoized_ivar} -- cgit v1.2.3 From d84d99a8f7dc672b050a6ab891c1680a323a7c97 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 10:52:30 -0500 Subject: Undefine old run method --- activesupport/lib/active_support/testing/setup_and_teardown.rb | 3 +++ 1 file changed, 3 insertions(+) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/testing/setup_and_teardown.rb b/activesupport/lib/active_support/testing/setup_and_teardown.rb index 21d71eb92a..a514b61fea 100644 --- a/activesupport/lib/active_support/testing/setup_and_teardown.rb +++ b/activesupport/lib/active_support/testing/setup_and_teardown.rb @@ -15,12 +15,15 @@ module ActiveSupport define_callbacks :setup, :teardown if defined?(::Mini) + undef_method :run alias_method :run, :run_with_callbacks_and_miniunit else begin require 'mocha' + undef_method :run alias_method :run, :run_with_callbacks_and_mocha rescue LoadError + undef_method :run alias_method :run, :run_with_callbacks_and_testunit end end -- cgit v1.2.3 From 8c2e839e5a0fb1662ae867c70114c3fc91850a55 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 11:04:23 -0500 Subject: Fix some warnings in i18n lib --- activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb | 6 +++--- .../lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb index 2185194da9..1bb65263a3 100755 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n.rb @@ -53,7 +53,7 @@ module I18n # storage. Decoupled for backends like a db backend that persist their # translations, so the backend can decide whether/when to yield or not. def populate(&block) - backend.populate &block + backend.populate(&block) end # Stores translations for the given locale in the backend. @@ -173,8 +173,8 @@ module I18n # keys are Symbols. def normalize_translation_keys(locale, key, scope) keys = [locale] + Array(scope) + [key] - keys = keys.map{|key| key.to_s.split(/\./) } - keys.flatten.map{|key| key.to_sym} + keys = keys.map{|k| k.to_s.split(/\./) } + keys.flatten.map{|k| k.to_sym} end end end diff --git a/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb index 284f2bfcbd..b8be1cecfb 100644 --- a/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb +++ b/activesupport/lib/active_support/vendor/i18n-0.0.1/lib/i18n/backend/simple.rb @@ -23,7 +23,7 @@ module I18n def translate(locale, key, options = {}) raise InvalidLocale.new(locale) if locale.nil? - return key.map{|key| translate locale, key, options } if key.is_a? Array + return key.map{|k| translate locale, k, options } if key.is_a? Array reserved = :scope, :default count, scope, default = options.values_at(:count, *reserved) @@ -66,7 +66,7 @@ module I18n def lookup(locale, key, scope = []) return unless key keys = I18n.send :normalize_translation_keys, locale, key, scope - keys.inject(@@translations){|result, key| result[key.to_sym] or return nil } + keys.inject(@@translations){|result, k| result[k.to_sym] or return nil } end # Evaluates a default translation. -- cgit v1.2.3 From da85251f85420d3a906a1eb949b73bb286122ea9 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sat, 19 Jul 2008 17:32:22 -0500 Subject: Refactor ActiveSupport::Dependencies injector so it would be possible to disable it --- activesupport/lib/active_support/dependencies.rb | 218 ++++++++++++----------- 1 file changed, 114 insertions(+), 104 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 2f3fa72bb4..e3d4f3d7eb 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -1,8 +1,3 @@ -require 'set' -require 'active_support/core_ext/module/attribute_accessors' -require 'active_support/core_ext/load_error' -require 'active_support/core_ext/kernel' - module ActiveSupport #:nodoc: module Dependencies #:nodoc: extend self @@ -52,6 +47,119 @@ module ActiveSupport #:nodoc: mattr_accessor :constant_watch_stack self.constant_watch_stack = [] + # Module includes this module + module ModuleConstMissing #:nodoc: + def self.included(base) #:nodoc: + base.class_eval do + # Rename the original handler so we can chain it to the new one + alias_method :rails_original_const_missing, :const_missing + + # Use const_missing to autoload associations so we don't have to + # require_association when using single-table inheritance. + def const_missing(class_id) + ActiveSupport::Dependencies.load_missing_constant self, class_id + end + end + end + + def unloadable(const_desc = self) + super(const_desc) + end + end + + # Class includes this module + module ClassConstMissing #:nodoc: + def const_missing(const_name) + if [Object, Kernel].include?(self) || parent == self + super + else + begin + begin + Dependencies.load_missing_constant self, const_name + rescue NameError + parent.send :const_missing, const_name + end + rescue NameError => e + # Make sure that the name we are missing is the one that caused the error + parent_qualified_name = Dependencies.qualified_name_for parent, const_name + raise unless e.missing_name? parent_qualified_name + qualified_name = Dependencies.qualified_name_for self, const_name + raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e) + end + end + end + end + + # Object includes this module + module Loadable #:nodoc: + def load(file, *extras) #:nodoc: + Dependencies.new_constants_in(Object) { super } + rescue Exception => exception # errors from loading file + exception.blame_file! file + raise + end + + def require(file, *extras) #:nodoc: + Dependencies.new_constants_in(Object) { super } + rescue Exception => exception # errors from required file + exception.blame_file! file + raise + end + + # Mark the given constant as unloadable. Unloadable constants are removed each + # time dependencies are cleared. + # + # Note that marking a constant for unloading need only be done once. Setup + # or init scripts may list each unloadable constant that may need unloading; + # each constant will be removed for every subsequent clear, as opposed to for + # the first clear. + # + # The provided constant descriptor may be a (non-anonymous) module or class, + # or a qualified constant name as a string or symbol. + # + # Returns true if the constant was not previously marked for unloading, false + # otherwise. + def unloadable(const_desc) + Dependencies.mark_for_unload const_desc + end + end + + # Exception file-blaming + module Blamable #:nodoc: + def blame_file!(file) + (@blamed_files ||= []).unshift file + end + + def blamed_files + @blamed_files ||= [] + end + + def describe_blame + return nil if blamed_files.empty? + "This error occurred while loading the following files:\n #{blamed_files.join "\n "}" + end + + def copy_blame!(exc) + @blamed_files = exc.blamed_files.clone + self + end + end + + def inject! + Object.instance_eval do + define_method(:require_or_load) { |file_name| Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load) + define_method(:require_dependency) { |file_name| Dependencies.depend_on(file_name) } unless Object.respond_to?(:require_dependency) + define_method(:require_association) { |file_name| Dependencies.associate_with(file_name) } unless Object.respond_to?(:require_association) + + alias_method :load_without_new_constant_marking, :load + include Loadable + end + + Module.instance_eval { include ModuleConstMissing } + Class.instance_eval { include ClassConstMissing } + Exception.instance_eval { include Blamable } + end + def load? mechanism == :load end @@ -452,102 +560,4 @@ module ActiveSupport #:nodoc: end end -Object.instance_eval do - define_method(:require_or_load) { |file_name| ActiveSupport::Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load) - define_method(:require_dependency) { |file_name| ActiveSupport::Dependencies.depend_on(file_name) } unless Object.respond_to?(:require_dependency) - define_method(:require_association) { |file_name| ActiveSupport::Dependencies.associate_with(file_name) } unless Object.respond_to?(:require_association) -end - -class Module #:nodoc: - # Rename the original handler so we can chain it to the new one - alias :rails_original_const_missing :const_missing - - # Use const_missing to autoload associations so we don't have to - # require_association when using single-table inheritance. - def const_missing(class_id) - ActiveSupport::Dependencies.load_missing_constant self, class_id - end - - def unloadable(const_desc = self) - super(const_desc) - end - -end - -class Class - def const_missing(const_name) - if [Object, Kernel].include?(self) || parent == self - super - else - begin - begin - ActiveSupport::Dependencies.load_missing_constant self, const_name - rescue NameError - parent.send :const_missing, const_name - end - rescue NameError => e - # Make sure that the name we are missing is the one that caused the error - parent_qualified_name = ActiveSupport::Dependencies.qualified_name_for parent, const_name - raise unless e.missing_name? parent_qualified_name - qualified_name = ActiveSupport::Dependencies.qualified_name_for self, const_name - raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e) - end - end - end -end - -class Object - alias_method :load_without_new_constant_marking, :load - - def load(file, *extras) #:nodoc: - ActiveSupport::Dependencies.new_constants_in(Object) { super } - rescue Exception => exception # errors from loading file - exception.blame_file! file - raise - end - - def require(file, *extras) #:nodoc: - ActiveSupport::Dependencies.new_constants_in(Object) { super } - rescue Exception => exception # errors from required file - exception.blame_file! file - raise - end - - # Mark the given constant as unloadable. Unloadable constants are removed each - # time dependencies are cleared. - # - # Note that marking a constant for unloading need only be done once. Setup - # or init scripts may list each unloadable constant that may need unloading; - # each constant will be removed for every subsequent clear, as opposed to for - # the first clear. - # - # The provided constant descriptor may be a (non-anonymous) module or class, - # or a qualified constant name as a string or symbol. - # - # Returns true if the constant was not previously marked for unloading, false - # otherwise. - def unloadable(const_desc) - ActiveSupport::Dependencies.mark_for_unload const_desc - end -end - -# Add file-blaming to exceptions -class Exception #:nodoc: - def blame_file!(file) - (@blamed_files ||= []).unshift file - end - - def blamed_files - @blamed_files ||= [] - end - - def describe_blame - return nil if blamed_files.empty? - "This error occurred while loading the following files:\n #{blamed_files.join "\n "}" - end - - def copy_blame!(exc) - @blamed_files = exc.blamed_files.clone - self - end -end +ActiveSupport::Dependencies.inject! -- cgit v1.2.3 From 006cbb8fde3f20a684eabcfd11c53ae762cf8435 Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 20 Jul 2008 21:23:18 +0200 Subject: Handle the case where 64bit time_t won't overflow. --- activesupport/test/core_ext/time_ext_test.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'activesupport') diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 17a0968c0e..14309f4e2d 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -528,7 +528,12 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_equal Time.time_with_datetime_fallback(:utc, 2005), Time.utc(2005) assert_equal Time.time_with_datetime_fallback(:utc, 2039), DateTime.civil(2039, 1, 1, 0, 0, 0, 0, 0) assert_equal Time.time_with_datetime_fallback(:utc, 2005, 2, 21, 17, 44, 30, 1), Time.utc(2005, 2, 21, 17, 44, 30, 1) #with usec - assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1), DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0) + # This won't overflow on 64bit linux + expected_to_overflow = Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1) + unless expected_to_overflow.is_a?(Time) + assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1), + DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0) + end assert_equal ::Date::ITALY, Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).start # use Ruby's default start value end -- cgit v1.2.3 From 5bec5848b22527ee77c007565f7eea336e5c864f Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sun, 20 Jul 2008 21:25:31 +0200 Subject: If it doesn't overflow, it won't have a .start method. So don't run the second assertion either --- activesupport/test/core_ext/time_ext_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activesupport') diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 14309f4e2d..8740497b3d 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -533,8 +533,8 @@ class TimeExtCalculationsTest < Test::Unit::TestCase unless expected_to_overflow.is_a?(Time) assert_equal Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1), DateTime.civil(2039, 2, 21, 17, 44, 30, 0, 0) + assert_equal ::Date::ITALY, Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).start # use Ruby's default start value end - assert_equal ::Date::ITALY, Time.time_with_datetime_fallback(:utc, 2039, 2, 21, 17, 44, 30, 1).start # use Ruby's default start value end def test_utc_time -- cgit v1.2.3 From 8a87d8a6c2c6dfb423bcaf61c750010d80993b16 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Jul 2008 10:26:44 -0500 Subject: Improved Memoizable test coverage and added support for multiple arguments --- .../active_support/core_ext/object/metaclass.rb | 5 + activesupport/lib/active_support/memoizable.rb | 55 ++++--- activesupport/test/memoizable_test.rb | 166 ++++++++++++++++----- 3 files changed, 165 insertions(+), 61 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/core_ext/object/metaclass.rb b/activesupport/lib/active_support/core_ext/object/metaclass.rb index 169a76dfb7..93fb0ad594 100644 --- a/activesupport/lib/active_support/core_ext/object/metaclass.rb +++ b/activesupport/lib/active_support/core_ext/object/metaclass.rb @@ -5,4 +5,9 @@ class Object self end end + + # If class_eval is called on an object, add those methods to its metaclass + def class_eval(*args, &block) + metaclass.class_eval(*args, &block) + end end diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index f7cd73d39c..21636b8af4 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -1,32 +1,43 @@ module ActiveSupport - module Memoizable #:nodoc: + module Memoizable + module Freezable + def self.included(base) + base.class_eval do + unless base.method_defined?(:freeze_without_memoizable) + alias_method_chain :freeze, :memoizable + end + end + end + + def freeze_with_memoizable + methods.each do |method| + if m = method.to_s.match(/^_unmemoized_(.*)/) + send(m[1]) + end + end + freeze_without_memoizable + end + end + def memoize(*symbols) symbols.each do |symbol| - original_method = "unmemoized_#{symbol}" - memoized_ivar = "@#{symbol}" + original_method = "_unmemoized_#{symbol}" + memoized_ivar = "@_memoized_#{symbol}" - klass = respond_to?(:class_eval) ? self : self.metaclass - raise "Already memoized #{symbol}" if klass.instance_methods.map(&:to_s).include?(original_method) + class_eval <<-EOS, __FILE__, __LINE__ + include Freezable - klass.class_eval <<-EOS, __FILE__, __LINE__ - unless instance_methods.map(&:to_s).include?("freeze_without_memoizable") - alias_method :freeze_without_memoizable, :freeze - def freeze - methods.each do |method| - if m = method.to_s.match(/^unmemoized_(.*)/) - send(m[1]) - end - end - freeze_without_memoizable - end - end + raise "Already memoized #{symbol}" if method_defined?(:#{original_method}) + alias #{original_method} #{symbol} + + def #{symbol}(*args) + #{memoized_ivar} ||= {} + reload = args.pop if args.last == true || args.last == :reload - alias_method :#{original_method}, :#{symbol} - def #{symbol}(reload = false) - if !reload && defined? #{memoized_ivar} - #{memoized_ivar} + if !reload && #{memoized_ivar} && #{memoized_ivar}.has_key?(args) + #{memoized_ivar}[args] else - #{memoized_ivar} = #{original_method}.freeze + #{memoized_ivar}[args] = #{original_method}(*args).freeze end end EOS diff --git a/activesupport/test/memoizable_test.rb b/activesupport/test/memoizable_test.rb index 79769631ad..cd84dcda53 100644 --- a/activesupport/test/memoizable_test.rb +++ b/activesupport/test/memoizable_test.rb @@ -5,86 +5,174 @@ uses_mocha 'Memoizable' do class Person extend ActiveSupport::Memoizable - def name - fetch_name_from_floppy + attr_reader :name_calls, :age_calls + def initialize + @name_calls = 0 + @age_calls = 0 end - memoize :name + def name + @name_calls += 1 + "Josh" + end def age + @age_calls += 1 nil end - def counter - @counter ||= 0 - @counter += 1 + memoize :name, :age + end + + class Company + attr_reader :name_calls + def initialize + @name_calls = 0 end - memoize :age, :counter + def name + @name_calls += 1 + "37signals" + end + end + + module Rates + extend ActiveSupport::Memoizable - private - def fetch_name_from_floppy - "Josh" + attr_reader :sales_tax_calls + def sales_tax(price) + @sales_tax_calls ||= 0 + @sales_tax_calls += 1 + price * 0.1025 + end + memoize :sales_tax + end + + class Calculator + extend ActiveSupport::Memoizable + include Rates + + attr_reader :fib_calls + def initialize + @fib_calls = 0 + end + + def fib(n) + @fib_calls += 1 + + if n == 0 || n == 1 + n + else + fib(n - 1) + fib(n - 2) end + end + memoize :fib + + def counter + @count ||= 0 + @count += 1 + end + memoize :counter end def setup @person = Person.new + @calculator = Calculator.new end def test_memoization assert_equal "Josh", @person.name + assert_equal 1, @person.name_calls - @person.expects(:fetch_name_from_floppy).never - 2.times { assert_equal "Josh", @person.name } + 3.times { assert_equal "Josh", @person.name } + assert_equal 1, @person.name_calls + end + + def test_memoization_with_nil_value + assert_equal nil, @person.age + assert_equal 1, @person.age_calls + + 3.times { assert_equal nil, @person.age } + assert_equal 1, @person.age_calls end def test_reloadable - counter = @person.counter - assert_equal 1, @person.counter - assert_equal 2, @person.counter(:reload) + counter = @calculator.counter + assert_equal 1, @calculator.counter + assert_equal 2, @calculator.counter(:reload) + assert_equal 2, @calculator.counter + assert_equal 3, @calculator.counter(true) + assert_equal 3, @calculator.counter end - def test_memoized_methods_are_frozen - assert_equal true, @person.name.frozen? + def test_memoization_cache_is_different_for_each_instance + assert_equal 1, @calculator.counter + assert_equal 2, @calculator.counter(:reload) + assert_equal 1, Calculator.new.counter + end + def test_memoized_is_not_affected_by_freeze @person.freeze assert_equal "Josh", @person.name - assert_equal true, @person.name.frozen? end - def test_memoization_frozen_with_nil_value - @person.freeze - assert_equal nil, @person.age + def test_memoization_with_args + assert_equal 55, @calculator.fib(10) + assert_equal 11, @calculator.fib_calls end - def test_double_memoization - assert_raise(RuntimeError) { Person.memoize :name } + def test_reloadable_with_args + assert_equal 55, @calculator.fib(10) + assert_equal 11, @calculator.fib_calls + assert_equal 55, @calculator.fib(10, :reload) + assert_equal 12, @calculator.fib_calls + assert_equal 55, @calculator.fib(10, true) + assert_equal 13, @calculator.fib_calls end - class Company - def name - lookup_name + def test_object_memoization + [Company.new, Company.new, Company.new].each do |company| + company.extend ActiveSupport::Memoizable + company.memoize :name + + assert_equal "37signals", company.name + assert_equal 1, company.name_calls + assert_equal "37signals", company.name + assert_equal 1, company.name_calls end + end - def lookup_name - "37signals" - end + def test_memoized_module_methods + assert_equal 1.025, @calculator.sales_tax(10) + assert_equal 1, @calculator.sales_tax_calls + assert_equal 1.025, @calculator.sales_tax(10) + assert_equal 1, @calculator.sales_tax_calls + assert_equal 2.5625, @calculator.sales_tax(25) + assert_equal 2, @calculator.sales_tax_calls end - def test_object_memoization + def test_object_memoized_module_methods company = Company.new - company.extend ActiveSupport::Memoizable - company.memoize :name + company.extend(Rates) + + assert_equal 1.025, company.sales_tax(10) + assert_equal 1, company.sales_tax_calls + assert_equal 1.025, company.sales_tax(10) + assert_equal 1, company.sales_tax_calls + assert_equal 2.5625, company.sales_tax(25) + assert_equal 2, company.sales_tax_calls + end - assert_equal "37signals", company.name - # Mocha doesn't play well with frozen objects - company.metaclass.instance_eval { define_method(:lookup_name) { b00m } } - assert_equal "37signals", company.name + def test_double_memoization + assert_raise(RuntimeError) { Person.memoize :name } + person = Person.new + person.extend ActiveSupport::Memoizable + assert_raise(RuntimeError) { person.memoize :name } - assert_equal true, company.name.frozen? - company.freeze - assert_equal true, company.name.frozen? + company = Company.new + company.extend ActiveSupport::Memoizable + company.memoize :name + assert_raise(RuntimeError) { company.memoize :name } end end end -- cgit v1.2.3 From 2681685450631238511cfc3c2f0fa044c1f8033a Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 22 Jul 2008 11:12:16 -0500 Subject: Extract ActiveSupport::TypedArray class to ensure an array is all of the same type [#673 state:resolved] --- activesupport/lib/active_support.rb | 1 + activesupport/lib/active_support/typed_array.rb | 31 +++++++++++++++ activesupport/test/typed_array_test.rb | 51 +++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 activesupport/lib/active_support/typed_array.rb create mode 100644 activesupport/test/typed_array_test.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 1df911a3f2..51067e910e 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -39,6 +39,7 @@ require 'active_support/cache' require 'active_support/dependencies' require 'active_support/deprecation' +require 'active_support/typed_array' require 'active_support/ordered_hash' require 'active_support/ordered_options' require 'active_support/option_merger' diff --git a/activesupport/lib/active_support/typed_array.rb b/activesupport/lib/active_support/typed_array.rb new file mode 100644 index 0000000000..1a4d8a8faf --- /dev/null +++ b/activesupport/lib/active_support/typed_array.rb @@ -0,0 +1,31 @@ +module ActiveSupport + class TypedArray < Array + def self.type_cast(obj) + obj + end + + def initialize(*args) + super(*args).map! { |obj| self.class.type_cast(obj) } + end + + def <<(obj) + super(self.class.type_cast(obj)) + end + + def concat(array) + super(array.map! { |obj| self.class.type_cast(obj) }) + end + + def insert(index, obj) + super(index, self.class.type_cast(obj)) + end + + def push(*objs) + super(*objs.map { |obj| self.class.type_cast(obj) }) + end + + def unshift(*objs) + super(*objs.map { |obj| self.class.type_cast(obj) }) + end + end +end diff --git a/activesupport/test/typed_array_test.rb b/activesupport/test/typed_array_test.rb new file mode 100644 index 0000000000..023f3a1b84 --- /dev/null +++ b/activesupport/test/typed_array_test.rb @@ -0,0 +1,51 @@ +require 'abstract_unit' + +class TypedArrayTest < Test::Unit::TestCase + class StringArray < ActiveSupport::TypedArray + def self.type_cast(obj) + obj.to_s + end + end + + def setup + @array = StringArray.new + end + + def test_string_array_initialize + assert_equal ["1", "2", "3"], StringArray.new([1, "2", :"3"]) + end + + def test_string_array_append + @array << 1 + @array << "2" + @array << :"3" + assert_equal ["1", "2", "3"], @array + end + + def test_string_array_concat + @array.concat([1, "2"]) + @array.concat([:"3"]) + assert_equal ["1", "2", "3"], @array + end + + def test_string_array_insert + @array.insert(0, 1) + @array.insert(1, "2") + @array.insert(2, :"3") + assert_equal ["1", "2", "3"], @array + end + + def test_string_array_push + @array.push(1) + @array.push("2") + @array.push(:"3") + assert_equal ["1", "2", "3"], @array + end + + def test_string_array_unshift + @array.unshift(:"3") + @array.unshift("2") + @array.unshift(1) + assert_equal ["1", "2", "3"], @array + end +end -- cgit v1.2.3 From 3fd9036fc554979e951422a79f0f77f061112bdc Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Thu, 24 Jul 2008 11:58:26 -0500 Subject: Added config.dependency_loading to enable or disable the dependency loader after initialization --- activesupport/lib/active_support/dependencies.rb | 78 ++++++++++++++++++------ activesupport/test/dependencies_test.rb | 12 ++++ 2 files changed, 71 insertions(+), 19 deletions(-) (limited to 'activesupport') diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index e3d4f3d7eb..a3f5f799a2 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -51,17 +51,28 @@ module ActiveSupport #:nodoc: module ModuleConstMissing #:nodoc: def self.included(base) #:nodoc: base.class_eval do - # Rename the original handler so we can chain it to the new one - alias_method :rails_original_const_missing, :const_missing + unless defined? const_missing_without_dependencies + alias_method_chain :const_missing, :dependencies + end + end + end - # Use const_missing to autoload associations so we don't have to - # require_association when using single-table inheritance. - def const_missing(class_id) - ActiveSupport::Dependencies.load_missing_constant self, class_id + def self.excluded(base) #:nodoc: + base.class_eval do + if defined? const_missing_without_dependencies + undef_method :const_missing + alias_method :const_missing, :const_missing_without_dependencies + undef_method :const_missing_without_dependencies end end end + # Use const_missing to autoload associations so we don't have to + # require_association when using single-table inheritance. + def const_missing_with_dependencies(class_id) + ActiveSupport::Dependencies.load_missing_constant self, class_id + end + def unloadable(const_desc = self) super(const_desc) end @@ -92,8 +103,38 @@ module ActiveSupport #:nodoc: # Object includes this module module Loadable #:nodoc: - def load(file, *extras) #:nodoc: - Dependencies.new_constants_in(Object) { super } + def self.included(base) #:nodoc: + base.class_eval do + unless defined? load_without_new_constant_marking + alias_method_chain :load, :new_constant_marking + end + end + end + + def self.excluded(base) #:nodoc: + base.class_eval do + if defined? load_without_new_constant_marking + undef_method :load + alias_method :load, :load_without_new_constant_marking + undef_method :load_without_new_constant_marking + end + end + end + + def require_or_load(file_name) + Dependencies.require_or_load(file_name) + end + + def require_dependency(file_name) + Dependencies.depend_on(file_name) + end + + def require_association(file_name) + Dependencies.associate_with(file_name) + end + + def load_with_new_constant_marking(file, *extras) #:nodoc: + Dependencies.new_constants_in(Object) { load_without_new_constant_marking(file, *extras) } rescue Exception => exception # errors from loading file exception.blame_file! file raise @@ -145,19 +186,18 @@ module ActiveSupport #:nodoc: end end - def inject! - Object.instance_eval do - define_method(:require_or_load) { |file_name| Dependencies.require_or_load(file_name) } unless Object.respond_to?(:require_or_load) - define_method(:require_dependency) { |file_name| Dependencies.depend_on(file_name) } unless Object.respond_to?(:require_dependency) - define_method(:require_association) { |file_name| Dependencies.associate_with(file_name) } unless Object.respond_to?(:require_association) - - alias_method :load_without_new_constant_marking, :load - include Loadable - end - + def hook! + Object.instance_eval { include Loadable } Module.instance_eval { include ModuleConstMissing } Class.instance_eval { include ClassConstMissing } Exception.instance_eval { include Blamable } + true + end + + def unhook! + ModuleConstMissing.excluded(Module) + Loadable.excluded(Object) + true end def load? @@ -560,4 +600,4 @@ module ActiveSupport #:nodoc: end end -ActiveSupport::Dependencies.inject! +ActiveSupport::Dependencies.hook! diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 038547a862..39c9c74c94 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -762,4 +762,16 @@ class DependenciesTest < Test::Unit::TestCase ensure ActiveSupport::Dependencies.load_once_paths = [] end + + def test_hook_called_multiple_times + assert_nothing_raised { ActiveSupport::Dependencies.hook! } + end + + def test_unhook + ActiveSupport::Dependencies.unhook! + assert !Module.new.respond_to?(:const_missing_without_dependencies) + assert !Module.new.respond_to?(:load_without_new_constant_marking) + ensure + ActiveSupport::Dependencies.hook! + end end -- cgit v1.2.3