From ef0ea782b1f5cf7b08e74ea3002a16c708f66645 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 31 May 2008 16:57:46 -0700 Subject: Added SQL escaping for :limit and :offset [#288 state:closed] (Aaron Bedra, Steven Bristol, Jonathan Wiess) --- .../abstract/database_statements.rb | 9 +++++++-- activerecord/test/cases/adapter_test.rb | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 16d405d3bd..5358491cde 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -106,11 +106,16 @@ module ActiveRecord # SELECT * FROM suppliers LIMIT 10 OFFSET 50 def add_limit_offset!(sql, options) if limit = options[:limit] - sql << " LIMIT #{limit}" + sql << " LIMIT #{sanitize_limit(limit)}" if offset = options[:offset] - sql << " OFFSET #{offset}" + sql << " OFFSET #{offset.to_i}" end end + sql + end + + def sanitize_limit(limit) + limit.to_s[/,/] ? limit.split(',').map{ |i| i.to_i }.join(',') : limit.to_i end # Appends a locking clause to an SQL statement. diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 91504af901..c77446f880 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -104,4 +104,24 @@ class AdapterTest < ActiveRecord::TestCase end end + def test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas + sql_inject = "1 select * from schema" + assert_equal " LIMIT 1", @connection.add_limit_offset!("", :limit=>sql_inject) + if current_adapter?(:MysqlAdapter) + assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) + else + assert_equal " LIMIT 1 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) + end + end + + def test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas + sql_inject = "1, 7 procedure help()" + if current_adapter?(:MysqlAdapter) + assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject) + assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) + else + assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject) + assert_equal " LIMIT 1,7 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) + end + end end -- cgit v1.2.3 From f6e921f9568d7f2e4807edf8728e6b0df8991816 Mon Sep 17 00:00:00 2001 From: "John D. Hume" Date: Wed, 28 May 2008 23:35:56 -0400 Subject: Substitute value into validates_format_of message Signed-off-by: Michael Koziarski --- activerecord/lib/active_record/validations.rb | 2 +- activerecord/test/cases/validations_test.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 0e150bddb2..c97aafb126 100755 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -692,7 +692,7 @@ module ActiveRecord raise(ArgumentError, "A regular expression must be supplied as the :with option of the configuration hash") unless configuration[:with].is_a?(Regexp) validates_each(attr_names, configuration) do |record, attr_name, value| - record.errors.add(attr_name, configuration[:message]) unless value.to_s =~ configuration[:with] + record.errors.add(attr_name, configuration[:message] % value) unless value.to_s =~ configuration[:with] end end diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index a4d9da4806..7b71647d25 100755 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -583,6 +583,12 @@ class ValidationsTest < ActiveRecord::TestCase assert_nil t.errors.on(:title) end + def test_validate_format_with_formatted_message + Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be %s") + t = Topic.create(:title => 'Invalid title') + assert_equal "can't be Invalid title", t.errors.on(:title) + end + def test_validates_inclusion_of Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) ) -- cgit v1.2.3 From ef21e013338461f33bf85f5cf6edd84b5ce9b6fe Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sat, 31 May 2008 16:39:32 -0700 Subject: Remove dead code, and the tests for it. --- actionpack/lib/action_view/compiled_templates.rb | 69 -------- .../test/template/compiled_templates_test.rb | 192 --------------------- 2 files changed, 261 deletions(-) delete mode 100644 actionpack/lib/action_view/compiled_templates.rb delete mode 100644 actionpack/test/template/compiled_templates_test.rb diff --git a/actionpack/lib/action_view/compiled_templates.rb b/actionpack/lib/action_view/compiled_templates.rb deleted file mode 100644 index 5a286432e3..0000000000 --- a/actionpack/lib/action_view/compiled_templates.rb +++ /dev/null @@ -1,69 +0,0 @@ -module ActionView - - # CompiledTemplates modules hold methods that have been compiled. - # Templates are compiled into these methods so that they do not need to be - # read and parsed for each request. - # - # Each template may be compiled into one or more methods. Each method accepts a given - # set of parameters which is used to implement local assigns passing. - # - # To use a compiled template module, create a new instance and include it into the class - # in which you want the template to be rendered. - class CompiledTemplates < Module - attr_reader :method_names - - def initialize - @method_names = Hash.new do |hash, key| - hash[key] = "__compiled_method_#{(hash.length + 1)}" - end - @mtimes = {} - end - - # Return the full key for the given identifier and argument names - def full_key(identifier, arg_names) - [identifier, arg_names] - end - - # Return the selector for this method or nil if it has not been compiled - def selector(identifier, arg_names) - key = full_key(identifier, arg_names) - method_names.key?(key) ? method_names[key] : nil - end - alias :compiled? :selector - - # Return the time at which the method for the given identifier and argument names was compiled. - def mtime(identifier, arg_names) - @mtimes[full_key(identifier, arg_names)] - end - - # Compile the provided source code for the given argument names and with the given initial line number. - # The identifier should be unique to this source. - # - # The file_name, if provided will appear in backtraces. If not provided, the file_name defaults - # to the identifier. - # - # This method will return the selector for the compiled version of this method. - def compile_source(identifier, arg_names, source, initial_line_number = 0, file_name = nil) - file_name ||= identifier - name = method_names[full_key(identifier, arg_names)] - arg_desc = arg_names.empty? ? '' : "(#{arg_names * ', '})" - fake_file_name = "#{file_name}#{arg_desc}" # Include the arguments for this version (for now) - - method_def = wrap_source(name, arg_names, source) - - begin - module_eval(method_def, fake_file_name, initial_line_number) - @mtimes[full_key(identifier, arg_names)] = Time.now - rescue Exception => e # errors from compiled source - e.blame_file! identifier - raise - end - name - end - - # Wrap the provided source in a def ... end block. - def wrap_source(name, arg_names, source) - "def #{name}(#{arg_names * ', '})\n#{source}\nend" - end - end -end diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb deleted file mode 100644 index 73e7ec1d76..0000000000 --- a/actionpack/test/template/compiled_templates_test.rb +++ /dev/null @@ -1,192 +0,0 @@ -require 'abstract_unit' -require 'action_view/helpers/date_helper' -require 'action_view/compiled_templates' - -class CompiledTemplateTests < Test::Unit::TestCase - def setup - @ct = ActionView::CompiledTemplates.new - @v = Class.new - @v.send :include, @ct - @a = './test_compile_template_a.rhtml' - @b = './test_compile_template_b.rhtml' - @s = './test_compile_template_link.rhtml' - end - def teardown - [@a, @b, @s].each do |f| - FileUtils.rm(f) if File.exist?(f) || File.symlink?(f) - end - end - attr_reader :ct, :v - - def test_name_allocation - hi_world = ct.method_names['hi world'] - hi_sexy = ct.method_names['hi sexy'] - wish_upon_a_star = ct.method_names['I love seeing decent error messages'] - - assert_equal hi_world, ct.method_names['hi world'] - assert_equal hi_sexy, ct.method_names['hi sexy'] - assert_equal wish_upon_a_star, ct.method_names['I love seeing decent error messages'] - assert_equal 3, [hi_world, hi_sexy, wish_upon_a_star].uniq.length - end - - def test_wrap_source - assert_equal( - "def aliased_assignment(value)\nself.value = value\nend", - @ct.wrap_source(:aliased_assignment, [:value], 'self.value = value') - ) - - assert_equal( - "def simple()\nnil\nend", - @ct.wrap_source(:simple, [], 'nil') - ) - end - - def test_compile_source_single_method - selector = ct.compile_source('doubling method', [:a], 'a + a') - assert_equal 2, @v.new.send(selector, 1) - assert_equal 4, @v.new.send(selector, 2) - assert_equal -4, @v.new.send(selector, -2) - assert_equal 0, @v.new.send(selector, 0) - selector - end - - def test_compile_source_two_method - sel1 = test_compile_source_single_method # compile the method in the other test - sel2 = ct.compile_source('doubling method', [:a, :b], 'a + b + a + b') - assert_not_equal sel1, sel2 - - assert_equal 2, @v.new.send(sel1, 1) - assert_equal 4, @v.new.send(sel1, 2) - - assert_equal 6, @v.new.send(sel2, 1, 2) - assert_equal 32, @v.new.send(sel2, 15, 1) - end - - def test_mtime - t1 = Time.now - - test_compile_source_single_method - mtime = ct.mtime('doubling method', [:a]) - - assert mtime < Time.now - assert mtime > t1 - end - - uses_mocha 'test_compile_time' do - - def test_compile_time - t = Time.now - - File.open(@a, "w"){|f| f.puts @a} - File.open(@b, "w"){|f| f.puts @b} - # windows doesn't support symlinks (even under cygwin) - windows = (RUBY_PLATFORM =~ /win32/) - `ln -s #{@a} #{@s}` unless windows - - v = ActionView::Base.new - v.base_path = '.' - v.cache_template_loading = false - - ta = ActionView::Template.new(v, @a, false, {}) - tb = ActionView::Template.new(v, @b, false, {}) - ts = ActionView::Template.new(v, @s, false, {}) - - @handler_class = ActionView::Template.handler_class_for_extension(:rhtml) - @handler = @handler_class.new(v) - - # All templates were created at t+1 - File::Stat.any_instance.expects(:mtime).times(windows ? 2 : 3).returns(t + 1.second) - - # private methods template_changed_since? and compile_template? - # should report true for all since they have not been compiled - assert @handler.send(:template_changed_since?, @a, t) - assert @handler.send(:template_changed_since?, @b, t) - assert @handler.send(:template_changed_since?, @s, t) unless windows - - assert @handler.send(:compile_template?, ta) - assert @handler.send(:compile_template?, tb) - assert @handler.send(:compile_template?, ts) unless windows - - # All templates are rendered at t+2 - Time.expects(:now).times(windows ? 2 : 3).returns(t + 2.seconds) - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - a_n = v.method_names[@a] - b_n = v.method_names[@b] - s_n = v.method_names[@s] unless windows - # all of the files have changed since last compile - assert @handler.compile_time[a_n] > t - assert @handler.compile_time[b_n] > t - assert @handler.compile_time[s_n] > t unless windows - - # private methods template_changed_since? and compile_template? - # should report false for all since none have changed since compile - File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 12).returns(t + 1.second) - assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) - assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) - assert !@handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows - assert !@handler.send(:compile_template?, ta) - assert !@handler.send(:compile_template?, tb) - assert !@handler.send(:compile_template?, ts) unless windows - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - # none of the files have changed since last compile - assert @handler.compile_time[a_n] < t + 3.seconds - assert @handler.compile_time[b_n] < t + 3.seconds - assert @handler.compile_time[s_n] < t + 3.seconds unless windows - - `rm #{@s}; ln -s #{@b} #{@s}` unless windows - # private methods template_changed_since? and compile_template? - # should report true for symlink since it has changed since compile - - # t + 3.seconds is for the symlink - File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 9).returns( - *(windows ? [ t + 1.second, t + 1.second ] : - [ t + 1.second, t + 1.second, t + 3.second ]) * 3) - assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) - assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) - assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows - assert !@handler.send(:compile_template?, ta) - assert !@handler.send(:compile_template?, tb) - assert @handler.send(:compile_template?, ts) unless windows - - # Only the symlink template gets rendered at t+3 - Time.stubs(:now).returns(t + 3.seconds) unless windows - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - # the symlink has changed since last compile - assert @handler.compile_time[a_n] < t + 3.seconds - assert @handler.compile_time[b_n] < t + 3.seconds - assert_equal @handler.compile_time[s_n], t + 3.seconds unless windows - - FileUtils.touch @b - # private methods template_changed_since? and compile_template? - # should report true for symlink and file at end of symlink - # since it has changed since last compile - # - # t+4 is for @b and also for the file that @s points to, which is @b - File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 12).returns( - *(windows ? [ t + 1.second, t + 4.seconds ] : - [ t + 1.second, t + 4.seconds, t + 3.second, t + 4.seconds ]) * 3) - assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) - assert @handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) - assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows - assert !@handler.send(:compile_template?, ta) - assert @handler.send(:compile_template?, tb) - assert @handler.send(:compile_template?, ts) unless windows - - Time.expects(:now).times(windows ? 1 : 2).returns(t + 5.seconds) - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - # the file at the end of the symlink has changed since last compile - # both the symlink and the file at the end of it should be recompiled - assert @handler.compile_time[a_n] < t + 5.seconds - assert_equal @handler.compile_time[b_n], t + 5.seconds - assert_equal @handler.compile_time[s_n], t + 5.seconds unless windows - end - end -end -- cgit v1.2.3 From f9db7695fe3c148c8d1077f1564e5b94d126b83b Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 31 May 2008 17:03:03 -0700 Subject: Making ready for release of 2.1 --- actionmailer/CHANGELOG | 2 +- actionmailer/Rakefile | 2 +- actionmailer/lib/action_mailer/version.rb | 4 ++-- actionpack/CHANGELOG | 4 ++-- actionpack/Rakefile | 2 +- actionpack/lib/action_pack/version.rb | 4 ++-- activerecord/CHANGELOG | 6 ++++-- activerecord/Rakefile | 2 +- activerecord/lib/active_record/version.rb | 4 ++-- activerecord/test/connections/native_mysql/connection.rb | 4 ++-- activeresource/CHANGELOG | 2 +- activeresource/Rakefile | 2 +- activeresource/lib/active_resource/version.rb | 4 ++-- activesupport/CHANGELOG | 4 ++-- activesupport/lib/active_support/version.rb | 4 ++-- railties/CHANGELOG | 8 +------- railties/Rakefile | 10 +++++----- railties/lib/rails/version.rb | 4 ++-- release.rb | 7 ++----- 19 files changed, 36 insertions(+), 43 deletions(-) diff --git a/actionmailer/CHANGELOG b/actionmailer/CHANGELOG index b2ce462f0c..bdae0d4d3d 100644 --- a/actionmailer/CHANGELOG +++ b/actionmailer/CHANGELOG @@ -1,4 +1,4 @@ -*2.1.0 RC1 (May 11th, 2008)* +*2.1.0 (May 31st, 2008)* * Fixed that a return-path header would be ignored #7572 [joost] diff --git a/actionmailer/Rakefile b/actionmailer/Rakefile index 22ed396417..c09526cbef 100755 --- a/actionmailer/Rakefile +++ b/actionmailer/Rakefile @@ -55,7 +55,7 @@ spec = Gem::Specification.new do |s| s.rubyforge_project = "actionmailer" s.homepage = "http://www.rubyonrails.org" - s.add_dependency('actionpack', '= 2.0.991' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.1.0' + PKG_BUILD) s.has_rdoc = true s.requirements << 'none' diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb index 88b9a3c200..c35b648baf 100644 --- a/actionmailer/lib/action_mailer/version.rb +++ b/actionmailer/lib/action_mailer/version.rb @@ -1,8 +1,8 @@ module ActionMailer module VERSION #:nodoc: MAJOR = 2 - MINOR = 0 - TINY = 991 + MINOR = 1 + TINY = 0 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 5c4bfbf3c9..cb684a925d 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,6 +1,6 @@ -* InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing] +*2.1.0 (May 31st, 2008)* -*2.1.0 RC1 (May 11th, 2008)* +* InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing] * Fixed that forgery protection can be used without session tracking (Peter Jones) [#139] diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 0147a5c1e8..b37f756c1e 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -76,7 +76,7 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' - s.add_dependency('activesupport', '= 2.0.991' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.1.0' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'action_controller' diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 70fc1ced8c..c67654d9a8 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -1,8 +1,8 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 - MINOR = 0 - TINY = 991 + MINOR = 1 + TINY = 0 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 78082665a4..f901e432d5 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,6 +1,8 @@ -* Add first/last methods to associations/named_scope. Resolved #226. [Ryan Bates] +*2.1.0 (May 31st, 2008)* -*2.1.0 RC1 (May 11th, 2008)* +* Added SQL escaping for :limit and :offset #288 [Aaron Bedra, Steven Bristol, Jonathan Wiess] + +* Added first/last methods to associations/named_scope. Resolved #226. [Ryan Bates] * Ensure hm:t preloading honours reflection options. Resolves #137. [Frederick Cheung] diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 043ab6d551..fc068b16c3 100755 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -171,7 +171,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.0.991' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.1.0' + PKG_BUILD) s.files.delete FIXTURES_ROOT + "/fixture_database.sqlite" s.files.delete FIXTURES_ROOT + "/fixture_database_2.sqlite" diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index 1463e84764..aaadef9979 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -1,8 +1,8 @@ module ActiveRecord module VERSION #:nodoc: MAJOR = 2 - MINOR = 0 - TINY = 991 + MINOR = 1 + TINY = 0 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activerecord/test/connections/native_mysql/connection.rb b/activerecord/test/connections/native_mysql/connection.rb index 1fab444e58..3ee031f384 100644 --- a/activerecord/test/connections/native_mysql/connection.rb +++ b/activerecord/test/connections/native_mysql/connection.rb @@ -12,13 +12,13 @@ ActiveRecord::Base.logger = RAILS_DEFAULT_LOGGER ActiveRecord::Base.configurations = { 'arunit' => { :adapter => 'mysql', - :username => 'rails', + :username => 'root', :encoding => 'utf8', :database => 'activerecord_unittest', }, 'arunit2' => { :adapter => 'mysql', - :username => 'rails', + :username => 'root', :database => 'activerecord_unittest2' } } diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index 9aa7d455a2..e124e40dd1 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,4 +1,4 @@ -*2.1.0 RC1 (May 11th, 2008)* +*2.1.0 (May 31st, 2008)* * Fixed response logging to use length instead of the entire thing (seangeo) [#27] diff --git a/activeresource/Rakefile b/activeresource/Rakefile index 75fe52aea8..9fd0ec4921 100644 --- a/activeresource/Rakefile +++ b/activeresource/Rakefile @@ -64,7 +64,7 @@ spec = Gem::Specification.new do |s| s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) } end - s.add_dependency('activesupport', '= 2.0.991' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.1.0' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'active_resource' diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb index 34fb05b703..88798ea1c1 100644 --- a/activeresource/lib/active_resource/version.rb +++ b/activeresource/lib/active_resource/version.rb @@ -1,8 +1,8 @@ module ActiveResource module VERSION #:nodoc: MAJOR = 2 - MINOR = 0 - TINY = 991 + MINOR = 1 + TINY = 0 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 7e408840c4..690bb987dc 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,3 +1,5 @@ +*2.1.0 (May 31st, 2008)* + * TimeZone#to_s shows offset as GMT instead of UTC, because GMT will be more familiar to end users (see time zone selects used by Windows OS, google.com and yahoo.com.) Reverts [8370] [Geoff Buesing] * Hash.from_xml: datetime xml types overflow to Ruby DateTime class when out of range of Time. Adding tests for utc offsets [Geoff Buesing] @@ -6,8 +8,6 @@ * Time#to_json: don't convert to utc before encoding. References #175 [Geoff Buesing] -*2.1.0 RC1 (May 11th, 2008)* - * Remove unused JSON::RESERVED_WORDS, JSON.valid_identifier? and JSON.reserved_word? methods. Resolves #164. [Cheah Chu Yeow] * Adding Date.current, which returns Time.zone.today if config.time_zone is set; otherwise returns Date.today [Geoff Buesing] diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index f3d141cf72..b346459a9b 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -1,8 +1,8 @@ module ActiveSupport module VERSION #:nodoc: MAJOR = 2 - MINOR = 0 - TINY = 991 + MINOR = 1 + TINY = 0 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 7f1c6ad747..fe517755ef 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,10 +1,4 @@ -*SVN* - -* Consolidate error messages for missing gems, and skip them when running rake gems:* tasks. [rick] - -* Use a system command to install gems, since GemRunner exits the ruby process. #210 [Tim Morgan] - -*2.1.0 RC1 (May 11th, 2008)* +*2.1.0 (May 31st, 2008)* * script/dbconsole fires up the command-line database client. #102 [Steve Purcell] diff --git a/railties/Rakefile b/railties/Rakefile index 45ba394299..a1d1095c37 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -304,11 +304,11 @@ spec = Gem::Specification.new do |s| EOF s.add_dependency('rake', '>= 0.8.1') - s.add_dependency('activesupport', '= 2.0.991' + PKG_BUILD) - s.add_dependency('activerecord', '= 2.0.991' + PKG_BUILD) - s.add_dependency('actionpack', '= 2.0.991' + PKG_BUILD) - s.add_dependency('actionmailer', '= 2.0.991' + PKG_BUILD) - s.add_dependency('activeresource', '= 2.0.991' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.1.0' + PKG_BUILD) + s.add_dependency('activerecord', '= 2.1.0' + PKG_BUILD) + s.add_dependency('actionpack', '= 2.1.0' + PKG_BUILD) + s.add_dependency('actionmailer', '= 2.1.0' + PKG_BUILD) + s.add_dependency('activeresource', '= 2.1.0' + PKG_BUILD) s.rdoc_options << '--exclude' << '.' s.has_rdoc = false diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb index fea63beea9..48d24da52e 100644 --- a/railties/lib/rails/version.rb +++ b/railties/lib/rails/version.rb @@ -1,8 +1,8 @@ module Rails module VERSION #:nodoc: MAJOR = 2 - MINOR = 0 - TINY = 991 + MINOR = 1 + TINY = 0 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/release.rb b/release.rb index 94d2219525..2e1e8610b1 100755 --- a/release.rb +++ b/release.rb @@ -4,7 +4,7 @@ VERSION = ARGV.first PACKAGES = %w(activesupport activerecord actionpack actionmailer activeresource) # Checkout source -`rm -rf release && svn export http://dev.rubyonrails.org/svn/rails/trunk release` +# `rm -rf release && svn export http://dev.rubyonrails.org/svn/rails/trunk release` # Create Rails packages `cd release/railties && rake template=jamis package` @@ -19,7 +19,4 @@ end # Upload rails tgz/zip `rubyforge add_release rails rails 'REL #{VERSION}' release/rails-#{VERSION}.tgz` -`rubyforge add_release rails rails 'REL #{VERSION}' release/rails-#{VERSION}.zip` - -# Create SVN tag -puts "Remember to create SVN tag" +`rubyforge add_release rails rails 'REL #{VERSION}' release/rails-#{VERSION}.zip` \ No newline at end of file -- cgit v1.2.3 From ea03b0885c110003496c1f99dc7d9d2f1534955b Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 31 May 2008 17:07:44 -0700 Subject: revert mysql test credential change --- activerecord/test/connections/native_mysql/connection.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/test/connections/native_mysql/connection.rb b/activerecord/test/connections/native_mysql/connection.rb index 3ee031f384..1fab444e58 100644 --- a/activerecord/test/connections/native_mysql/connection.rb +++ b/activerecord/test/connections/native_mysql/connection.rb @@ -12,13 +12,13 @@ ActiveRecord::Base.logger = RAILS_DEFAULT_LOGGER ActiveRecord::Base.configurations = { 'arunit' => { :adapter => 'mysql', - :username => 'root', + :username => 'rails', :encoding => 'utf8', :database => 'activerecord_unittest', }, 'arunit2' => { :adapter => 'mysql', - :username => 'root', + :username => 'rails', :database => 'activerecord_unittest2' } } -- cgit v1.2.3 From 72483c0d4c1e4ea794919974100acc2f255f6fd2 Mon Sep 17 00:00:00 2001 From: rick Date: Sat, 31 May 2008 17:13:11 -0700 Subject: Add ActiveRecord::Base.sti_name that checks ActiveRecord::Base#store_full_sti_class? and returns either the full or demodulized name. [rick] [#114 state:resolved] --- activerecord/CHANGELOG | 2 ++ .../associations/has_many_through_association.rb | 2 +- activerecord/lib/active_record/base.rb | 16 ++++++++++++---- activerecord/test/cases/inheritance_test.rb | 18 +++++++++++++++++- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 78082665a4..31e1c22f0d 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,3 +1,5 @@ +* Add ActiveRecord::Base.sti_name that checks ActiveRecord::Base#store_full_sti_class? and returns either the full or demodulized name. [rick] + * Add first/last methods to associations/named_scope. Resolved #226. [Ryan Bates] *2.1.0 RC1 (May 11th, 2008)* diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index ebcf462f2e..52ced36d16 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -237,7 +237,7 @@ module ActiveRecord end def build_sti_condition - "#{@reflection.through_reflection.quoted_table_name}.#{@reflection.through_reflection.klass.inheritance_column} = #{@reflection.klass.quote_value(@reflection.through_reflection.klass.name.demodulize)}" + "#{@reflection.through_reflection.quoted_table_name}.#{@reflection.through_reflection.klass.inheritance_column} = #{@reflection.klass.quote_value(@reflection.through_reflection.klass.sti_name)}" end alias_method :sql_conditions, :conditions diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d2d9bda6ba..92a24ecc5f 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1293,6 +1293,10 @@ module ActiveRecord #:nodoc: super end + def sti_name + store_full_sti_class ? name : name.demodulize + end + private def find_initial(options) options.update(:limit => 1) @@ -1452,7 +1456,11 @@ module ActiveRecord #:nodoc: # Nest the type name in the same module as this class. # Bar is "MyApp::Business::Bar" relative to MyApp::Business::Foo def type_name_with_module(type_name) - (/^::/ =~ type_name) ? type_name : "#{parent.name}::#{type_name}" + if store_full_sti_class + type_name + else + (/^::/ =~ type_name) ? type_name : "#{parent.name}::#{type_name}" + end end def construct_finder_sql(options) @@ -1571,8 +1579,8 @@ module ActiveRecord #:nodoc: def type_condition quoted_inheritance_column = connection.quote_column_name(inheritance_column) - type_condition = subclasses.inject("#{quoted_table_name}.#{quoted_inheritance_column} = '#{store_full_sti_class ? name : name.demodulize}' ") do |condition, subclass| - condition << "OR #{quoted_table_name}.#{quoted_inheritance_column} = '#{store_full_sti_class ? subclass.name : subclass.name.demodulize}' " + type_condition = subclasses.inject("#{quoted_table_name}.#{quoted_inheritance_column} = '#{sti_name}' ") do |condition, subclass| + condition << "OR #{quoted_table_name}.#{quoted_inheritance_column} = '#{subclass.sti_name}' " end " (#{type_condition}) " @@ -2508,7 +2516,7 @@ module ActiveRecord #:nodoc: # Message class in that example. def ensure_proper_type unless self.class.descends_from_active_record? - write_attribute(self.class.inheritance_column, store_full_sti_class ? self.class.name : self.class.name.demodulize) + write_attribute(self.class.inheritance_column, self.class.sti_name) end end diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index 27394924a1..f09b617e6d 100755 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -5,7 +5,23 @@ require 'models/subscriber' class InheritanceTest < ActiveRecord::TestCase fixtures :companies, :projects, :subscribers, :accounts - + + def test_class_with_store_full_sti_class_returns_full_name + old = ActiveRecord::Base.store_full_sti_class + ActiveRecord::Base.store_full_sti_class = true + assert_equal 'Namespaced::Company', Namespaced::Company.sti_name + ensure + ActiveRecord::Base.store_full_sti_class = old + end + + def test_class_without_store_full_sti_class_returns_demodulized_name + old = ActiveRecord::Base.store_full_sti_class + ActiveRecord::Base.store_full_sti_class = false + assert_equal 'Company', Namespaced::Company.sti_name + ensure + ActiveRecord::Base.store_full_sti_class = old + end + def test_should_store_demodulized_class_name_with_store_full_sti_class_option_disabled old = ActiveRecord::Base.store_full_sti_class ActiveRecord::Base.store_full_sti_class = false -- cgit v1.2.3 From 71528b1825ce5184b23d09f923cb72f4073ce8ed Mon Sep 17 00:00:00 2001 From: rick Date: Sat, 31 May 2008 17:22:16 -0700 Subject: Previously we only added the "lib" subdirectory to the load path when setting up gem dependencies for frozen gems. Now we add the "ext" subdirectory as well for those gems which have compiled C extensions as well. [Wincent Colaiuta] [#268 state:resolved] --- railties/lib/rails/gem_dependency.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb index 0e39f6fd0a..9f088a18dd 100644 --- a/railties/lib/rails/gem_dependency.rb +++ b/railties/lib/rails/gem_dependency.rb @@ -31,7 +31,9 @@ module Rails args << @requirement.to_s if @requirement gem *args else - $LOAD_PATH << File.join(unpacked_paths.first, 'lib') + $LOAD_PATH.unshift File.join(unpacked_paths.first, 'lib') + ext = File.join(unpacked_paths.first, 'ext') + $LOAD_PATH.unshift(ext) if File.exist?(ext) @frozen = true end @load_paths_added = true -- cgit v1.2.3 From 3282bf3b5016f0c9028cfff1012e8c31a13b40b7 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 1 Jun 2008 09:15:11 -0700 Subject: Added SQL escaping for :limit and :offset in MySQL [Jonathan Wiess] --- activerecord/CHANGELOG | 5 +++++ activerecord/lib/active_record/connection_adapters/mysql_adapter.rb | 3 ++- activerecord/test/cases/adapter_test.rb | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index 1c7c977141..a65771648e 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,3 +1,8 @@ +*Edge* + +* Added SQL escaping for :limit and :offset in MySQL [Jonathan Wiess] + + *2.1.0 (May 31st, 2008)* * Add ActiveRecord::Base.sti_name that checks ActiveRecord::Base#store_full_sti_class? and returns either the full or demodulized name. [rick] diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index f00a2c8950..653b45021d 100755 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -336,10 +336,11 @@ module ActiveRecord def add_limit_offset!(sql, options) #:nodoc: if limit = options[:limit] + limit = sanitize_limit(limit) unless offset = options[:offset] sql << " LIMIT #{limit}" else - sql << " LIMIT #{offset}, #{limit}" + sql << " LIMIT #{offset.to_i}, #{limit}" end end end diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index c77446f880..11f9870534 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -118,7 +118,7 @@ class AdapterTest < ActiveRecord::TestCase sql_inject = "1, 7 procedure help()" if current_adapter?(:MysqlAdapter) assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject) - assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) + assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=> '1 ; DROP TABLE USERS', :offset=>7) else assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject) assert_equal " LIMIT 1,7 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7) -- cgit v1.2.3 From 06cb20708be13fbf736447aa0e5e6dd7d64c8b5d Mon Sep 17 00:00:00 2001 From: Ezra Zygmuntowicz Date: Sun, 1 Jun 2008 11:25:11 -0700 Subject: Added Rack processor Signed-off-by: Joshua Peek --- actionpack/CHANGELOG | 3 + actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/dispatcher.rb | 8 +- actionpack/lib/action_controller/rack_process.rb | 321 +++++++++++++++++++++++ actionpack/test/controller/cgi_test.rb | 33 +++ actionpack/test/controller/rack_test.rb | 150 +++++++++++ 6 files changed, 515 insertions(+), 1 deletion(-) create mode 100644 actionpack/lib/action_controller/rack_process.rb create mode 100644 actionpack/test/controller/rack_test.rb diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index cb684a925d..9622029362 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,3 +1,6 @@ +* Added Rack processor [Ezra Zygmuntowicz, Josh Peek] + + *2.1.0 (May 31st, 2008)* * InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing] diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 810a5fb9b5..3c4a339d50 100755 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -53,6 +53,7 @@ require 'action_controller/streaming' require 'action_controller/session_management' require 'action_controller/http_authentication' require 'action_controller/components' +require 'action_controller/rack_process' require 'action_controller/record_identifier' require 'action_controller/request_forgery_protection' require 'action_controller/headers' diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index 6e1e7a261f..b40f1ba9be 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -96,7 +96,7 @@ module ActionController include ActiveSupport::Callbacks define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch - def initialize(output, request = nil, response = nil) + def initialize(output = $stdout, request = nil, response = nil) @output, @request, @response = output, request, response end @@ -123,6 +123,12 @@ module ActionController failsafe_rescue exception end + def call(env) + @request = RackRequest.new(env) + @response = RackResponse.new + dispatch + end + def reload_application # Run prepare callbacks before every request in development mode run_callbacks :prepare_dispatch diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb new file mode 100644 index 0000000000..16625519b6 --- /dev/null +++ b/actionpack/lib/action_controller/rack_process.rb @@ -0,0 +1,321 @@ +require 'action_controller/cgi_ext' +require 'action_controller/session/cookie_store' + +module ActionController #:nodoc: + class RackRequest < AbstractRequest #:nodoc: + attr_accessor :env, :session_options + + class SessionFixationAttempt < StandardError #:nodoc: + end + + DEFAULT_SESSION_OPTIONS = { + :database_manager => CGI::Session::CookieStore, # store data in cookie + :prefix => "ruby_sess.", # prefix session file names + :session_path => "/", # available to all paths in app + :session_key => "_session_id", + :cookie_only => true + } unless const_defined?(:DEFAULT_SESSION_OPTIONS) + + def initialize(env, session_options = DEFAULT_SESSION_OPTIONS) + @session_options = session_options + @env = env + @cgi = CGIWrapper.new(self) + super() + end + + # The request body is an IO input stream. If the RAW_POST_DATA environment + # variable is already set, wrap it in a StringIO. + def body + if raw_post = env['RAW_POST_DATA'] + StringIO.new(raw_post) + else + @env['rack.input'] + end + end + + def key?(key) + @env.key? key + end + + def query_parameters + @query_parameters ||= self.class.parse_query_parameters(query_string) + end + + def request_parameters + @request_parameters ||= parse_formatted_request_parameters + end + + def cookies + return {} unless @env["HTTP_COOKIE"] + + if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"] + @env["rack.request.cookie_hash"] + else + @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"] + # According to RFC 2109: + # If multiple cookies satisfy the criteria above, they are ordered in + # the Cookie header such that those with more specific Path attributes + # precede those with less specific. Ordering with respect to other + # attributes (e.g., Domain) is unspecified. + @env["rack.request.cookie_hash"] = + parse_query(@env["rack.request.cookie_string"], ';,').inject({}) { |h, (k,v)| + h[k] = Array === v ? v.first : v + h + } + end + end + + def host_with_port_without_standard_port_handling + if forwarded = @env["HTTP_X_FORWARDED_HOST"] + forwarded.split(/,\s?/).last + elsif http_host = @env['HTTP_HOST'] + http_host + elsif server_name = @env['SERVER_NAME'] + server_name + else + "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}" + end + end + + def host + host_with_port_without_standard_port_handling.sub(/:\d+$/, '') + end + + def port + if host_with_port_without_standard_port_handling =~ /:(\d+)$/ + $1.to_i + else + standard_port + end + end + + def remote_addr + @env['REMOTE_ADDR'] + end + + def session + unless defined?(@session) + if @session_options == false + @session = Hash.new + else + stale_session_check! do + if cookie_only? && query_parameters[session_options_with_string_keys['session_key']] + raise SessionFixationAttempt + end + case value = session_options_with_string_keys['new_session'] + when true + @session = new_session + when false + begin + @session = CGI::Session.new(@cgi, session_options_with_string_keys) + # CGI::Session raises ArgumentError if 'new_session' == false + # and no session cookie or query param is present. + rescue ArgumentError + @session = Hash.new + end + when nil + @session = CGI::Session.new(@cgi, session_options_with_string_keys) + else + raise ArgumentError, "Invalid new_session option: #{value}" + end + @session['__valid_session'] + end + end + end + @session + end + + def reset_session + @session.delete if defined?(@session) && @session.is_a?(CGI::Session) + @session = new_session + end + + private + # Delete an old session if it exists then create a new one. + def new_session + if @session_options == false + Hash.new + else + CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil + CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true)) + end + end + + def cookie_only? + session_options_with_string_keys['cookie_only'] + end + + def stale_session_check! + yield + rescue ArgumentError => argument_error + if argument_error.message =~ %r{undefined class/module ([\w:]*\w)} + begin + # Note that the regexp does not allow $1 to end with a ':' + $1.constantize + rescue LoadError, NameError => const_error + raise ActionController::SessionRestoreError, <<-end_msg +Session contains objects whose class definition isn\'t available. +Remember to require the classes for all objects kept in the session. +(Original exception: #{const_error.message} [#{const_error.class}]) +end_msg + end + + retry + else + raise + end + end + + def session_options_with_string_keys + @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys + end + + # From Rack::Utils + def parse_query(qs, d = '&;') + params = {} + (qs || '').split(/[#{d}] */n).inject(params) { |h,p| + k, v = unescape(p).split('=',2) + if cur = params[k] + if cur.class == Array + params[k] << v + else + params[k] = [cur, v] + end + else + params[k] = v + end + } + + return params + end + + def unescape(s) + s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ + [$1.delete('%')].pack('H*') + } + end + end + + class RackResponse < AbstractResponse #:nodoc: + attr_accessor :status + + def initialize + @writer = lambda { |x| @body << x } + @block = nil + super() + end + + def out(output = $stdout, &block) + @block = block + normalize_headers(@headers) + if [204, 304].include?(@status.to_i) + @headers.delete "Content-Type" + [status.to_i, @headers.to_hash, []] + else + [status.to_i, @headers.to_hash, self] + end + end + alias to_a out + + def each(&callback) + if @body.respond_to?(:call) + @writer = lambda { |x| callback.call(x) } + @body.call(self, self) + else + @body.each(&callback) + end + + @writer = callback + @block.call(self) if @block + end + + def write(str) + @writer.call str.to_s + str + end + + def close + @body.close if @body.respond_to?(:close) + end + + def empty? + @block == nil && @body.empty? + end + + private + def normalize_headers(options = "text/html") + if options.is_a?(String) + headers['Content-Type'] = options unless headers['Content-Type'] + else + headers['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length'] + + headers['Content-Type'] = options.delete('type') || "text/html" + headers['Content-Type'] += "; charset=" + options.delete('charset') if options['charset'] + + headers['Content-Language'] = options.delete('language') if options['language'] + headers['Expires'] = options.delete('expires') if options['expires'] + + @status = options.delete('Status') if options['Status'] + @status ||= 200 + # Convert 'cookie' header to 'Set-Cookie' headers. + # Because Set-Cookie header can appear more the once in the response body, + # we store it in a line break seperated string that will be translated to + # multiple Set-Cookie header by the handler. + if cookie = options.delete('cookie') + cookies = [] + + case cookie + when Array then cookie.each { |c| cookies << c.to_s } + when Hash then cookie.each { |_, c| cookies << c.to_s } + else cookies << cookie.to_s + end + + @output_cookies.each { |c| cookies << c.to_s } if @output_cookies + + headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].compact.join("\n") + end + + options.each { |k,v| headers[k] = v } + end + + "" + end + end + + class CGIWrapper < ::CGI + def initialize(request, *args) + @request = request + @args = *args + @input = request.body + + super *args + end + + def params + @params ||= @request.params + end + + def cookies + @request.cookies + end + + def query_string + @request.query_string + end + + # Used to wrap the normal args variable used inside CGI. + def args + @args + end + + # Used to wrap the normal env_table variable used inside CGI. + def env_table + @request.env + end + + # Used to wrap the normal stdinput variable used inside CGI. + def stdinput + @input + end + end +end diff --git a/actionpack/test/controller/cgi_test.rb b/actionpack/test/controller/cgi_test.rb index 87f72fda77..f0f3a4b826 100755 --- a/actionpack/test/controller/cgi_test.rb +++ b/actionpack/test/controller/cgi_test.rb @@ -114,3 +114,36 @@ class CgiRequestNeedsRewoundTest < BaseCgiTest assert_equal 0, request.body.pos end end + +class CgiResponseTest < BaseCgiTest + def setup + super + @fake_cgi.expects(:header).returns("HTTP/1.0 200 OK\nContent-Type: text/html\n") + @response = ActionController::CgiResponse.new(@fake_cgi) + @output = StringIO.new('') + end + + def test_simple_output + @response.body = "Hello, World!" + + @response.out(@output) + assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\nHello, World!", @output.string + end + + def test_head_request + @fake_cgi.env_table['REQUEST_METHOD'] = 'HEAD' + @response.body = "Hello, World!" + + @response.out(@output) + assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n", @output.string + end + + def test_streaming_block + @response.body = Proc.new do |response, output| + 5.times { |n| output.write(n) } + end + + @response.out(@output) + assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n01234", @output.string + end +end diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb new file mode 100644 index 0000000000..cd4151783e --- /dev/null +++ b/actionpack/test/controller/rack_test.rb @@ -0,0 +1,150 @@ +require 'abstract_unit' +require 'action_controller/rack_process' + +class BaseRackTest < Test::Unit::TestCase + def setup + @env = {"HTTP_MAX_FORWARDS"=>"10", "SERVER_NAME"=>"glu.ttono.us:8007", "FCGI_ROLE"=>"RESPONDER", "HTTP_X_FORWARDED_HOST"=>"glu.ttono.us", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1", "PATH_INFO"=>"", "HTTP_ACCEPT_LANGUAGE"=>"en", "HTTP_HOST"=>"glu.ttono.us:8007", "SERVER_PROTOCOL"=>"HTTP/1.1", "REDIRECT_URI"=>"/dispatch.fcgi", "SCRIPT_NAME"=>"/dispatch.fcgi", "SERVER_ADDR"=>"207.7.108.53", "REMOTE_ADDR"=>"207.7.108.53", "SERVER_SOFTWARE"=>"lighttpd/1.4.5", "HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", "HTTP_X_FORWARDED_SERVER"=>"glu.ttono.us", "REQUEST_URI"=>"/admin", "DOCUMENT_ROOT"=>"/home/kevinc/sites/typo/public", "SERVER_PORT"=>"8007", "QUERY_STRING"=>"", "REMOTE_PORT"=>"63137", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_X_FORWARDED_FOR"=>"65.88.180.234", "HTTP_ACCEPT"=>"*/*", "SCRIPT_FILENAME"=>"/home/kevinc/sites/typo/public/dispatch.fcgi", "REDIRECT_STATUS"=>"200", "REQUEST_METHOD"=>"GET"} + # some Nokia phone browsers omit the space after the semicolon separator. + # some developers have grown accustomed to using comma in cookie values. + @alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"} + @request = ActionController::RackRequest.new(@env) + end + + def default_test; end +end + + +class RackRequestTest < BaseRackTest + def test_proxy_request + assert_equal 'glu.ttono.us', @request.host_with_port + end + + def test_http_host + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "rubyonrails.org:8080" + assert_equal "rubyonrails.org:8080", @request.host_with_port + + @env['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org" + assert_equal "www.secondhost.org", @request.host + end + + def test_http_host_with_default_port_overrides_server_port + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "rubyonrails.org" + assert_equal "rubyonrails.org", @request.host_with_port + end + + def test_host_with_port_defaults_to_server_name_if_no_host_headers + @env.delete "HTTP_X_FORWARDED_HOST" + @env.delete "HTTP_HOST" + assert_equal "glu.ttono.us:8007", @request.host_with_port + end + + def test_host_with_port_falls_back_to_server_addr_if_necessary + @env.delete "HTTP_X_FORWARDED_HOST" + @env.delete "HTTP_HOST" + @env.delete "SERVER_NAME" + assert_equal "207.7.108.53:8007", @request.host_with_port + end + + def test_host_with_port_if_http_standard_port_is_specified + @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80" + assert_equal "glu.ttono.us", @request.host_with_port + end + + def test_host_with_port_if_https_standard_port_is_specified + @env['HTTP_X_FORWARDED_PROTO'] = "https" + @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443" + assert_equal "glu.ttono.us", @request.host_with_port + end + + def test_host_if_ipv6_reference + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]" + assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host + end + + def test_host_if_ipv6_reference_with_port + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008" + assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host + end + + def test_cookie_syntax_resilience + cookies = CGI::Cookie::parse(@env["HTTP_COOKIE"]); + assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"], cookies.inspect + assert_equal ["yes"], cookies["is_admin"], cookies.inspect + + alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]); + assert_equal ["c84ace847,96670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect + assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect + end +end + + +class RackRequestParamsParsingTest < BaseRackTest + def test_doesnt_break_when_content_type_has_charset + data = 'flamenco=love' + @request.env['CONTENT_LENGTH'] = data.length + @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' + @request.env['RAW_POST_DATA'] = data + assert_equal({"flamenco"=> "love"}, @request.request_parameters) + end + + def test_doesnt_interpret_request_uri_as_query_string_when_missing + @request.env['REQUEST_URI'] = 'foo' + assert_equal({}, @request.query_parameters) + end +end + + +class RackRequestNeedsRewoundTest < BaseRackTest + def test_body_should_be_rewound + data = 'foo' + @env['rack.input'] = StringIO.new(data) + @env['CONTENT_LENGTH'] = data.length + @env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' + + # Read the request body by parsing params. + request = ActionController::RackRequest.new(@env) + request.request_parameters + + # Should have rewound the body. + assert_equal 0, request.body.pos + end +end + + +class RackResponseTest < BaseRackTest + def setup + super + @response = ActionController::RackResponse.new + @output = StringIO.new('') + end + + def test_simple_output + @response.body = "Hello, World!" + + status, headers, body = @response.out(@output) + assert_equal 200, status + assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers) + + parts = [] + body.each { |part| parts << part } + assert_equal ["Hello, World!"], parts + end + + def test_streaming_block + @response.body = Proc.new do |response, output| + 5.times { |n| output.write(n) } + end + + status, headers, body = @response.out(@output) + assert_equal 200, status + assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers) + + parts = [] + body.each { |part| parts << part } + assert_equal ["0", "1", "2", "3", "4"], parts + end +end -- cgit v1.2.3 From a980eb8c7734f14109d8c2a02a88dafdf682e0dc Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Sun, 1 Jun 2008 13:18:31 -0700 Subject: Added Rack middleware to handle static files. --- railties/lib/initializer.rb | 1 + railties/lib/rails/rack.rb | 5 +++++ railties/lib/rails/rack/static.rb | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 railties/lib/rails/rack.rb create mode 100644 railties/lib/rails/rack/static.rb diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index ca63fa734b..bdadfeea8f 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -8,6 +8,7 @@ require 'rails/version' require 'rails/plugin/locator' require 'rails/plugin/loader' require 'rails/gem_dependency' +require 'rails/rack' RAILS_ENV = (ENV['RAILS_ENV'] || 'development').dup unless defined?(RAILS_ENV) diff --git a/railties/lib/rails/rack.rb b/railties/lib/rails/rack.rb new file mode 100644 index 0000000000..abcd0741bf --- /dev/null +++ b/railties/lib/rails/rack.rb @@ -0,0 +1,5 @@ +module Rails + module Rack + autoload :Static, "rails/rack/static" + end +end diff --git a/railties/lib/rails/rack/static.rb b/railties/lib/rails/rack/static.rb new file mode 100644 index 0000000000..45eb0e5921 --- /dev/null +++ b/railties/lib/rails/rack/static.rb @@ -0,0 +1,35 @@ +module Rails + module Rack + class Static + FILE_METHODS = %w(GET HEAD).freeze + + def initialize(app) + @app = app + @file_server = ::Rack::File.new(File.join(RAILS_ROOT, "public")) + end + + def call(env) + path = env['PATH_INFO'].chomp('/') + method = env['REQUEST_METHOD'] + cached_path = (path.empty? ? 'index' : path) + ::ActionController::Base.page_cache_extension + + if FILE_METHODS.include?(method) + if file_exist?(path) + return @file_server.call(env) + elsif file_exist?(cached_path) + env['PATH_INFO'] = cached_path + return @file_server.call(env) + end + end + + @app.call(env) + end + + private + def file_exist?(path) + full_path = File.join(@file_server.root, ::Rack::Utils.unescape(path)) + File.file?(full_path) && File.readable?(full_path) + end + end + end +end -- cgit v1.2.3 From 4210d85a3f3ce4e980f473e9ed2becb84b58363b Mon Sep 17 00:00:00 2001 From: Jonathan Viney Date: Mon, 2 Jun 2008 15:00:15 +1200 Subject: Ensure Associations#sum returns 0 when no rows are returned. [#295 state:resolved] Signed-off-by: Pratik Naik --- activerecord/lib/active_record/calculations.rb | 6 +++--- activerecord/test/cases/calculations_test.rb | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 10e8330d1c..889b7845fb 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -71,7 +71,7 @@ module ActiveRecord # # Person.sum('age') def sum(column_name, options = {}) - calculate(:sum, column_name, options) || 0 + calculate(:sum, column_name, options) end # This calculates aggregate values in the given column. Methods for count, sum, average, minimum, and maximum have been added as shortcuts. @@ -265,8 +265,8 @@ module ActiveRecord def type_cast_calculated_value(value, column, operation = nil) operation = operation.to_s.downcase case operation - when 'count' then value.to_i - when 'avg' then value && value.to_f + when 'count', 'sum' then value.to_i + when 'avg' then value && value.to_f else column ? column.type_cast(value) : value end end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index a87fc26905..5cf655f7b7 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -99,6 +99,7 @@ class CalculationsTest < ActiveRecord::TestCase def test_should_return_zero_if_sum_conditions_return_nothing assert_equal 0, Account.sum(:credit_limit, :conditions => '1 = 2') + assert_equal 0, companies(:rails_core).companies.sum(:id, :conditions => '1 = 2') end def test_should_group_by_summed_field_with_conditions @@ -266,6 +267,6 @@ class CalculationsTest < ActiveRecord::TestCase end def test_should_sum_expression - assert_equal "636", Account.sum("2 * credit_limit") + assert_equal 636, Account.sum("2 * credit_limit") end end -- cgit v1.2.3 From 14a65cd982161364028da312f415c1ebe0bcd5ac Mon Sep 17 00:00:00 2001 From: Marcos Tapajos Date: Mon, 2 Jun 2008 09:47:53 -0500 Subject: Fixed changelog merge. Signed-off-by: Joshua Peek --- activesupport/CHANGELOG | 1 - 1 file changed, 1 deletion(-) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 690bb987dc..15c25debf4 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -166,7 +166,6 @@ * Introduce ActiveSupport::TimeWithZone, for wrapping Time instances with a TimeZone. Introduce instance methods to Time for creating TimeWithZone instances, and class methods for managing a global time zone. [Geoff Buesing] ->>>>>>> .r8815 * Replace non-dst-aware TimeZone class with dst-aware class from tzinfo_timezone plugin. TimeZone#adjust and #unadjust are no longer available; tzinfo gem must now be present in order to perform time zone calculations, via #local_to_utc and #utc_to_local methods. [Geoff Buesing] * Extract ActiveSupport::Callbacks from Active Record, test case setup and teardown, and ActionController::Dispatcher. #10727 [Josh Peek] -- cgit v1.2.3 From 185fe2e9cce737d69d3b47a656f3651ce152c0c1 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 2 Jun 2008 09:54:36 -0500 Subject: In 9c4f003, gem installation quotes versions. Do the same for unpack and update tests to reflect the change. --- railties/lib/rails/gem_dependency.rb | 16 ++++++++-------- railties/test/gem_dependency_test.rb | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/railties/lib/rails/gem_dependency.rb b/railties/lib/rails/gem_dependency.rb index 9f088a18dd..30bdf416fc 100644 --- a/railties/lib/rails/gem_dependency.rb +++ b/railties/lib/rails/gem_dependency.rb @@ -39,7 +39,7 @@ module Rails @load_paths_added = true rescue Gem::LoadError end - + def dependencies all_dependencies = specification.dependencies.map do |dependency| GemDependency.new(dependency.name, :requirement => dependency.version_requirements) @@ -47,7 +47,7 @@ module Rails all_dependencies += all_dependencies.map(&:dependencies).flatten all_dependencies.uniq end - + def gem_dir(base_directory) File.join(base_directory, specification.full_name) end @@ -78,13 +78,13 @@ module Rails puts cmd puts %x(#{cmd}) end - + def unpack_to(directory) FileUtils.mkdir_p directory Dir.chdir directory do Gem::GemRunner.new.run(unpack_command) end - + # copy the gem's specification into GEMDIR/.specification so that # we can access information about the gem on deployment systems # without having the gem installed @@ -103,7 +103,7 @@ private ################################################################### def specification @spec ||= Gem.source_index.search(Gem::Dependency.new(@name, @requirement)).sort_by { |s| s.version }.last end - + def gem_command RUBY_PLATFORM =~ /win32/ ? 'gem.bat' : 'gem' end @@ -114,11 +114,11 @@ private ################################################################### cmd << "--source" << @source if @source cmd end - + def unpack_command cmd = %w(unpack) << @name - cmd << "--version" << "#{@requirement.to_s}" if @requirement + cmd << "--version" << %("#{@requirement.to_s}") if @requirement cmd end end -end \ No newline at end of file +end diff --git a/railties/test/gem_dependency_test.rb b/railties/test/gem_dependency_test.rb index 3ae0189327..b5946aa7b8 100644 --- a/railties/test/gem_dependency_test.rb +++ b/railties/test/gem_dependency_test.rb @@ -10,13 +10,13 @@ uses_mocha "Plugin Tests" do @gem = Rails::GemDependency.new "hpricot" @gem_with_source = Rails::GemDependency.new "hpricot", :source => "http://code.whytheluckystiff.net" @gem_with_version = Rails::GemDependency.new "hpricot", :version => "= 0.6" - @gem_with_lib = Rails::GemDependency.new "aws-s3", :lib => "aws/s3" + @gem_with_lib = Rails::GemDependency.new "aws-s3", :lib => "aws/s3" end def test_configuration_adds_gem_dependency config = Rails::Configuration.new config.gem "aws-s3", :lib => "aws/s3", :version => "0.4.0" - assert_equal [["install", "aws-s3", "--version", "= 0.4.0"]], config.gems.collect(&:install_command) + assert_equal [["install", "aws-s3", "--version", '"= 0.4.0"']], config.gems.collect(&:install_command) end def test_gem_creates_install_command @@ -28,7 +28,7 @@ uses_mocha "Plugin Tests" do end def test_gem_with_version_creates_install_command - assert_equal ["install", "hpricot", "--version", "= 0.6"], @gem_with_version.install_command + assert_equal ["install", "hpricot", "--version", '"= 0.6"'], @gem_with_version.install_command end def test_gem_creates_unpack_command @@ -36,26 +36,26 @@ uses_mocha "Plugin Tests" do end def test_gem_with_version_unpack_install_command - assert_equal ["unpack", "hpricot", "--version", "= 0.6"], @gem_with_version.unpack_command + assert_equal ["unpack", "hpricot", "--version", '"= 0.6"'], @gem_with_version.unpack_command end def test_gem_adds_load_paths @gem.expects(:gem).with(@gem.name) @gem.add_load_paths end - + def test_gem_with_version_adds_load_paths @gem_with_version.expects(:gem).with(@gem_with_version.name, @gem_with_version.requirement.to_s) @gem_with_version.add_load_paths end - + def test_gem_loading @gem.expects(:gem).with(@gem.name) @gem.expects(:require).with(@gem.name) @gem.add_load_paths @gem.load end - + def test_gem_with_lib_loading @gem_with_lib.expects(:gem).with(@gem_with_lib.name) @gem_with_lib.expects(:require).with(@gem_with_lib.lib) @@ -63,4 +63,4 @@ uses_mocha "Plugin Tests" do @gem_with_lib.load end end -end \ No newline at end of file +end -- cgit v1.2.3 From 714d42d1a6140ac4f6fc478bf1766d2b0aedb251 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 2 Jun 2008 10:40:01 -0500 Subject: Fixed initializer tests by stubbing out gems dependencies check. --- railties/lib/initializer.rb | 11 +++++++---- railties/test/initializer_test.rb | 34 ++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index bdadfeea8f..9e6e02e8e0 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -79,7 +79,10 @@ module Rails # The set of loaded plugins. attr_reader :loaded_plugins - + + # Whether or not all the gem dependencies have been met + attr_reader :gems_dependencies_loaded + # Runs the initializer. By default, this will invoke the #process method, # which simply executes all of the initialization routines. Alternately, # you can specify explicitly which initialization routine you want: @@ -307,7 +310,7 @@ module Rails end def load_observers - if @gems_dependencies_loaded && configuration.frameworks.include?(:active_record) + if gems_dependencies_loaded && configuration.frameworks.include?(:active_record) ActiveRecord::Base.instantiate_observers end end @@ -463,7 +466,7 @@ module Rails # Fires the user-supplied after_initialize block (Configuration#after_initialize) def after_initialize - if @gems_dependencies_loaded + if gems_dependencies_loaded configuration.after_initialize_blocks.each do |block| block.call end @@ -471,7 +474,7 @@ module Rails end def load_application_initializers - if @gems_dependencies_loaded + if gems_dependencies_loaded Dir["#{configuration.root_path}/config/initializers/**/*.rb"].sort.each do |initializer| load(initializer) end diff --git a/railties/test/initializer_test.rb b/railties/test/initializer_test.rb index 0df0164ca6..efce4f292d 100644 --- a/railties/test/initializer_test.rb +++ b/railties/test/initializer_test.rb @@ -38,27 +38,28 @@ class Initializer_after_initialize_with_blocks_environment_Test < Test::Unit::Te end config.after_initialize do $test_after_initialize_block2 = "congratulations" - end + end assert_nil $test_after_initialize_block1 - assert_nil $test_after_initialize_block2 + assert_nil $test_after_initialize_block2 + Rails::Initializer.any_instance.expects(:gems_dependencies_loaded).returns(true) Rails::Initializer.run(:after_initialize, config) end - + def teardown $test_after_initialize_block1 = nil - $test_after_initialize_block2 = nil + $test_after_initialize_block2 = nil end def test_should_have_called_the_first_after_initialize_block assert_equal "success", $test_after_initialize_block1 end - + def test_should_have_called_the_second_after_initialize_block assert_equal "congratulations", $test_after_initialize_block2 end end - + class Initializer_after_initialize_with_no_block_environment_Test < Test::Unit::TestCase def setup @@ -69,15 +70,16 @@ class Initializer_after_initialize_with_no_block_environment_Test < Test::Unit:: config.after_initialize # don't pass a block, this is what we're testing! config.after_initialize do $test_after_initialize_block2 = "congratulations" - end + end assert_nil $test_after_initialize_block1 + Rails::Initializer.any_instance.expects(:gems_dependencies_loaded).returns(true) Rails::Initializer.run(:after_initialize, config) end def teardown $test_after_initialize_block1 = nil - $test_after_initialize_block2 = nil + $test_after_initialize_block2 = nil end def test_should_have_called_the_first_after_initialize_block @@ -95,7 +97,7 @@ uses_mocha 'framework paths' do def setup @config = Rails::Configuration.new @config.frameworks.clear - + File.stubs(:directory?).returns(true) @config.stubs(:framework_root_path).returns('') end @@ -112,7 +114,7 @@ uses_mocha 'framework paths' do def test_actioncontroller_or_actionview_add_actionpack @config.frameworks << :action_controller assert_framework_path '/actionpack/lib' - + @config.frameworks = [:action_view] assert_framework_path '/actionpack/lib' end @@ -204,22 +206,22 @@ uses_mocha "Initializer plugin loading tests" do load_plugins! end end - + def test_should_ensure_all_loaded_plugins_load_paths_are_added_to_the_load_path only_load_the_following_plugins! [:stubby, :acts_as_chunky_bacon] @initializer.add_plugin_load_paths - + assert $LOAD_PATH.include?(File.join(plugin_fixture_path('default/stubby'), 'lib')) assert $LOAD_PATH.include?(File.join(plugin_fixture_path('default/acts/acts_as_chunky_bacon'), 'lib')) end - + private - + def load_plugins! @initializer.add_plugin_load_paths @initializer.load_plugins end end - -end + +end -- cgit v1.2.3 From 952ec79bec313e0001adfc8c86f7970448d32db9 Mon Sep 17 00:00:00 2001 From: Cheah Chu Yeow Date: Mon, 2 Jun 2008 01:42:38 -0700 Subject: Faster Hash#slice that doesn't use Enumerable#include?. Signed-off-by: Jeremy Kemper --- activesupport/lib/active_support/core_ext/hash/slice.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index 6fe5e05330..44b6964c69 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -15,7 +15,9 @@ module ActiveSupport #:nodoc: # Returns a new hash with only the given keys. def slice(*keys) allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys) - reject { |key,| !allowed.include?(key) } + hash = {} + allowed.each { |k| hash[k] = self[k] } + hash end # Replaces the hash with only the given keys. -- cgit v1.2.3 From bd75a722a2e9f979bfd1b1d89442e4dd6f3e3af7 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 2 Jun 2008 20:40:25 +0100 Subject: Ensure AR#sum result is typecasted properly --- activerecord/lib/active_record/calculations.rb | 5 +++-- activerecord/test/cases/calculations_test.rb | 5 +++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 889b7845fb..caa8c539d5 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -265,8 +265,9 @@ module ActiveRecord def type_cast_calculated_value(value, column, operation = nil) operation = operation.to_s.downcase case operation - when 'count', 'sum' then value.to_i - when 'avg' then value && value.to_f + when 'count' then value.to_i + when 'sum' then value =~ /\./ ? value.to_f : value.to_i + when 'avg' then value && value.to_f else column ? column.type_cast(value) : value end end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 5cf655f7b7..aefb13ea9a 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -102,6 +102,11 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal 0, companies(:rails_core).companies.sum(:id, :conditions => '1 = 2') end + def test_sum_should_return_valid_values_for_decimals + NumericData.create(:bank_balance => 19.83) + assert_equal 19.83, NumericData.sum(:bank_balance) + end + def test_should_group_by_summed_field_with_conditions c = Account.sum(:credit_limit, :conditions => 'firm_id > 1', :group => :firm_id) -- cgit v1.2.3 From a855ab89b7aae7cb451611301a1e83495f647f8a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 2 Jun 2008 17:42:10 -0500 Subject: Added a test for Gzip --- activesupport/test/gzip_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 activesupport/test/gzip_test.rb diff --git a/activesupport/test/gzip_test.rb b/activesupport/test/gzip_test.rb new file mode 100644 index 0000000000..2a24c0bd0d --- /dev/null +++ b/activesupport/test/gzip_test.rb @@ -0,0 +1,7 @@ +require 'abstract_unit' + +class GzipTest < Test::Unit::TestCase + def test_compress_should_decompress_to_the_same_value + assert_equal "Hello World", ActiveSupport::Gzip.decompress(ActiveSupport::Gzip.compress("Hello World")) + end +end \ No newline at end of file -- cgit v1.2.3 From 4b4aa8f6e08ba2aa2ddce56f1d5b631a78eeef6c Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 2 Jun 2008 18:43:08 -0500 Subject: AR can be disabled, new_rails_defaults.rb should check [#303 state:resolved] (Jesper Hvirring Henriksen) --- railties/configs/initializers/new_rails_defaults.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/railties/configs/initializers/new_rails_defaults.rb b/railties/configs/initializers/new_rails_defaults.rb index 5e60c0ade4..78e0117cc4 100644 --- a/railties/configs/initializers/new_rails_defaults.rb +++ b/railties/configs/initializers/new_rails_defaults.rb @@ -1,11 +1,13 @@ # These settings change the behavior of Rails 2 apps and will be defaults # for Rails 3. You can remove this initializer when Rails 3 is released. -# Include Active Record class name as root for JSON serialized output. -ActiveRecord::Base.include_root_in_json = true +if defined?(ActiveRecord) + # Include Active Record class name as root for JSON serialized output. + ActiveRecord::Base.include_root_in_json = true -# Store the full class name (including module namespace) in STI type column. -ActiveRecord::Base.store_full_sti_class = true + # Store the full class name (including module namespace) in STI type column. + ActiveRecord::Base.store_full_sti_class = true +end # Use ISO 8601 format for JSON serialized times and dates. ActiveSupport.use_standard_json_time_format = true -- cgit v1.2.3 From 29641ff05ad1beb659582a3c4347c1395f940285 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 2 Jun 2008 19:00:25 -0500 Subject: Fixed the brokeness from 952ec79bec313e0001adfc8c86f7970448d32db9 --- activesupport/lib/active_support/core_ext/hash/slice.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index 44b6964c69..1b2c8f63e3 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -16,7 +16,7 @@ module ActiveSupport #:nodoc: def slice(*keys) allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys) hash = {} - allowed.each { |k| hash[k] = self[k] } + allowed.each { |k| hash[k] = self[k] if has_key?(k) } hash end -- cgit v1.2.3 From 92050f6c6f586b2a73aeb61da4f41b9822bbcf6d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 2 Jun 2008 21:02:26 -0500 Subject: Ensure Rack processor reads CGI output_cookies for the session cookie. --- actionpack/lib/action_controller/dispatcher.rb | 2 +- actionpack/lib/action_controller/rack_process.rb | 10 +++-- actionpack/test/controller/rack_test.rb | 56 ++++++++++++++++++++++-- 3 files changed, 60 insertions(+), 8 deletions(-) diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index b40f1ba9be..7162fb8b1f 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -125,7 +125,7 @@ module ActionController def call(env) @request = RackRequest.new(env) - @response = RackResponse.new + @response = RackResponse.new(@request) dispatch end diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb index 16625519b6..d5fb78c44d 100644 --- a/actionpack/lib/action_controller/rack_process.rb +++ b/actionpack/lib/action_controller/rack_process.rb @@ -4,6 +4,7 @@ require 'action_controller/session/cookie_store' module ActionController #:nodoc: class RackRequest < AbstractRequest #:nodoc: attr_accessor :env, :session_options + attr_reader :cgi class SessionFixationAttempt < StandardError #:nodoc: end @@ -199,7 +200,8 @@ end_msg class RackResponse < AbstractResponse #:nodoc: attr_accessor :status - def initialize + def initialize(request) + @request = request @writer = lambda { |x| @body << x } @block = nil super() @@ -270,9 +272,9 @@ end_msg else cookies << cookie.to_s end - @output_cookies.each { |c| cookies << c.to_s } if @output_cookies + @request.cgi.output_cookies.each { |c| cookies << c.to_s } if @request.cgi.output_cookies - headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].compact.join("\n") + headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].flatten.compact end options.each { |k,v| headers[k] = v } @@ -283,6 +285,8 @@ end_msg end class CGIWrapper < ::CGI + attr_reader :output_cookies + def initialize(request, *args) @request = request @args = *args diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb index cd4151783e..026b0195d1 100644 --- a/actionpack/test/controller/rack_test.rb +++ b/actionpack/test/controller/rack_test.rb @@ -3,7 +3,36 @@ require 'action_controller/rack_process' class BaseRackTest < Test::Unit::TestCase def setup - @env = {"HTTP_MAX_FORWARDS"=>"10", "SERVER_NAME"=>"glu.ttono.us:8007", "FCGI_ROLE"=>"RESPONDER", "HTTP_X_FORWARDED_HOST"=>"glu.ttono.us", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1", "PATH_INFO"=>"", "HTTP_ACCEPT_LANGUAGE"=>"en", "HTTP_HOST"=>"glu.ttono.us:8007", "SERVER_PROTOCOL"=>"HTTP/1.1", "REDIRECT_URI"=>"/dispatch.fcgi", "SCRIPT_NAME"=>"/dispatch.fcgi", "SERVER_ADDR"=>"207.7.108.53", "REMOTE_ADDR"=>"207.7.108.53", "SERVER_SOFTWARE"=>"lighttpd/1.4.5", "HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", "HTTP_X_FORWARDED_SERVER"=>"glu.ttono.us", "REQUEST_URI"=>"/admin", "DOCUMENT_ROOT"=>"/home/kevinc/sites/typo/public", "SERVER_PORT"=>"8007", "QUERY_STRING"=>"", "REMOTE_PORT"=>"63137", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_X_FORWARDED_FOR"=>"65.88.180.234", "HTTP_ACCEPT"=>"*/*", "SCRIPT_FILENAME"=>"/home/kevinc/sites/typo/public/dispatch.fcgi", "REDIRECT_STATUS"=>"200", "REQUEST_METHOD"=>"GET"} + @env = { + "HTTP_MAX_FORWARDS" => "10", + "SERVER_NAME" => "glu.ttono.us:8007", + "FCGI_ROLE" => "RESPONDER", + "HTTP_X_FORWARDED_HOST" => "glu.ttono.us", + "HTTP_ACCEPT_ENCODING" => "gzip, deflate", + "HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", + "PATH_INFO" => "", + "HTTP_ACCEPT_LANGUAGE" => "en", + "HTTP_HOST" => "glu.ttono.us:8007", + "SERVER_PROTOCOL" => "HTTP/1.1", + "REDIRECT_URI" => "/dispatch.fcgi", + "SCRIPT_NAME" => "/dispatch.fcgi", + "SERVER_ADDR" => "207.7.108.53", + "REMOTE_ADDR" => "207.7.108.53", + "SERVER_SOFTWARE" => "lighttpd/1.4.5", + "HTTP_COOKIE" => "_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", + "HTTP_X_FORWARDED_SERVER" => "glu.ttono.us", + "REQUEST_URI" => "/admin", + "DOCUMENT_ROOT" => "/home/kevinc/sites/typo/public", + "SERVER_PORT" => "8007", + "QUERY_STRING" => "", + "REMOTE_PORT" => "63137", + "GATEWAY_INTERFACE" => "CGI/1.1", + "HTTP_X_FORWARDED_FOR" => "65.88.180.234", + "HTTP_ACCEPT" => "*/*", + "SCRIPT_FILENAME" => "/home/kevinc/sites/typo/public/dispatch.fcgi", + "REDIRECT_STATUS" => "200", + "REQUEST_METHOD" => "GET" + } # some Nokia phone browsers omit the space after the semicolon separator. # some developers have grown accustomed to using comma in cookie values. @alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"} @@ -118,7 +147,7 @@ end class RackResponseTest < BaseRackTest def setup super - @response = ActionController::RackResponse.new + @response = ActionController::RackResponse.new(@request) @output = StringIO.new('') end @@ -127,7 +156,7 @@ class RackResponseTest < BaseRackTest status, headers, body = @response.out(@output) assert_equal 200, status - assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers) + assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers) parts = [] body.each { |part| parts << part } @@ -141,10 +170,29 @@ class RackResponseTest < BaseRackTest status, headers, body = @response.out(@output) assert_equal 200, status - assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers) + assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers) parts = [] body.each { |part| parts << part } assert_equal ["0", "1", "2", "3", "4"], parts end + + def test_set_session_cookie + cookie = CGI::Cookie.new({"name" => "name", "value" => "Josh"}) + @request.cgi.send :instance_variable_set, '@output_cookies', [cookie] + + @response.body = "Hello, World!" + + status, headers, body = @response.out(@output) + assert_equal 200, status + assert_equal({ + "Content-Type" => "text/html", + "Cache-Control" => "no-cache", + "Set-Cookie" => ["name=Josh; path="] + }, headers) + + parts = [] + body.each { |part| parts << part } + assert_equal ["Hello, World!"], parts + end end -- cgit v1.2.3 From 442d2f1032df6744c11f21b5ec6281c77e39c242 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 2 Jun 2008 21:55:42 -0500 Subject: Fixed that RailsInfoController wasnt considering all requests local in development mode (Edgard Castro) [#310 state:resolved] --- railties/CHANGELOG | 5 +++++ railties/builtin/rails_info/rails/info_controller.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/railties/CHANGELOG b/railties/CHANGELOG index fe517755ef..4d1c86e643 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,3 +1,8 @@ +*Edge* + +* Fixed that RailsInfoController wasn't considering all requests local in development mode (Edgard Castro) [#310 state:resolved] + + *2.1.0 (May 31st, 2008)* * script/dbconsole fires up the command-line database client. #102 [Steve Purcell] diff --git a/railties/builtin/rails_info/rails/info_controller.rb b/railties/builtin/rails_info/rails/info_controller.rb index 39f8b1f120..05745d606d 100644 --- a/railties/builtin/rails_info/rails/info_controller.rb +++ b/railties/builtin/rails_info/rails/info_controller.rb @@ -1,6 +1,6 @@ class Rails::InfoController < ActionController::Base def properties - if local_request? + if consider_all_requests_local || local_request? render :inline => Rails::Info.to_html else render :text => '

For security purposes, this information is only available to local requests.

', :status => 500 -- cgit v1.2.3 From e7a305f08d8f72f81449e1d8934fba31f038ad88 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 2 Jun 2008 21:58:42 -0500 Subject: Fixed Base#exists? to check status code as integer [#299 state:resolved] (Wes Oldenbeuving) --- activeresource/CHANGELOG | 5 +++++ activeresource/lib/active_resource/base.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/activeresource/CHANGELOG b/activeresource/CHANGELOG index e124e40dd1..673e20de28 100644 --- a/activeresource/CHANGELOG +++ b/activeresource/CHANGELOG @@ -1,3 +1,8 @@ +*Edge* + +* Fixed Base#exists? to check status code as integer [#299 state:resolved] (Wes Oldenbeuving) + + *2.1.0 (May 31st, 2008)* * Fixed response logging to use length instead of the entire thing (seangeo) [#27] diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index 463ee9f1e7..55dacfdf06 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -538,7 +538,7 @@ module ActiveResource prefix_options, query_options = split_options(options[:params]) path = element_path(id, prefix_options, query_options) response = connection.head(path, headers) - response.code == 200 + response.code.to_i == 200 end # id && !find_single(id, options).nil? rescue ActiveResource::ResourceNotFound -- cgit v1.2.3 From 64fea9c45c515496bb60df4a1e141f44cac4d158 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 2 Jun 2008 22:02:43 -0500 Subject: Fixed Date#end_of_quarter to not blow up on May 31st [#289 state:resolved] (Danger) --- activesupport/CHANGELOG | 5 +++++ activesupport/lib/active_support/core_ext/date/calculations.rb | 2 +- activesupport/test/core_ext/date_ext_test.rb | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 15c25debf4..a1a3c7ac2b 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,3 +1,8 @@ +*Edge* + +* Fixed Date#end_of_quarter to not blow up on May 31st [#289 state:resolved] (Danger) + + *2.1.0 (May 31st, 2008)* * TimeZone#to_s shows offset as GMT instead of UTC, because GMT will be more familiar to end users (see time zone selects used by Windows OS, google.com and yahoo.com.) Reverts [8370] [Geoff Buesing] diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 1e2dbf118e..b5180c9592 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -184,7 +184,7 @@ module ActiveSupport #:nodoc: # Returns a new Date/DateTime representing the end of the quarter (last day of march, june, september, december; DateTime objects will have time set to 23:59:59) def end_of_quarter - change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month + beginning_of_month.change(:month => [3, 6, 9, 12].detect { |m| m >= self.month }).end_of_month end alias :at_end_of_quarter :end_of_quarter diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 5925ae3a61..e1d2f0e7f2 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -76,6 +76,7 @@ class DateExtCalculationsTest < Test::Unit::TestCase assert_equal Date.new(2008,3,31), Date.new(2008,3,31).end_of_quarter assert_equal Date.new(2008,12,31), Date.new(2008,10,8).end_of_quarter assert_equal Date.new(2008,6,30), Date.new(2008,4,14).end_of_quarter + assert_equal Date.new(2008,6,30), Date.new(2008,5,31).end_of_quarter assert_equal Date.new(2008,9,30), Date.new(2008,8,21).end_of_quarter end -- cgit v1.2.3 From da91450e687fe9faa7b0575062c2b2aacc261f68 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Mon, 2 Jun 2008 22:05:27 -0500 Subject: Added tests [#279 state:resolved] (Nicholas Schlueter) --- activesupport/test/core_ext/array_ext_test.rb | 5 +++++ activesupport/test/core_ext/bigdecimal.rb | 1 + activesupport/test/core_ext/date_ext_test.rb | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index ccab0f70d0..20bc5dfcb7 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -222,6 +222,11 @@ class ArrayToXmlTests < Test::Unit::TestCase assert xml.include?(%(2)), xml end + + def test_to_xml_with_empty + xml = [].to_xml + assert_match /type="array"\/>/, xml + end end class ArrayExtractOptionsTests < Test::Unit::TestCase diff --git a/activesupport/test/core_ext/bigdecimal.rb b/activesupport/test/core_ext/bigdecimal.rb index 0417a2bca0..9faad9146f 100644 --- a/activesupport/test/core_ext/bigdecimal.rb +++ b/activesupport/test/core_ext/bigdecimal.rb @@ -5,5 +5,6 @@ class BigDecimalTest < Test::Unit::TestCase assert_equal("--- 100000.30020320320000000000000000000000000000001\n", BigDecimal.new('100000.30020320320000000000000000000000000000001').to_yaml) assert_equal("--- .Inf\n", BigDecimal.new('Infinity').to_yaml) assert_equal("--- .NaN\n", BigDecimal.new('NaN').to_yaml) + assert_equal("--- -.Inf\n", BigDecimal.new('-Infinity').to_yaml) end end \ No newline at end of file diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index e1d2f0e7f2..11b6056047 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -198,6 +198,10 @@ class DateExtCalculationsTest < Test::Unit::TestCase assert_equal Time.local(2005,2,21,23,59,59), Date.new(2005,2,21).end_of_day end + def test_date_acts_like_date + assert Date.new.acts_like_date? + end + def test_xmlschema with_env_tz 'US/Eastern' do assert_match(/^1980-02-28T00:00:00-05:?00$/, Date.new(1980, 2, 28).xmlschema) -- cgit v1.2.3 From c08547d2266c75f0a82d06dd91c6d0500740e12e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 3 Jun 2008 13:32:53 -0500 Subject: Namespace Inflector, Dependencies, OrderedOptions, and TimeZone under ActiveSupport [#238 state:resolved] --- actionpack/lib/action_controller/dispatcher.rb | 2 +- actionpack/lib/action_controller/routing.rb | 2 +- actionpack/test/controller/dispatcher_test.rb | 4 +- .../test/controller/integration_upload_test.rb | 2 +- actionpack/test/controller/routing_test.rb | 4 +- activerecord/lib/active_record/base.rb | 2 +- activerecord/test/cases/attribute_methods_test.rb | 20 +- activerecord/test/cases/base_test.rb | 28 +- activerecord/test/cases/inheritance_test.rb | 4 +- activerecord/test/cases/multiple_db_test.rb | 2 +- activesupport/lib/active_support.rb | 4 + activesupport/lib/active_support/dependencies.rb | 785 +++++++++++---------- activesupport/lib/active_support/deprecation.rb | 32 +- activesupport/lib/active_support/inflections.rb | 100 +-- activesupport/lib/active_support/inflector.rb | 526 +++++++------- .../lib/active_support/ordered_options.rb | 26 +- .../lib/active_support/values/time_zone.rb | 694 +++++++++--------- activesupport/test/core_ext/date_ext_test.rb | 14 +- activesupport/test/core_ext/date_time_ext_test.rb | 24 +- activesupport/test/core_ext/duration_test.rb | 8 +- activesupport/test/core_ext/time_ext_test.rb | 28 +- activesupport/test/core_ext/time_with_zone_test.rb | 240 +++---- activesupport/test/dependencies_test.rb | 240 +++---- activesupport/test/deprecation_test.rb | 10 + activesupport/test/inflector_test.rb | 126 ++-- activesupport/test/ordered_options_test.rb | 6 +- activesupport/test/time_zone_test.rb | 135 ++-- 27 files changed, 1554 insertions(+), 1514 deletions(-) diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index 7162fb8b1f..fe4f6b4a7e 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -141,7 +141,7 @@ module ActionController # be reloaded on the next request without restarting the server. def cleanup_application ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord) - Dependencies.clear + ActiveSupport::Dependencies.clear ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord) end diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index 6aa266513d..8846dcc504 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -369,7 +369,7 @@ module ActionController Routes = RouteSet.new - ::Inflector.module_eval do + ActiveSupport::Inflector.module_eval do # Ensures that routes are reloaded when Rails inflections are updated. def inflections_with_route_reloading(&block) returning(inflections_without_route_reloading(&block)) { diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index eea0813ed5..911fcab67b 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -27,14 +27,14 @@ class DispatcherTest < Test::Unit::TestCase def test_clears_dependencies_after_dispatch_if_in_loading_mode ActionController::Routing::Routes.expects(:reload).once - Dependencies.expects(:clear).once + ActiveSupport::Dependencies.expects(:clear).once dispatch(@output, false) end def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode ActionController::Routing::Routes.expects(:reload).never - Dependencies.expects(:clear).never + ActiveSupport::Dependencies.expects(:clear).never dispatch end diff --git a/actionpack/test/controller/integration_upload_test.rb b/actionpack/test/controller/integration_upload_test.rb index 33df1131cb..4af9b7e697 100644 --- a/actionpack/test/controller/integration_upload_test.rb +++ b/actionpack/test/controller/integration_upload_test.rb @@ -28,7 +28,7 @@ class SessionUploadTest < ActionController::IntegrationTest # end def test_post_with_upload uses_mocha "test_post_with_upload" do - Dependencies.stubs(:load?).returns(false) + ActiveSupport::Dependencies.stubs(:load?).returns(false) with_routing do |set| set.draw do |map| map.update 'update', :controller => "upload_test", :action => "update", :method => :post diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 5e5503fd52..068d71a8f8 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -2392,10 +2392,10 @@ uses_mocha 'route loading' do end def test_adding_inflections_forces_reload - Inflector::Inflections.instance.expects(:uncountable).with('equipment') + ActiveSupport::Inflector::Inflections.instance.expects(:uncountable).with('equipment') routes.expects(:reload!) - Inflector.inflections { |inflect| inflect.uncountable('equipment') } + ActiveSupport::Inflector.inflections { |inflect| inflect.uncountable('equipment') } end def test_load_with_configuration diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 92a24ecc5f..8dd07eb478 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -372,7 +372,7 @@ module ActiveRecord #:nodoc: def self.reset_subclasses #:nodoc: nonreloadables = [] subclasses.each do |klass| - unless Dependencies.autoloaded? klass + unless ActiveSupport::Dependencies.autoloaded? klass nonreloadables << klass next end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index c336fd9afb..7999e29264 100755 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -137,7 +137,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase end end end - + def test_time_attributes_are_retrieved_in_current_time_zone in_time_zone "Pacific Time (US & Canada)" do utc_time = Time.utc(2008, 1, 1) @@ -145,7 +145,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase record[:written_on] = utc_time assert_equal utc_time, record.written_on # record.written on is equal to (i.e., simultaneous with) utc_time assert_kind_of ActiveSupport::TimeWithZone, record.written_on # but is a TimeWithZone - assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone + assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time # and represents time values adjusted accordingly end end @@ -156,7 +156,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase record = @target.new record.written_on = utc_time assert_equal utc_time, record.written_on - assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone + assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time end end @@ -168,7 +168,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase record = @target.new record.written_on = cst_time assert_equal utc_time, record.written_on - assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone + assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time end end @@ -181,12 +181,12 @@ class AttributeMethodsTest < ActiveRecord::TestCase record = @target.new record.written_on = time_string assert_equal Time.zone.parse(time_string), record.written_on - assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone + assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time end end end - + def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil in_time_zone "Pacific Time (US & Canada)" do record = @target.new @@ -202,7 +202,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase record = @target.new record.written_on = time_string assert_equal Time.zone.parse(time_string), record.written_on - assert_equal TimeZone[timezone_offset], record.written_on.time_zone + assert_equal ActiveSupport::TimeZone[timezone_offset], record.written_on.time_zone assert_equal Time.utc(2008, 1, 1), record.written_on.time end end @@ -214,7 +214,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase record = @target.new record.written_on = utc_time.in_time_zone assert_equal utc_time, record.written_on - assert_equal TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone + assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time end end @@ -223,12 +223,12 @@ class AttributeMethodsTest < ActiveRecord::TestCase def time_related_columns_on_topic Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name) end - + def in_time_zone(zone) old_zone = Time.zone old_tz = ActiveRecord::Base.time_zone_aware_attributes - Time.zone = zone ? TimeZone[zone] : nil + Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil ActiveRecord::Base.time_zone_aware_attributes = !zone.nil? yield ensure diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index e07ec50c46..a4be629fbd 100755 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -133,19 +133,19 @@ class BasicsTest < ActiveRecord::TestCase category_attrs = {"name"=>"Test categoty", "type" => nil} assert_equal category_attrs , category.attributes_before_type_cast end - + if current_adapter?(:MysqlAdapter) def test_read_attributes_before_type_cast_on_boolean bool = Booleantest.create({ "value" => false }) assert_equal 0, bool.attributes_before_type_cast["value"] end end - + def test_read_attributes_before_type_cast_on_datetime developer = Developer.find(:first) assert_equal developer.created_at.to_s(:db) , developer.attributes_before_type_cast["created_at"] end - + def test_hash_content topic = Topic.new topic.content = { "one" => 1, "two" => 2 } @@ -251,7 +251,7 @@ class BasicsTest < ActiveRecord::TestCase topic = Topic.create("title" => "New Topic") topicReloaded = Topic.find(topic.id) assert_equal(topic, topicReloaded) - end + end def test_create_through_factory_with_block topic = Topic.create("title" => "New Topic") do |t| @@ -576,7 +576,7 @@ class BasicsTest < ActiveRecord::TestCase def test_destroy_all original_count = Topic.count topics_by_mary = Topic.count(:conditions => mary = "author_name = 'Mary'") - + Topic.destroy_all mary assert_equal original_count - topics_by_mary, Topic.count end @@ -665,7 +665,7 @@ class BasicsTest < ActiveRecord::TestCase def test_delete_all assert Topic.count > 0 - + assert_equal Topic.count, Topic.delete_all end @@ -970,7 +970,7 @@ class BasicsTest < ActiveRecord::TestCase topic.attributes = attributes assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on end - + def test_multiparameter_attributes_on_time_with_old_date attributes = { "written_on(1i)" => "1850", "written_on(2i)" => "6", "written_on(3i)" => "24", @@ -998,7 +998,7 @@ class BasicsTest < ActiveRecord::TestCase def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes ActiveRecord::Base.time_zone_aware_attributes = true ActiveRecord::Base.default_timezone = :utc - Time.zone = TimeZone[-28800] + Time.zone = ActiveSupport::TimeZone[-28800] attributes = { "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" @@ -1016,7 +1016,7 @@ class BasicsTest < ActiveRecord::TestCase def test_multiparameter_attributes_on_time_with_time_zone_aware_attributes_false ActiveRecord::Base.time_zone_aware_attributes = false - Time.zone = TimeZone[-28800] + Time.zone = ActiveSupport::TimeZone[-28800] attributes = { "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00" @@ -1032,7 +1032,7 @@ class BasicsTest < ActiveRecord::TestCase def test_multiparameter_attributes_on_time_with_skip_time_zone_conversion_for_attributes ActiveRecord::Base.time_zone_aware_attributes = true ActiveRecord::Base.default_timezone = :utc - Time.zone = TimeZone[-28800] + Time.zone = ActiveSupport::TimeZone[-28800] Topic.skip_time_zone_conversion_for_attributes = [:written_on] attributes = { "written_on(1i)" => "2004", "written_on(2i)" => "6", "written_on(3i)" => "24", @@ -1647,7 +1647,7 @@ class BasicsTest < ActiveRecord::TestCase last = Developer.find :last assert_equal last, Developer.find(:first, :order => 'id desc') end - + def test_last assert_equal Developer.find(:first, :order => 'id desc'), Developer.last end @@ -1655,7 +1655,7 @@ class BasicsTest < ActiveRecord::TestCase def test_all_with_conditions assert_equal Developer.find(:all, :order => 'id desc'), Developer.all(:order => 'id desc') end - + def test_find_ordered_last last = Developer.find :last, :order => 'developers.salary ASC' assert_equal last, Developer.find(:all, :order => 'developers.salary ASC').last @@ -1670,14 +1670,14 @@ class BasicsTest < ActiveRecord::TestCase last = Developer.find :last, :order => 'developers.name, developers.salary DESC' assert_equal last, Developer.find(:all, :order => 'developers.name, developers.salary DESC').last end - + def test_find_scoped_ordered_last last_developer = Developer.with_scope(:find => { :order => 'developers.salary ASC' }) do Developer.find(:last) end assert_equal last_developer, Developer.find(:all, :order => 'developers.salary ASC').last end - + def test_abstract_class assert !ActiveRecord::Base.abstract_class? assert LoosePerson.abstract_class? diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index f09b617e6d..47fb5c608f 100755 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -223,11 +223,11 @@ class InheritanceComputeTypeTest < ActiveRecord::TestCase fixtures :companies def setup - Dependencies.log_activity = true + ActiveSupport::Dependencies.log_activity = true end def teardown - Dependencies.log_activity = false + ActiveSupport::Dependencies.log_activity = false self.class.const_remove :FirmOnTheFly rescue nil Firm.const_remove :FirmOnTheFly rescue nil end diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb index 236e4710a4..eb3e43c8ac 100644 --- a/activerecord/test/cases/multiple_db_test.rb +++ b/activerecord/test/cases/multiple_db_test.rb @@ -51,7 +51,7 @@ class MultipleDbTest < ActiveRecord::TestCase def test_course_connection_should_survive_dependency_reload assert Course.connection - Dependencies.clear + ActiveSupport::Dependencies.clear Object.send(:remove_const, :Course) require_dependency 'models/course' diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index e4cb145c98..982adfb0cf 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -53,3 +53,7 @@ require 'active_support/multibyte' require 'active_support/base64' require 'active_support/time_with_zone' + +Inflector = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Inflector', 'ActiveSupport::Inflector') +Dependencies = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Dependencies', 'ActiveSupport::Dependencies') +TimeZone = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('TimeZone', 'ActiveSupport::TimeZone') diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index da2ece610a..7a8c4d0326 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -3,458 +3,459 @@ require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/load_error' require 'active_support/core_ext/kernel' -module Dependencies #:nodoc: - extend self - - # Should we turn on Ruby warnings on the first load of dependent files? - mattr_accessor :warnings_on_first_load - self.warnings_on_first_load = false - - # All files ever loaded. - mattr_accessor :history - self.history = Set.new - - # All files currently loaded. - mattr_accessor :loaded - self.loaded = Set.new - - # Should we load files or require them? - mattr_accessor :mechanism - self.mechanism = :load - - # The set of directories from which we may automatically load files. Files - # under these directories will be reloaded on each request in development mode, - # unless the directory also appears in load_once_paths. - mattr_accessor :load_paths - self.load_paths = [] - - # The set of directories from which automatically loaded constants are loaded - # only once. All directories in this set must also be present in +load_paths+. - mattr_accessor :load_once_paths - self.load_once_paths = [] - - # An array of qualified constant names that have been loaded. Adding a name to - # this array will cause it to be unloaded the next time Dependencies are cleared. - mattr_accessor :autoloaded_constants - self.autoloaded_constants = [] - - # An array of constant names that need to be unloaded on every request. Used - # to allow arbitrary constants to be marked for unloading. - mattr_accessor :explicitly_unloadable_constants - self.explicitly_unloadable_constants = [] - - # Set to true to enable logging of const_missing and file loads - mattr_accessor :log_activity - self.log_activity = false - - # An internal stack used to record which constants are loaded by any block. - mattr_accessor :constant_watch_stack - self.constant_watch_stack = [] - - def load? - mechanism == :load - end - - def depend_on(file_name, swallow_load_errors = false) - path = search_for_file(file_name) - require_or_load(path || file_name) - rescue LoadError - raise unless swallow_load_errors - end - - def associate_with(file_name) - depend_on(file_name, true) - end +module ActiveSupport #:nodoc: + module Dependencies #:nodoc: + extend self + + # Should we turn on Ruby warnings on the first load of dependent files? + mattr_accessor :warnings_on_first_load + self.warnings_on_first_load = false + + # All files ever loaded. + mattr_accessor :history + self.history = Set.new + + # All files currently loaded. + mattr_accessor :loaded + self.loaded = Set.new + + # Should we load files or require them? + mattr_accessor :mechanism + self.mechanism = :load + + # The set of directories from which we may automatically load files. Files + # under these directories will be reloaded on each request in development mode, + # unless the directory also appears in load_once_paths. + mattr_accessor :load_paths + self.load_paths = [] + + # The set of directories from which automatically loaded constants are loaded + # only once. All directories in this set must also be present in +load_paths+. + mattr_accessor :load_once_paths + self.load_once_paths = [] + + # An array of qualified constant names that have been loaded. Adding a name to + # this array will cause it to be unloaded the next time Dependencies are cleared. + mattr_accessor :autoloaded_constants + self.autoloaded_constants = [] + + # An array of constant names that need to be unloaded on every request. Used + # to allow arbitrary constants to be marked for unloading. + mattr_accessor :explicitly_unloadable_constants + self.explicitly_unloadable_constants = [] + + # Set to true to enable logging of const_missing and file loads + mattr_accessor :log_activity + self.log_activity = false + + # An internal stack used to record which constants are loaded by any block. + mattr_accessor :constant_watch_stack + self.constant_watch_stack = [] + + def load? + mechanism == :load + end - def clear - log_call - loaded.clear - remove_unloadable_constants! - end + def depend_on(file_name, swallow_load_errors = false) + path = search_for_file(file_name) + require_or_load(path || file_name) + rescue LoadError + raise unless swallow_load_errors + end - def require_or_load(file_name, const_path = nil) - log_call file_name, const_path - file_name = $1 if file_name =~ /^(.*)\.rb$/ - expanded = File.expand_path(file_name) - return if loaded.include?(expanded) + def associate_with(file_name) + depend_on(file_name, true) + end - # Record that we've seen this file *before* loading it to avoid an - # infinite loop with mutual dependencies. - loaded << expanded + def clear + log_call + loaded.clear + remove_unloadable_constants! + end - begin - if load? - log "loading #{file_name}" + def require_or_load(file_name, const_path = nil) + log_call file_name, const_path + file_name = $1 if file_name =~ /^(.*)\.rb$/ + expanded = File.expand_path(file_name) + return if loaded.include?(expanded) - # Enable warnings iff this file has not been loaded before and - # warnings_on_first_load is set. - load_args = ["#{file_name}.rb"] - load_args << const_path unless const_path.nil? + # Record that we've seen this file *before* loading it to avoid an + # infinite loop with mutual dependencies. + loaded << expanded - if !warnings_on_first_load or history.include?(expanded) - result = load_file(*load_args) + begin + if load? + log "loading #{file_name}" + + # Enable warnings iff this file has not been loaded before and + # warnings_on_first_load is set. + load_args = ["#{file_name}.rb"] + load_args << const_path unless const_path.nil? + + if !warnings_on_first_load or history.include?(expanded) + result = load_file(*load_args) + else + enable_warnings { result = load_file(*load_args) } + end else - enable_warnings { result = load_file(*load_args) } + log "requiring #{file_name}" + result = require file_name end - else - log "requiring #{file_name}" - result = require file_name + rescue Exception + loaded.delete expanded + raise end - rescue Exception - loaded.delete expanded - raise - end - # Record history *after* loading so first load gets warnings. - history << expanded - return result - end + # Record history *after* loading so first load gets warnings. + history << expanded + return result + end - # Is the provided constant path defined? - def qualified_const_defined?(path) - raise NameError, "#{path.inspect} is not a valid constant name!" unless - /^(::)?([A-Z]\w*)(::[A-Z]\w*)*$/ =~ path + # Is the provided constant path defined? + def qualified_const_defined?(path) + raise NameError, "#{path.inspect} is not a valid constant name!" unless + /^(::)?([A-Z]\w*)(::[A-Z]\w*)*$/ =~ path - names = path.to_s.split('::') - names.shift if names.first.empty? + names = path.to_s.split('::') + names.shift if names.first.empty? - # We can't use defined? because it will invoke const_missing for the parent - # of the name we are checking. - names.inject(Object) do |mod, name| - return false unless uninherited_const_defined?(mod, name) - mod.const_get name + # We can't use defined? because it will invoke const_missing for the parent + # of the name we are checking. + names.inject(Object) do |mod, name| + return false unless uninherited_const_defined?(mod, name) + mod.const_get name + end + return true end - return true - end - if Module.method(:const_defined?).arity == 1 - # Does this module define this constant? - # Wrapper to accomodate changing Module#const_defined? in Ruby 1.9 - def uninherited_const_defined?(mod, const) - mod.const_defined?(const) - end - else - def uninherited_const_defined?(mod, const) #:nodoc: - mod.const_defined?(const, false) + if Module.method(:const_defined?).arity == 1 + # Does this module define this constant? + # Wrapper to accomodate changing Module#const_defined? in Ruby 1.9 + def uninherited_const_defined?(mod, const) + mod.const_defined?(const) + end + else + def uninherited_const_defined?(mod, const) #:nodoc: + mod.const_defined?(const, false) + end end - end - - # Given +path+, a filesystem path to a ruby file, return an array of constant - # paths which would cause Dependencies to attempt to load this file. - def loadable_constants_for_path(path, bases = load_paths) - path = $1 if path =~ /\A(.*)\.rb\Z/ - expanded_path = File.expand_path(path) - - bases.collect do |root| - expanded_root = File.expand_path(root) - next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path - - nesting = expanded_path[(expanded_root.size)..-1] - nesting = nesting[1..-1] if nesting && nesting[0] == ?/ - next if nesting.blank? - - [ - nesting.camelize, - # Special case: application.rb might define ApplicationControlller. - ('ApplicationController' if nesting == 'application') - ] - end.flatten.compact.uniq - end - # Search for a file in load_paths matching the provided suffix. - def search_for_file(path_suffix) - path_suffix = path_suffix + '.rb' unless path_suffix.ends_with? '.rb' - load_paths.each do |root| - path = File.join(root, path_suffix) - return path if File.file? path + # Given +path+, a filesystem path to a ruby file, return an array of constant + # paths which would cause Dependencies to attempt to load this file. + def loadable_constants_for_path(path, bases = load_paths) + path = $1 if path =~ /\A(.*)\.rb\Z/ + expanded_path = File.expand_path(path) + + bases.collect do |root| + expanded_root = File.expand_path(root) + next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path + + nesting = expanded_path[(expanded_root.size)..-1] + nesting = nesting[1..-1] if nesting && nesting[0] == ?/ + next if nesting.blank? + + [ + nesting.camelize, + # Special case: application.rb might define ApplicationControlller. + ('ApplicationController' if nesting == 'application') + ] + end.flatten.compact.uniq end - nil # Gee, I sure wish we had first_match ;-) - end - # Does the provided path_suffix correspond to an autoloadable module? - # Instead of returning a boolean, the autoload base for this module is returned. - def autoloadable_module?(path_suffix) - load_paths.each do |load_path| - return load_path if File.directory? File.join(load_path, path_suffix) + # Search for a file in load_paths matching the provided suffix. + def search_for_file(path_suffix) + path_suffix = path_suffix + '.rb' unless path_suffix.ends_with? '.rb' + load_paths.each do |root| + path = File.join(root, path_suffix) + return path if File.file? path + end + nil # Gee, I sure wish we had first_match ;-) end - nil - end - - def load_once_path?(path) - load_once_paths.any? { |base| path.starts_with? base } - end - # Attempt to autoload the provided module name by searching for a directory - # matching the expect path suffix. If found, the module is created and assigned - # to +into+'s constants with the name +const_name+. Provided that the directory - # was loaded from a reloadable base path, it is added to the set of constants - # that are to be unloaded. - def autoload_module!(into, const_name, qualified_name, path_suffix) - return nil unless base_path = autoloadable_module?(path_suffix) - mod = Module.new - into.const_set const_name, mod - autoloaded_constants << qualified_name unless load_once_paths.include?(base_path) - return mod - end - - # Load the file at the provided path. +const_paths+ is a set of qualified - # constant names. When loading the file, Dependencies will watch for the - # addition of these constants. Each that is defined will be marked as - # autoloaded, and will be removed when Dependencies.clear is next called. - # - # If the second parameter is left off, then Dependencies will construct a set - # of names that the file at +path+ may define. See - # +loadable_constants_for_path+ for more details. - def load_file(path, const_paths = loadable_constants_for_path(path)) - log_call path, const_paths - const_paths = [const_paths].compact unless const_paths.is_a? Array - parent_paths = const_paths.collect { |const_path| /(.*)::[^:]+\Z/ =~ const_path ? $1 : :Object } - - result = nil - newly_defined_paths = new_constants_in(*parent_paths) do - result = load_without_new_constant_marking path + # Does the provided path_suffix correspond to an autoloadable module? + # Instead of returning a boolean, the autoload base for this module is returned. + def autoloadable_module?(path_suffix) + load_paths.each do |load_path| + return load_path if File.directory? File.join(load_path, path_suffix) + end + nil end - autoloaded_constants.concat newly_defined_paths unless load_once_path?(path) - autoloaded_constants.uniq! - log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty? - return result - end + def load_once_path?(path) + load_once_paths.any? { |base| path.starts_with? base } + end - # Return the constant path for the provided parent and constant name. - def qualified_name_for(mod, name) - mod_name = to_constant_name mod - (%w(Object Kernel).include? mod_name) ? name.to_s : "#{mod_name}::#{name}" - end + # Attempt to autoload the provided module name by searching for a directory + # matching the expect path suffix. If found, the module is created and assigned + # to +into+'s constants with the name +const_name+. Provided that the directory + # was loaded from a reloadable base path, it is added to the set of constants + # that are to be unloaded. + def autoload_module!(into, const_name, qualified_name, path_suffix) + return nil unless base_path = autoloadable_module?(path_suffix) + mod = Module.new + into.const_set const_name, mod + autoloaded_constants << qualified_name unless load_once_paths.include?(base_path) + return mod + end - # Load the constant named +const_name+ which is missing from +from_mod+. If - # it is not possible to load the constant into from_mod, try its parent module - # using const_missing. - def load_missing_constant(from_mod, const_name) - log_call from_mod, const_name - if from_mod == Kernel - if ::Object.const_defined?(const_name) - log "Returning Object::#{const_name} for Kernel::#{const_name}" - return ::Object.const_get(const_name) - else - log "Substituting Object for Kernel" - from_mod = Object + # Load the file at the provided path. +const_paths+ is a set of qualified + # constant names. When loading the file, Dependencies will watch for the + # addition of these constants. Each that is defined will be marked as + # autoloaded, and will be removed when Dependencies.clear is next called. + # + # If the second parameter is left off, then Dependencies will construct a set + # of names that the file at +path+ may define. See + # +loadable_constants_for_path+ for more details. + def load_file(path, const_paths = loadable_constants_for_path(path)) + log_call path, const_paths + const_paths = [const_paths].compact unless const_paths.is_a? Array + parent_paths = const_paths.collect { |const_path| /(.*)::[^:]+\Z/ =~ const_path ? $1 : :Object } + + result = nil + newly_defined_paths = new_constants_in(*parent_paths) do + result = load_without_new_constant_marking path end - end - # If we have an anonymous module, all we can do is attempt to load from Object. - from_mod = Object if from_mod.name.blank? + autoloaded_constants.concat newly_defined_paths unless load_once_path?(path) + autoloaded_constants.uniq! + log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty? + return result + end - unless qualified_const_defined?(from_mod.name) && from_mod.name.constantize.object_id == from_mod.object_id - raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!" + # Return the constant path for the provided parent and constant name. + def qualified_name_for(mod, name) + mod_name = to_constant_name mod + (%w(Object Kernel).include? mod_name) ? name.to_s : "#{mod_name}::#{name}" end - raise ArgumentError, "#{from_mod} is not missing constant #{const_name}!" if uninherited_const_defined?(from_mod, const_name) + # Load the constant named +const_name+ which is missing from +from_mod+. If + # it is not possible to load the constant into from_mod, try its parent module + # using const_missing. + def load_missing_constant(from_mod, const_name) + log_call from_mod, const_name + if from_mod == Kernel + if ::Object.const_defined?(const_name) + log "Returning Object::#{const_name} for Kernel::#{const_name}" + return ::Object.const_get(const_name) + else + log "Substituting Object for Kernel" + from_mod = Object + end + end - qualified_name = qualified_name_for from_mod, const_name - path_suffix = qualified_name.underscore - name_error = NameError.new("uninitialized constant #{qualified_name}") + # If we have an anonymous module, all we can do is attempt to load from Object. + from_mod = Object if from_mod.name.blank? - file_path = search_for_file(path_suffix) - if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load - require_or_load file_path - raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless uninherited_const_defined?(from_mod, const_name) - return from_mod.const_get(const_name) - elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix) - return mod - elsif (parent = from_mod.parent) && parent != from_mod && - ! from_mod.parents.any? { |p| uninherited_const_defined?(p, const_name) } - # If our parents do not have a constant named +const_name+ then we are free - # to attempt to load upwards. If they do have such a constant, then this - # const_missing must be due to from_mod::const_name, which should not - # return constants from from_mod's parents. - begin - return parent.const_missing(const_name) - rescue NameError => e - raise unless e.missing_name? qualified_name_for(parent, const_name) + unless qualified_const_defined?(from_mod.name) && from_mod.name.constantize.object_id == from_mod.object_id + raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!" + end + + raise ArgumentError, "#{from_mod} is not missing constant #{const_name}!" if uninherited_const_defined?(from_mod, const_name) + + qualified_name = qualified_name_for from_mod, const_name + path_suffix = qualified_name.underscore + name_error = NameError.new("uninitialized constant #{qualified_name}") + + file_path = search_for_file(path_suffix) + if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load + require_or_load file_path + raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless uninherited_const_defined?(from_mod, const_name) + return from_mod.const_get(const_name) + elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix) + return mod + elsif (parent = from_mod.parent) && parent != from_mod && + ! from_mod.parents.any? { |p| uninherited_const_defined?(p, const_name) } + # If our parents do not have a constant named +const_name+ then we are free + # to attempt to load upwards. If they do have such a constant, then this + # const_missing must be due to from_mod::const_name, which should not + # return constants from from_mod's parents. + begin + return parent.const_missing(const_name) + rescue NameError => e + raise unless e.missing_name? qualified_name_for(parent, const_name) + raise name_error + end + else raise name_error end - else - raise name_error end - end - # Remove the constants that have been autoloaded, and those that have been - # marked for unloading. - def remove_unloadable_constants! - autoloaded_constants.each { |const| remove_constant const } - autoloaded_constants.clear - explicitly_unloadable_constants.each { |const| remove_constant const } - end + # Remove the constants that have been autoloaded, and those that have been + # marked for unloading. + def remove_unloadable_constants! + autoloaded_constants.each { |const| remove_constant const } + autoloaded_constants.clear + explicitly_unloadable_constants.each { |const| remove_constant const } + end - # Determine if the given constant has been automatically loaded. - def autoloaded?(desc) - # No name => anonymous module. - return false if desc.is_a?(Module) && desc.name.blank? - name = to_constant_name desc - return false unless qualified_const_defined? name - return autoloaded_constants.include?(name) - end + # Determine if the given constant has been automatically loaded. + def autoloaded?(desc) + # No name => anonymous module. + return false if desc.is_a?(Module) && desc.name.blank? + name = to_constant_name desc + return false unless qualified_const_defined? name + return autoloaded_constants.include?(name) + end - # Will the provided constant descriptor be unloaded? - def will_unload?(const_desc) - autoloaded?(desc) || - explicitly_unloadable_constants.include?(to_constant_name(const_desc)) - end + # Will the provided constant descriptor be unloaded? + def will_unload?(const_desc) + autoloaded?(desc) || + explicitly_unloadable_constants.include?(to_constant_name(const_desc)) + end - # Mark the provided constant name for unloading. This constant will be - # unloaded on each request, not just the next one. - def mark_for_unload(const_desc) - name = to_constant_name const_desc - if explicitly_unloadable_constants.include? name - return false - else - explicitly_unloadable_constants << name - return true + # Mark the provided constant name for unloading. This constant will be + # unloaded on each request, not just the next one. + def mark_for_unload(const_desc) + name = to_constant_name const_desc + if explicitly_unloadable_constants.include? name + return false + else + explicitly_unloadable_constants << name + return true + end end - end - # Run the provided block and detect the new constants that were loaded during - # its execution. Constants may only be regarded as 'new' once -- so if the - # block calls +new_constants_in+ again, then the constants defined within the - # inner call will not be reported in this one. - # - # If the provided block does not run to completion, and instead raises an - # exception, any new constants are regarded as being only partially defined - # and will be removed immediately. - def new_constants_in(*descs) - log_call(*descs) - - # Build the watch frames. Each frame is a tuple of - # [module_name_as_string, constants_defined_elsewhere] - watch_frames = descs.collect do |desc| - if desc.is_a? Module - mod_name = desc.name - initial_constants = desc.local_constant_names - elsif desc.is_a?(String) || desc.is_a?(Symbol) - mod_name = desc.to_s - - # Handle the case where the module has yet to be defined. - initial_constants = if qualified_const_defined?(mod_name) - mod_name.constantize.local_constant_names + # Run the provided block and detect the new constants that were loaded during + # its execution. Constants may only be regarded as 'new' once -- so if the + # block calls +new_constants_in+ again, then the constants defined within the + # inner call will not be reported in this one. + # + # If the provided block does not run to completion, and instead raises an + # exception, any new constants are regarded as being only partially defined + # and will be removed immediately. + def new_constants_in(*descs) + log_call(*descs) + + # Build the watch frames. Each frame is a tuple of + # [module_name_as_string, constants_defined_elsewhere] + watch_frames = descs.collect do |desc| + if desc.is_a? Module + mod_name = desc.name + initial_constants = desc.local_constant_names + elsif desc.is_a?(String) || desc.is_a?(Symbol) + mod_name = desc.to_s + + # Handle the case where the module has yet to be defined. + initial_constants = if qualified_const_defined?(mod_name) + mod_name.constantize.local_constant_names + else + [] + end else - [] + raise Argument, "#{desc.inspect} does not describe a module!" end - else - raise Argument, "#{desc.inspect} does not describe a module!" - end - [mod_name, initial_constants] - end + [mod_name, initial_constants] + end - constant_watch_stack.concat watch_frames + constant_watch_stack.concat watch_frames - aborting = true - begin - yield # Now yield to the code that is to define new constants. - aborting = false - ensure - # Find the new constants. - new_constants = watch_frames.collect do |mod_name, prior_constants| - # Module still doesn't exist? Treat it as if it has no constants. - next [] unless qualified_const_defined?(mod_name) - - mod = mod_name.constantize - next [] unless mod.is_a? Module - new_constants = mod.local_constant_names - prior_constants - - # Make sure no other frames takes credit for these constants. - constant_watch_stack.each do |frame_name, constants| - constants.concat new_constants if frame_name == mod_name + aborting = true + begin + yield # Now yield to the code that is to define new constants. + aborting = false + ensure + # Find the new constants. + new_constants = watch_frames.collect do |mod_name, prior_constants| + # Module still doesn't exist? Treat it as if it has no constants. + next [] unless qualified_const_defined?(mod_name) + + mod = mod_name.constantize + next [] unless mod.is_a? Module + new_constants = mod.local_constant_names - prior_constants + + # Make sure no other frames takes credit for these constants. + constant_watch_stack.each do |frame_name, constants| + constants.concat new_constants if frame_name == mod_name + end + + new_constants.collect do |suffix| + mod_name == "Object" ? suffix : "#{mod_name}::#{suffix}" + end + end.flatten + + log "New constants: #{new_constants * ', '}" + + if aborting + log "Error during loading, removing partially loaded constants " + new_constants.each { |name| remove_constant name } + new_constants.clear end + end - new_constants.collect do |suffix| - mod_name == "Object" ? suffix : "#{mod_name}::#{suffix}" + return new_constants + ensure + # Remove the stack frames that we added. + if defined?(watch_frames) && ! watch_frames.blank? + frame_ids = watch_frames.collect(&:object_id) + constant_watch_stack.delete_if do |watch_frame| + frame_ids.include? watch_frame.object_id end - end.flatten - - log "New constants: #{new_constants * ', '}" - - if aborting - log "Error during loading, removing partially loaded constants " - new_constants.each { |name| remove_constant name } - new_constants.clear end end - return new_constants - ensure - # Remove the stack frames that we added. - if defined?(watch_frames) && ! watch_frames.blank? - frame_ids = watch_frames.collect(&:object_id) - constant_watch_stack.delete_if do |watch_frame| - frame_ids.include? watch_frame.object_id + class LoadingModule #:nodoc: + # Old style environment.rb referenced this method directly. Please note, it doesn't + # actually *do* anything any more. + def self.root(*args) + if defined?(RAILS_DEFAULT_LOGGER) + RAILS_DEFAULT_LOGGER.warn "Your environment.rb uses the old syntax, it may not continue to work in future releases." + RAILS_DEFAULT_LOGGER.warn "For upgrade instructions please see: http://manuals.rubyonrails.com/read/book/19" + end end end - end - class LoadingModule #:nodoc: - # Old style environment.rb referenced this method directly. Please note, it doesn't - # actually *do* anything any more. - def self.root(*args) - if defined?(RAILS_DEFAULT_LOGGER) - RAILS_DEFAULT_LOGGER.warn "Your environment.rb uses the old syntax, it may not continue to work in future releases." - RAILS_DEFAULT_LOGGER.warn "For upgrade instructions please see: http://manuals.rubyonrails.com/read/book/19" + # Convert the provided const desc to a qualified constant name (as a string). + # A module, class, symbol, or string may be provided. + def to_constant_name(desc) #:nodoc: + name = case desc + when String then desc.starts_with?('::') ? desc[2..-1] : desc + when Symbol then desc.to_s + when Module + raise ArgumentError, "Anonymous modules have no name to be referenced by" if desc.name.blank? + desc.name + else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}" end end - end - # Convert the provided const desc to a qualified constant name (as a string). - # A module, class, symbol, or string may be provided. - def to_constant_name(desc) #:nodoc: - name = case desc - when String then desc.starts_with?('::') ? desc[2..-1] : desc - when Symbol then desc.to_s - when Module - raise ArgumentError, "Anonymous modules have no name to be referenced by" if desc.name.blank? - desc.name - else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}" - end - end + def remove_constant(const) #:nodoc: + return false unless qualified_const_defined? const - def remove_constant(const) #:nodoc: - return false unless qualified_const_defined? const + const = $1 if /\A::(.*)\Z/ =~ const.to_s + names = const.to_s.split('::') + if names.size == 1 # It's under Object + parent = Object + else + parent = (names[0..-2] * '::').constantize + end - const = $1 if /\A::(.*)\Z/ =~ const.to_s - names = const.to_s.split('::') - if names.size == 1 # It's under Object - parent = Object - else - parent = (names[0..-2] * '::').constantize + log "removing constant #{const}" + parent.instance_eval { remove_const names.last } + return true end - log "removing constant #{const}" - parent.instance_eval { remove_const names.last } - return true - end - -protected - def log_call(*args) - if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER && log_activity - arg_str = args.collect(&:inspect) * ', ' - /in `([a-z_\?\!]+)'/ =~ caller(1).first - selector = $1 || '' - log "called #{selector}(#{arg_str})" - end - end + protected + def log_call(*args) + if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER && log_activity + arg_str = args.collect(&:inspect) * ', ' + /in `([a-z_\?\!]+)'/ =~ caller(1).first + selector = $1 || '' + log "called #{selector}(#{arg_str})" + end + end - def log(msg) - if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER && log_activity - RAILS_DEFAULT_LOGGER.debug "Dependencies: #{msg}" - end + def log(msg) + if defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER && log_activity + RAILS_DEFAULT_LOGGER.debug "Dependencies: #{msg}" + end + end end - end 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) + 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: @@ -464,7 +465,7 @@ class Module #:nodoc: # Use const_missing to autoload associations so we don't have to # require_association when using single-table inheritance. def const_missing(class_id) - Dependencies.load_missing_constant self, class_id + ActiveSupport::Dependencies.load_missing_constant self, class_id end def unloadable(const_desc = self) @@ -480,15 +481,15 @@ class Class else begin begin - Dependencies.load_missing_constant self, const_name + 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 = Dependencies.qualified_name_for parent, const_name + parent_qualified_name = ActiveSupport::Dependencies.qualified_name_for parent, const_name raise unless e.missing_name? parent_qualified_name - qualified_name = Dependencies.qualified_name_for self, const_name + qualified_name = ActiveSupport::Dependencies.qualified_name_for self, const_name raise NameError.new("uninitialized constant #{qualified_name}").copy_blame!(e) end end @@ -499,14 +500,14 @@ class Object alias_method :load_without_new_constant_marking, :load def load(file, *extras) #:nodoc: - Dependencies.new_constants_in(Object) { super } + 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: - Dependencies.new_constants_in(Object) { super } + ActiveSupport::Dependencies.new_constants_in(Object) { super } rescue Exception => exception # errors from required file exception.blame_file! file raise @@ -526,7 +527,7 @@ class Object # Returns true if the constant was not previously marked for unloading, false # otherwise. def unloadable(const_desc) - Dependencies.mark_for_unload const_desc + ActiveSupport::Dependencies.mark_for_unload const_desc end end diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index 758aef5445..ebdaf86146 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -144,17 +144,11 @@ module ActiveSupport end end - # Stand-in for @request, @attributes, @params, etc. - # which emits deprecation warnings on any method call (except +inspect+). - class DeprecatedInstanceVariableProxy #:nodoc: + class DeprecationProxy #:nodoc: silence_warnings do instance_methods.each { |m| undef_method m unless m =~ /^__/ } end - def initialize(instance, method, var = "@#{method}") - @instance, @method, @var = instance, method, var - end - # Don't give a deprecation warning on inspect since test/unit and error # logs rely on it for diagnostics. def inspect @@ -166,7 +160,16 @@ module ActiveSupport warn caller, called, args target.__send__(called, *args, &block) end + end + # Stand-in for @request, @attributes, @params, etc. + # which emits deprecation warnings on any method call (except +inspect+). + class DeprecatedInstanceVariableProxy < DeprecationProxy #:nodoc: + def initialize(instance, method, var = "@#{method}") + @instance, @method, @var = instance, method, var + end + + private def target @instance.__send__(@method) end @@ -176,6 +179,21 @@ module ActiveSupport end end + class DeprecatedConstantProxy < DeprecationProxy #:nodoc: + def initialize(old_const, new_const) + @old_const = old_const + @new_const = new_const + end + + private + def target + @new_const.to_s.constantize + end + + def warn(callstack, called, args) + ActiveSupport::Deprecation.warn("#{@old_const} is deprecated! Use #{@new_const} instead.", callstack) + end + end end end diff --git a/activesupport/lib/active_support/inflections.rb b/activesupport/lib/active_support/inflections.rb index 967722c2bf..b6d276953a 100644 --- a/activesupport/lib/active_support/inflections.rb +++ b/activesupport/lib/active_support/inflections.rb @@ -1,53 +1,55 @@ -Inflector.inflections do |inflect| - inflect.plural(/$/, 's') - inflect.plural(/s$/i, 's') - inflect.plural(/(ax|test)is$/i, '\1es') - inflect.plural(/(octop|vir)us$/i, '\1i') - inflect.plural(/(alias|status)$/i, '\1es') - inflect.plural(/(bu)s$/i, '\1ses') - inflect.plural(/(buffal|tomat)o$/i, '\1oes') - inflect.plural(/([ti])um$/i, '\1a') - inflect.plural(/sis$/i, 'ses') - inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves') - inflect.plural(/(hive)$/i, '\1s') - inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies') - inflect.plural(/(x|ch|ss|sh)$/i, '\1es') - inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices') - inflect.plural(/([m|l])ouse$/i, '\1ice') - inflect.plural(/^(ox)$/i, '\1en') - inflect.plural(/(quiz)$/i, '\1zes') +module ActiveSupport + Inflector.inflections do |inflect| + inflect.plural(/$/, 's') + inflect.plural(/s$/i, 's') + inflect.plural(/(ax|test)is$/i, '\1es') + inflect.plural(/(octop|vir)us$/i, '\1i') + inflect.plural(/(alias|status)$/i, '\1es') + inflect.plural(/(bu)s$/i, '\1ses') + inflect.plural(/(buffal|tomat)o$/i, '\1oes') + inflect.plural(/([ti])um$/i, '\1a') + inflect.plural(/sis$/i, 'ses') + inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves') + inflect.plural(/(hive)$/i, '\1s') + inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies') + inflect.plural(/(x|ch|ss|sh)$/i, '\1es') + inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices') + inflect.plural(/([m|l])ouse$/i, '\1ice') + inflect.plural(/^(ox)$/i, '\1en') + inflect.plural(/(quiz)$/i, '\1zes') - inflect.singular(/s$/i, '') - inflect.singular(/(n)ews$/i, '\1ews') - inflect.singular(/([ti])a$/i, '\1um') - inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis') - inflect.singular(/(^analy)ses$/i, '\1sis') - inflect.singular(/([^f])ves$/i, '\1fe') - inflect.singular(/(hive)s$/i, '\1') - inflect.singular(/(tive)s$/i, '\1') - inflect.singular(/([lr])ves$/i, '\1f') - inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y') - inflect.singular(/(s)eries$/i, '\1eries') - inflect.singular(/(m)ovies$/i, '\1ovie') - inflect.singular(/(x|ch|ss|sh)es$/i, '\1') - inflect.singular(/([m|l])ice$/i, '\1ouse') - inflect.singular(/(bus)es$/i, '\1') - inflect.singular(/(o)es$/i, '\1') - inflect.singular(/(shoe)s$/i, '\1') - inflect.singular(/(cris|ax|test)es$/i, '\1is') - inflect.singular(/(octop|vir)i$/i, '\1us') - inflect.singular(/(alias|status)es$/i, '\1') - inflect.singular(/^(ox)en/i, '\1') - inflect.singular(/(vert|ind)ices$/i, '\1ex') - inflect.singular(/(matr)ices$/i, '\1ix') - inflect.singular(/(quiz)zes$/i, '\1') + inflect.singular(/s$/i, '') + inflect.singular(/(n)ews$/i, '\1ews') + inflect.singular(/([ti])a$/i, '\1um') + inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis') + inflect.singular(/(^analy)ses$/i, '\1sis') + inflect.singular(/([^f])ves$/i, '\1fe') + inflect.singular(/(hive)s$/i, '\1') + inflect.singular(/(tive)s$/i, '\1') + inflect.singular(/([lr])ves$/i, '\1f') + inflect.singular(/([^aeiouy]|qu)ies$/i, '\1y') + inflect.singular(/(s)eries$/i, '\1eries') + inflect.singular(/(m)ovies$/i, '\1ovie') + inflect.singular(/(x|ch|ss|sh)es$/i, '\1') + inflect.singular(/([m|l])ice$/i, '\1ouse') + inflect.singular(/(bus)es$/i, '\1') + inflect.singular(/(o)es$/i, '\1') + inflect.singular(/(shoe)s$/i, '\1') + inflect.singular(/(cris|ax|test)es$/i, '\1is') + inflect.singular(/(octop|vir)i$/i, '\1us') + inflect.singular(/(alias|status)es$/i, '\1') + inflect.singular(/^(ox)en/i, '\1') + inflect.singular(/(vert|ind)ices$/i, '\1ex') + inflect.singular(/(matr)ices$/i, '\1ix') + inflect.singular(/(quiz)zes$/i, '\1') - inflect.irregular('person', 'people') - inflect.irregular('man', 'men') - inflect.irregular('child', 'children') - inflect.irregular('sex', 'sexes') - inflect.irregular('move', 'moves') - inflect.irregular('cow', 'kine') + inflect.irregular('person', 'people') + inflect.irregular('man', 'men') + inflect.irregular('child', 'children') + inflect.irregular('sex', 'sexes') + inflect.irregular('move', 'moves') + inflect.irregular('cow', 'kine') - inflect.uncountable(%w(equipment information rice money species series fish sheep)) + inflect.uncountable(%w(equipment information rice money species series fish sheep)) + end end diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index a4fd619317..fc88d80cdb 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -1,305 +1,307 @@ require 'singleton' -# The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without, -# and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept -# in inflections.rb. -# -# The Rails core team has stated patches for the inflections library will not be accepted -# in order to avoid breaking legacy applications which may be relying on errant inflections. -# If you discover an incorrect inflection and require it for your application, you'll need -# to correct it yourself (explained below). -module Inflector - # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional - # inflection rules. Examples: +module ActiveSupport + # The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without, + # and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept + # in inflections.rb. # - # Inflector.inflections do |inflect| - # inflect.plural /^(ox)$/i, '\1\2en' - # inflect.singular /^(ox)en/i, '\1' - # - # inflect.irregular 'octopus', 'octopi' - # - # inflect.uncountable "equipment" - # end - # - # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the - # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may - # already have been loaded. - class Inflections - include Singleton + # The Rails core team has stated patches for the inflections library will not be accepted + # in order to avoid breaking legacy applications which may be relying on errant inflections. + # If you discover an incorrect inflection and require it for your application, you'll need + # to correct it yourself (explained below). + module Inflector + # A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional + # inflection rules. Examples: + # + # Inflector.inflections do |inflect| + # inflect.plural /^(ox)$/i, '\1\2en' + # inflect.singular /^(ox)en/i, '\1' + # + # inflect.irregular 'octopus', 'octopi' + # + # inflect.uncountable "equipment" + # end + # + # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the + # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may + # already have been loaded. + class Inflections + include Singleton - attr_reader :plurals, :singulars, :uncountables + attr_reader :plurals, :singulars, :uncountables - def initialize - @plurals, @singulars, @uncountables = [], [], [] - end + def initialize + @plurals, @singulars, @uncountables = [], [], [] + end + + # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression. + # The replacement should always be a string that may include references to the matched data from the rule. + def plural(rule, replacement) + @plurals.insert(0, [rule, replacement]) + end + + # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression. + # The replacement should always be a string that may include references to the matched data from the rule. + def singular(rule, replacement) + @singulars.insert(0, [rule, replacement]) + end - # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression. - # The replacement should always be a string that may include references to the matched data from the rule. - def plural(rule, replacement) - @plurals.insert(0, [rule, replacement]) + # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used + # for strings, not regular expressions. You simply pass the irregular in singular and plural form. + # + # Examples: + # irregular 'octopus', 'octopi' + # irregular 'person', 'people' + def irregular(singular, plural) + if singular[0,1].upcase == plural[0,1].upcase + plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1]) + singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1]) + else + plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1]) + plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1]) + singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1]) + singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1]) + end + end + + # Add uncountable words that shouldn't be attempted inflected. + # + # Examples: + # uncountable "money" + # uncountable "money", "information" + # uncountable %w( money information rice ) + def uncountable(*words) + (@uncountables << words).flatten! + end + + # Clears the loaded inflections within a given scope (default is :all). + # Give the scope as a symbol of the inflection type, the options are: :plurals, + # :singulars, :uncountables. + # + # Examples: + # clear :all + # clear :plurals + def clear(scope = :all) + case scope + when :all + @plurals, @singulars, @uncountables = [], [], [] + else + instance_variable_set "@#{scope}", [] + end + end end - # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression. - # The replacement should always be a string that may include references to the matched data from the rule. - def singular(rule, replacement) - @singulars.insert(0, [rule, replacement]) + extend self + + # Yields a singleton instance of Inflector::Inflections so you can specify additional + # inflector rules. + # + # Example: + # Inflector.inflections do |inflect| + # inflect.uncountable "rails" + # end + def inflections + if block_given? + yield Inflections.instance + else + Inflections.instance + end end - # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used - # for strings, not regular expressions. You simply pass the irregular in singular and plural form. + # Returns the plural form of the word in the string. # # Examples: - # irregular 'octopus', 'octopi' - # irregular 'person', 'people' - def irregular(singular, plural) - if singular[0,1].upcase == plural[0,1].upcase - plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1]) - singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1]) + # "post".pluralize # => "posts" + # "octopus".pluralize # => "octopi" + # "sheep".pluralize # => "sheep" + # "words".pluralize # => "words" + # "the blue mailman".pluralize # => "the blue mailmen" + # "CamelOctopus".pluralize # => "CamelOctopi" + def pluralize(word) + result = word.to_s.dup + + if word.empty? || inflections.uncountables.include?(result.downcase) + result else - plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1]) - plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1]) - singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1]) - singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1]) + inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } + result end end - # Add uncountable words that shouldn't be attempted inflected. + # The reverse of +pluralize+, returns the singular form of a word in a string. # # Examples: - # uncountable "money" - # uncountable "money", "information" - # uncountable %w( money information rice ) - def uncountable(*words) - (@uncountables << words).flatten! + # "posts".singularize # => "post" + # "octopi".singularize # => "octopus" + # "sheep".singluarize # => "sheep" + # "word".singluarize # => "word" + # "the blue mailmen".singularize # => "the blue mailman" + # "CamelOctopi".singularize # => "CamelOctopus" + def singularize(word) + result = word.to_s.dup + + if inflections.uncountables.include?(result.downcase) + result + else + inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } + result + end end - # Clears the loaded inflections within a given scope (default is :all). - # Give the scope as a symbol of the inflection type, the options are: :plurals, - # :singulars, :uncountables. + # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+ + # is set to :lower then +camelize+ produces lowerCamelCase. + # + # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces. # # Examples: - # clear :all - # clear :plurals - def clear(scope = :all) - case scope - when :all - @plurals, @singulars, @uncountables = [], [], [] - else - instance_variable_set "@#{scope}", [] + # "active_record".camelize # => "ActiveRecord" + # "active_record".camelize(:lower) # => "activeRecord" + # "active_record/errors".camelize # => "ActiveRecord::Errors" + # "active_record/errors".camelize(:lower) # => "activeRecord::Errors" + def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true) + if first_letter_in_uppercase + lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } + else + lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1] end end - end - - extend self - # Yields a singleton instance of Inflector::Inflections so you can specify additional - # inflector rules. - # - # Example: - # Inflector.inflections do |inflect| - # inflect.uncountable "rails" - # end - def inflections - if block_given? - yield Inflections.instance - else - Inflections.instance + # Capitalizes all the words and replaces some characters in the string to create + # a nicer looking title. +titleize+ is meant for creating pretty output. It is not + # used in the Rails internals. + # + # +titleize+ is also aliased as as +titlecase+. + # + # Examples: + # "man from the boondocks".titleize # => "Man From The Boondocks" + # "x-men: the last stand".titleize # => "X Men: The Last Stand" + def titleize(word) + humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize } end - end - - # Returns the plural form of the word in the string. - # - # Examples: - # "post".pluralize # => "posts" - # "octopus".pluralize # => "octopi" - # "sheep".pluralize # => "sheep" - # "words".pluralize # => "words" - # "the blue mailman".pluralize # => "the blue mailmen" - # "CamelOctopus".pluralize # => "CamelOctopi" - def pluralize(word) - result = word.to_s.dup - if word.empty? || inflections.uncountables.include?(result.downcase) - result - else - inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } - result + # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string. + # + # Changes '::' to '/' to convert namespaces to paths. + # + # Examples: + # "ActiveRecord".underscore # => "active_record" + # "ActiveRecord::Errors".underscore # => active_record/errors + def underscore(camel_cased_word) + camel_cased_word.to_s.gsub(/::/, '/'). + gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). + gsub(/([a-z\d])([A-Z])/,'\1_\2'). + tr("-", "_"). + downcase end - end - - # The reverse of +pluralize+, returns the singular form of a word in a string. - # - # Examples: - # "posts".singularize # => "post" - # "octopi".singularize # => "octopus" - # "sheep".singluarize # => "sheep" - # "word".singluarize # => "word" - # "the blue mailmen".singularize # => "the blue mailman" - # "CamelOctopi".singularize # => "CamelOctopus" - def singularize(word) - result = word.to_s.dup - if inflections.uncountables.include?(result.downcase) - result - else - inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } - result + # Replaces underscores with dashes in the string. + # + # Example: + # "puni_puni" # => "puni-puni" + def dasherize(underscored_word) + underscored_word.gsub(/_/, '-') end - end - # By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+ - # is set to :lower then +camelize+ produces lowerCamelCase. - # - # +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces. - # - # Examples: - # "active_record".camelize # => "ActiveRecord" - # "active_record".camelize(:lower) # => "activeRecord" - # "active_record/errors".camelize # => "ActiveRecord::Errors" - # "active_record/errors".camelize(:lower) # => "activeRecord::Errors" - def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true) - if first_letter_in_uppercase - lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } - else - lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1] + # Capitalizes the first word and turns underscores into spaces and strips a + # trailing "_id", if any. Like +titleize+, this is meant for creating pretty output. + # + # Examples: + # "employee_salary" # => "Employee salary" + # "author_id" # => "Author" + def humanize(lower_case_and_underscored_word) + lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize end - end - # Capitalizes all the words and replaces some characters in the string to create - # a nicer looking title. +titleize+ is meant for creating pretty output. It is not - # used in the Rails internals. - # - # +titleize+ is also aliased as as +titlecase+. - # - # Examples: - # "man from the boondocks".titleize # => "Man From The Boondocks" - # "x-men: the last stand".titleize # => "X Men: The Last Stand" - def titleize(word) - humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize } - end - - # The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string. - # - # Changes '::' to '/' to convert namespaces to paths. - # - # Examples: - # "ActiveRecord".underscore # => "active_record" - # "ActiveRecord::Errors".underscore # => active_record/errors - def underscore(camel_cased_word) - camel_cased_word.to_s.gsub(/::/, '/'). - gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). - gsub(/([a-z\d])([A-Z])/,'\1_\2'). - tr("-", "_"). - downcase - end - - # Replaces underscores with dashes in the string. - # - # Example: - # "puni_puni" # => "puni-puni" - def dasherize(underscored_word) - underscored_word.gsub(/_/, '-') - end - - # Capitalizes the first word and turns underscores into spaces and strips a - # trailing "_id", if any. Like +titleize+, this is meant for creating pretty output. - # - # Examples: - # "employee_salary" # => "Employee salary" - # "author_id" # => "Author" - def humanize(lower_case_and_underscored_word) - lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize - end + # Removes the module part from the expression in the string. + # + # Examples: + # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections" + # "Inflections".demodulize # => "Inflections" + def demodulize(class_name_in_module) + class_name_in_module.to_s.gsub(/^.*::/, '') + end - # Removes the module part from the expression in the string. - # - # Examples: - # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections" - # "Inflections".demodulize # => "Inflections" - def demodulize(class_name_in_module) - class_name_in_module.to_s.gsub(/^.*::/, '') - end + # Create the name of a table like Rails does for models to table names. This method + # uses the +pluralize+ method on the last word in the string. + # + # Examples + # "RawScaledScorer".tableize # => "raw_scaled_scorers" + # "egg_and_ham".tableize # => "egg_and_hams" + # "fancyCategory".tableize # => "fancy_categories" + def tableize(class_name) + pluralize(underscore(class_name)) + end - # Create the name of a table like Rails does for models to table names. This method - # uses the +pluralize+ method on the last word in the string. - # - # Examples - # "RawScaledScorer".tableize # => "raw_scaled_scorers" - # "egg_and_ham".tableize # => "egg_and_hams" - # "fancyCategory".tableize # => "fancy_categories" - def tableize(class_name) - pluralize(underscore(class_name)) - end + # Create a class name from a plural table name like Rails does for table names to models. + # Note that this returns a string and not a Class. (To convert to an actual class + # follow +classify+ with +constantize+.) + # + # Examples: + # "egg_and_hams".classify # => "EggAndHam" + # "posts".classify # => "Post" + # + # Singular names are not handled correctly: + # "business".classify # => "Busines" + def classify(table_name) + # strip out any leading schema name + camelize(singularize(table_name.to_s.sub(/.*\./, ''))) + end - # Create a class name from a plural table name like Rails does for table names to models. - # Note that this returns a string and not a Class. (To convert to an actual class - # follow +classify+ with +constantize+.) - # - # Examples: - # "egg_and_hams".classify # => "EggAndHam" - # "posts".classify # => "Post" - # - # Singular names are not handled correctly: - # "business".classify # => "Busines" - def classify(table_name) - # strip out any leading schema name - camelize(singularize(table_name.to_s.sub(/.*\./, ''))) - end + # Creates a foreign key name from a class name. + # +separate_class_name_and_id_with_underscore+ sets whether + # the method should put '_' between the name and 'id'. + # + # Examples: + # "Message".foreign_key # => "message_id" + # "Message".foreign_key(false) # => "messageid" + # "Admin::Post".foreign_key # => "post_id" + def foreign_key(class_name, separate_class_name_and_id_with_underscore = true) + underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id") + end - # Creates a foreign key name from a class name. - # +separate_class_name_and_id_with_underscore+ sets whether - # the method should put '_' between the name and 'id'. - # - # Examples: - # "Message".foreign_key # => "message_id" - # "Message".foreign_key(false) # => "messageid" - # "Admin::Post".foreign_key # => "post_id" - def foreign_key(class_name, separate_class_name_and_id_with_underscore = true) - underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id") - end + # Tries to find a constant with the name specified in the argument string: + # + # "Module".constantize # => Module + # "Test::Unit".constantize # => Test::Unit + # + # The name is assumed to be the one of a top-level constant, no matter whether + # it starts with "::" or not. No lexical context is taken into account: + # + # C = 'outside' + # module M + # C = 'inside' + # C # => 'inside' + # "C".constantize # => 'outside', same as ::C + # end + # + # NameError is raised when the name is not in CamelCase or the constant is + # unknown. + def constantize(camel_cased_word) + unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word + raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!" + end - # Tries to find a constant with the name specified in the argument string: - # - # "Module".constantize # => Module - # "Test::Unit".constantize # => Test::Unit - # - # The name is assumed to be the one of a top-level constant, no matter whether - # it starts with "::" or not. No lexical context is taken into account: - # - # C = 'outside' - # module M - # C = 'inside' - # C # => 'inside' - # "C".constantize # => 'outside', same as ::C - # end - # - # NameError is raised when the name is not in CamelCase or the constant is - # unknown. - def constantize(camel_cased_word) - unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word - raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!" + Object.module_eval("::#{$1}", __FILE__, __LINE__) end - Object.module_eval("::#{$1}", __FILE__, __LINE__) - end - - # Turns a number into an ordinal string used to denote the position in an - # ordered sequence such as 1st, 2nd, 3rd, 4th. - # - # Examples: - # ordinalize(1) # => "1st" - # ordinalize(2) # => "2nd" - # ordinalize(1002) # => "1002nd" - # ordinalize(1003) # => "1003rd" - def ordinalize(number) - if (11..13).include?(number.to_i % 100) - "#{number}th" - else - case number.to_i % 10 - when 1; "#{number}st" - when 2; "#{number}nd" - when 3; "#{number}rd" - else "#{number}th" + # Turns a number into an ordinal string used to denote the position in an + # ordered sequence such as 1st, 2nd, 3rd, 4th. + # + # Examples: + # ordinalize(1) # => "1st" + # ordinalize(2) # => "2nd" + # ordinalize(1002) # => "1002nd" + # ordinalize(1003) # => "1003rd" + def ordinalize(number) + if (11..13).include?(number.to_i % 100) + "#{number}th" + else + case number.to_i % 10 + when 1; "#{number}st" + when 2; "#{number}nd" + when 3; "#{number}rd" + else "#{number}th" + end end end end diff --git a/activesupport/lib/active_support/ordered_options.rb b/activesupport/lib/active_support/ordered_options.rb index 306376e9ae..642045186f 100644 --- a/activesupport/lib/active_support/ordered_options.rb +++ b/activesupport/lib/active_support/ordered_options.rb @@ -1,17 +1,19 @@ -class OrderedOptions < ActiveSupport::OrderedHash #:nodoc: - def []=(key, value) - super(key.to_sym, value) - end +module ActiveSupport #:nodoc: + class OrderedOptions < OrderedHash #:nodoc: + def []=(key, value) + super(key.to_sym, value) + end - def [](key) - super(key.to_sym) - end + def [](key) + super(key.to_sym) + end - def method_missing(name, *args) - if name.to_s =~ /(.*)=$/ - self[$1.to_sym] = args.first - else - self[name] + def method_missing(name, *args) + if name.to_s =~ /(.*)=$/ + self[$1.to_sym] = args.first + else + self[name] + end end end end diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index a71c819fba..5b2d42aa3c 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -4,7 +4,7 @@ # * Retrieve and display zones with a friendlier name (e.g., "Eastern Time (US & Canada)" instead of "America/New_York"). # * Lazily load TZInfo::Timezone instances only when they're needed. # * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, +parse+, +at+ and +now+ methods. -# +# # If you set config.time_zone in the Rails Initializer, you can access this TimeZone object via Time.zone: # # # environment.rb: @@ -16,379 +16,381 @@ # Time.zone.name # => "Eastern Time (US & Canada)" # Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00 # -# The version of TZInfo bundled with Active Support only includes the definitions necessary to support the zones +# The version of TZInfo bundled with Active Support only includes the definitions necessary to support the zones # defined by the TimeZone class. If you need to use zones that aren't defined by TimeZone, you'll need to install the TZInfo gem # (if a recent version of the gem is installed locally, this will be used instead of the bundled version.) -class TimeZone - unless const_defined?(:MAPPING) - # Keys are Rails TimeZone names, values are TZInfo identifiers - MAPPING = { - "International Date Line West" => "Pacific/Midway", - "Midway Island" => "Pacific/Midway", - "Samoa" => "Pacific/Pago_Pago", - "Hawaii" => "Pacific/Honolulu", - "Alaska" => "America/Juneau", - "Pacific Time (US & Canada)" => "America/Los_Angeles", - "Tijuana" => "America/Tijuana", - "Mountain Time (US & Canada)" => "America/Denver", - "Arizona" => "America/Phoenix", - "Chihuahua" => "America/Chihuahua", - "Mazatlan" => "America/Mazatlan", - "Central Time (US & Canada)" => "America/Chicago", - "Saskatchewan" => "America/Regina", - "Guadalajara" => "America/Mexico_City", - "Mexico City" => "America/Mexico_City", - "Monterrey" => "America/Monterrey", - "Central America" => "America/Guatemala", - "Eastern Time (US & Canada)" => "America/New_York", - "Indiana (East)" => "America/Indiana/Indianapolis", - "Bogota" => "America/Bogota", - "Lima" => "America/Lima", - "Quito" => "America/Lima", - "Atlantic Time (Canada)" => "America/Halifax", - "Caracas" => "America/Caracas", - "La Paz" => "America/La_Paz", - "Santiago" => "America/Santiago", - "Newfoundland" => "America/St_Johns", - "Brasilia" => "America/Argentina/Buenos_Aires", - "Buenos Aires" => "America/Argentina/Buenos_Aires", - "Georgetown" => "America/Argentina/San_Juan", - "Greenland" => "America/Godthab", - "Mid-Atlantic" => "Atlantic/South_Georgia", - "Azores" => "Atlantic/Azores", - "Cape Verde Is." => "Atlantic/Cape_Verde", - "Dublin" => "Europe/Dublin", - "Edinburgh" => "Europe/Dublin", - "Lisbon" => "Europe/Lisbon", - "London" => "Europe/London", - "Casablanca" => "Africa/Casablanca", - "Monrovia" => "Africa/Monrovia", - "UTC" => "Etc/UTC", - "Belgrade" => "Europe/Belgrade", - "Bratislava" => "Europe/Bratislava", - "Budapest" => "Europe/Budapest", - "Ljubljana" => "Europe/Ljubljana", - "Prague" => "Europe/Prague", - "Sarajevo" => "Europe/Sarajevo", - "Skopje" => "Europe/Skopje", - "Warsaw" => "Europe/Warsaw", - "Zagreb" => "Europe/Zagreb", - "Brussels" => "Europe/Brussels", - "Copenhagen" => "Europe/Copenhagen", - "Madrid" => "Europe/Madrid", - "Paris" => "Europe/Paris", - "Amsterdam" => "Europe/Amsterdam", - "Berlin" => "Europe/Berlin", - "Bern" => "Europe/Berlin", - "Rome" => "Europe/Rome", - "Stockholm" => "Europe/Stockholm", - "Vienna" => "Europe/Vienna", - "West Central Africa" => "Africa/Algiers", - "Bucharest" => "Europe/Bucharest", - "Cairo" => "Africa/Cairo", - "Helsinki" => "Europe/Helsinki", - "Kyev" => "Europe/Kiev", - "Riga" => "Europe/Riga", - "Sofia" => "Europe/Sofia", - "Tallinn" => "Europe/Tallinn", - "Vilnius" => "Europe/Vilnius", - "Athens" => "Europe/Athens", - "Istanbul" => "Europe/Istanbul", - "Minsk" => "Europe/Minsk", - "Jerusalem" => "Asia/Jerusalem", - "Harare" => "Africa/Harare", - "Pretoria" => "Africa/Johannesburg", - "Moscow" => "Europe/Moscow", - "St. Petersburg" => "Europe/Moscow", - "Volgograd" => "Europe/Moscow", - "Kuwait" => "Asia/Kuwait", - "Riyadh" => "Asia/Riyadh", - "Nairobi" => "Africa/Nairobi", - "Baghdad" => "Asia/Baghdad", - "Tehran" => "Asia/Tehran", - "Abu Dhabi" => "Asia/Muscat", - "Muscat" => "Asia/Muscat", - "Baku" => "Asia/Baku", - "Tbilisi" => "Asia/Tbilisi", - "Yerevan" => "Asia/Yerevan", - "Kabul" => "Asia/Kabul", - "Ekaterinburg" => "Asia/Yekaterinburg", - "Islamabad" => "Asia/Karachi", - "Karachi" => "Asia/Karachi", - "Tashkent" => "Asia/Tashkent", - "Chennai" => "Asia/Kolkata", - "Kolkata" => "Asia/Kolkata", - "Mumbai" => "Asia/Kolkata", - "New Delhi" => "Asia/Kolkata", - "Kathmandu" => "Asia/Katmandu", - "Astana" => "Asia/Dhaka", - "Dhaka" => "Asia/Dhaka", - "Sri Jayawardenepura" => "Asia/Dhaka", - "Almaty" => "Asia/Almaty", - "Novosibirsk" => "Asia/Novosibirsk", - "Rangoon" => "Asia/Rangoon", - "Bangkok" => "Asia/Bangkok", - "Hanoi" => "Asia/Bangkok", - "Jakarta" => "Asia/Jakarta", - "Krasnoyarsk" => "Asia/Krasnoyarsk", - "Beijing" => "Asia/Shanghai", - "Chongqing" => "Asia/Chongqing", - "Hong Kong" => "Asia/Hong_Kong", - "Urumqi" => "Asia/Urumqi", - "Kuala Lumpur" => "Asia/Kuala_Lumpur", - "Singapore" => "Asia/Singapore", - "Taipei" => "Asia/Taipei", - "Perth" => "Australia/Perth", - "Irkutsk" => "Asia/Irkutsk", - "Ulaan Bataar" => "Asia/Ulaanbaatar", - "Seoul" => "Asia/Seoul", - "Osaka" => "Asia/Tokyo", - "Sapporo" => "Asia/Tokyo", - "Tokyo" => "Asia/Tokyo", - "Yakutsk" => "Asia/Yakutsk", - "Darwin" => "Australia/Darwin", - "Adelaide" => "Australia/Adelaide", - "Canberra" => "Australia/Melbourne", - "Melbourne" => "Australia/Melbourne", - "Sydney" => "Australia/Sydney", - "Brisbane" => "Australia/Brisbane", - "Hobart" => "Australia/Hobart", - "Vladivostok" => "Asia/Vladivostok", - "Guam" => "Pacific/Guam", - "Port Moresby" => "Pacific/Port_Moresby", - "Magadan" => "Asia/Magadan", - "Solomon Is." => "Asia/Magadan", - "New Caledonia" => "Pacific/Noumea", - "Fiji" => "Pacific/Fiji", - "Kamchatka" => "Asia/Kamchatka", - "Marshall Is." => "Pacific/Majuro", - "Auckland" => "Pacific/Auckland", - "Wellington" => "Pacific/Auckland", - "Nuku'alofa" => "Pacific/Tongatapu" - }.each { |name, zone| name.freeze; zone.freeze } - MAPPING.freeze - end - - include Comparable - attr_reader :name +module ActiveSupport + class TimeZone + unless const_defined?(:MAPPING) + # Keys are Rails TimeZone names, values are TZInfo identifiers + MAPPING = { + "International Date Line West" => "Pacific/Midway", + "Midway Island" => "Pacific/Midway", + "Samoa" => "Pacific/Pago_Pago", + "Hawaii" => "Pacific/Honolulu", + "Alaska" => "America/Juneau", + "Pacific Time (US & Canada)" => "America/Los_Angeles", + "Tijuana" => "America/Tijuana", + "Mountain Time (US & Canada)" => "America/Denver", + "Arizona" => "America/Phoenix", + "Chihuahua" => "America/Chihuahua", + "Mazatlan" => "America/Mazatlan", + "Central Time (US & Canada)" => "America/Chicago", + "Saskatchewan" => "America/Regina", + "Guadalajara" => "America/Mexico_City", + "Mexico City" => "America/Mexico_City", + "Monterrey" => "America/Monterrey", + "Central America" => "America/Guatemala", + "Eastern Time (US & Canada)" => "America/New_York", + "Indiana (East)" => "America/Indiana/Indianapolis", + "Bogota" => "America/Bogota", + "Lima" => "America/Lima", + "Quito" => "America/Lima", + "Atlantic Time (Canada)" => "America/Halifax", + "Caracas" => "America/Caracas", + "La Paz" => "America/La_Paz", + "Santiago" => "America/Santiago", + "Newfoundland" => "America/St_Johns", + "Brasilia" => "America/Argentina/Buenos_Aires", + "Buenos Aires" => "America/Argentina/Buenos_Aires", + "Georgetown" => "America/Argentina/San_Juan", + "Greenland" => "America/Godthab", + "Mid-Atlantic" => "Atlantic/South_Georgia", + "Azores" => "Atlantic/Azores", + "Cape Verde Is." => "Atlantic/Cape_Verde", + "Dublin" => "Europe/Dublin", + "Edinburgh" => "Europe/Dublin", + "Lisbon" => "Europe/Lisbon", + "London" => "Europe/London", + "Casablanca" => "Africa/Casablanca", + "Monrovia" => "Africa/Monrovia", + "UTC" => "Etc/UTC", + "Belgrade" => "Europe/Belgrade", + "Bratislava" => "Europe/Bratislava", + "Budapest" => "Europe/Budapest", + "Ljubljana" => "Europe/Ljubljana", + "Prague" => "Europe/Prague", + "Sarajevo" => "Europe/Sarajevo", + "Skopje" => "Europe/Skopje", + "Warsaw" => "Europe/Warsaw", + "Zagreb" => "Europe/Zagreb", + "Brussels" => "Europe/Brussels", + "Copenhagen" => "Europe/Copenhagen", + "Madrid" => "Europe/Madrid", + "Paris" => "Europe/Paris", + "Amsterdam" => "Europe/Amsterdam", + "Berlin" => "Europe/Berlin", + "Bern" => "Europe/Berlin", + "Rome" => "Europe/Rome", + "Stockholm" => "Europe/Stockholm", + "Vienna" => "Europe/Vienna", + "West Central Africa" => "Africa/Algiers", + "Bucharest" => "Europe/Bucharest", + "Cairo" => "Africa/Cairo", + "Helsinki" => "Europe/Helsinki", + "Kyev" => "Europe/Kiev", + "Riga" => "Europe/Riga", + "Sofia" => "Europe/Sofia", + "Tallinn" => "Europe/Tallinn", + "Vilnius" => "Europe/Vilnius", + "Athens" => "Europe/Athens", + "Istanbul" => "Europe/Istanbul", + "Minsk" => "Europe/Minsk", + "Jerusalem" => "Asia/Jerusalem", + "Harare" => "Africa/Harare", + "Pretoria" => "Africa/Johannesburg", + "Moscow" => "Europe/Moscow", + "St. Petersburg" => "Europe/Moscow", + "Volgograd" => "Europe/Moscow", + "Kuwait" => "Asia/Kuwait", + "Riyadh" => "Asia/Riyadh", + "Nairobi" => "Africa/Nairobi", + "Baghdad" => "Asia/Baghdad", + "Tehran" => "Asia/Tehran", + "Abu Dhabi" => "Asia/Muscat", + "Muscat" => "Asia/Muscat", + "Baku" => "Asia/Baku", + "Tbilisi" => "Asia/Tbilisi", + "Yerevan" => "Asia/Yerevan", + "Kabul" => "Asia/Kabul", + "Ekaterinburg" => "Asia/Yekaterinburg", + "Islamabad" => "Asia/Karachi", + "Karachi" => "Asia/Karachi", + "Tashkent" => "Asia/Tashkent", + "Chennai" => "Asia/Kolkata", + "Kolkata" => "Asia/Kolkata", + "Mumbai" => "Asia/Kolkata", + "New Delhi" => "Asia/Kolkata", + "Kathmandu" => "Asia/Katmandu", + "Astana" => "Asia/Dhaka", + "Dhaka" => "Asia/Dhaka", + "Sri Jayawardenepura" => "Asia/Dhaka", + "Almaty" => "Asia/Almaty", + "Novosibirsk" => "Asia/Novosibirsk", + "Rangoon" => "Asia/Rangoon", + "Bangkok" => "Asia/Bangkok", + "Hanoi" => "Asia/Bangkok", + "Jakarta" => "Asia/Jakarta", + "Krasnoyarsk" => "Asia/Krasnoyarsk", + "Beijing" => "Asia/Shanghai", + "Chongqing" => "Asia/Chongqing", + "Hong Kong" => "Asia/Hong_Kong", + "Urumqi" => "Asia/Urumqi", + "Kuala Lumpur" => "Asia/Kuala_Lumpur", + "Singapore" => "Asia/Singapore", + "Taipei" => "Asia/Taipei", + "Perth" => "Australia/Perth", + "Irkutsk" => "Asia/Irkutsk", + "Ulaan Bataar" => "Asia/Ulaanbaatar", + "Seoul" => "Asia/Seoul", + "Osaka" => "Asia/Tokyo", + "Sapporo" => "Asia/Tokyo", + "Tokyo" => "Asia/Tokyo", + "Yakutsk" => "Asia/Yakutsk", + "Darwin" => "Australia/Darwin", + "Adelaide" => "Australia/Adelaide", + "Canberra" => "Australia/Melbourne", + "Melbourne" => "Australia/Melbourne", + "Sydney" => "Australia/Sydney", + "Brisbane" => "Australia/Brisbane", + "Hobart" => "Australia/Hobart", + "Vladivostok" => "Asia/Vladivostok", + "Guam" => "Pacific/Guam", + "Port Moresby" => "Pacific/Port_Moresby", + "Magadan" => "Asia/Magadan", + "Solomon Is." => "Asia/Magadan", + "New Caledonia" => "Pacific/Noumea", + "Fiji" => "Pacific/Fiji", + "Kamchatka" => "Asia/Kamchatka", + "Marshall Is." => "Pacific/Majuro", + "Auckland" => "Pacific/Auckland", + "Wellington" => "Pacific/Auckland", + "Nuku'alofa" => "Pacific/Tongatapu" + }.each { |name, zone| name.freeze; zone.freeze } + MAPPING.freeze + end - # Create a new TimeZone object with the given name and offset. The - # offset is the number of seconds that this time zone is offset from UTC - # (GMT). Seconds were chosen as the offset unit because that is the unit that - # Ruby uses to represent time zone offsets (see Time#utc_offset). - def initialize(name, utc_offset, tzinfo = nil) - @name = name - @utc_offset = utc_offset - @tzinfo = tzinfo - end + include Comparable + attr_reader :name - def utc_offset - @utc_offset ||= tzinfo.current_period.utc_offset - end - - # Returns the offset of this time zone as a formatted string, of the - # format "+HH:MM". - def formatted_offset(colon=true, alternate_utc_string = nil) - utc_offset == 0 && alternate_utc_string || utc_offset.to_utc_offset_s(colon) - end + # Create a new TimeZone object with the given name and offset. The + # offset is the number of seconds that this time zone is offset from UTC + # (GMT). Seconds were chosen as the offset unit because that is the unit that + # Ruby uses to represent time zone offsets (see Time#utc_offset). + def initialize(name, utc_offset, tzinfo = nil) + @name = name + @utc_offset = utc_offset + @tzinfo = tzinfo + end - # Compare this time zone to the parameter. The two are comapred first on - # their offsets, and then by name. - def <=>(zone) - result = (utc_offset <=> zone.utc_offset) - result = (name <=> zone.name) if result == 0 - result - end + def utc_offset + @utc_offset ||= tzinfo.current_period.utc_offset + end - # Returns a textual representation of this time zone. - def to_s - "(GMT#{formatted_offset}) #{name}" - end + # Returns the offset of this time zone as a formatted string, of the + # format "+HH:MM". + def formatted_offset(colon=true, alternate_utc_string = nil) + utc_offset == 0 && alternate_utc_string || utc_offset.to_utc_offset_s(colon) + end - # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from given values. Example: - # - # Time.zone = "Hawaii" # => "Hawaii" - # Time.zone.local(2007, 2, 1, 15, 30, 45) # => Thu, 01 Feb 2007 15:30:45 HST -10:00 - def local(*args) - time = Time.utc_time(*args) - ActiveSupport::TimeWithZone.new(nil, self, time) - end + # Compare this time zone to the parameter. The two are comapred first on + # their offsets, and then by name. + def <=>(zone) + result = (utc_offset <=> zone.utc_offset) + result = (name <=> zone.name) if result == 0 + result + end - # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from number of seconds since the Unix epoch. Example: - # - # Time.zone = "Hawaii" # => "Hawaii" - # Time.utc(2000).to_f # => 946684800.0 - # Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00 - def at(secs) - utc = Time.at(secs).utc rescue DateTime.civil(1970).since(secs) - utc.in_time_zone(self) - end + # Returns a textual representation of this time zone. + def to_s + "(GMT#{formatted_offset}) #{name}" + end - # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from parsed string. Example: - # - # Time.zone = "Hawaii" # => "Hawaii" - # Time.zone.parse('1999-12-31 14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00 - # - # If upper components are missing from the string, they are supplied from TimeZone#now: - # - # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00 - # Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00 - def parse(str, now=now) - date_parts = Date._parse(str) - return if date_parts.blank? - time = Time.parse(str, now) rescue DateTime.parse(str) - if date_parts[:offset].nil? + # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from given values. Example: + # + # Time.zone = "Hawaii" # => "Hawaii" + # Time.zone.local(2007, 2, 1, 15, 30, 45) # => Thu, 01 Feb 2007 15:30:45 HST -10:00 + def local(*args) + time = Time.utc_time(*args) ActiveSupport::TimeWithZone.new(nil, self, time) - else - time.in_time_zone(self) end - end - # Returns an ActiveSupport::TimeWithZone instance representing the current time - # in the time zone represented by +self+. Example: - # - # Time.zone = 'Hawaii' # => "Hawaii" - # Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00 - def now - Time.now.utc.in_time_zone(self) - end + # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from number of seconds since the Unix epoch. Example: + # + # Time.zone = "Hawaii" # => "Hawaii" + # Time.utc(2000).to_f # => 946684800.0 + # Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + def at(secs) + utc = Time.at(secs).utc rescue DateTime.civil(1970).since(secs) + utc.in_time_zone(self) + end - # Return the current date in this time zone. - def today - tzinfo.now.to_date - end + # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from parsed string. Example: + # + # Time.zone = "Hawaii" # => "Hawaii" + # Time.zone.parse('1999-12-31 14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # + # If upper components are missing from the string, they are supplied from TimeZone#now: + # + # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00 + # Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00 + def parse(str, now=now) + date_parts = Date._parse(str) + return if date_parts.blank? + time = Time.parse(str, now) rescue DateTime.parse(str) + if date_parts[:offset].nil? + ActiveSupport::TimeWithZone.new(nil, self, time) + else + time.in_time_zone(self) + end + end - # Adjust the given time to the simultaneous time in the time zone represented by +self+. Returns a - # Time.utc() instance -- if you want an ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead. - def utc_to_local(time) - tzinfo.utc_to_local(time) - end + # Returns an ActiveSupport::TimeWithZone instance representing the current time + # in the time zone represented by +self+. Example: + # + # Time.zone = 'Hawaii' # => "Hawaii" + # Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00 + def now + Time.now.utc.in_time_zone(self) + end - # Adjust the given time to the simultaneous time in UTC. Returns a Time.utc() instance. - def local_to_utc(time, dst=true) - tzinfo.local_to_utc(time, dst) - end + # Return the current date in this time zone. + def today + tzinfo.now.to_date + end - # Available so that TimeZone instances respond like TZInfo::Timezone instances - def period_for_utc(time) - tzinfo.period_for_utc(time) - end + # Adjust the given time to the simultaneous time in the time zone represented by +self+. Returns a + # Time.utc() instance -- if you want an ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead. + def utc_to_local(time) + tzinfo.utc_to_local(time) + end - # Available so that TimeZone instances respond like TZInfo::Timezone instances - def period_for_local(time, dst=true) - tzinfo.period_for_local(time, dst) - end + # Adjust the given time to the simultaneous time in UTC. Returns a Time.utc() instance. + def local_to_utc(time, dst=true) + tzinfo.local_to_utc(time, dst) + end - # TODO: Preload instead of lazy load for thread safety - def tzinfo - @tzinfo ||= TZInfo::Timezone.get(MAPPING[name]) - end + # Available so that TimeZone instances respond like TZInfo::Timezone instances + def period_for_utc(time) + tzinfo.period_for_utc(time) + end - unless const_defined?(:ZONES) - ZONES = [] - ZONES_MAP = {} - [[-39_600, "International Date Line West", "Midway Island", "Samoa" ], - [-36_000, "Hawaii" ], - [-32_400, "Alaska" ], - [-28_800, "Pacific Time (US & Canada)", "Tijuana" ], - [-25_200, "Mountain Time (US & Canada)", "Chihuahua", "Mazatlan", - "Arizona" ], - [-21_600, "Central Time (US & Canada)", "Saskatchewan", "Guadalajara", - "Mexico City", "Monterrey", "Central America" ], - [-18_000, "Eastern Time (US & Canada)", "Indiana (East)", "Bogota", - "Lima", "Quito" ], - [-14_400, "Atlantic Time (Canada)", "Caracas", "La Paz", "Santiago" ], - [-12_600, "Newfoundland" ], - [-10_800, "Brasilia", "Buenos Aires", "Georgetown", "Greenland" ], - [ -7_200, "Mid-Atlantic" ], - [ -3_600, "Azores", "Cape Verde Is." ], - [ 0, "Dublin", "Edinburgh", "Lisbon", "London", "Casablanca", - "Monrovia", "UTC" ], - [ 3_600, "Belgrade", "Bratislava", "Budapest", "Ljubljana", "Prague", - "Sarajevo", "Skopje", "Warsaw", "Zagreb", "Brussels", - "Copenhagen", "Madrid", "Paris", "Amsterdam", "Berlin", - "Bern", "Rome", "Stockholm", "Vienna", - "West Central Africa" ], - [ 7_200, "Bucharest", "Cairo", "Helsinki", "Kyev", "Riga", "Sofia", - "Tallinn", "Vilnius", "Athens", "Istanbul", "Minsk", - "Jerusalem", "Harare", "Pretoria" ], - [ 10_800, "Moscow", "St. Petersburg", "Volgograd", "Kuwait", "Riyadh", - "Nairobi", "Baghdad" ], - [ 12_600, "Tehran" ], - [ 14_400, "Abu Dhabi", "Muscat", "Baku", "Tbilisi", "Yerevan" ], - [ 16_200, "Kabul" ], - [ 18_000, "Ekaterinburg", "Islamabad", "Karachi", "Tashkent" ], - [ 19_800, "Chennai", "Kolkata", "Mumbai", "New Delhi" ], - [ 20_700, "Kathmandu" ], - [ 21_600, "Astana", "Dhaka", "Sri Jayawardenepura", "Almaty", - "Novosibirsk" ], - [ 23_400, "Rangoon" ], - [ 25_200, "Bangkok", "Hanoi", "Jakarta", "Krasnoyarsk" ], - [ 28_800, "Beijing", "Chongqing", "Hong Kong", "Urumqi", - "Kuala Lumpur", "Singapore", "Taipei", "Perth", "Irkutsk", - "Ulaan Bataar" ], - [ 32_400, "Seoul", "Osaka", "Sapporo", "Tokyo", "Yakutsk" ], - [ 34_200, "Darwin", "Adelaide" ], - [ 36_000, "Canberra", "Melbourne", "Sydney", "Brisbane", "Hobart", - "Vladivostok", "Guam", "Port Moresby" ], - [ 39_600, "Magadan", "Solomon Is.", "New Caledonia" ], - [ 43_200, "Fiji", "Kamchatka", "Marshall Is.", "Auckland", - "Wellington" ], - [ 46_800, "Nuku'alofa" ]]. - each do |offset, *places| - places.each do |place| - place.freeze - zone = new(place, offset) - ZONES << zone - ZONES_MAP[place] = zone - end + # Available so that TimeZone instances respond like TZInfo::Timezone instances + def period_for_local(time, dst=true) + tzinfo.period_for_local(time, dst) end - ZONES.sort! - ZONES.freeze - ZONES_MAP.freeze - US_ZONES = ZONES.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ } - US_ZONES.freeze - end + # TODO: Preload instead of lazy load for thread safety + def tzinfo + @tzinfo ||= TZInfo::Timezone.get(MAPPING[name]) + end - class << self - alias_method :create, :new + unless const_defined?(:ZONES) + ZONES = [] + ZONES_MAP = {} + [[-39_600, "International Date Line West", "Midway Island", "Samoa" ], + [-36_000, "Hawaii" ], + [-32_400, "Alaska" ], + [-28_800, "Pacific Time (US & Canada)", "Tijuana" ], + [-25_200, "Mountain Time (US & Canada)", "Chihuahua", "Mazatlan", + "Arizona" ], + [-21_600, "Central Time (US & Canada)", "Saskatchewan", "Guadalajara", + "Mexico City", "Monterrey", "Central America" ], + [-18_000, "Eastern Time (US & Canada)", "Indiana (East)", "Bogota", + "Lima", "Quito" ], + [-14_400, "Atlantic Time (Canada)", "Caracas", "La Paz", "Santiago" ], + [-12_600, "Newfoundland" ], + [-10_800, "Brasilia", "Buenos Aires", "Georgetown", "Greenland" ], + [ -7_200, "Mid-Atlantic" ], + [ -3_600, "Azores", "Cape Verde Is." ], + [ 0, "Dublin", "Edinburgh", "Lisbon", "London", "Casablanca", + "Monrovia", "UTC" ], + [ 3_600, "Belgrade", "Bratislava", "Budapest", "Ljubljana", "Prague", + "Sarajevo", "Skopje", "Warsaw", "Zagreb", "Brussels", + "Copenhagen", "Madrid", "Paris", "Amsterdam", "Berlin", + "Bern", "Rome", "Stockholm", "Vienna", + "West Central Africa" ], + [ 7_200, "Bucharest", "Cairo", "Helsinki", "Kyev", "Riga", "Sofia", + "Tallinn", "Vilnius", "Athens", "Istanbul", "Minsk", + "Jerusalem", "Harare", "Pretoria" ], + [ 10_800, "Moscow", "St. Petersburg", "Volgograd", "Kuwait", "Riyadh", + "Nairobi", "Baghdad" ], + [ 12_600, "Tehran" ], + [ 14_400, "Abu Dhabi", "Muscat", "Baku", "Tbilisi", "Yerevan" ], + [ 16_200, "Kabul" ], + [ 18_000, "Ekaterinburg", "Islamabad", "Karachi", "Tashkent" ], + [ 19_800, "Chennai", "Kolkata", "Mumbai", "New Delhi" ], + [ 20_700, "Kathmandu" ], + [ 21_600, "Astana", "Dhaka", "Sri Jayawardenepura", "Almaty", + "Novosibirsk" ], + [ 23_400, "Rangoon" ], + [ 25_200, "Bangkok", "Hanoi", "Jakarta", "Krasnoyarsk" ], + [ 28_800, "Beijing", "Chongqing", "Hong Kong", "Urumqi", + "Kuala Lumpur", "Singapore", "Taipei", "Perth", "Irkutsk", + "Ulaan Bataar" ], + [ 32_400, "Seoul", "Osaka", "Sapporo", "Tokyo", "Yakutsk" ], + [ 34_200, "Darwin", "Adelaide" ], + [ 36_000, "Canberra", "Melbourne", "Sydney", "Brisbane", "Hobart", + "Vladivostok", "Guam", "Port Moresby" ], + [ 39_600, "Magadan", "Solomon Is.", "New Caledonia" ], + [ 43_200, "Fiji", "Kamchatka", "Marshall Is.", "Auckland", + "Wellington" ], + [ 46_800, "Nuku'alofa" ]]. + each do |offset, *places| + places.each do |place| + place.freeze + zone = new(place, offset) + ZONES << zone + ZONES_MAP[place] = zone + end + end + ZONES.sort! + ZONES.freeze + ZONES_MAP.freeze - # Return a TimeZone instance with the given name, or +nil+ if no - # such TimeZone instance exists. (This exists to support the use of - # this class with the +composed_of+ macro.) - def new(name) - self[name] + US_ZONES = ZONES.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ } + US_ZONES.freeze end - # Return an array of all TimeZone objects. There are multiple - # TimeZone objects per time zone, in many cases, to make it easier - # for users to find their own time zone. - def all - ZONES - end + class << self + alias_method :create, :new - # Locate a specific time zone object. If the argument is a string, it - # is interpreted to mean the name of the timezone to locate. If it is a - # numeric value it is either the hour offset, or the second offset, of the - # timezone to find. (The first one with that offset will be returned.) - # Returns +nil+ if no such time zone is known to the system. - def [](arg) - case arg - when String - ZONES_MAP[arg] - when Numeric, ActiveSupport::Duration - arg *= 3600 if arg.abs <= 13 - all.find { |z| z.utc_offset == arg.to_i } - else - raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}" + # Return a TimeZone instance with the given name, or +nil+ if no + # such TimeZone instance exists. (This exists to support the use of + # this class with the +composed_of+ macro.) + def new(name) + self[name] end - end - # A convenience method for returning a collection of TimeZone objects - # for time zones in the USA. - def us_zones - US_ZONES + # Return an array of all TimeZone objects. There are multiple + # TimeZone objects per time zone, in many cases, to make it easier + # for users to find their own time zone. + def all + ZONES + end + + # Locate a specific time zone object. If the argument is a string, it + # is interpreted to mean the name of the timezone to locate. If it is a + # numeric value it is either the hour offset, or the second offset, of the + # timezone to find. (The first one with that offset will be returned.) + # Returns +nil+ if no such time zone is known to the system. + def [](arg) + case arg + when String + ZONES_MAP[arg] + when Numeric, ActiveSupport::Duration + arg *= 3600 if arg.abs <= 13 + all.find { |z| z.utc_offset == arg.to_i } + else + raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}" + end + end + + # A convenience method for returning a collection of TimeZone objects + # for time zones in the USA. + def us_zones + US_ZONES + end end end end diff --git a/activesupport/test/core_ext/date_ext_test.rb b/activesupport/test/core_ext/date_ext_test.rb index 11b6056047..ddfe1f904f 100644 --- a/activesupport/test/core_ext/date_ext_test.rb +++ b/activesupport/test/core_ext/date_ext_test.rb @@ -172,7 +172,7 @@ class DateExtCalculationsTest < Test::Unit::TestCase def test_last_month_on_31st assert_equal Date.new(2004, 2, 29), Date.new(2004, 3, 31).last_month - end + end def test_yesterday_constructor assert_equal Date.today - 1, Date.yesterday @@ -197,11 +197,11 @@ class DateExtCalculationsTest < Test::Unit::TestCase def test_end_of_day assert_equal Time.local(2005,2,21,23,59,59), Date.new(2005,2,21).end_of_day end - + def test_date_acts_like_date assert Date.new.acts_like_date? end - + def test_xmlschema with_env_tz 'US/Eastern' do assert_match(/^1980-02-28T00:00:00-05:?00$/, Date.new(1980, 2, 28).xmlschema) @@ -213,7 +213,7 @@ class DateExtCalculationsTest < Test::Unit::TestCase end end end - + uses_mocha 'TestDateCurrent' do def test_current_returns_date_today_when_zone_default_not_set with_env_tz 'US/Central' do @@ -222,10 +222,10 @@ class DateExtCalculationsTest < Test::Unit::TestCase assert_equal Date.new(1999, 12, 31), Date.current end end - + def test_current_returns_time_zone_today_when_zone_default_set silence_warnings do # silence warnings raised by tzinfo gem - Time.zone_default = TimeZone['Eastern Time (US & Canada)'] + Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'US/Central' do Time.stubs(:now).returns Time.local(1999, 12, 31, 23) assert_equal Date.new(1999, 12, 31), Date.today @@ -243,5 +243,5 @@ class DateExtCalculationsTest < Test::Unit::TestCase yield ensure old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') - end + end end diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index ed45daf403..854a3a05e1 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -219,14 +219,14 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_equal Rational(-6, 24), DateTime.local_offset end end - + def test_utc? assert_equal true, DateTime.civil(2005, 2, 21, 10, 11, 12).utc? assert_equal true, DateTime.civil(2005, 2, 21, 10, 11, 12, 0).utc? assert_equal false, DateTime.civil(2005, 2, 21, 10, 11, 12, 0.25).utc? assert_equal false, DateTime.civil(2005, 2, 21, 10, 11, 12, -0.25).utc? end - + def test_utc_offset assert_equal 0, DateTime.civil(2005, 2, 21, 10, 11, 12).utc_offset assert_equal 0, DateTime.civil(2005, 2, 21, 10, 11, 12, 0).utc_offset @@ -234,7 +234,7 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_equal( -21600, DateTime.civil(2005, 2, 21, 10, 11, 12, -0.25).utc_offset ) assert_equal( -18000, DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-5, 24)).utc_offset ) end - + def test_utc assert_equal DateTime.civil(2005, 2, 21, 16, 11, 12, 0), DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-6, 24)).utc assert_equal DateTime.civil(2005, 2, 21, 15, 11, 12, 0), DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-5, 24)).utc @@ -242,37 +242,37 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_equal DateTime.civil(2005, 2, 21, 9, 11, 12, 0), DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(1, 24)).utc assert_equal DateTime.civil(2005, 2, 21, 9, 11, 12, 0), DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(1, 24)).getutc end - + def test_formatted_offset_with_utc assert_equal '+00:00', DateTime.civil(2000).formatted_offset assert_equal '+0000', DateTime.civil(2000).formatted_offset(false) assert_equal 'UTC', DateTime.civil(2000).formatted_offset(true, 'UTC') end - + def test_formatted_offset_with_local dt = DateTime.civil(2005, 2, 21, 10, 11, 12, Rational(-5, 24)) assert_equal '-05:00', dt.formatted_offset assert_equal '-0500', dt.formatted_offset(false) end - + def test_compare_with_time assert_equal 1, DateTime.civil(2000) <=> Time.utc(1999, 12, 31, 23, 59, 59) assert_equal 0, DateTime.civil(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0) assert_equal(-1, DateTime.civil(2000) <=> Time.utc(2000, 1, 1, 0, 0, 1)) end - + def test_compare_with_datetime assert_equal 1, DateTime.civil(2000) <=> DateTime.civil(1999, 12, 31, 23, 59, 59) assert_equal 0, DateTime.civil(2000) <=> DateTime.civil(2000, 1, 1, 0, 0, 0) assert_equal(-1, DateTime.civil(2000) <=> DateTime.civil(2000, 1, 1, 0, 0, 1)) end - + def test_compare_with_time_with_zone - assert_equal 1, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(1999, 12, 31, 23, 59, 59), TimeZone['UTC'] ) - assert_equal 0, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0), TimeZone['UTC'] ) - assert_equal(-1, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), TimeZone['UTC'] )) + assert_equal 1, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone['UTC'] ) + assert_equal 0, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['UTC'] ) + assert_equal(-1, DateTime.civil(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone['UTC'] )) end - + def test_to_f assert_equal 946684800.0, DateTime.civil(2000).to_f assert_equal 946684800.0, DateTime.civil(1999,12,31,19,0,0,Rational(-5,24)).to_f diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index 7b17fe71db..f802ed8760 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -29,7 +29,7 @@ class DurationTest < Test::Unit::TestCase flunk("ArgumentError should be raised, but we got #{$!.class} instead") end end - + uses_mocha 'TestDurationSinceAndAgoWithCurrentTime' do def test_since_and_ago_anchored_to_time_now_when_time_zone_default_not_set Time.zone_default = nil @@ -43,10 +43,10 @@ class DurationTest < Test::Unit::TestCase assert_equal Time.local(1999,12,31,23,59,55), 5.seconds.ago end end - + def test_since_and_ago_anchored_to_time_zone_now_when_time_zone_default_set silence_warnings do # silence warnings raised by tzinfo gem - Time.zone_default = TimeZone['Eastern Time (US & Canada)'] + Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(2000) # since @@ -63,7 +63,7 @@ class DurationTest < Test::Unit::TestCase Time.zone_default = nil end end - + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index e53b7193ea..17a0968c0e 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -449,7 +449,7 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_equal "17:44", time.to_s(:time) assert_equal "February 21, 2005 17:44", time.to_s(:long) assert_equal "February 21st, 2005 17:44", time.to_s(:long_ordinal) - with_env_tz "UTC" do + with_env_tz "UTC" do assert_equal "Mon, 21 Feb 2005 17:44:30 +0000", time.to_s(:rfc822) end end @@ -505,13 +505,13 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_equal 30, Time.days_in_month(11, 2005) assert_equal 31, Time.days_in_month(12, 2005) end - + uses_mocha 'TestTimeDaysInMonthWithoutYearArg' do def test_days_in_month_feb_in_common_year_without_year_arg Time.stubs(:now).returns(Time.utc(2007)) assert_equal 28, Time.days_in_month(2) end - + def test_days_in_month_feb_in_leap_year_without_year_arg Time.stubs(:now).returns(Time.utc(2008)) assert_equal 29, Time.days_in_month(2) @@ -559,13 +559,13 @@ class TimeExtCalculationsTest < Test::Unit::TestCase def test_acts_like_time assert Time.new.acts_like_time? end - + def test_formatted_offset_with_utc assert_equal '+00:00', Time.utc(2000).formatted_offset assert_equal '+0000', Time.utc(2000).formatted_offset(false) assert_equal 'UTC', Time.utc(2000).formatted_offset(true, 'UTC') end - + def test_formatted_offset_with_local with_env_tz 'US/Eastern' do assert_equal '-05:00', Time.local(2000).formatted_offset @@ -574,27 +574,27 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_equal '-0400', Time.local(2000, 7).formatted_offset(false) end end - + def test_compare_with_time assert_equal 1, Time.utc(2000) <=> Time.utc(1999, 12, 31, 23, 59, 59, 999) assert_equal 0, Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0) assert_equal(-1, Time.utc(2000) <=> Time.utc(2000, 1, 1, 0, 0, 0, 001)) end - + def test_compare_with_datetime assert_equal 1, Time.utc(2000) <=> DateTime.civil(1999, 12, 31, 23, 59, 59) assert_equal 0, Time.utc(2000) <=> DateTime.civil(2000, 1, 1, 0, 0, 0) assert_equal(-1, Time.utc(2000) <=> DateTime.civil(2000, 1, 1, 0, 0, 1)) end - + def test_compare_with_time_with_zone - assert_equal 1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(1999, 12, 31, 23, 59, 59), TimeZone['UTC'] ) - assert_equal 0, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0), TimeZone['UTC'] ) - assert_equal(-1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), TimeZone['UTC'] )) + assert_equal 1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone['UTC'] ) + assert_equal 0, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['UTC'] ) + assert_equal(-1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone['UTC'] )) end - + def test_minus_with_time_with_zone - assert_equal 86_400.0, Time.utc(2000, 1, 2) - ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), TimeZone['UTC'] ) + assert_equal 86_400.0, Time.utc(2000, 1, 2) - ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone['UTC'] ) end def test_time_created_with_local_constructor_cannot_represent_times_during_hour_skipped_by_dst @@ -608,7 +608,7 @@ class TimeExtCalculationsTest < Test::Unit::TestCase def test_case_equality assert Time === Time.utc(2000) - assert Time === ActiveSupport::TimeWithZone.new(Time.utc(2000), TimeZone['UTC']) + assert Time === ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) assert_equal false, Time === DateTime.civil(2000) end diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index c373bca88d..012ec373c9 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -1,10 +1,10 @@ require 'abstract_unit' - + class TimeWithZoneTest < Test::Unit::TestCase def setup @utc = Time.utc(2000, 1, 1, 0) - @time_zone = TimeZone['Eastern Time (US & Canada)'] + @time_zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] @twz = ActiveSupport::TimeWithZone.new(@utc, @time_zone) end @@ -21,114 +21,114 @@ class TimeWithZoneTest < Test::Unit::TestCase def test_time_zone assert_equal @time_zone, @twz.time_zone end - + def test_in_time_zone Time.use_zone 'Alaska' do - assert_equal ActiveSupport::TimeWithZone.new(@utc, TimeZone['Alaska']), @twz.in_time_zone + assert_equal ActiveSupport::TimeWithZone.new(@utc, ActiveSupport::TimeZone['Alaska']), @twz.in_time_zone end end def test_in_time_zone_with_argument - assert_equal ActiveSupport::TimeWithZone.new(@utc, TimeZone['Alaska']), @twz.in_time_zone('Alaska') + assert_equal ActiveSupport::TimeWithZone.new(@utc, ActiveSupport::TimeZone['Alaska']), @twz.in_time_zone('Alaska') end - + def test_in_time_zone_with_new_zone_equal_to_old_zone_does_not_create_new_object - assert_equal @twz.object_id, @twz.in_time_zone(TimeZone['Eastern Time (US & Canada)']).object_id + assert_equal @twz.object_id, @twz.in_time_zone(ActiveSupport::TimeZone['Eastern Time (US & Canada)']).object_id end def test_utc? assert_equal false, @twz.utc? - assert_equal true, ActiveSupport::TimeWithZone.new(Time.utc(2000), TimeZone['UTC']).utc? + assert_equal true, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']).utc? end - + def test_formatted_offset silence_warnings do # silence warnings raised by tzinfo gem assert_equal '-05:00', @twz.formatted_offset assert_equal '-04:00', ActiveSupport::TimeWithZone.new(Time.utc(2000, 6), @time_zone).formatted_offset #dst end end - + def test_dst? silence_warnings do # silence warnings raised by tzinfo gem assert_equal false, @twz.dst? assert_equal true, ActiveSupport::TimeWithZone.new(Time.utc(2000, 6), @time_zone).dst? end end - + def test_zone silence_warnings do # silence warnings raised by tzinfo gem assert_equal 'EST', @twz.zone assert_equal 'EDT', ActiveSupport::TimeWithZone.new(Time.utc(2000, 6), @time_zone).zone #dst end end - + def test_to_json silence_warnings do # silence warnings raised by tzinfo gem assert_equal "\"1999/12/31 19:00:00 -0500\"", @twz.to_json end end - + def test_to_json_with_use_standard_json_time_format_config_set_to_true old, ActiveSupport.use_standard_json_time_format = ActiveSupport.use_standard_json_time_format, true assert_equal "\"1999-12-31T19:00:00-05:00\"", @twz.to_json ensure ActiveSupport.use_standard_json_time_format = old end - + def test_strftime silence_warnings do # silence warnings raised by tzinfo gem assert_equal '1999-12-31 19:00:00 EST -0500', @twz.strftime('%Y-%m-%d %H:%M:%S %Z %z') end end - + def test_inspect silence_warnings do # silence warnings raised by tzinfo gem assert_equal 'Fri, 31 Dec 1999 19:00:00 EST -05:00', @twz.inspect end end - + def test_to_s silence_warnings do # silence warnings raised by tzinfo gem assert_equal '1999-12-31 19:00:00 -0500', @twz.to_s end end - + def test_to_s_db silence_warnings do # silence warnings raised by tzinfo gem assert_equal '2000-01-01 00:00:00', @twz.to_s(:db) end end - + def test_xmlschema silence_warnings do # silence warnings raised by tzinfo gem assert_equal "1999-12-31T19:00:00-05:00", @twz.xmlschema end end - + def test_to_yaml silence_warnings do # silence warnings raised by tzinfo gem assert_equal "--- 1999-12-31 19:00:00 -05:00\n", @twz.to_yaml end end - + def test_ruby_to_yaml silence_warnings do assert_equal "--- \n:twz: 2000-01-01 00:00:00 Z\n", {:twz => @twz}.to_yaml end end - + def test_httpdate silence_warnings do # silence warnings raised by tzinfo gem assert_equal 'Sat, 01 Jan 2000 00:00:00 GMT', @twz.httpdate end end - + def test_rfc2822 silence_warnings do # silence warnings raised by tzinfo gem assert_equal "Fri, 31 Dec 1999 19:00:00 -0500", @twz.rfc2822 end end - + def test_compare_with_time assert_equal 1, @twz <=> Time.utc(1999, 12, 31, 23, 59, 59) assert_equal 0, @twz <=> Time.utc(2000, 1, 1, 0, 0, 0) @@ -142,27 +142,27 @@ class TimeWithZoneTest < Test::Unit::TestCase end def test_compare_with_time_with_zone - assert_equal 1, @twz <=> ActiveSupport::TimeWithZone.new( Time.utc(1999, 12, 31, 23, 59, 59), TimeZone['UTC'] ) - assert_equal 0, @twz <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0), TimeZone['UTC'] ) - assert_equal(-1, @twz <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), TimeZone['UTC'] )) + assert_equal 1, @twz <=> ActiveSupport::TimeWithZone.new( Time.utc(1999, 12, 31, 23, 59, 59), ActiveSupport::TimeZone['UTC'] ) + assert_equal 0, @twz <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 0), ActiveSupport::TimeZone['UTC'] ) + assert_equal(-1, @twz <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone['UTC'] )) end - + def test_between? assert @twz.between?(Time.utc(1999,12,31,23,59,59), Time.utc(2000,1,1,0,0,1)) assert_equal false, @twz.between?(Time.utc(2000,1,1,0,0,1), Time.utc(2000,1,1,0,0,2)) end - + def test_eql? assert @twz.eql?(Time.utc(2000)) - assert @twz.eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), TimeZone["Hawaii"]) ) + assert @twz.eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) ) end - + def test_plus_with_integer silence_warnings do # silence warnings raised by tzinfo gem assert_equal Time.utc(1999, 12, 31, 19, 0 ,5), (@twz + 5).time end end - + def test_plus_with_integer_when_self_wraps_datetime silence_warnings do # silence warnings raised by tzinfo gem datetime = DateTime.civil(2000, 1, 1, 0) @@ -170,26 +170,26 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal DateTime.civil(1999, 12, 31, 19, 0 ,5), (twz + 5).time end end - + def test_plus_when_crossing_time_class_limit silence_warnings do # silence warnings raised by tzinfo gem twz = ActiveSupport::TimeWithZone.new(Time.utc(2038, 1, 19), @time_zone) assert_equal [0, 0, 19, 19, 1, 2038], (twz + 86_400).to_a[0,6] end end - + def test_plus_with_duration silence_warnings do # silence warnings raised by tzinfo gem assert_equal Time.utc(2000, 1, 5, 19, 0 ,0), (@twz + 5.days).time end end - + def test_minus_with_integer silence_warnings do # silence warnings raised by tzinfo gem assert_equal Time.utc(1999, 12, 31, 18, 59 ,55), (@twz - 5).time end end - + def test_minus_with_integer_when_self_wraps_datetime silence_warnings do # silence warnings raised by tzinfo gem datetime = DateTime.civil(2000, 1, 1, 0) @@ -197,24 +197,24 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal DateTime.civil(1999, 12, 31, 18, 59 ,55), (twz - 5).time end end - + def test_minus_with_duration silence_warnings do # silence warnings raised by tzinfo gem assert_equal Time.utc(1999, 12, 26, 19, 0 ,0), (@twz - 5.days).time end end - + def test_minus_with_time - assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), TimeZone['UTC'] ) - Time.utc(2000, 1, 1) - assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), TimeZone['Hawaii'] ) - Time.utc(2000, 1, 1) + assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) - Time.utc(2000, 1, 1) + assert_equal 86_400.0, ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone['Hawaii'] ) - Time.utc(2000, 1, 1) end - + def test_minus_with_time_with_zone - twz1 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), TimeZone['UTC'] ) - twz2 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), TimeZone['UTC'] ) + twz1 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone['UTC'] ) + twz2 = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2), ActiveSupport::TimeZone['UTC'] ) assert_equal 86_400.0, twz2 - twz1 end - + def test_plus_and_minus_enforce_spring_dst_rules silence_warnings do # silence warnings raised by tzinfo gem utc = Time.utc(2006,4,2,6,59,59) # == Apr 2 2006 01:59:59 EST; i.e., 1 second before daylight savings start @@ -232,7 +232,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal 'EST', twz.zone end end - + def test_plus_and_minus_enforce_fall_dst_rules silence_warnings do # silence warnings raised by tzinfo gem utc = Time.utc(2006,10,29,5,59,59) # == Oct 29 2006 01:59:59 EST; i.e., 1 second before daylight savings end @@ -250,25 +250,25 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal 'EDT', twz.zone end end - + def test_to_a silence_warnings do # silence warnings raised by tzinfo gem - assert_equal [45, 30, 5, 1, 2, 2000, 2, 32, false, "HST"], ActiveSupport::TimeWithZone.new( Time.utc(2000, 2, 1, 15, 30, 45), TimeZone['Hawaii'] ).to_a + assert_equal [45, 30, 5, 1, 2, 2000, 2, 32, false, "HST"], ActiveSupport::TimeWithZone.new( Time.utc(2000, 2, 1, 15, 30, 45), ActiveSupport::TimeZone['Hawaii'] ).to_a end end - + def test_to_f - result = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), TimeZone['Hawaii'] ).to_f + result = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone['Hawaii'] ).to_f assert_equal 946684800.0, result assert result.is_a?(Float) end - + def test_to_i - result = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), TimeZone['Hawaii'] ).to_i + result = ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone['Hawaii'] ).to_i assert_equal 946684800, result assert result.is_a?(Integer) end - + def test_to_time assert_equal @twz, @twz.to_time end @@ -276,55 +276,55 @@ class TimeWithZoneTest < Test::Unit::TestCase def test_to_date silence_warnings do # silence warnings raised by tzinfo gem # 1 sec before midnight Jan 1 EST - assert_equal Date.new(1999, 12, 31), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 4, 59, 59), TimeZone['Eastern Time (US & Canada)'] ).to_date + assert_equal Date.new(1999, 12, 31), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 4, 59, 59), ActiveSupport::TimeZone['Eastern Time (US & Canada)'] ).to_date # midnight Jan 1 EST - assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 5, 0, 0), TimeZone['Eastern Time (US & Canada)'] ).to_date + assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 5, 0, 0), ActiveSupport::TimeZone['Eastern Time (US & Canada)'] ).to_date # 1 sec before midnight Jan 2 EST - assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 4, 59, 59), TimeZone['Eastern Time (US & Canada)'] ).to_date + assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 4, 59, 59), ActiveSupport::TimeZone['Eastern Time (US & Canada)'] ).to_date # midnight Jan 2 EST - assert_equal Date.new(2000, 1, 2), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 5, 0, 0), TimeZone['Eastern Time (US & Canada)'] ).to_date + assert_equal Date.new(2000, 1, 2), ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 2, 5, 0, 0), ActiveSupport::TimeZone['Eastern Time (US & Canada)'] ).to_date end end - + def test_to_datetime silence_warnings do # silence warnings raised by tzinfo gem assert_equal DateTime.civil(1999, 12, 31, 19, 0, 0, Rational(-18_000, 86_400)), @twz.to_datetime end end - + def test_acts_like_time assert @twz.acts_like?(:time) assert ActiveSupport::TimeWithZone.new(DateTime.civil(2000), @time_zone).acts_like?(:time) end - + def test_acts_like_date assert_equal false, @twz.acts_like?(:date) assert_equal false, ActiveSupport::TimeWithZone.new(DateTime.civil(2000), @time_zone).acts_like?(:date) end - + def test_is_a assert @twz.is_a?(Time) assert @twz.kind_of?(Time) assert @twz.is_a?(ActiveSupport::TimeWithZone) end - + def test_method_missing_with_time_return_value silence_warnings do # silence warnings raised by tzinfo gem assert_instance_of ActiveSupport::TimeWithZone, @twz.months_since(1) assert_equal Time.utc(2000, 1, 31, 19, 0 ,0), @twz.months_since(1).time end end - + def test_marshal_dump_and_load silence_warnings do # silence warnings raised by tzinfo gem marshal_str = Marshal.dump(@twz) mtime = Marshal.load(marshal_str) assert_equal Time.utc(2000, 1, 1, 0), mtime.utc - assert_equal TimeZone['Eastern Time (US & Canada)'], mtime.time_zone + assert_equal ActiveSupport::TimeZone['Eastern Time (US & Canada)'], mtime.time_zone assert_equal Time.utc(1999, 12, 31, 19), mtime.time end end - + def test_marshal_dump_and_load_with_tzinfo_identifier silence_warnings do # silence warnings raised by tzinfo gem twz = ActiveSupport::TimeWithZone.new(@utc, TZInfo::Timezone.get('America/New_York')) @@ -335,14 +335,14 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal Time.utc(1999, 12, 31, 19), mtime.time end end - + def test_method_missing_with_non_time_return_value silence_warnings do # silence warnings raised by tzinfo gem @twz.time.expects(:foo).returns('bar') assert_equal 'bar', @twz.foo end end - + def test_date_part_value_methods silence_warnings do # silence warnings raised by tzinfo gem twz = ActiveSupport::TimeWithZone.new(Time.utc(1999,12,31,19,18,17,500), @time_zone) @@ -356,14 +356,14 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal 500, twz.usec end end - + def test_usec_returns_0_when_datetime_is_wrapped silence_warnings do # silence warnings raised by tzinfo gem twz = ActiveSupport::TimeWithZone.new(DateTime.civil(2000), @time_zone) assert_equal 0, twz.usec end end - + def test_utc_to_local_conversion_saves_period_in_instance_variable silence_warnings do # silence warnings raised by tzinfo gem assert_nil @twz.instance_variable_get('@period') @@ -371,14 +371,14 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_kind_of TZInfo::TimezonePeriod, @twz.instance_variable_get('@period') end end - + def test_instance_created_with_local_time_returns_correct_utc_time silence_warnings do # silence warnings raised by tzinfo gem twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(1999, 12, 31, 19)) assert_equal Time.utc(2000), twz.utc end end - + def test_instance_created_with_local_time_enforces_spring_dst_rules silence_warnings do # silence warnings raised by tzinfo gem twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,2)) # first second of DST @@ -387,7 +387,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal 'EDT', twz.zone end end - + def test_instance_created_with_local_time_enforces_fall_dst_rules silence_warnings do # silence warnings raised by tzinfo gem twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,10,29,1)) # 1AM can be either DST or non-DST; we'll pick DST @@ -396,14 +396,14 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal 'EDT', twz.zone end end - + def test_ruby_19_weekday_name_query_methods ruby_19_or_greater = RUBY_VERSION >= '1.9' %w(sunday? monday? tuesday? wednesday? thursday? friday? saturday?).each do |name| assert_equal ruby_19_or_greater, @twz.respond_to?(name) end end - + def test_utc_to_local_conversion_with_far_future_datetime silence_warnings do # silence warnings raised by tzinfo gem assert_equal [0,0,19,31,12,2049], ActiveSupport::TimeWithZone.new(DateTime.civil(2050), @time_zone).to_a[0,6] @@ -415,7 +415,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal DateTime.civil(2050).to_f, ActiveSupport::TimeWithZone.new(nil, @time_zone, DateTime.civil(2049,12,31,19)).to_f end end - + def test_change assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", @twz.inspect assert_equal "Mon, 31 Dec 2001 19:00:00 EST -05:00", @twz.change(:year => 2001).inspect @@ -426,7 +426,7 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Fri, 31 Dec 1999 19:15:00 EST -05:00", @twz.change(:min => 15).inspect assert_equal "Fri, 31 Dec 1999 19:00:30 EST -05:00", @twz.change(:sec => 30).inspect end - + def test_advance assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", @twz.inspect assert_equal "Mon, 31 Dec 2001 19:00:00 EST -05:00", @twz.advance(:years => 2).inspect @@ -436,77 +436,77 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal "Fri, 31 Dec 1999 19:15:00 EST -05:00", @twz.advance(:minutes => 15).inspect assert_equal "Fri, 31 Dec 1999 19:00:30 EST -05:00", @twz.advance(:seconds => 30).inspect end - + def beginning_of_year assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", @twz.inspect assert_equal "Fri, 01 Jan 1999 00:00:00 EST -05:00", @twz.beginning_of_year.inspect end - + def end_of_year assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", @twz.inspect assert_equal "Fri, 31 Dec 1999 23:59:59 EST -05:00", @twz.end_of_year.inspect end - + def beginning_of_month assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", @twz.inspect assert_equal "Fri, 01 Dec 1999 00:00:00 EST -05:00", @twz.beginning_of_month.inspect end - + def end_of_month assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", @twz.inspect assert_equal "Fri, 31 Dec 1999 23:59:59 EST -05:00", @twz.end_of_month.inspect end - + def beginning_of_day assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", @twz.inspect assert_equal "Fri, 31 Dec 1999 00:00:00 EST -05:00", @twz.beginning_of_day.inspect end - + def end_of_day assert_equal "Fri, 31 Dec 1999 19:00:00 EST -05:00", @twz.inspect assert_equal "Fri, 31 Dec 1999 23:59:59 EST -05:00", @twz.end_of_day.inspect end - + def test_since assert_equal "Fri, 31 Dec 1999 19:00:01 EST -05:00", @twz.since(1).inspect end - + def test_ago assert_equal "Fri, 31 Dec 1999 18:59:59 EST -05:00", @twz.ago(1).inspect end - + def test_seconds_since_midnight assert_equal 19 * 60 * 60, @twz.seconds_since_midnight end - + def test_advance_1_year_from_leap_day twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2004,2,29)) assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.advance(:years => 1).inspect assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.years_since(1).inspect assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", (twz + 1.year).inspect end - + def test_advance_1_month_from_last_day_of_january twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2005,1,31)) assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.advance(:months => 1).inspect assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", twz.months_since(1).inspect assert_equal "Mon, 28 Feb 2005 00:00:00 EST -05:00", (twz + 1.month).inspect end - + def test_advance_1_month_from_last_day_of_january_during_leap_year twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2000,1,31)) assert_equal "Tue, 29 Feb 2000 00:00:00 EST -05:00", twz.advance(:months => 1).inspect assert_equal "Tue, 29 Feb 2000 00:00:00 EST -05:00", twz.months_since(1).inspect assert_equal "Tue, 29 Feb 2000 00:00:00 EST -05:00", (twz + 1.month).inspect end - + def test_advance_1_month_into_spring_dst_gap twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,3,2,2)) assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.advance(:months => 1).inspect assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.months_since(1).inspect assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", (twz + 1.month).inspect end - + def test_advance_1_second_into_spring_dst_gap twz = ActiveSupport::TimeWithZone.new(nil, @time_zone, Time.utc(2006,4,2,1,59,59)) assert_equal "Sun, 02 Apr 2006 03:00:00 EDT -04:00", twz.advance(:seconds => 1).inspect @@ -519,11 +519,11 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase def setup @t, @dt = Time.utc(2000), DateTime.civil(2000) end - + def teardown Time.zone = nil end - + def test_in_time_zone silence_warnings do # silence warnings raised by tzinfo gem Time.use_zone 'Alaska' do @@ -554,7 +554,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase end end end - + def test_in_time_zone_with_time_local_instance silence_warnings do # silence warnings raised by tzinfo gem with_env_tz 'US/Eastern' do @@ -563,85 +563,85 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase end end end - + def test_use_zone Time.zone = 'Alaska' Time.use_zone 'Hawaii' do - assert_equal TimeZone['Hawaii'], Time.zone + assert_equal ActiveSupport::TimeZone['Hawaii'], Time.zone end - assert_equal TimeZone['Alaska'], Time.zone + assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone end - + def test_use_zone_with_exception_raised Time.zone = 'Alaska' assert_raises RuntimeError do Time.use_zone('Hawaii') { raise RuntimeError } end - assert_equal TimeZone['Alaska'], Time.zone + assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone end - + def test_time_zone_getter_and_setter - Time.zone = TimeZone['Alaska'] - assert_equal TimeZone['Alaska'], Time.zone + Time.zone = ActiveSupport::TimeZone['Alaska'] + assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone Time.zone = 'Alaska' - assert_equal TimeZone['Alaska'], Time.zone + assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone Time.zone = -9.hours - assert_equal TimeZone['Alaska'], Time.zone + assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone Time.zone = nil assert_equal nil, Time.zone end - + def test_time_zone_getter_and_setter_with_zone_default - Time.zone_default = TimeZone['Alaska'] - assert_equal TimeZone['Alaska'], Time.zone - Time.zone = TimeZone['Hawaii'] - assert_equal TimeZone['Hawaii'], Time.zone + Time.zone_default = ActiveSupport::TimeZone['Alaska'] + assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone + Time.zone = ActiveSupport::TimeZone['Hawaii'] + assert_equal ActiveSupport::TimeZone['Hawaii'], Time.zone Time.zone = nil - assert_equal TimeZone['Alaska'], Time.zone + assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone ensure Time.zone_default = nil end - + def test_time_zone_setter_is_thread_safe Time.use_zone 'Paris' do t1 = Thread.new { Time.zone = 'Alaska' }.join t2 = Thread.new { Time.zone = 'Hawaii' }.join assert t1.stop?, "Thread 1 did not finish running" assert t2.stop?, "Thread 2 did not finish running" - assert_equal TimeZone['Paris'], Time.zone - assert_equal TimeZone['Alaska'], t1[:time_zone] - assert_equal TimeZone['Hawaii'], t2[:time_zone] + assert_equal ActiveSupport::TimeZone['Paris'], Time.zone + assert_equal ActiveSupport::TimeZone['Alaska'], t1[:time_zone] + assert_equal ActiveSupport::TimeZone['Hawaii'], t2[:time_zone] end end - + def test_time_zone_setter_with_tzinfo_timezone_object_wraps_in_rails_time_zone silence_warnings do # silence warnings raised by tzinfo gem tzinfo = TZInfo::Timezone.get('America/New_York') Time.zone = tzinfo - assert_kind_of TimeZone, Time.zone + assert_kind_of ActiveSupport::TimeZone, Time.zone assert_equal tzinfo, Time.zone.tzinfo assert_equal 'America/New_York', Time.zone.name assert_equal(-18_000, Time.zone.utc_offset) end end - + def test_time_zone_setter_with_tzinfo_timezone_identifier_does_lookup_and_wraps_in_rails_time_zone silence_warnings do # silence warnings raised by tzinfo gem Time.zone = 'America/New_York' - assert_kind_of TimeZone, Time.zone + assert_kind_of ActiveSupport::TimeZone, Time.zone assert_equal 'America/New_York', Time.zone.tzinfo.name assert_equal 'America/New_York', Time.zone.name assert_equal(-18_000, Time.zone.utc_offset) end end - + def test_time_zone_setter_with_non_identifying_argument_returns_nil Time.zone = 'foo' assert_equal nil, Time.zone Time.zone = -15.hours assert_equal nil, Time.zone end - + uses_mocha 'TestTimeCurrent' do def test_current_returns_time_now_when_zone_default_not_set with_env_tz 'US/Eastern' do @@ -650,10 +650,10 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase assert_equal Time.local(2000), Time.current end end - + def test_current_returns_time_zone_now_when_zone_default_set silence_warnings do # silence warnings raised by tzinfo gem - Time.zone_default = TimeZone['Eastern Time (US & Canada)'] + Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] with_env_tz 'US/Eastern' do Time.stubs(:now).returns Time.local(2000) assert_equal true, Time.current.is_a?(ActiveSupport::TimeWithZone) @@ -665,7 +665,7 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase Time.zone_default = nil end end - + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index 0309ab6858..038547a862 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -15,31 +15,31 @@ end class DependenciesTest < Test::Unit::TestCase def teardown - Dependencies.clear + ActiveSupport::Dependencies.clear end def with_loading(*from) - old_mechanism, Dependencies.mechanism = Dependencies.mechanism, :load + old_mechanism, ActiveSupport::Dependencies.mechanism = ActiveSupport::Dependencies.mechanism, :load dir = File.dirname(__FILE__) - prior_load_paths = Dependencies.load_paths - Dependencies.load_paths = from.collect { |f| "#{dir}/#{f}" } + prior_load_paths = ActiveSupport::Dependencies.load_paths + ActiveSupport::Dependencies.load_paths = from.collect { |f| "#{dir}/#{f}" } yield ensure - Dependencies.load_paths = prior_load_paths - Dependencies.mechanism = old_mechanism - Dependencies.explicitly_unloadable_constants = [] + ActiveSupport::Dependencies.load_paths = prior_load_paths + ActiveSupport::Dependencies.mechanism = old_mechanism + ActiveSupport::Dependencies.explicitly_unloadable_constants = [] end def test_tracking_loaded_files require_dependency 'dependencies/service_one' require_dependency 'dependencies/service_two' - assert_equal 2, Dependencies.loaded.size + assert_equal 2, ActiveSupport::Dependencies.loaded.size end def test_tracking_identical_loaded_files require_dependency 'dependencies/service_one' require_dependency 'dependencies/service_one' - assert_equal 1, Dependencies.loaded.size + assert_equal 1, ActiveSupport::Dependencies.loaded.size end def test_missing_dependency_raises_missing_source_file @@ -64,46 +64,46 @@ class DependenciesTest < Test::Unit::TestCase end assert_equal count + 1, $raises_exception_load_count - assert !Dependencies.loaded.include?(filename) - assert !Dependencies.history.include?(filename) + assert !ActiveSupport::Dependencies.loaded.include?(filename) + assert !ActiveSupport::Dependencies.history.include?(filename) end end end def test_warnings_should_be_enabled_on_first_load with_loading 'dependencies' do - old_warnings, Dependencies.warnings_on_first_load = Dependencies.warnings_on_first_load, true + old_warnings, ActiveSupport::Dependencies.warnings_on_first_load = ActiveSupport::Dependencies.warnings_on_first_load, true filename = "check_warnings" expanded = File.expand_path("test/dependencies/#{filename}") $check_warnings_load_count = 0 - assert !Dependencies.loaded.include?(expanded) - assert !Dependencies.history.include?(expanded) + assert !ActiveSupport::Dependencies.loaded.include?(expanded) + assert !ActiveSupport::Dependencies.history.include?(expanded) silence_warnings { require_dependency filename } assert_equal 1, $check_warnings_load_count assert_equal true, $checked_verbose, 'On first load warnings should be enabled.' - assert Dependencies.loaded.include?(expanded) - Dependencies.clear - assert !Dependencies.loaded.include?(expanded) - assert Dependencies.history.include?(expanded) + assert ActiveSupport::Dependencies.loaded.include?(expanded) + ActiveSupport::Dependencies.clear + assert !ActiveSupport::Dependencies.loaded.include?(expanded) + assert ActiveSupport::Dependencies.history.include?(expanded) silence_warnings { require_dependency filename } assert_equal 2, $check_warnings_load_count assert_equal nil, $checked_verbose, 'After first load warnings should be left alone.' - assert Dependencies.loaded.include?(expanded) - Dependencies.clear - assert !Dependencies.loaded.include?(expanded) - assert Dependencies.history.include?(expanded) + assert ActiveSupport::Dependencies.loaded.include?(expanded) + ActiveSupport::Dependencies.clear + assert !ActiveSupport::Dependencies.loaded.include?(expanded) + assert ActiveSupport::Dependencies.history.include?(expanded) enable_warnings { require_dependency filename } assert_equal 3, $check_warnings_load_count assert_equal true, $checked_verbose, 'After first load warnings should be left alone.' - assert Dependencies.loaded.include?(expanded) + assert ActiveSupport::Dependencies.loaded.include?(expanded) end end @@ -113,7 +113,7 @@ class DependenciesTest < Test::Unit::TestCase assert_nothing_raised { require_dependency 'mutual_one' } assert_equal 2, $mutual_dependencies_count - Dependencies.clear + ActiveSupport::Dependencies.clear $mutual_dependencies_count = 0 assert_nothing_raised { require_dependency 'mutual_two' } @@ -239,7 +239,7 @@ class DependenciesTest < Test::Unit::TestCase end def test_loadable_constants_for_path_should_handle_empty_autoloads - assert_equal [], Dependencies.loadable_constants_for_path('hello') + assert_equal [], ActiveSupport::Dependencies.loadable_constants_for_path('hello') end def test_loadable_constants_for_path_should_handle_relative_paths @@ -247,7 +247,7 @@ class DependenciesTest < Test::Unit::TestCase relative_root = File.dirname(__FILE__) + '/dependencies' ['', '/'].each do |suffix| with_loading fake_root + suffix do - assert_equal ["A::B"], Dependencies.loadable_constants_for_path(relative_root + '/a/b') + assert_equal ["A::B"], ActiveSupport::Dependencies.loadable_constants_for_path(relative_root + '/a/b') end end end @@ -255,106 +255,106 @@ class DependenciesTest < Test::Unit::TestCase def test_loadable_constants_for_path_should_provide_all_results fake_root = '/usr/apps/backpack' with_loading fake_root, fake_root + '/lib' do - root = Dependencies.load_paths.first - assert_equal ["Lib::A::B", "A::B"], Dependencies.loadable_constants_for_path(root + '/lib/a/b') + root = ActiveSupport::Dependencies.load_paths.first + assert_equal ["Lib::A::B", "A::B"], ActiveSupport::Dependencies.loadable_constants_for_path(root + '/lib/a/b') end end def test_loadable_constants_for_path_should_uniq_results fake_root = '/usr/apps/backpack/lib' with_loading fake_root, fake_root + '/' do - root = Dependencies.load_paths.first - assert_equal ["A::B"], Dependencies.loadable_constants_for_path(root + '/a/b') + root = ActiveSupport::Dependencies.load_paths.first + assert_equal ["A::B"], ActiveSupport::Dependencies.loadable_constants_for_path(root + '/a/b') end end def test_loadable_constants_with_load_path_without_trailing_slash path = File.dirname(__FILE__) + '/autoloading_fixtures/class_folder/inline_class.rb' with_loading 'autoloading_fixtures/class/' do - assert_equal [], Dependencies.loadable_constants_for_path(path) + assert_equal [], ActiveSupport::Dependencies.loadable_constants_for_path(path) end end def test_qualified_const_defined - assert Dependencies.qualified_const_defined?("Object") - assert Dependencies.qualified_const_defined?("::Object") - assert Dependencies.qualified_const_defined?("::Object::Kernel") - assert Dependencies.qualified_const_defined?("::Object::Dependencies") - assert Dependencies.qualified_const_defined?("::Test::Unit::TestCase") + assert ActiveSupport::Dependencies.qualified_const_defined?("Object") + assert ActiveSupport::Dependencies.qualified_const_defined?("::Object") + assert ActiveSupport::Dependencies.qualified_const_defined?("::Object::Kernel") + assert ActiveSupport::Dependencies.qualified_const_defined?("::Object::Dependencies") + assert ActiveSupport::Dependencies.qualified_const_defined?("::Test::Unit::TestCase") end def test_qualified_const_defined_should_not_call_method_missing ModuleWithMissing.missing_count = 0 - assert ! Dependencies.qualified_const_defined?("ModuleWithMissing::A") + assert ! ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A") assert_equal 0, ModuleWithMissing.missing_count - assert ! Dependencies.qualified_const_defined?("ModuleWithMissing::A::B") + assert ! ActiveSupport::Dependencies.qualified_const_defined?("ModuleWithMissing::A::B") assert_equal 0, ModuleWithMissing.missing_count end def test_autoloaded? with_loading 'autoloading_fixtures' do - assert ! Dependencies.autoloaded?("ModuleFolder") - assert ! Dependencies.autoloaded?("ModuleFolder::NestedClass") + assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder") + assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") - assert Dependencies.autoloaded?(ModuleFolder) + assert ActiveSupport::Dependencies.autoloaded?(ModuleFolder) - assert Dependencies.autoloaded?("ModuleFolder") - assert ! Dependencies.autoloaded?("ModuleFolder::NestedClass") + assert ActiveSupport::Dependencies.autoloaded?("ModuleFolder") + assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") - assert Dependencies.autoloaded?(ModuleFolder::NestedClass) + assert ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass) - assert Dependencies.autoloaded?("ModuleFolder") - assert Dependencies.autoloaded?("ModuleFolder::NestedClass") + assert ActiveSupport::Dependencies.autoloaded?("ModuleFolder") + assert ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") - assert Dependencies.autoloaded?("::ModuleFolder") - assert Dependencies.autoloaded?(:ModuleFolder) + assert ActiveSupport::Dependencies.autoloaded?("::ModuleFolder") + assert ActiveSupport::Dependencies.autoloaded?(:ModuleFolder) # Anonymous modules aren't autoloaded. - assert !Dependencies.autoloaded?(Module.new) + assert !ActiveSupport::Dependencies.autoloaded?(Module.new) nil_name = Module.new def nil_name.name() nil end - assert !Dependencies.autoloaded?(nil_name) + assert !ActiveSupport::Dependencies.autoloaded?(nil_name) Object.class_eval { remove_const :ModuleFolder } end end def test_qualified_name_for - assert_equal "A", Dependencies.qualified_name_for(Object, :A) - assert_equal "A", Dependencies.qualified_name_for(:Object, :A) - assert_equal "A", Dependencies.qualified_name_for("Object", :A) - assert_equal "A", Dependencies.qualified_name_for("::Object", :A) - assert_equal "A", Dependencies.qualified_name_for("::Kernel", :A) + assert_equal "A", ActiveSupport::Dependencies.qualified_name_for(Object, :A) + assert_equal "A", ActiveSupport::Dependencies.qualified_name_for(:Object, :A) + assert_equal "A", ActiveSupport::Dependencies.qualified_name_for("Object", :A) + assert_equal "A", ActiveSupport::Dependencies.qualified_name_for("::Object", :A) + assert_equal "A", ActiveSupport::Dependencies.qualified_name_for("::Kernel", :A) - assert_equal "Dependencies::A", Dependencies.qualified_name_for(:Dependencies, :A) - assert_equal "Dependencies::A", Dependencies.qualified_name_for(Dependencies, :A) + assert_equal "ActiveSupport::Dependencies::A", ActiveSupport::Dependencies.qualified_name_for(:'ActiveSupport::Dependencies', :A) + assert_equal "ActiveSupport::Dependencies::A", ActiveSupport::Dependencies.qualified_name_for(ActiveSupport::Dependencies, :A) end def test_file_search with_loading 'dependencies' do - root = Dependencies.load_paths.first - assert_equal nil, Dependencies.search_for_file('service_three') - assert_equal nil, Dependencies.search_for_file('service_three.rb') - assert_equal root + '/service_one.rb', Dependencies.search_for_file('service_one') - assert_equal root + '/service_one.rb', Dependencies.search_for_file('service_one.rb') + root = ActiveSupport::Dependencies.load_paths.first + assert_equal nil, ActiveSupport::Dependencies.search_for_file('service_three') + assert_equal nil, ActiveSupport::Dependencies.search_for_file('service_three.rb') + assert_equal root + '/service_one.rb', ActiveSupport::Dependencies.search_for_file('service_one') + assert_equal root + '/service_one.rb', ActiveSupport::Dependencies.search_for_file('service_one.rb') end end def test_file_search_uses_first_in_load_path with_loading 'dependencies', 'autoloading_fixtures' do - deps, autoload = Dependencies.load_paths + deps, autoload = ActiveSupport::Dependencies.load_paths assert_match %r/dependencies/, deps assert_match %r/autoloading_fixtures/, autoload - assert_equal deps + '/conflict.rb', Dependencies.search_for_file('conflict') + assert_equal deps + '/conflict.rb', ActiveSupport::Dependencies.search_for_file('conflict') end with_loading 'autoloading_fixtures', 'dependencies' do - autoload, deps = Dependencies.load_paths + autoload, deps = ActiveSupport::Dependencies.load_paths assert_match %r/dependencies/, deps assert_match %r/autoloading_fixtures/, autoload - assert_equal autoload + '/conflict.rb', Dependencies.search_for_file('conflict') + assert_equal autoload + '/conflict.rb', ActiveSupport::Dependencies.search_for_file('conflict') end end @@ -383,7 +383,7 @@ class DependenciesTest < Test::Unit::TestCase with_loading 'autoloading_fixtures' do require_dependency '././counting_loader' assert_equal 1, $counting_loaded_times - assert_raises(ArgumentError) { Dependencies.load_missing_constant Object, :CountingLoader } + assert_raises(ArgumentError) { ActiveSupport::Dependencies.load_missing_constant Object, :CountingLoader } assert_equal 1, $counting_loaded_times end end @@ -407,12 +407,12 @@ class DependenciesTest < Test::Unit::TestCase def test_removal_from_tree_should_be_detected with_loading 'dependencies' do - root = Dependencies.load_paths.first + root = ActiveSupport::Dependencies.load_paths.first c = ServiceOne - Dependencies.clear + ActiveSupport::Dependencies.clear assert ! defined?(ServiceOne) begin - Dependencies.load_missing_constant(c, :FakeMissing) + ActiveSupport::Dependencies.load_missing_constant(c, :FakeMissing) flunk "Expected exception" rescue ArgumentError => e assert_match %r{ServiceOne has been removed from the module tree}i, e.message @@ -430,25 +430,25 @@ class DependenciesTest < Test::Unit::TestCase def test_load_once_paths_do_not_add_to_autoloaded_constants with_loading 'autoloading_fixtures' do - Dependencies.load_once_paths = Dependencies.load_paths.dup + ActiveSupport::Dependencies.load_once_paths = ActiveSupport::Dependencies.load_paths.dup - assert ! Dependencies.autoloaded?("ModuleFolder") - assert ! Dependencies.autoloaded?("ModuleFolder::NestedClass") - assert ! Dependencies.autoloaded?(ModuleFolder) + assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder") + assert ! ActiveSupport::Dependencies.autoloaded?("ModuleFolder::NestedClass") + assert ! ActiveSupport::Dependencies.autoloaded?(ModuleFolder) 1 if ModuleFolder::NestedClass # 1 if to avoid warning - assert ! Dependencies.autoloaded?(ModuleFolder::NestedClass) + assert ! ActiveSupport::Dependencies.autoloaded?(ModuleFolder::NestedClass) end ensure Object.class_eval { remove_const :ModuleFolder } - Dependencies.load_once_paths = [] + ActiveSupport::Dependencies.load_once_paths = [] end def test_application_should_special_case_application_controller with_loading 'autoloading_fixtures' do require_dependency 'application' assert_equal 10, ApplicationController - assert Dependencies.autoloaded?(:ApplicationController) + assert ActiveSupport::Dependencies.autoloaded?(:ApplicationController) end end @@ -463,15 +463,15 @@ class DependenciesTest < Test::Unit::TestCase def test_preexisting_constants_are_not_marked_as_autoloaded with_loading 'autoloading_fixtures' do require_dependency 'e' - assert Dependencies.autoloaded?(:E) - Dependencies.clear + assert ActiveSupport::Dependencies.autoloaded?(:E) + ActiveSupport::Dependencies.clear end Object.const_set :E, Class.new with_loading 'autoloading_fixtures' do require_dependency 'e' - assert ! Dependencies.autoloaded?(:E), "E shouldn't be marked autoloaded!" - Dependencies.clear + assert ! ActiveSupport::Dependencies.autoloaded?(:E), "E shouldn't be marked autoloaded!" + ActiveSupport::Dependencies.clear end ensure @@ -483,11 +483,11 @@ class DependenciesTest < Test::Unit::TestCase Object.const_set :M, Module.new M.unloadable - Dependencies.clear + ActiveSupport::Dependencies.clear assert ! defined?(M) Object.const_set :M, Module.new - Dependencies.clear + ActiveSupport::Dependencies.clear assert ! defined?(M), "Dependencies should unload unloadable constants each time" end end @@ -508,24 +508,24 @@ class DependenciesTest < Test::Unit::TestCase end def test_new_contants_in_without_constants - assert_equal [], (Dependencies.new_constants_in(Object) { }) - assert Dependencies.constant_watch_stack.empty? + assert_equal [], (ActiveSupport::Dependencies.new_constants_in(Object) { }) + assert ActiveSupport::Dependencies.constant_watch_stack.empty? end def test_new_constants_in_with_a_single_constant - assert_equal ["Hello"], Dependencies.new_constants_in(Object) { + assert_equal ["Hello"], ActiveSupport::Dependencies.new_constants_in(Object) { Object.const_set :Hello, 10 }.map(&:to_s) - assert Dependencies.constant_watch_stack.empty? + assert ActiveSupport::Dependencies.constant_watch_stack.empty? ensure Object.class_eval { remove_const :Hello } end def test_new_constants_in_with_nesting - outer = Dependencies.new_constants_in(Object) do + outer = ActiveSupport::Dependencies.new_constants_in(Object) do Object.const_set :OuterBefore, 10 - assert_equal ["Inner"], Dependencies.new_constants_in(Object) { + assert_equal ["Inner"], ActiveSupport::Dependencies.new_constants_in(Object) { Object.const_set :Inner, 20 }.map(&:to_s) @@ -533,7 +533,7 @@ class DependenciesTest < Test::Unit::TestCase end assert_equal ["OuterAfter", "OuterBefore"], outer.sort.map(&:to_s) - assert Dependencies.constant_watch_stack.empty? + assert ActiveSupport::Dependencies.constant_watch_stack.empty? ensure %w(OuterBefore Inner OuterAfter).each do |name| Object.class_eval { remove_const name if const_defined?(name) } @@ -543,10 +543,10 @@ class DependenciesTest < Test::Unit::TestCase def test_new_constants_in_module Object.const_set :M, Module.new - outer = Dependencies.new_constants_in(M) do + outer = ActiveSupport::Dependencies.new_constants_in(M) do M.const_set :OuterBefore, 10 - inner = Dependencies.new_constants_in(M) do + inner = ActiveSupport::Dependencies.new_constants_in(M) do M.const_set :Inner, 20 end assert_equal ["M::Inner"], inner @@ -554,17 +554,17 @@ class DependenciesTest < Test::Unit::TestCase M.const_set :OuterAfter, 30 end assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort - assert Dependencies.constant_watch_stack.empty? + assert ActiveSupport::Dependencies.constant_watch_stack.empty? ensure Object.class_eval { remove_const :M } end def test_new_constants_in_module_using_name - outer = Dependencies.new_constants_in(:M) do + outer = ActiveSupport::Dependencies.new_constants_in(:M) do Object.const_set :M, Module.new M.const_set :OuterBefore, 10 - inner = Dependencies.new_constants_in(:M) do + inner = ActiveSupport::Dependencies.new_constants_in(:M) do M.const_set :Inner, 20 end assert_equal ["M::Inner"], inner @@ -572,13 +572,13 @@ class DependenciesTest < Test::Unit::TestCase M.const_set :OuterAfter, 30 end assert_equal ["M::OuterAfter", "M::OuterBefore"], outer.sort - assert Dependencies.constant_watch_stack.empty? + assert ActiveSupport::Dependencies.constant_watch_stack.empty? ensure Object.class_eval { remove_const :M } end def test_new_constants_in_with_inherited_constants - m = Dependencies.new_constants_in(:Object) do + m = ActiveSupport::Dependencies.new_constants_in(:Object) do Object.class_eval { include ModuleWithConstant } end assert_equal [], m @@ -586,7 +586,7 @@ class DependenciesTest < Test::Unit::TestCase def test_new_constants_in_with_illegal_module_name_raises_correct_error assert_raises(NameError) do - Dependencies.new_constants_in("Illegal-Name") {} + ActiveSupport::Dependencies.new_constants_in("Illegal-Name") {} end end @@ -598,10 +598,10 @@ class DependenciesTest < Test::Unit::TestCase require_dependency 'multiple_constant_file' assert defined?(MultipleConstantFile) assert defined?(SiblingConstant) - assert Dependencies.autoloaded?(:MultipleConstantFile) - assert Dependencies.autoloaded?(:SiblingConstant) + assert ActiveSupport::Dependencies.autoloaded?(:MultipleConstantFile) + assert ActiveSupport::Dependencies.autoloaded?(:SiblingConstant) - Dependencies.clear + ActiveSupport::Dependencies.clear assert ! defined?(MultipleConstantFile) assert ! defined?(SiblingConstant) @@ -617,10 +617,10 @@ class DependenciesTest < Test::Unit::TestCase assert defined?(MultipleConstantFile) assert defined?(SiblingConstant) - assert Dependencies.autoloaded?(:MultipleConstantFile) - assert Dependencies.autoloaded?(:SiblingConstant) + assert ActiveSupport::Dependencies.autoloaded?(:MultipleConstantFile) + assert ActiveSupport::Dependencies.autoloaded?(:SiblingConstant) - Dependencies.clear + ActiveSupport::Dependencies.clear assert ! defined?(MultipleConstantFile) assert ! defined?(SiblingConstant) @@ -636,10 +636,10 @@ class DependenciesTest < Test::Unit::TestCase assert defined?(ClassFolder::NestedClass) assert defined?(ClassFolder::SiblingClass) - assert Dependencies.autoloaded?("ClassFolder::NestedClass") - assert Dependencies.autoloaded?("ClassFolder::SiblingClass") + assert ActiveSupport::Dependencies.autoloaded?("ClassFolder::NestedClass") + assert ActiveSupport::Dependencies.autoloaded?("ClassFolder::SiblingClass") - Dependencies.clear + ActiveSupport::Dependencies.clear assert ! defined?(ClassFolder::NestedClass) assert ! defined?(ClassFolder::SiblingClass) @@ -655,10 +655,10 @@ class DependenciesTest < Test::Unit::TestCase assert defined?(ClassFolder::NestedClass) assert defined?(ClassFolder::SiblingClass) - assert Dependencies.autoloaded?("ClassFolder::NestedClass") - assert Dependencies.autoloaded?("ClassFolder::SiblingClass") + assert ActiveSupport::Dependencies.autoloaded?("ClassFolder::NestedClass") + assert ActiveSupport::Dependencies.autoloaded?("ClassFolder::SiblingClass") - Dependencies.clear + ActiveSupport::Dependencies.clear assert ! defined?(ClassFolder::NestedClass) assert ! defined?(ClassFolder::SiblingClass) @@ -693,7 +693,7 @@ class DependenciesTest < Test::Unit::TestCase def test_autoload_doesnt_shadow_error_when_mechanism_not_set_to_load with_loading 'autoloading_fixtures' do - Dependencies.mechanism = :require + ActiveSupport::Dependencies.mechanism = :require 2.times do assert_raise(NameError) {"RaisesNameError".constantize} end @@ -727,39 +727,39 @@ class DependenciesTest < Test::Unit::TestCase def test_remove_constant_handles_double_colon_at_start Object.const_set 'DeleteMe', Module.new DeleteMe.const_set 'OrMe', Module.new - Dependencies.remove_constant "::DeleteMe::OrMe" + ActiveSupport::Dependencies.remove_constant "::DeleteMe::OrMe" assert ! defined?(DeleteMe::OrMe) assert defined?(DeleteMe) - Dependencies.remove_constant "::DeleteMe" + ActiveSupport::Dependencies.remove_constant "::DeleteMe" assert ! defined?(DeleteMe) end def test_load_once_constants_should_not_be_unloaded with_loading 'autoloading_fixtures' do - Dependencies.load_once_paths = Dependencies.load_paths + ActiveSupport::Dependencies.load_once_paths = ActiveSupport::Dependencies.load_paths ::A.to_s assert defined?(A) - Dependencies.clear + ActiveSupport::Dependencies.clear assert defined?(A) end ensure - Dependencies.load_once_paths = [] + ActiveSupport::Dependencies.load_once_paths = [] Object.class_eval { remove_const :A if const_defined?(:A) } end def test_load_once_paths_should_behave_when_recursively_loading with_loading 'dependencies', 'autoloading_fixtures' do - Dependencies.load_once_paths = [Dependencies.load_paths.last] + ActiveSupport::Dependencies.load_once_paths = [ActiveSupport::Dependencies.load_paths.last] assert !defined?(CrossSiteDependency) assert_nothing_raised { CrossSiteDepender.nil? } assert defined?(CrossSiteDependency) - assert !Dependencies.autoloaded?(CrossSiteDependency), + assert !ActiveSupport::Dependencies.autoloaded?(CrossSiteDependency), "CrossSiteDependency shouldn't be marked as autoloaded!" - Dependencies.clear + ActiveSupport::Dependencies.clear assert defined?(CrossSiteDependency), "CrossSiteDependency shouldn't have been unloaded!" end ensure - Dependencies.load_once_paths = [] + ActiveSupport::Dependencies.load_once_paths = [] end end diff --git a/activesupport/test/deprecation_test.rb b/activesupport/test/deprecation_test.rb index ebfa405947..27e9573ce2 100644 --- a/activesupport/test/deprecation_test.rb +++ b/activesupport/test/deprecation_test.rb @@ -24,6 +24,11 @@ class Deprecatee def d; end def e; end deprecate :a, :b, :c => :e, :d => "you now need to do something extra for this one" + + module B + C = 1 + end + A = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('Deprecatee::A', 'Deprecatee::B::C') end @@ -83,6 +88,11 @@ class DeprecationTest < Test::Unit::TestCase assert_not_deprecated { assert_equal @dtc.request.inspect, @dtc.old_request.inspect } end + def test_deprecated_constant_proxy + assert_not_deprecated { Deprecatee::B::C } + assert_deprecated('Deprecatee::A') { assert_equal Deprecatee::B::C, Deprecatee::A } + end + def test_assert_deprecation_without_match assert_deprecated do @dtc.partially diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index 26af245ff7..4ce9cbb705 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -12,188 +12,188 @@ class InflectorTest < Test::Unit::TestCase include InflectorTestCases def test_pluralize_plurals - assert_equal "plurals", Inflector.pluralize("plurals") - assert_equal "Plurals", Inflector.pluralize("Plurals") + assert_equal "plurals", ActiveSupport::Inflector.pluralize("plurals") + assert_equal "Plurals", ActiveSupport::Inflector.pluralize("Plurals") end def test_pluralize_empty_string - assert_equal "", Inflector.pluralize("") + assert_equal "", ActiveSupport::Inflector.pluralize("") end SingularToPlural.each do |singular, plural| define_method "test_pluralize_#{singular}" do - assert_equal(plural, Inflector.pluralize(singular)) - assert_equal(plural.capitalize, Inflector.pluralize(singular.capitalize)) + assert_equal(plural, ActiveSupport::Inflector.pluralize(singular)) + assert_equal(plural.capitalize, ActiveSupport::Inflector.pluralize(singular.capitalize)) end end SingularToPlural.each do |singular, plural| define_method "test_singularize_#{plural}" do - assert_equal(singular, Inflector.singularize(plural)) - assert_equal(singular.capitalize, Inflector.singularize(plural.capitalize)) + assert_equal(singular, ActiveSupport::Inflector.singularize(plural)) + assert_equal(singular.capitalize, ActiveSupport::Inflector.singularize(plural.capitalize)) end end MixtureToTitleCase.each do |before, titleized| define_method "test_titleize_#{before}" do - assert_equal(titleized, Inflector.titleize(before)) + assert_equal(titleized, ActiveSupport::Inflector.titleize(before)) end end def test_camelize CamelToUnderscore.each do |camel, underscore| - assert_equal(camel, Inflector.camelize(underscore)) + assert_equal(camel, ActiveSupport::Inflector.camelize(underscore)) end end def test_underscore CamelToUnderscore.each do |camel, underscore| - assert_equal(underscore, Inflector.underscore(camel)) + assert_equal(underscore, ActiveSupport::Inflector.underscore(camel)) end CamelToUnderscoreWithoutReverse.each do |camel, underscore| - assert_equal(underscore, Inflector.underscore(camel)) + assert_equal(underscore, ActiveSupport::Inflector.underscore(camel)) end end def test_camelize_with_module CamelWithModuleToUnderscoreWithSlash.each do |camel, underscore| - assert_equal(camel, Inflector.camelize(underscore)) + assert_equal(camel, ActiveSupport::Inflector.camelize(underscore)) end end def test_underscore_with_slashes CamelWithModuleToUnderscoreWithSlash.each do |camel, underscore| - assert_equal(underscore, Inflector.underscore(camel)) + assert_equal(underscore, ActiveSupport::Inflector.underscore(camel)) end end def test_demodulize - assert_equal "Account", Inflector.demodulize("MyApplication::Billing::Account") + assert_equal "Account", ActiveSupport::Inflector.demodulize("MyApplication::Billing::Account") end def test_foreign_key ClassNameToForeignKeyWithUnderscore.each do |klass, foreign_key| - assert_equal(foreign_key, Inflector.foreign_key(klass)) + assert_equal(foreign_key, ActiveSupport::Inflector.foreign_key(klass)) end ClassNameToForeignKeyWithoutUnderscore.each do |klass, foreign_key| - assert_equal(foreign_key, Inflector.foreign_key(klass, false)) + assert_equal(foreign_key, ActiveSupport::Inflector.foreign_key(klass, false)) end end def test_tableize ClassNameToTableName.each do |class_name, table_name| - assert_equal(table_name, Inflector.tableize(class_name)) + assert_equal(table_name, ActiveSupport::Inflector.tableize(class_name)) end end def test_classify ClassNameToTableName.each do |class_name, table_name| - assert_equal(class_name, Inflector.classify(table_name)) - assert_equal(class_name, Inflector.classify("table_prefix." + table_name)) + assert_equal(class_name, ActiveSupport::Inflector.classify(table_name)) + assert_equal(class_name, ActiveSupport::Inflector.classify("table_prefix." + table_name)) end end def test_classify_with_symbol assert_nothing_raised do - assert_equal 'FooBar', Inflector.classify(:foo_bars) + assert_equal 'FooBar', ActiveSupport::Inflector.classify(:foo_bars) end end def test_classify_with_leading_schema_name - assert_equal 'FooBar', Inflector.classify('schema.foo_bar') + assert_equal 'FooBar', ActiveSupport::Inflector.classify('schema.foo_bar') end def test_humanize UnderscoreToHuman.each do |underscore, human| - assert_equal(human, Inflector.humanize(underscore)) + assert_equal(human, ActiveSupport::Inflector.humanize(underscore)) end end def test_constantize - assert_nothing_raised { assert_equal Ace::Base::Case, Inflector.constantize("Ace::Base::Case") } - assert_nothing_raised { assert_equal Ace::Base::Case, Inflector.constantize("::Ace::Base::Case") } - assert_nothing_raised { assert_equal InflectorTest, Inflector.constantize("InflectorTest") } - assert_nothing_raised { assert_equal InflectorTest, Inflector.constantize("::InflectorTest") } - assert_raises(NameError) { Inflector.constantize("UnknownClass") } - assert_raises(NameError) { Inflector.constantize("An invalid string") } - assert_raises(NameError) { Inflector.constantize("InvalidClass\n") } + assert_nothing_raised { assert_equal Ace::Base::Case, ActiveSupport::Inflector.constantize("Ace::Base::Case") } + assert_nothing_raised { assert_equal Ace::Base::Case, ActiveSupport::Inflector.constantize("::Ace::Base::Case") } + assert_nothing_raised { assert_equal InflectorTest, ActiveSupport::Inflector.constantize("InflectorTest") } + assert_nothing_raised { assert_equal InflectorTest, ActiveSupport::Inflector.constantize("::InflectorTest") } + assert_raises(NameError) { ActiveSupport::Inflector.constantize("UnknownClass") } + assert_raises(NameError) { ActiveSupport::Inflector.constantize("An invalid string") } + assert_raises(NameError) { ActiveSupport::Inflector.constantize("InvalidClass\n") } end def test_constantize_does_lexical_lookup - assert_raises(NameError) { Inflector.constantize("Ace::Base::InflectorTest") } + assert_raises(NameError) { ActiveSupport::Inflector.constantize("Ace::Base::InflectorTest") } end def test_ordinal OrdinalNumbers.each do |number, ordinalized| - assert_equal(ordinalized, Inflector.ordinalize(number)) + assert_equal(ordinalized, ActiveSupport::Inflector.ordinalize(number)) end end def test_dasherize UnderscoresToDashes.each do |underscored, dasherized| - assert_equal(dasherized, Inflector.dasherize(underscored)) + assert_equal(dasherized, ActiveSupport::Inflector.dasherize(underscored)) end end def test_underscore_as_reverse_of_dasherize UnderscoresToDashes.each do |underscored, dasherized| - assert_equal(underscored, Inflector.underscore(Inflector.dasherize(underscored))) + assert_equal(underscored, ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.dasherize(underscored))) end end def test_underscore_to_lower_camel UnderscoreToLowerCamel.each do |underscored, lower_camel| - assert_equal(lower_camel, Inflector.camelize(underscored, false)) + assert_equal(lower_camel, ActiveSupport::Inflector.camelize(underscored, false)) end end - + %w{plurals singulars uncountables}.each do |inflection_type| class_eval " def test_clear_#{inflection_type} - cached_values = Inflector.inflections.#{inflection_type} - Inflector.inflections.clear :#{inflection_type} - assert Inflector.inflections.#{inflection_type}.empty?, \"#{inflection_type} inflections should be empty after clear :#{inflection_type}\" - Inflector.inflections.instance_variable_set :@#{inflection_type}, cached_values + cached_values = ActiveSupport::Inflector.inflections.#{inflection_type} + ActiveSupport::Inflector.inflections.clear :#{inflection_type} + assert ActiveSupport::Inflector.inflections.#{inflection_type}.empty?, \"#{inflection_type} inflections should be empty after clear :#{inflection_type}\" + ActiveSupport::Inflector.inflections.instance_variable_set :@#{inflection_type}, cached_values end " end - + def test_clear_all - cached_values = Inflector.inflections.plurals, Inflector.inflections.singulars, Inflector.inflections.uncountables - Inflector.inflections.clear :all - assert Inflector.inflections.plurals.empty? - assert Inflector.inflections.singulars.empty? - assert Inflector.inflections.uncountables.empty? - Inflector.inflections.instance_variable_set :@plurals, cached_values[0] - Inflector.inflections.instance_variable_set :@singulars, cached_values[1] - Inflector.inflections.instance_variable_set :@uncountables, cached_values[2] - end - + cached_values = ActiveSupport::Inflector.inflections.plurals, ActiveSupport::Inflector.inflections.singulars, ActiveSupport::Inflector.inflections.uncountables + ActiveSupport::Inflector.inflections.clear :all + assert ActiveSupport::Inflector.inflections.plurals.empty? + assert ActiveSupport::Inflector.inflections.singulars.empty? + assert ActiveSupport::Inflector.inflections.uncountables.empty? + ActiveSupport::Inflector.inflections.instance_variable_set :@plurals, cached_values[0] + ActiveSupport::Inflector.inflections.instance_variable_set :@singulars, cached_values[1] + ActiveSupport::Inflector.inflections.instance_variable_set :@uncountables, cached_values[2] + end + def test_clear_with_default - cached_values = Inflector.inflections.plurals, Inflector.inflections.singulars, Inflector.inflections.uncountables - Inflector.inflections.clear - assert Inflector.inflections.plurals.empty? - assert Inflector.inflections.singulars.empty? - assert Inflector.inflections.uncountables.empty? - Inflector.inflections.instance_variable_set :@plurals, cached_values[0] - Inflector.inflections.instance_variable_set :@singulars, cached_values[1] - Inflector.inflections.instance_variable_set :@uncountables, cached_values[2] + cached_values = ActiveSupport::Inflector.inflections.plurals, ActiveSupport::Inflector.inflections.singulars, ActiveSupport::Inflector.inflections.uncountables + ActiveSupport::Inflector.inflections.clear + assert ActiveSupport::Inflector.inflections.plurals.empty? + assert ActiveSupport::Inflector.inflections.singulars.empty? + assert ActiveSupport::Inflector.inflections.uncountables.empty? + ActiveSupport::Inflector.inflections.instance_variable_set :@plurals, cached_values[0] + ActiveSupport::Inflector.inflections.instance_variable_set :@singulars, cached_values[1] + ActiveSupport::Inflector.inflections.instance_variable_set :@uncountables, cached_values[2] end Irregularities.each do |irregularity| singular, plural = *irregularity - Inflector.inflections do |inflect| + ActiveSupport::Inflector.inflections do |inflect| define_method("test_irregularity_between_#{singular}_and_#{plural}") do inflect.irregular(singular, plural) - assert_equal singular, Inflector.singularize(plural) - assert_equal plural, Inflector.pluralize(singular) + assert_equal singular, ActiveSupport::Inflector.singularize(plural) + assert_equal plural, ActiveSupport::Inflector.pluralize(singular) end end end [ :all, [] ].each do |scope| - Inflector.inflections do |inflect| + ActiveSupport::Inflector.inflections do |inflect| define_method("test_clear_inflections_with_#{scope.kind_of?(Array) ? "no_arguments" : scope}") do # save all the inflections singulars, plurals, uncountables = inflect.singulars, inflect.plurals, inflect.uncountables @@ -218,7 +218,7 @@ class InflectorTest < Test::Unit::TestCase end { :singulars => :singular, :plurals => :plural, :uncountables => :uncountable }.each do |scope, method| - Inflector.inflections do |inflect| + ActiveSupport::Inflector.inflections do |inflect| define_method("test_clear_inflections_with_#{scope}") do # save the inflections values = inflect.send(scope) diff --git a/activesupport/test/ordered_options_test.rb b/activesupport/test/ordered_options_test.rb index fb7a58d0b0..e48425ca25 100644 --- a/activesupport/test/ordered_options_test.rb +++ b/activesupport/test/ordered_options_test.rb @@ -2,7 +2,7 @@ require 'abstract_unit' class OrderedOptionsTest < Test::Unit::TestCase def test_usage - a = OrderedOptions.new + a = ActiveSupport::OrderedOptions.new assert_nil a[:not_set] @@ -20,7 +20,7 @@ class OrderedOptionsTest < Test::Unit::TestCase end def test_looping - a = OrderedOptions.new + a = ActiveSupport::OrderedOptions.new a[:allow_concurreny] = true a["else_where"] = 56 @@ -34,7 +34,7 @@ class OrderedOptionsTest < Test::Unit::TestCase end def test_method_access - a = OrderedOptions.new + a = ActiveSupport::OrderedOptions.new assert_nil a.not_set diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index f3069b589b..b42dcd17f8 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -1,10 +1,9 @@ require 'abstract_unit' class TimeZoneTest < Test::Unit::TestCase - def test_utc_to_local silence_warnings do # silence warnings raised by tzinfo gem - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] assert_equal Time.utc(1999, 12, 31, 19), zone.utc_to_local(Time.utc(2000, 1)) # standard offset -0500 assert_equal Time.utc(2000, 6, 30, 20), zone.utc_to_local(Time.utc(2000, 7)) # dst offset -0400 end @@ -12,41 +11,41 @@ class TimeZoneTest < Test::Unit::TestCase def test_local_to_utc silence_warnings do # silence warnings raised by tzinfo gem - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] assert_equal Time.utc(2000, 1, 1, 5), zone.local_to_utc(Time.utc(2000, 1)) # standard offset -0500 assert_equal Time.utc(2000, 7, 1, 4), zone.local_to_utc(Time.utc(2000, 7)) # dst offset -0400 end end - + def test_period_for_local silence_warnings do # silence warnings raised by tzinfo gem - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] assert_instance_of TZInfo::TimezonePeriod, zone.period_for_local(Time.utc(2000)) end end - - TimeZone::MAPPING.keys.each do |name| + + ActiveSupport::TimeZone::MAPPING.keys.each do |name| define_method("test_map_#{name.downcase.gsub(/[^a-z]/, '_')}_to_tzinfo") do silence_warnings do # silence warnings raised by tzinfo gem - zone = TimeZone[name] + zone = ActiveSupport::TimeZone[name] assert zone.tzinfo.respond_to?(:period_for_local) end end end def test_from_integer_to_map - assert_instance_of TimeZone, TimeZone[-28800] # PST + assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone[-28800] # PST end def test_from_duration_to_map - assert_instance_of TimeZone, TimeZone[-480.minutes] # PST + assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone[-480.minutes] # PST end - TimeZone.all.each do |zone| + ActiveSupport::TimeZone.all.each do |zone| name = zone.name.downcase.gsub(/[^a-z]/, '_') define_method("test_from_#{name}_to_map") do silence_warnings do # silence warnings raised by tzinfo gem - assert_instance_of TimeZone, TimeZone[zone.name] + assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone[zone.name] end end @@ -62,62 +61,62 @@ class TimeZoneTest < Test::Unit::TestCase def test_now with_env_tz 'US/Eastern' do Time.stubs(:now).returns(Time.local(2000)) - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] assert_instance_of ActiveSupport::TimeWithZone, zone.now assert_equal Time.utc(2000,1,1,5), zone.now.utc assert_equal Time.utc(2000), zone.now.time assert_equal zone, zone.now.time_zone end end - + def test_now_enforces_spring_dst_rules with_env_tz 'US/Eastern' do Time.stubs(:now).returns(Time.local(2006,4,2,2)) # 2AM springs forward to 3AM - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] assert_equal Time.utc(2006,4,2,3), zone.now.time assert_equal true, zone.now.dst? end end - + def test_now_enforces_fall_dst_rules with_env_tz 'US/Eastern' do Time.stubs(:now).returns(Time.at(1162098000)) # equivalent to 1AM DST - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] assert_equal Time.utc(2006,10,29,1), zone.now.time assert_equal true, zone.now.dst? end end - + def test_today Time.stubs(:now).returns(Time.utc(2000, 1, 1, 4, 59, 59)) # 1 sec before midnight Jan 1 EST - assert_equal Date.new(1999, 12, 31), TimeZone['Eastern Time (US & Canada)'].today + assert_equal Date.new(1999, 12, 31), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].today Time.stubs(:now).returns(Time.utc(2000, 1, 1, 5)) # midnight Jan 1 EST - assert_equal Date.new(2000, 1, 1), TimeZone['Eastern Time (US & Canada)'].today + assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].today Time.stubs(:now).returns(Time.utc(2000, 1, 2, 4, 59, 59)) # 1 sec before midnight Jan 2 EST - assert_equal Date.new(2000, 1, 1), TimeZone['Eastern Time (US & Canada)'].today + assert_equal Date.new(2000, 1, 1), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].today Time.stubs(:now).returns(Time.utc(2000, 1, 2, 5)) # midnight Jan 2 EST - assert_equal Date.new(2000, 1, 2), TimeZone['Eastern Time (US & Canada)'].today + assert_equal Date.new(2000, 1, 2), ActiveSupport::TimeZone['Eastern Time (US & Canada)'].today end end - + def test_local silence_warnings do # silence warnings raised by tzinfo gem - time = TimeZone["Hawaii"].local(2007, 2, 5, 15, 30, 45) + time = ActiveSupport::TimeZone["Hawaii"].local(2007, 2, 5, 15, 30, 45) assert_equal Time.utc(2007, 2, 5, 15, 30, 45), time.time - assert_equal TimeZone["Hawaii"], time.time_zone + assert_equal ActiveSupport::TimeZone["Hawaii"], time.time_zone end end def test_local_with_old_date silence_warnings do # silence warnings raised by tzinfo gem - time = TimeZone["Hawaii"].local(1850, 2, 5, 15, 30, 45) + time = ActiveSupport::TimeZone["Hawaii"].local(1850, 2, 5, 15, 30, 45) assert_equal [45,30,15,5,2,1850], time.to_a[0,6] - assert_equal TimeZone["Hawaii"], time.time_zone + assert_equal ActiveSupport::TimeZone["Hawaii"], time.time_zone end end def test_local_enforces_spring_dst_rules - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] twz = zone.local(2006,4,2,1,59,59) # 1 second before DST start assert_equal Time.utc(2006,4,2,1,59,59), twz.time assert_equal Time.utc(2006,4,2,6,59,59), twz.utc @@ -138,16 +137,16 @@ class TimeZoneTest < Test::Unit::TestCase def test_local_enforces_fall_dst_rules # 1AM during fall DST transition is ambiguous, it could be either DST or non-DST 1AM # Mirroring Time.local behavior, this method selects the DST time - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] twz = zone.local(2006,10,29,1) assert_equal Time.utc(2006,10,29,1), twz.time assert_equal Time.utc(2006,10,29,5), twz.utc - assert_equal true, twz.dst? + assert_equal true, twz.dst? assert_equal 'EDT', twz.zone end def test_at - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] secs = 946684800.0 twz = zone.at(secs) assert_equal Time.utc(1999,12,31,19), twz.time @@ -157,7 +156,7 @@ class TimeZoneTest < Test::Unit::TestCase end def test_at_with_old_date - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] secs = DateTime.civil(1850).to_f twz = zone.at(secs) assert_equal [1850, 1, 1, 0], [twz.utc.year, twz.utc.mon, twz.utc.day, twz.utc.hour] @@ -166,7 +165,7 @@ class TimeZoneTest < Test::Unit::TestCase end def test_parse - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] twz = zone.parse('1999-12-31 19:00:00') assert_equal Time.utc(1999,12,31,19), twz.time assert_equal Time.utc(2000), twz.utc @@ -175,7 +174,7 @@ class TimeZoneTest < Test::Unit::TestCase def test_parse_string_with_timezone (-11..13).each do |timezone_offset| - zone = TimeZone[timezone_offset] + zone = ActiveSupport::TimeZone[timezone_offset] twz = zone.parse('1999-12-31 19:00:00') assert_equal twz, zone.parse(twz.to_s) end @@ -183,25 +182,25 @@ class TimeZoneTest < Test::Unit::TestCase def test_parse_with_old_date silence_warnings do # silence warnings raised by tzinfo gem - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] twz = zone.parse('1850-12-31 19:00:00') assert_equal [0,0,19,31,12,1850], twz.to_a[0,6] assert_equal zone, twz.time_zone end end - + def test_parse_far_future_date_with_time_zone_offset_in_string silence_warnings do # silence warnings raised by tzinfo gem - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] twz = zone.parse('2050-12-31 19:00:00 -10:00') # i.e., 2050-01-01 05:00:00 UTC assert_equal [0,0,0,1,1,2051], twz.to_a[0,6] assert_equal zone, twz.time_zone end end - + def test_parse_returns_nil_when_string_without_date_information_is_passed_in silence_warnings do # silence warnings raised by tzinfo gem - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] assert_nil zone.parse('foobar') assert_nil zone.parse(' ') end @@ -209,80 +208,80 @@ class TimeZoneTest < Test::Unit::TestCase uses_mocha 'TestParseWithIncompleteDate' do def test_parse_with_incomplete_date - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] zone.stubs(:now).returns zone.local(1999,12,31) twz = zone.parse('19:00:00') assert_equal Time.utc(1999,12,31,19), twz.time end end - + def test_utc_offset_lazy_loaded_from_tzinfo_when_not_passed_in_to_initialize silence_warnings do # silence warnings raised by tzinfo gem tzinfo = TZInfo::Timezone.get('America/New_York') - zone = TimeZone.create(tzinfo.name, nil, tzinfo) + zone = ActiveSupport::TimeZone.create(tzinfo.name, nil, tzinfo) assert_equal nil, zone.instance_variable_get('@utc_offset') assert_equal(-18_000, zone.utc_offset) end end - + def test_formatted_offset_positive - zone = TimeZone['Moscow'] + zone = ActiveSupport::TimeZone['Moscow'] assert_equal "+03:00", zone.formatted_offset assert_equal "+0300", zone.formatted_offset(false) end - + def test_formatted_offset_negative - zone = TimeZone['Eastern Time (US & Canada)'] + zone = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] assert_equal "-05:00", zone.formatted_offset assert_equal "-0500", zone.formatted_offset(false) end - + def test_formatted_offset_zero - zone = TimeZone['London'] + zone = ActiveSupport::TimeZone['London'] assert_equal "+00:00", zone.formatted_offset assert_equal "UTC", zone.formatted_offset(true, 'UTC') end - + def test_zone_compare - zone1 = TimeZone['Central Time (US & Canada)'] # offset -0600 - zone2 = TimeZone['Eastern Time (US & Canada)'] # offset -0500 + zone1 = ActiveSupport::TimeZone['Central Time (US & Canada)'] # offset -0600 + zone2 = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] # offset -0500 assert zone1 < zone2 assert zone2 > zone1 assert zone1 == zone1 end - + def test_to_s - assert_equal "(GMT+03:00) Moscow", TimeZone['Moscow'].to_s + assert_equal "(GMT+03:00) Moscow", ActiveSupport::TimeZone['Moscow'].to_s end - + def test_all_sorted - all = TimeZone.all + all = ActiveSupport::TimeZone.all 1.upto( all.length-1 ) do |i| assert all[i-1] < all[i] end end - + def test_index - assert_nil TimeZone["bogus"] - assert_instance_of TimeZone, TimeZone["Central Time (US & Canada)"] - assert_instance_of TimeZone, TimeZone[8] - assert_raises(ArgumentError) { TimeZone[false] } + assert_nil ActiveSupport::TimeZone["bogus"] + assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone["Central Time (US & Canada)"] + assert_instance_of ActiveSupport::TimeZone, ActiveSupport::TimeZone[8] + assert_raises(ArgumentError) { ActiveSupport::TimeZone[false] } end - + def test_new - assert_equal TimeZone["Central Time (US & Canada)"], TimeZone.new("Central Time (US & Canada)") + assert_equal ActiveSupport::TimeZone["Central Time (US & Canada)"], ActiveSupport::TimeZone.new("Central Time (US & Canada)") end - + def test_us_zones - assert TimeZone.us_zones.include?(TimeZone["Hawaii"]) - assert !TimeZone.us_zones.include?(TimeZone["Kuala Lumpur"]) - end - + assert ActiveSupport::TimeZone.us_zones.include?(ActiveSupport::TimeZone["Hawaii"]) + assert !ActiveSupport::TimeZone.us_zones.include?(ActiveSupport::TimeZone["Kuala Lumpur"]) + end + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz yield ensure old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ') - end + end end -- cgit v1.2.3 From a977f3e88e989cd7d795c46504451b4b1b4db79d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 3 Jun 2008 13:35:03 -0500 Subject: Fixed ambiguous first argument warning in ArrayExtTest. --- activesupport/test/core_ext/array_ext_test.rb | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index 20bc5dfcb7..73fbeb8b6b 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -21,7 +21,7 @@ class ArrayExtToParamTests < Test::Unit::TestCase "#{self}1" end end - + def test_string_array assert_equal '', %w().to_param assert_equal 'hello/world', %w(hello world).to_param @@ -31,7 +31,7 @@ class ArrayExtToParamTests < Test::Unit::TestCase def test_number_array assert_equal '10/20', [10, 20].to_param end - + def test_to_param_array assert_equal 'custom1/param1', [ToParam.new('custom'), ToParam.new('param')].to_param end @@ -222,10 +222,10 @@ class ArrayToXmlTests < Test::Unit::TestCase assert xml.include?(%(2)), xml end - + def test_to_xml_with_empty xml = [].to_xml - assert_match /type="array"\/>/, xml + assert_match(/type="array"\/>/, xml) end end @@ -239,17 +239,15 @@ class ArrayExtractOptionsTests < Test::Unit::TestCase end uses_mocha "ArrayExtRandomTests" do + class ArrayExtRandomTests < Test::Unit::TestCase + def test_random_element_from_array + assert_nil [].rand -class ArrayExtRandomTests < Test::Unit::TestCase - def test_random_element_from_array - assert_nil [].rand - - Kernel.expects(:rand).with(1).returns(0) - assert_equal 'x', ['x'].rand + Kernel.expects(:rand).with(1).returns(0) + assert_equal 'x', ['x'].rand - Kernel.expects(:rand).with(3).returns(1) - assert_equal 2, [1, 2, 3].rand + Kernel.expects(:rand).with(3).returns(1) + assert_equal 2, [1, 2, 3].rand + end end end - -end -- cgit v1.2.3 From aa1771668877f20ca044e8f45a9736fbb7c8402e Mon Sep 17 00:00:00 2001 From: Craig Demyanovich Date: Tue, 3 Jun 2008 13:38:00 -0500 Subject: Callbacks fire before notifying observers [#230 state:resolved] Signed-off-by: Joshua Peek --- activerecord/lib/active_record/callbacks.rb | 4 +-- .../test/cases/callbacks_observers_test.rb | 38 ++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 activerecord/test/cases/callbacks_observers_test.rb diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 41ec5c5e61..4edc209c65 100755 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -293,14 +293,14 @@ module ActiveRecord private def callback(method) - notify(method) - result = run_callbacks(method) { |result, object| result == false } if result != false && respond_to_without_attributes?(method) result = send(method) end + notify(method) + return result end diff --git a/activerecord/test/cases/callbacks_observers_test.rb b/activerecord/test/cases/callbacks_observers_test.rb new file mode 100644 index 0000000000..87de524923 --- /dev/null +++ b/activerecord/test/cases/callbacks_observers_test.rb @@ -0,0 +1,38 @@ +require "cases/helper" + +class Comment < ActiveRecord::Base + attr_accessor :callers + + before_validation :record_callers + + def after_validation + record_callers + end + + def record_callers + callers << self.class if callers + end +end + +class CommentObserver < ActiveRecord::Observer + attr_accessor :callers + + def after_validation(model) + callers << self.class if callers + end +end + +class CallbacksObserversTest < ActiveRecord::TestCase + def test_model_callbacks_fire_before_observers_are_notified + callers = [] + + comment = Comment.new + comment.callers = callers + + CommentObserver.instance.callers = callers + + comment.valid? + + assert_equal [Comment, Comment, CommentObserver], callers, "model callbacks did not fire before observers were notified" + end +end -- cgit v1.2.3 From d54d90f2b590c763fe710482a9b993923fe03ec0 Mon Sep 17 00:00:00 2001 From: josevalim Date: Tue, 3 Jun 2008 14:02:51 -0500 Subject: Allow caches_action to accept a layout option [#198 state:resolved] Signed-off-by: Joshua Peek --- actionpack/CHANGELOG | 2 ++ actionpack/lib/action_controller/caching/actions.rb | 20 +++++++++++++++++--- actionpack/test/controller/caching_test.rb | 15 +++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 9622029362..ba2b16849c 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,3 +1,5 @@ +* Allow caches_action to accept a layout option [José Valim] + * Added Rack processor [Ezra Zygmuntowicz, Josh Peek] diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index 1ef9e60a21..c4b0a97a33 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -40,6 +40,8 @@ module ActionController #:nodoc: # controller.send(:list_url, c.params[:id]) } # end # + # If you pass :layout => false, it will only cache your action content. It is useful when your layout has dynamic information. + # module Actions def self.included(base) #:nodoc: base.extend(ClassMethods) @@ -54,7 +56,8 @@ module ActionController #:nodoc: def caches_action(*actions) return unless cache_configured? options = actions.extract_options! - around_filter(ActionCacheFilter.new(:cache_path => options.delete(:cache_path)), {:only => actions}.merge(options)) + cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path)) + around_filter(cache_filter, {:only => actions}.merge(options)) end end @@ -81,7 +84,9 @@ module ActionController #:nodoc: if cache = controller.read_fragment(cache_path.path) controller.rendered_action_cache = true set_content_type!(controller, cache_path.extension) - controller.send!(:render_for_text, cache) + options = { :text => cache } + options.merge!(:layout => true) if cache_layout? + controller.send!(:render, options) false else controller.action_cache_path = cache_path @@ -90,7 +95,8 @@ module ActionController #:nodoc: def after(controller) return if controller.rendered_action_cache || !caching_allowed(controller) - controller.write_fragment(controller.action_cache_path.path, controller.response.body) + action_content = cache_layout? ? content_for_layout(controller) : controller.response.body + controller.write_fragment(controller.action_cache_path.path, action_content) end private @@ -105,6 +111,14 @@ module ActionController #:nodoc: def caching_allowed(controller) controller.request.get? && controller.response.headers['Status'].to_i == 200 end + + def cache_layout? + @options[:layout] == false + end + + def content_for_layout(controller) + controller.response.layout && controller.response.template.instance_variable_get('@content_for_layout') + end end class ActionCachePath diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index f9b6b87bc6..c6d61bb504 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -156,6 +156,7 @@ class ActionCachingTestController < ActionController::Base caches_action :show, :cache_path => 'http://test.host/custom/show' caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" } caches_action :with_layout + caches_action :layout_false, :layout => false layout 'talk_from_action.erb' @@ -181,6 +182,7 @@ class ActionCachingTestController < ActionController::Base alias_method :show, :index alias_method :edit, :index alias_method :destroy, :index + alias_method :layout_false, :with_layout def expire expire_action :controller => 'action_caching_test', :action => 'index' @@ -263,6 +265,19 @@ class ActionCacheTest < Test::Unit::TestCase assert_equal @response.body, read_fragment('hostname.com/action_caching_test/with_layout') end + def test_action_cache_with_layout_and_layout_cache_false + get :layout_false + cached_time = content_to_cache + assert_not_equal cached_time, @response.body + assert fragment_exist?('hostname.com/action_caching_test/layout_false') + reset! + + get :layout_false + assert_not_equal cached_time, @response.body + + assert_equal cached_time, read_fragment('hostname.com/action_caching_test/layout_false') + end + def test_action_cache_conditional_options @request.env['HTTP_ACCEPT'] = 'application/json' get :index -- cgit v1.2.3 From 7cfa6ec8a37b70ec302f09929df5160ac42971e7 Mon Sep 17 00:00:00 2001 From: Steve Purcell Date: Tue, 3 Jun 2008 14:15:33 -0500 Subject: Add more standard Hash methods to ActiveSupport::OrderedHash [#314 state:resolved] Signed-off-by: Joshua Peek --- activesupport/CHANGELOG | 4 ++++ activesupport/lib/active_support/ordered_hash.rb | 14 ++++++++++++++ activesupport/test/ordered_hash_test.rb | 19 +++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index a1a3c7ac2b..a6ce37ac14 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,9 @@ *Edge* +* Add more standard Hash methods to ActiveSupport::OrderedHash [Steve Purcell] + +* Namespace Inflector, Dependencies, OrderedOptions, and TimeZone under ActiveSupport [Josh Peek] + * Fixed Date#end_of_quarter to not blow up on May 31st [#289 state:resolved] (Danger) diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 6993621ef9..59ceaec696 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -38,6 +38,20 @@ module ActiveSupport each { |array| hash[array[0]] = array[1] } end end + + def has_key?(k) + !assoc(k).nil? + end + + alias_method :key?, :has_key? + alias_method :include?, :has_key? + alias_method :member?, :has_key? + + def has_value?(v) + any? { |key, value| value == v } + end + + alias_method :value?, :has_value? end end end diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 14be48724e..98a6ad6b26 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -42,4 +42,23 @@ class OrderedHashTest < Test::Unit::TestCase assert_nil @ordered_hash.delete(bad_key) end + + def test_has_key + assert_equal true, @ordered_hash.has_key?('blue') + assert_equal true, @ordered_hash.key?('blue') + assert_equal true, @ordered_hash.include?('blue') + assert_equal true, @ordered_hash.member?('blue') + + assert_equal false, @ordered_hash.has_key?('indigo') + assert_equal false, @ordered_hash.key?('indigo') + assert_equal false, @ordered_hash.include?('indigo') + assert_equal false, @ordered_hash.member?('indigo') + end + + def test_has_value + assert_equal true, @ordered_hash.has_value?('000099') + assert_equal true, @ordered_hash.value?('000099') + assert_equal false, @ordered_hash.has_value?('ABCABC') + assert_equal false, @ordered_hash.value?('ABCABC') + end end -- cgit v1.2.3 From 8afa725f4b98a6e0ceee4792e8ebaebb6189e5f6 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 3 Jun 2008 17:44:56 -0500 Subject: Wrapped Rails.env in StringQuestioneer so you can do Rails.env.development? [DHH] --- activesupport/CHANGELOG | 2 ++ activesupport/lib/active_support.rb | 2 ++ activesupport/lib/active_support/string_questioneer.rb | 9 +++++++++ activesupport/test/string_questioneer_test.rb | 15 +++++++++++++++ railties/CHANGELOG | 2 ++ railties/lib/initializer.rb | 2 +- 6 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 activesupport/lib/active_support/string_questioneer.rb create mode 100644 activesupport/test/string_questioneer_test.rb diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index a6ce37ac14..894b43928f 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -4,6 +4,8 @@ * Namespace Inflector, Dependencies, OrderedOptions, and TimeZone under ActiveSupport [Josh Peek] +* Added StringQuestioneer for doing things like StringQuestioneer.new("production").production? # => true and StringQuestioneer.new("production").development? # => false [DHH] + * Fixed Date#end_of_quarter to not blow up on May 31st [#289 state:resolved] (Danger) diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 982adfb0cf..2b418a1bb7 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -43,6 +43,8 @@ require 'active_support/ordered_hash' require 'active_support/ordered_options' require 'active_support/option_merger' +require 'active_support/string_questioneer' + require 'active_support/values/time_zone' require 'active_support/duration' diff --git a/activesupport/lib/active_support/string_questioneer.rb b/activesupport/lib/active_support/string_questioneer.rb new file mode 100644 index 0000000000..7732f8b401 --- /dev/null +++ b/activesupport/lib/active_support/string_questioneer.rb @@ -0,0 +1,9 @@ +class StringQuestioneer < String + def method_missing(method_name, *arguments) + if method_name.to_s.ends_with?("?") + self == method_name.to_s[0..-2] + else + super + end + end +end \ No newline at end of file diff --git a/activesupport/test/string_questioneer_test.rb b/activesupport/test/string_questioneer_test.rb new file mode 100644 index 0000000000..ff9d2c17f9 --- /dev/null +++ b/activesupport/test/string_questioneer_test.rb @@ -0,0 +1,15 @@ +require 'abstract_unit' + +class StringQuestioneerTest < Test::Unit::TestCase + def test_match + assert StringQuestioneer.new("production").production? + end + + def test_miss + assert !StringQuestioneer.new("production").development? + end + + def test_missing_question_mark + assert_raises(NoMethodError) { StringQuestioneer.new("production").production } + end +end \ No newline at end of file diff --git a/railties/CHANGELOG b/railties/CHANGELOG index 4d1c86e643..abadeb693f 100644 --- a/railties/CHANGELOG +++ b/railties/CHANGELOG @@ -1,5 +1,7 @@ *Edge* +* Wrapped Rails.env in StringQuestioneer so you can do Rails.env.development? [DHH] + * Fixed that RailsInfoController wasn't considering all requests local in development mode (Edgard Castro) [#310 state:resolved] diff --git a/railties/lib/initializer.rb b/railties/lib/initializer.rb index 9e6e02e8e0..d80d014527 100644 --- a/railties/lib/initializer.rb +++ b/railties/lib/initializer.rb @@ -37,7 +37,7 @@ module Rails end def env - RAILS_ENV + StringQuestioneer.new(RAILS_ENV) end def cache -- cgit v1.2.3