diff options
Diffstat (limited to 'activesupport')
49 files changed, 582 insertions, 618 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 373236ce9a..69e9cbfd42 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,9 @@ *Rails 3.1.0 (unreleased)* +* Add String#inquiry as a convenience method for turning a string into a StringInquirer object [DHH] + +* Add Object#in? to test if an object is included in another object [Prem Sichanugrist, Brian Morearty, John Reitano] + * LocalCache strategy is now a real middleware class, not an anonymous class posing for pictures. diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec index df7f68fecf..c5b5b57dec 100644 --- a/activesupport/activesupport.gemspec +++ b/activesupport/activesupport.gemspec @@ -17,5 +17,5 @@ Gem::Specification.new do |s| s.files = Dir['CHANGELOG', 'README.rdoc', 'lib/**/*'] s.require_path = 'lib' - s.has_rdoc = true + s.add_dependency('multi_json', '~> 1.0') end diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 6b662ac660..6b87774978 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -42,7 +42,6 @@ module ActiveSupport autoload :DescendantsTracker autoload :FileUpdateChecker - autoload :FileWatcher autoload :LogSubscriber autoload :Notifications diff --git a/activesupport/lib/active_support/buffered_logger.rb b/activesupport/lib/active_support/buffered_logger.rb index e41731f3e7..88b50fc506 100644 --- a/activesupport/lib/active_support/buffered_logger.rb +++ b/activesupport/lib/active_support/buffered_logger.rb @@ -41,7 +41,7 @@ module ActiveSupport def initialize(log, level = DEBUG) @level = level - @buffer = {} + @buffer = Hash.new { |h,k| h[k] = [] } @auto_flushing = 1 @guard = Mutex.new @@ -100,13 +100,8 @@ module ActiveSupport def flush @guard.synchronize do - unless buffer.empty? - old_buffer = buffer - all_content = StringIO.new - old_buffer.each do |content| - all_content << content - end - @log.write(all_content.string) + buffer.each do |content| + @log.write(content) end # Important to do this even if buffer was empty or else @buffer will @@ -127,7 +122,7 @@ module ActiveSupport end def buffer - @buffer[Thread.current] ||= [] + @buffer[Thread.current] end def clear_buffer diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 18182bbb40..a1376ae52a 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/file/atomic' require 'active_support/core_ext/string/conversions' +require 'active_support/core_ext/object/inclusion' require 'rack/utils' module ActiveSupport @@ -20,7 +21,7 @@ module ActiveSupport end def clear(options = nil) - root_dirs = Dir.entries(cache_path).reject{|f| ['.', '..'].include?(f)} + root_dirs = Dir.entries(cache_path).reject{|f| f.in?(['.', '..'])} FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)}) end @@ -120,7 +121,7 @@ module ActiveSupport # Lock a file for a block so only one process can modify it at a time. def lock_file(file_name, &block) # :nodoc: if File.exist?(file_name) - File.open(file_name, 'r') do |f| + File.open(file_name, 'r+') do |f| begin f.flock File::LOCK_EX yield @@ -161,7 +162,7 @@ module ActiveSupport # Delete empty directories in the cache. def delete_empty_directories(dir) return if dir == cache_path - if Dir.entries(dir).reject{|f| ['.', '..'].include?(f)}.empty? + if Dir.entries(dir).reject{|f| f.in?(['.', '..'])}.empty? File.delete(dir) rescue nil delete_empty_directories(File.dirname(dir)) end diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 45263d482f..7ef1497ac2 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -4,7 +4,9 @@ rescue LoadError => e $stderr.puts "You don't have memcache-client installed in your application. Please add it to your Gemfile and run bundle install" raise e end + require 'digest/md5' +require 'active_support/core_ext/string/encoding' module ActiveSupport module Cache @@ -157,8 +159,14 @@ module ActiveSupport end private + + # Memcache keys are binaries. So we need to force their encoding to binary + # before applying the regular expression to ensure we are escaping all + # characters properly. def escape_key(key) - key = key.to_s.gsub(ESCAPE_KEY_CHARS){|match| "%#{match.getbyte(0).to_s(16).upcase}"} + key = key.to_s.dup + key = key.force_encoding("BINARY") if key.encoding_aware? + key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" } key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250 key end diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 418102352f..656cba625c 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -4,6 +4,7 @@ require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/singleton_class' +require 'active_support/core_ext/object/inclusion' module ActiveSupport # \Callbacks are code hooks that are run at key points in an object's lifecycle. @@ -412,7 +413,7 @@ module ActiveSupport # CallbackChain. # def __update_callbacks(name, filters = [], block = nil) #:nodoc: - type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before + type = filters.first.in?([:before, :after, :around]) ? filters.shift : :before options = filters.last.is_a?(Hash) ? filters.pop : {} filters.unshift(block) if block diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb index 8c56a21ef7..a94446acde 100644 --- a/activesupport/lib/active_support/configurable.rb +++ b/activesupport/lib/active_support/configurable.rb @@ -2,6 +2,7 @@ require 'active_support/concern' require 'active_support/ordered_options' require 'active_support/core_ext/kernel/singleton_class' require 'active_support/core_ext/module/delegation' +require 'active_support/core_ext/array/extract_options' module ActiveSupport # Configurable provides a <tt>config</tt> method to store and retrieve @@ -51,14 +52,16 @@ module ActiveSupport # user.allowed_access # => true # def config_accessor(*names) + options = names.extract_options! + names.each do |name| - code, line = <<-RUBY, __LINE__ + 1 - def #{name}; config.#{name}; end - def #{name}=(value); config.#{name} = value; end - RUBY + reader, line = "def #{name}; config.#{name}; end", __LINE__ + writer, line = "def #{name}=(value); config.#{name} = value; end", __LINE__ - singleton_class.class_eval code, __FILE__, line - class_eval code, __FILE__, line + singleton_class.class_eval reader, __FILE__, line + singleton_class.class_eval writer, __FILE__, line + class_eval reader, __FILE__, line unless options[:instance_reader] == false + class_eval writer, __FILE__, line unless options[:instance_writer] == false end end end diff --git a/activesupport/lib/active_support/core_ext/array/access.rb b/activesupport/lib/active_support/core_ext/array/access.rb index c69a015f12..2df4fd1da1 100644 --- a/activesupport/lib/active_support/core_ext/array/access.rb +++ b/activesupport/lib/active_support/core_ext/array/access.rb @@ -3,10 +3,10 @@ class Array # # %w( a b c d ).from(0) # => %w( a b c d ) # %w( a b c d ).from(2) # => %w( c d ) - # %w( a b c d ).from(10) # => nil + # %w( a b c d ).from(10) # => %w() # %w().from(0) # => %w() def from(position) - self[position..-1] + self[position, length] || [] end # Returns the beginning of the array up to +position+. diff --git a/activesupport/lib/active_support/core_ext/array/random_access.rb b/activesupport/lib/active_support/core_ext/array/random_access.rb index ab1fa7cd5b..9eba4642b8 100644 --- a/activesupport/lib/active_support/core_ext/array/random_access.rb +++ b/activesupport/lib/active_support/core_ext/array/random_access.rb @@ -1,12 +1,15 @@ class Array # Backport of Array#sample based on Marc-Andre Lafortune's https://github.com/marcandre/backports/ # Returns a random element or +n+ random elements from the array. - # If the array is empty and +n+ is nil, returns <tt>nil</tt>. if +n+ is passed, returns <tt>[]</tt>. + # If the array is empty and +n+ is nil, returns <tt>nil</tt>. + # If +n+ is passed and its value is less than 0, it raises an +ArgumentError+ exception. + # If the value of +n+ is equal or greater than 0 it returns <tt>[]</tt>. # - # [1,2,3,4,5,6].sample # => 4 - # [1,2,3,4,5,6].sample(3) # => [2, 4, 5] - # [].sample # => nil - # [].sample(3) # => [] + # [1,2,3,4,5,6].sample # => 4 + # [1,2,3,4,5,6].sample(3) # => [2, 4, 5] + # [1,2,3,4,5,6].sample(-3) # => ArgumentError: negative sample number + # [].sample # => nil + # [].sample(3) # => [] def sample(n=nil) return self[Kernel.rand(size)] if n.nil? n = n.to_int diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb index 5414b3a18f..7baba75ad3 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute.rb @@ -84,7 +84,7 @@ class Class val end - remove_method :#{name} if method_defined?(:#{name}) + remove_possible_method :#{name} def #{name} defined?(@#{name}) ? @#{name} : self.class.#{name} end diff --git a/activesupport/lib/active_support/core_ext/date_time/zones.rb b/activesupport/lib/active_support/core_ext/date_time/zones.rb index 82a4f7ac5a..6fa55a9255 100644 --- a/activesupport/lib/active_support/core_ext/date_time/zones.rb +++ b/activesupport/lib/active_support/core_ext/date_time/zones.rb @@ -16,6 +16,6 @@ class DateTime def in_time_zone(zone = ::Time.zone) return self unless zone - ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone)) + ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone)) end end diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 3005fef44c..102378a029 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -108,7 +108,8 @@ class Hash raise "can't typecast #{entries.inspect}" end end - elsif value.has_key?("__content__") + elsif value['type'] == 'file' || + (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?)) content = value["__content__"] if parser = ActiveSupport::XmlMini::PARSING[value["type"]] parser.arity == 1 ? parser.call(content) : parser.call(content, value) diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb index 790a26f5c1..9ad1e12699 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/object/acts_like' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/object/try' +require 'active_support/core_ext/object/inclusion' require 'active_support/core_ext/object/conversions' require 'active_support/core_ext/object/instance_variables' diff --git a/activesupport/lib/active_support/core_ext/object/blank.rb b/activesupport/lib/active_support/core_ext/object/blank.rb index d0c1ea8326..20085c4fb3 100644 --- a/activesupport/lib/active_support/core_ext/object/blank.rb +++ b/activesupport/lib/active_support/core_ext/object/blank.rb @@ -18,8 +18,8 @@ class Object !blank? end - # Returns object if it's #present? otherwise returns nil. - # object.presence is equivalent to object.present? ? object : nil. + # Returns object if it's <tt>present?</tt> otherwise returns +nil+. + # <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>. # # This is handy for any representation of objects where blank is the same # as not present at all. For example, this simplifies a common check for @@ -37,39 +37,72 @@ class Object end end -class NilClass #:nodoc: +class NilClass + # +nil+ is blank: + # + # nil.blank? # => true + # def blank? true end end -class FalseClass #:nodoc: +class FalseClass + # +false+ is blank: + # + # false.blank? # => true + # def blank? true end end -class TrueClass #:nodoc: +class TrueClass + # +true+ is not blank: + # + # true.blank? # => false + # def blank? false end end -class Array #:nodoc: +class Array + # An array is blank if it's empty: + # + # [].blank? # => true + # [1,2,3].blank? # => false + # alias_method :blank?, :empty? end -class Hash #:nodoc: +class Hash + # A hash is blank if it's empty: + # + # {}.blank? # => true + # {:key => 'value'}.blank? # => false + # alias_method :blank?, :empty? end -class String #:nodoc: +class String + # A string is blank if it's empty or contains whitespaces only: + # + # "".blank? # => true + # " ".blank? # => true + # " something here ".blank? # => false + # def blank? self !~ /\S/ end end class Numeric #:nodoc: + # No number is blank: + # + # 1.blank? # => false + # 0.blank? # => false + # def blank? false end diff --git a/activesupport/lib/active_support/core_ext/object/duplicable.rb b/activesupport/lib/active_support/core_ext/object/duplicable.rb index b05325790c..02cb5dfee7 100644 --- a/activesupport/lib/active_support/core_ext/object/duplicable.rb +++ b/activesupport/lib/active_support/core_ext/object/duplicable.rb @@ -15,50 +15,89 @@ # That's why we hardcode the following cases and check duplicable? instead of # using that rescue idiom. class Object - # Can you safely .dup this object? - # False for nil, false, true, symbols, numbers, class and module objects; true otherwise. + # Can you safely dup this object? + # + # False for +nil+, +false+, +true+, symbols, numbers, class and module objects; + # true otherwise. def duplicable? true end end -class NilClass #:nodoc: +class NilClass + # +nil+ is not duplicable: + # + # nil.duplicable? # => false + # nil.dup # => TypeError: can't dup NilClass + # def duplicable? false end end -class FalseClass #:nodoc: +class FalseClass + # +false+ is not duplicable: + # + # false.duplicable? # => false + # false.dup # => TypeError: can't dup FalseClass + # def duplicable? false end end -class TrueClass #:nodoc: +class TrueClass + # +true+ is not duplicable: + # + # true.duplicable? # => false + # true.dup # => TypeError: can't dup TrueClass + # def duplicable? false end end -class Symbol #:nodoc: +class Symbol + # Symbols are not duplicable: + # + # :my_symbol.duplicable? # => false + # :my_symbol.dup # => TypeError: can't dup Symbol + # def duplicable? false end end -class Numeric #:nodoc: +class Numeric + # Numbers are not duplicable: + # + # 3.duplicable? # => false + # 3.dup # => TypeError: can't dup Fixnum + # def duplicable? false end end -class Class #:nodoc: +class Class + # Classes are not duplicable: + # + # c = Class.new # => #<Class:0x10328fd80> + # c.dup # => #<Class:0x10328fd80> + # + # Note +dup+ returned the same class object. def duplicable? false end end -class Module #:nodoc: +class Module + # Modules are not duplicable: + # + # m = Module.new # => #<Module:0x10328b6e0> + # m.dup # => #<Module:0x10328b6e0> + # + # Note +dup+ returned the same module object. def duplicable? false end diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb new file mode 100644 index 0000000000..b5671f66d0 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb @@ -0,0 +1,15 @@ +class Object + # Returns true if this object is included in the argument. Argument must be + # any object which responds to +#include?+. Usage: + # + # characters = ["Konata", "Kagami", "Tsukasa"] + # "Konata".in?(characters) # => true + # + # This will throw an ArgumentError if the argument doesn't respond + # to +#include?+. + def in?(another_object) + another_object.include?(self) + rescue NoMethodError + raise ArgumentError.new("The parameter passed to #in? must respond to #include?") + end +end diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index 04619124a1..e77a9da0ec 100644 --- a/activesupport/lib/active_support/core_ext/object/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -9,12 +9,12 @@ class Object # # ==== Examples # - # Without try + # Without +try+ # @person && @person.name # or # @person ? @person.name : nil # - # With try + # With +try+ # @person.try(:name) # # +try+ also accepts arguments and/or a block, for the method it is trying @@ -34,7 +34,19 @@ class Object end end -class NilClass #:nodoc: +class NilClass + # Calling +try+ on +nil+ always returns +nil+. + # It becomes specially helpful when navigating through associations that may return +nil+. + # + # === Examples + # + # nil.try(:name) # => nil + # + # Without +try+ + # @person && !@person.children.blank? && @person.children.first.name + # + # With +try+ + # @person.try(:children).try(:first).try(:name) def try(*args) nil end diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb index 8fb8c31ade..72522d395c 100644 --- a/activesupport/lib/active_support/core_ext/string.rb +++ b/activesupport/lib/active_support/core_ext/string.rb @@ -11,3 +11,4 @@ require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/string/exclude' require 'active_support/core_ext/string/encoding' require 'active_support/core_ext/string/strip' +require 'active_support/core_ext/string/inquiry' diff --git a/activesupport/lib/active_support/core_ext/string/inquiry.rb b/activesupport/lib/active_support/core_ext/string/inquiry.rb new file mode 100644 index 0000000000..604f3bf4dc --- /dev/null +++ b/activesupport/lib/active_support/core_ext/string/inquiry.rb @@ -0,0 +1,13 @@ +require 'active_support/string_inquirer' + +class String + # Wraps the current string in the ActiveSupport::StringInquirer class, + # which gives you a prettier way to test for equality. Example: + # + # env = "production".inquiry + # env.production? # => true + # env.development? # => false + def inquiry + ActiveSupport::StringInquirer.new(self) + end +end diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index addd4dab95..c27cbc37c5 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -9,7 +9,7 @@ class ERB # A utility method for escaping HTML tag characters. # This method is also aliased as <tt>h</tt>. # - # In your ERb templates, use this method to escape any unsafe content. For example: + # In your ERB templates, use this method to escape any unsafe content. For example: # <%=h @person.name %> # # ==== Example: diff --git a/activesupport/lib/active_support/core_ext/time/zones.rb b/activesupport/lib/active_support/core_ext/time/zones.rb index ff90d7ca58..0c5962858e 100644 --- a/activesupport/lib/active_support/core_ext/time/zones.rb +++ b/activesupport/lib/active_support/core_ext/time/zones.rb @@ -34,29 +34,36 @@ class Time # end # end def zone=(time_zone) - Thread.current[:time_zone] = get_zone(time_zone) + Thread.current[:time_zone] = find_zone!(time_zone) end # Allows override of <tt>Time.zone</tt> locally inside supplied block; resets <tt>Time.zone</tt> to existing value when done. def use_zone(time_zone) - old_zone, ::Time.zone = ::Time.zone, get_zone(time_zone) - yield - ensure - ::Time.zone = old_zone + new_zone = find_zone!(time_zone) + begin + old_zone, ::Time.zone = ::Time.zone, new_zone + yield + ensure + ::Time.zone = old_zone + end end - private - def get_zone(time_zone) - return time_zone if time_zone.nil? || time_zone.is_a?(ActiveSupport::TimeZone) - # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone) - unless time_zone.respond_to?(:period_for_local) - time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone) rescue nil - end - # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone - if time_zone - time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone) - end + # Returns a TimeZone instance or nil, or raises an ArgumentError for invalid timezones. + def find_zone!(time_zone) + return time_zone if time_zone.nil? || time_zone.is_a?(ActiveSupport::TimeZone) + # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone) + unless time_zone.respond_to?(:period_for_local) + time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone) end + # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone + time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone) + rescue TZInfo::InvalidTimezoneIdentifier + raise ArgumentError, "Invalid Timezone: #{time_zone}" + end + + def find_zone(time_zone) + find_zone!(time_zone) rescue nil + end end # Returns the simultaneous time in <tt>Time.zone</tt>. @@ -74,6 +81,6 @@ class Time def in_time_zone(zone = ::Time.zone) return self unless zone - ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.__send__(:get_zone, zone)) + ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone)) end end diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index ce0775a690..45b9dda5ca 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -9,7 +9,7 @@ module ActiveSupport # The version the deprecated behavior will be removed, by default. attr_accessor :deprecation_horizon end - self.deprecation_horizon = '3.1' + self.deprecation_horizon = '3.2' # By default, warnings are not silenced and debugging is off. self.silenced = false diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index ced08b8783..5d7e241d1a 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -5,10 +5,8 @@ module ActiveSupport # Outputs a deprecation warning to the output configured by <tt>ActiveSupport::Deprecation.behavior</tt> # - # Example: - # # ActiveSupport::Deprecation.warn("something broke!") - # #=> "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" + # # => "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" def warn(message = nil, callstack = caller) return if silenced deprecation_message(callstack, message).tap do |m| diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index e136e4c5b3..d5d55b7207 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -96,7 +96,7 @@ module ActiveSupport def clear(scope = :all) case scope when :all - @plurals, @singulars, @uncountables = [], [], [] + @plurals, @singulars, @uncountables, @humans = [], [], [], [] else instance_variable_set "@#{scope}", [] end diff --git a/activesupport/lib/active_support/json/backends/jsongem.rb b/activesupport/lib/active_support/json/backends/jsongem.rb deleted file mode 100644 index 533ba25da3..0000000000 --- a/activesupport/lib/active_support/json/backends/jsongem.rb +++ /dev/null @@ -1,47 +0,0 @@ -require 'json' unless defined?(JSON) - -module ActiveSupport - module JSON - module Backends - module JSONGem - ParseError = ::JSON::ParserError - extend self - - # Parses a JSON string or IO and convert it into an object - def decode(json) - if json.respond_to?(:read) - json = json.read - end - data = ::JSON.parse(json) - if ActiveSupport.parse_json_times - convert_dates_from(data) - else - data - end - end - - private - def convert_dates_from(data) - case data - when nil - nil - when DATE_REGEX - begin - DateTime.parse(data) - rescue ArgumentError - data - end - when Array - data.map! { |d| convert_dates_from(d) } - when Hash - data.each do |key, value| - data[key] = convert_dates_from(value) - end - else - data - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/json/backends/yajl.rb b/activesupport/lib/active_support/json/backends/yajl.rb deleted file mode 100644 index 58818658c7..0000000000 --- a/activesupport/lib/active_support/json/backends/yajl.rb +++ /dev/null @@ -1,44 +0,0 @@ -require 'yajl' unless defined?(Yajl) - -module ActiveSupport - module JSON - module Backends - module Yajl - ParseError = ::Yajl::ParseError - extend self - - # Parses a JSON string or IO and convert it into an object - def decode(json) - data = ::Yajl::Parser.new.parse(json) - if ActiveSupport.parse_json_times - convert_dates_from(data) - else - data - end - end - - private - def convert_dates_from(data) - case data - when nil - nil - when DATE_REGEX - begin - DateTime.parse(data) - rescue ArgumentError - data - end - when Array - data.map! { |d| convert_dates_from(d) } - when Hash - data.each do |key, value| - data[key] = convert_dates_from(value) - end - else - data - end - end - end - end - end -end diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb deleted file mode 100644 index e25e29d36b..0000000000 --- a/activesupport/lib/active_support/json/backends/yaml.rb +++ /dev/null @@ -1,113 +0,0 @@ -require 'active_support/core_ext/string/starts_ends_with' - -module ActiveSupport - module JSON - module Backends - module Yaml - ParseError = ::StandardError - extend self - - EXCEPTIONS = [::ArgumentError] # :nodoc: - begin - require 'psych' - EXCEPTIONS << Psych::SyntaxError - rescue LoadError - end - - # Parses a JSON string or IO and converts it into an object - def decode(json) - if json.respond_to?(:read) - json = json.read - end - YAML.load(convert_json_to_yaml(json)) - rescue *EXCEPTIONS => e - raise ParseError, "Invalid JSON string: '%s'" % json - end - - protected - # Ensure that ":" and "," are always followed by a space - def convert_json_to_yaml(json) #:nodoc: - require 'strscan' unless defined? ::StringScanner - scanner, quoting, marks, pos, times = ::StringScanner.new(json), false, [], nil, [] - while scanner.scan_until(/(\\['"]|['":,\\]|\\.|[\]])/) - case char = scanner[1] - when '"', "'" - if !quoting - quoting = char - pos = scanner.pos - elsif quoting == char - if valid_date?(json[pos..scanner.pos-2]) - # found a date, track the exact positions of the quotes so we can - # overwrite them with spaces later. - times << pos - end - quoting = false - end - when ":",",", "]" - marks << scanner.pos - 1 unless quoting - when "\\" - scanner.skip(/\\/) - end - end - - if marks.empty? - json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do - ustr = $1 - if ustr.start_with?('u') - char = [ustr[1..-1].to_i(16)].pack("U") - # "\n" needs extra escaping due to yaml formatting - char == "\n" ? "\\n" : char - elsif ustr == '\\' - '\\\\' - else - ustr - end - end - else - left_pos = [-1].push(*marks) - right_pos = marks << scanner.pos + scanner.rest_size - output = [] - left_pos.each_with_index do |left, i| - scanner.pos = left.succ - chunk = scanner.peek(right_pos[i] - scanner.pos + 1) - if ActiveSupport.parse_json_times - # overwrite the quotes found around the dates with spaces - while times.size > 0 && times[0] <= right_pos[i] - chunk.insert(times.shift - scanner.pos - 1, '! ') - end - end - chunk.gsub!(/\\([\\\/]|u[[:xdigit:]]{4})/) do - ustr = $1 - if ustr.start_with?('u') - char = [ustr[1..-1].to_i(16)].pack("U") - # "\n" needs extra escaping due to yaml formatting - char == "\n" ? "\\n" : char - elsif ustr == '\\' - '\\\\' - else - ustr - end - end - output << chunk - end - output = output * " " - - output.gsub!(/\\\//, '/') - output - end - end - - private - def valid_date?(date_string) - begin - date_string =~ DATE_REGEX && DateTime.parse(date_string) - rescue ArgumentError - false - end - end - - end - end - end -end - diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb index c1f6330c6c..cbeb6c0a28 100644 --- a/activesupport/lib/active_support/json/decoding.rb +++ b/activesupport/lib/active_support/json/decoding.rb @@ -1,32 +1,31 @@ require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/module/delegation' +require 'multi_json' module ActiveSupport # Look for and parse json strings that look like ISO 8601 times. mattr_accessor :parse_json_times module JSON - # Listed in order of preference. - DECODERS = %w(Yajl Yaml) - class << self - attr_reader :parse_error - delegate :decode, :to => :backend + def decode(json, options ={}) + data = MultiJson.decode(json, options) + if ActiveSupport.parse_json_times + convert_dates_from(data) + else + data + end + end - def backend - set_default_backend unless defined?(@backend) - @backend + def engine + MultiJson.engine end + alias :backend :engine - def backend=(name) - if name.is_a?(Module) - @backend = name - else - require "active_support/json/backends/#{name.to_s.downcase}" - @backend = ActiveSupport::JSON::Backends::const_get(name) - end - @parse_error = @backend::ParseError + def engine=(name) + MultiJson.engine = name end + alias :backend= :engine= def with_backend(name) old_backend, self.backend = backend, name @@ -35,15 +34,30 @@ module ActiveSupport self.backend = old_backend end - def set_default_backend - DECODERS.find do |name| + def parse_error + MultiJson::DecodeError + end + + private + + def convert_dates_from(data) + case data + when nil + nil + when DATE_REGEX begin - self.backend = name - true - rescue LoadError - # Try next decoder. - false + DateTime.parse(data) + rescue ArgumentError + data end + when Array + data.map! { |d| convert_dates_from(d) } + when Hash + data.each do |key, value| + data[key] = convert_dates_from(value) + end + else + data end end end diff --git a/activesupport/lib/active_support/log_subscriber.rb b/activesupport/lib/active_support/log_subscriber.rb index 10675edac5..1c4dd24227 100644 --- a/activesupport/lib/active_support/log_subscriber.rb +++ b/activesupport/lib/active_support/log_subscriber.rb @@ -81,7 +81,7 @@ module ActiveSupport # Flush all log_subscribers' logger. def flush_all! - flushable_loggers.each(&:flush) + flushable_loggers.each { |log| log.flush } end end diff --git a/activesupport/lib/active_support/notifications/fanout.rb b/activesupport/lib/active_support/notifications/fanout.rb index adc34f3286..a9aa5464e9 100644 --- a/activesupport/lib/active_support/notifications/fanout.rb +++ b/activesupport/lib/active_support/notifications/fanout.rb @@ -1,7 +1,7 @@ module ActiveSupport module Notifications - # This is a default queue implementation that ships with Notifications. It - # just pushes events to all registered log subscribers. + # This is a default queue implementation that ships with Notifications. + # It just pushes events to all registered log subscribers. class Fanout def initialize @subscribers = [] @@ -33,7 +33,7 @@ module ActiveSupport listeners_for(name).any? end - # This is a sync queue, so there is not waiting. + # This is a sync queue, so there is no waiting. def wait end diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb index c2deba3b1b..04df2ea562 100644 --- a/activesupport/lib/active_support/railtie.rb +++ b/activesupport/lib/active_support/railtie.rb @@ -46,7 +46,7 @@ module ActiveSupport # If assigned value cannot be matched to a TimeZone, an exception will be raised. initializer "active_support.initialize_time_zone" do |app| require 'active_support/core_ext/time/zones' - zone_default = Time.__send__(:get_zone, app.config.time_zone) + zone_default = Time.find_zone!(app.config.time_zone) unless zone_default raise \ diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb index a4e0361a32..05da50e150 100644 --- a/activesupport/lib/active_support/testing/assertions.rb +++ b/activesupport/lib/active_support/testing/assertions.rb @@ -29,22 +29,33 @@ module ActiveSupport # post :create, :article => {...} # end # + # A lambda or a list of lambdas can be passed in and evaluated: + # + # assert_difference lambda { Article.count }, 2 do + # post :create, :article => {...} + # end + # + # assert_difference [->{ Article.count }, ->{ Post.count }], 2 do + # post :create, :article => {...} + # end + # # A error message can be specified. # # assert_difference 'Article.count', -1, "An Article should be destroyed" do # post :delete, :id => ... # end def assert_difference(expression, difference = 1, message = nil, &block) - b = block.send(:binding) - exps = Array.wrap(expression) - before = exps.map { |e| eval(e, b) } + exps = Array.wrap(expression).map { |e| + e.respond_to?(:call) ? e : lambda { eval(e, block.binding) } + } + before = exps.map { |e| e.call } yield exps.each_with_index do |e, i| - error = "#{e.inspect} didn't change by #{difference}" - error = "#{message}.\n#{error}" if message - assert_equal(before[i] + difference, eval(e, b), error) + error = "#{e.inspect} didn't change by #{difference}" + error = "#{message}.\n#{error}" if message + assert_equal(before[i] + difference, e.call, error) end end diff --git a/activesupport/lib/active_support/testing/performance.rb b/activesupport/lib/active_support/testing/performance.rb index 8c91a061fb..7cd9bfa947 100644 --- a/activesupport/lib/active_support/testing/performance.rb +++ b/activesupport/lib/active_support/testing/performance.rb @@ -3,12 +3,100 @@ begin require 'fileutils' require 'rails/version' + require 'active_support/concern' require 'active_support/core_ext/class/delegating_attributes' require 'active_support/core_ext/string/inflections' module ActiveSupport module Testing module Performance + extend ActiveSupport::Concern + + included do + superclass_delegating_accessor :profile_options + self.profile_options = DEFAULTS + + if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions + include ForMiniTest + else + include ForClassicTestUnit + end + end + + module ForMiniTest + def run(runner) + @runner = runner + + run_warmup + if profile_options && metrics = profile_options[:metrics] + metrics.each do |metric_name| + if klass = Metrics[metric_name.to_sym] + run_profile(klass.new) + end + end + end + end + + def run_test(metric, mode) + result = '.' + begin + run_callbacks :setup + setup + metric.send(mode) { __send__ method_name } + rescue Exception => e + result = @runner.puke(self.class, method_name, e) + ensure + begin + teardown + run_callbacks :teardown, :enumerator => :reverse_each + rescue Exception => e + result = @runner.puke(self.class, method_name, e) + end + end + result + end + end + + module ForClassicTestUnit + def run(result) + return if method_name =~ /^default_test$/ + + yield(self.class::STARTED, name) + @_result = result + + run_warmup + if profile_options && metrics = profile_options[:metrics] + metrics.each do |metric_name| + if klass = Metrics[metric_name.to_sym] + run_profile(klass.new) + result.add_run + end + end + end + + yield(self.class::FINISHED, name) + end + + def run_test(metric, mode) + run_callbacks :setup + setup + metric.send(mode) { __send__ @method_name } + rescue ::Test::Unit::AssertionFailedError => e + add_failure(e.message, e.backtrace) + rescue StandardError, ScriptError => e + add_error(e) + ensure + begin + teardown + run_callbacks :teardown, :enumerator => :reverse_each + rescue ::Test::Unit::AssertionFailedError => e + add_failure(e.message, e.backtrace) + rescue StandardError, ScriptError => e + add_error(e) + end + end + end + DEFAULTS = if benchmark = ARGV.include?('--benchmark') # HAX for rake test { :benchmark => true, @@ -24,53 +112,10 @@ begin :output => 'tmp/performance' } end.freeze - def self.included(base) - base.superclass_delegating_accessor :profile_options - base.profile_options = DEFAULTS - end - def full_test_name "#{self.class.name}##{method_name}" end - def run(result) - return if method_name =~ /^default_test$/ - - yield(self.class::STARTED, name) - @_result = result - - run_warmup - if profile_options && metrics = profile_options[:metrics] - metrics.each do |metric_name| - if klass = Metrics[metric_name.to_sym] - run_profile(klass.new) - result.add_run - end - end - end - - yield(self.class::FINISHED, name) - end - - def run_test(metric, mode) - run_callbacks :setup - setup - metric.send(mode) { __send__ @method_name } - rescue ::Test::Unit::AssertionFailedError => e - add_failure(e.message, e.backtrace) - rescue StandardError, ScriptError => e - add_error(e) - ensure - begin - teardown - run_callbacks :teardown, :enumerator => :reverse_each - rescue ::Test::Unit::AssertionFailedError => e - add_failure(e.message, e.backtrace) - rescue StandardError, ScriptError => e - add_error(e) - end - end - protected def run_warmup GC.start diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index c66aa78ce8..3d092529d6 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -1,5 +1,6 @@ require "active_support/values/time_zone" require 'active_support/core_ext/object/acts_like' +require 'active_support/core_ext/object/inclusion' module ActiveSupport # A Time-like class that can represent a time in any time zone. Necessary because standard Ruby Time instances are @@ -309,7 +310,7 @@ module ActiveSupport end def marshal_load(variables) - initialize(variables[0].utc, ::Time.__send__(:get_zone, variables[1]), variables[2].utc) + initialize(variables[0].utc, ::Time.find_zone(variables[1]), variables[2].utc) end # Ensure proxy class responds to all methods that underlying time instance responds to. @@ -344,7 +345,7 @@ module ActiveSupport end def duration_of_variable_length?(obj) - ActiveSupport::Duration === obj && obj.parts.any? {|p| [:years, :months, :days].include? p[0] } + ActiveSupport::Duration === obj && obj.parts.any? {|p| p[0].in?([:years, :months, :days]) } end end end diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index 690fc7f0fc..c2cf39e391 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -3,7 +3,7 @@ module ActiveSupport MAJOR = 3 MINOR = 1 TINY = 0 - PRE = "beta" + PRE = "beta1" STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb index bcedbfd57a..577db5018e 100644 --- a/activesupport/lib/active_support/whiny_nil.rb +++ b/activesupport/lib/active_support/whiny_nil.rb @@ -41,7 +41,7 @@ class NilClass end private - def method_missing(method, *args, &block) + def method_missing(method, *args) if klass = METHOD_CLASS_MAP[method] raise_nil_warning_for klass, method, caller else diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index e5668e29d7..476d55fffd 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -352,6 +352,43 @@ module CacheStoreBehavior end end +# https://rails.lighthouseapp.com/projects/8994/tickets/6225-memcachestore-cant-deal-with-umlauts-and-special-characters +# The error is caused by charcter encodings that can't be compared with ASCII-8BIT regular expressions and by special +# characters like the umlaut in UTF-8. +module EncodedKeyCacheBehavior + if defined?(Encoding) + Encoding.list.each do |encoding| + define_method "test_#{encoding.name.underscore}_encoded_values" do + key = "foo".force_encoding(encoding) + assert_equal true, @cache.write(key, "1", :raw => true) + assert_equal "1", @cache.read(key) + assert_equal "1", @cache.fetch(key) + assert_equal true, @cache.delete(key) + assert_equal "2", @cache.fetch(key, :raw => true) { "2" } + assert_equal 3, @cache.increment(key) + assert_equal 2, @cache.decrement(key) + end + end + + def test_common_utf8_values + key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8) + assert_equal true, @cache.write(key, "1", :raw => true) + assert_equal "1", @cache.read(key) + assert_equal "1", @cache.fetch(key) + assert_equal true, @cache.delete(key) + assert_equal "2", @cache.fetch(key, :raw => true) { "2" } + assert_equal 3, @cache.increment(key) + assert_equal 2, @cache.decrement(key) + end + + def test_retains_encoding + key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8) + assert_equal true, @cache.write(key, "1", :raw => true) + assert_equal Encoding::UTF_8, key.encoding + end + end +end + module CacheDeleteMatchedBehavior def test_delete_matched @cache.write("foo", "bar") @@ -617,6 +654,7 @@ uses_memcached 'memcached backed store' do include CacheStoreBehavior include LocalCacheBehavior include CacheIncrementDecrementBehavior + include EncodedKeyCacheBehavior def test_raw_values cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, :raw => true) diff --git a/activesupport/test/configurable_test.rb b/activesupport/test/configurable_test.rb index 2b28e61815..c6d8191298 100644 --- a/activesupport/test/configurable_test.rb +++ b/activesupport/test/configurable_test.rb @@ -5,6 +5,7 @@ class ConfigurableActiveSupport < ActiveSupport::TestCase class Parent include ActiveSupport::Configurable config_accessor :foo + config_accessor :bar, :instance_reader => false, :instance_writer => false end class Child < Parent @@ -36,6 +37,12 @@ class ConfigurableActiveSupport < ActiveSupport::TestCase assert_equal :bar, Parent.config.foo end + test "configuration accessors is not available on instance" do + instance = Parent.new + assert !instance.respond_to?(:bar) + assert !instance.respond_to?(:bar=) + end + test "configuration hash is available on instance" do instance = Parent.new assert_equal :bar, instance.config.foo diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index d7ab3ce605..0e5407bc35 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -10,7 +10,7 @@ class ArrayExtAccessTests < Test::Unit::TestCase def test_from assert_equal %w( a b c d ), %w( a b c d ).from(0) assert_equal %w( c d ), %w( a b c d ).from(2) - assert_nil %w( a b c d ).from(10) + assert_equal %w(), %w( a b c d ).from(10) end def test_to diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index a0479d45ac..3ef080e1cb 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -889,6 +889,21 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal 'application/octet-stream', file.content_type end + def test_tag_with_attrs_and_whitespace + xml = <<-XML + <blog name="bacon is the best"> + </blog> + XML + hash = Hash.from_xml(xml) + assert_equal "bacon is the best", hash['blog']['name'] + end + + def test_empty_cdata_from_xml + xml = "<data><![CDATA[]]></data>" + + assert_equal "", Hash.from_xml(xml)["data"] + end + def test_xsd_like_types_from_xml bacon_xml = <<-EOT <bacon> @@ -931,7 +946,7 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal expected_product_hash, Hash.from_xml(product_xml)["product"] end - + def test_should_use_default_value_for_unknown_key hash_wia = HashWithIndifferentAccess.new(3) assert_equal 3, hash_wia[:new_key] diff --git a/activesupport/test/core_ext/object/inclusion_test.rb b/activesupport/test/core_ext/object/inclusion_test.rb new file mode 100644 index 0000000000..1de857d678 --- /dev/null +++ b/activesupport/test/core_ext/object/inclusion_test.rb @@ -0,0 +1,50 @@ +require 'abstract_unit' +require 'active_support/core_ext/object/inclusion' + +class InTest < Test::Unit::TestCase + def test_in_array + assert 1.in?([1,2]) + assert !3.in?([1,2]) + end + + def test_in_hash + h = { "a" => 100, "b" => 200 } + assert "a".in?(h) + assert !"z".in?(h) + end + + def test_in_string + assert "lo".in?("hello") + assert !"ol".in?("hello") + assert ?h.in?("hello") + end + + def test_in_range + assert 25.in?(1..50) + assert !75.in?(1..50) + end + + def test_in_set + s = Set.new([1,2]) + assert 1.in?(s) + assert !3.in?(s) + end + + module A + end + class B + include A + end + class C < B + end + + def test_in_module + assert A.in?(B) + assert A.in?(C) + assert !A.in?(A) + end + + def test_no_method_catching + assert_raise(ArgumentError) { 1.in?(1) } + end +end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index bb865cae91..f0c289a418 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -250,6 +250,11 @@ class StringInflectionsTest < Test::Unit::TestCase # And changes the original string: assert_equal original, expected end + + def test_string_inquiry + assert "production".inquiry.production? + assert !"production".inquiry.development? + end def test_truncate assert_equal "Hello World!", "Hello World!".truncate(12) diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index bafa335a09..72b55183ba 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -36,6 +36,12 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal @twz.object_id, @twz.in_time_zone(ActiveSupport::TimeZone['Eastern Time (US & Canada)']).object_id end + def test_in_time_zone_with_bad_argument + assert_raise(ArgumentError) { @twz.in_time_zone('No such timezone exists') } + assert_raise(ArgumentError) { @twz.in_time_zone(-15.hours) } + assert_raise(ArgumentError) { @twz.in_time_zone(Object.new) } + end + def test_localtime assert_equal @twz.localtime, @twz.utc.getlocal end @@ -760,6 +766,15 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase end end + def test_in_time_zone_with_invalid_argument + assert_raise(ArgumentError) { @t.in_time_zone("No such timezone exists") } + assert_raise(ArgumentError) { @dt.in_time_zone("No such timezone exists") } + assert_raise(ArgumentError) { @t.in_time_zone(-15.hours) } + assert_raise(ArgumentError) { @dt.in_time_zone(-15.hours) } + assert_raise(ArgumentError) { @t.in_time_zone(Object.new) } + assert_raise(ArgumentError) { @dt.in_time_zone(Object.new) } + end + def test_in_time_zone_with_time_local_instance with_env_tz 'US/Eastern' do time = Time.local(1999, 12, 31, 19) # == Time.utc(2000) @@ -790,6 +805,14 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone end + def test_use_zone_raises_on_invalid_timezone + Time.zone = 'Alaska' + assert_raise ArgumentError do + Time.use_zone("No such timezone exists") { } + end + assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone + end + def test_time_zone_getter_and_setter Time.zone = ActiveSupport::TimeZone['Alaska'] assert_equal ActiveSupport::TimeZone['Alaska'], Time.zone @@ -843,11 +866,27 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase end def test_time_zone_setter_with_invalid_zone - Time.zone = 'foo' - assert_nil Time.zone + assert_raise(ArgumentError){ Time.zone = "No such timezone exists" } + assert_raise(ArgumentError){ Time.zone = -15.hours } + assert_raise(ArgumentError){ Time.zone = Object.new } + end + + def test_find_zone_without_bang_returns_nil_if_time_zone_can_not_be_found + assert_nil Time.find_zone('No such timezone exists') + assert_nil Time.find_zone(-15.hours) + assert_nil Time.find_zone(Object.new) + end + + def test_find_zone_with_bang_raises_if_time_zone_can_not_be_found + assert_raise(ArgumentError) { Time.find_zone!('No such timezone exists') } + assert_raise(ArgumentError) { Time.find_zone!(-15.hours) } + assert_raise(ArgumentError) { Time.find_zone!(Object.new) } + end - Time.zone = -15.hours - assert_nil Time.zone + def test_time_zone_setter_with_find_zone_without_bang + assert_nil Time.zone = Time.find_zone('No such timezone exists') + assert_nil Time.zone = Time.find_zone(-15.hours) + assert_nil Time.zone = Time.find_zone(Object.new) end def test_current_returns_time_now_when_zone_not_set diff --git a/activesupport/test/file_watcher_test.rb b/activesupport/test/file_watcher_test.rb deleted file mode 100644 index 7b4d4be24f..0000000000 --- a/activesupport/test/file_watcher_test.rb +++ /dev/null @@ -1,233 +0,0 @@ -require 'abstract_unit' -require 'fssm' -require "fileutils" -require "timeout" - - -class FileWatcherTest < ActiveSupport::TestCase - class DumbBackend < ActiveSupport::FileWatcher::Backend - end - - def setup - @watcher = ActiveSupport::FileWatcher.new - - # In real life, the backend would take the path and use it to observe the file - # system. In our case, we will manually trigger the events for unit testing, - # so we can pass any path. - @backend = DumbBackend.new("RAILS_WOOT", @watcher) - - @payload = [] - @watcher.watch %r{^app/assets/.*\.scss$} do |pay| - pay.each do |status, files| - files.sort! - end - @payload << pay - end - end - - def test_use_triple_equals - fw = ActiveSupport::FileWatcher.new - called = [] - fw.watch("some_arbitrary_file.rb") do |file| - called << "omg" - end - fw.trigger(%w{ some_arbitrary_file.rb }) - assert_equal ['omg'], called - end - - def test_one_change - @backend.trigger("app/assets/main.scss" => :changed) - assert_equal({:changed => ["app/assets/main.scss"]}, @payload.first) - end - - def test_multiple_changes - @backend.trigger("app/assets/main.scss" => :changed, "app/assets/javascripts/foo.coffee" => :changed) - assert_equal([{:changed => ["app/assets/main.scss"]}], @payload) - end - - def test_multiple_changes_match - @backend.trigger("app/assets/main.scss" => :changed, "app/assets/print.scss" => :changed, "app/assets/javascripts/foo.coffee" => :changed) - assert_equal([{:changed => ["app/assets/main.scss", "app/assets/print.scss"]}], @payload) - end - - def test_multiple_state_changes - @backend.trigger("app/assets/main.scss" => :created, "app/assets/print.scss" => :changed) - assert_equal([{:changed => ["app/assets/print.scss"], :created => ["app/assets/main.scss"]}], @payload) - end - - def test_more_blocks - payload = [] - @watcher.watch %r{^config/routes\.rb$} do |pay| - payload << pay - end - - @backend.trigger "config/routes.rb" => :changed - assert_equal [:changed => ["config/routes.rb"]], payload - assert_equal [], @payload - end - - def test_overlapping_watchers - payload = [] - @watcher.watch %r{^app/assets/main\.scss$} do |pay| - payload << pay - end - - @backend.trigger "app/assets/print.scss" => :changed, "app/assets/main.scss" => :changed - assert_equal [:changed => ["app/assets/main.scss"]], payload - assert_equal [:changed => ["app/assets/main.scss", "app/assets/print.scss"]], @payload - end -end - -module FSSM::Backends - class Polling - def initialize_with_low_latency(options={}) - initialize_without_low_latency(options.merge(:latency => 0.1)) - end - alias_method_chain :initialize, :low_latency - end -end - -class FSSMFileWatcherTest < ActiveSupport::TestCase - class FSSMBackend < ActiveSupport::FileWatcher::Backend - def initialize(path, watcher) - super - - monitor = FSSM::Monitor.new - monitor.path(path, '**/*') do |p| - p.update { |base, relative| trigger relative => :changed } - p.delete { |base, relative| trigger relative => :deleted } - p.create { |base, relative| trigger relative => :created } - end - - @thread = Thread.new do - monitor.run - end - end - - def stop - @thread.kill - end - end - - def setup - Thread.abort_on_exception = true - - @payload = [] - @triggered = false - - @watcher = ActiveSupport::FileWatcher.new - - @path = path = File.expand_path("../tmp", __FILE__) - FileUtils.rm_rf path - - create "app/assets/main.scss", true - create "app/assets/javascripts/foo.coffee", true - create "app/assets/print.scss", true - create "app/assets/videos.scss", true - - @backend = FSSMBackend.new(path, @watcher) - - @watcher.watch %r{^app/assets/.*\.scss$} do |pay| - pay.each do |status, files| - files.sort! - end - @payload << pay - trigger - end - end - - def teardown - @backend.stop - Thread.abort_on_exception = false - end - - def create(path, past = false) - wait(past) do - path = File.join(@path, path) - FileUtils.mkdir_p(File.dirname(path)) - - FileUtils.touch(path) - File.utime(Time.now - 100, Time.now - 100, path) if past - end - end - - def change(path) - wait do - FileUtils.touch(File.join(@path, path)) - end - end - - def delete(path) - wait do - FileUtils.rm(File.join(@path, path)) - end - end - - def wait(past = false) - yield - return if past - - begin - Timeout.timeout(1) do - sleep 0.05 until @triggered - end - rescue Timeout::Error - end - - @triggered = false - end - - def trigger - @triggered = true - end - - def test_one_change - change "app/assets/main.scss" - assert_equal({:changed => ["app/assets/main.scss"]}, @payload.first) - end - - def test_multiple_changes - change "app/assets/main.scss" - change "app/assets/javascripts/foo.coffee" - assert_equal([{:changed => ["app/assets/main.scss"]}], @payload) - end - - def test_multiple_changes_match - change "app/assets/main.scss" - change "app/assets/print.scss" - change "app/assets/javascripts/foo.coffee" - assert_equal([{:changed => ["app/assets/main.scss"]}, {:changed => ["app/assets/print.scss"]}], @payload) - end - - def test_multiple_state_changes - create "app/assets/new.scss" - change "app/assets/print.scss" - delete "app/assets/videos.scss" - assert_equal([{:created => ["app/assets/new.scss"]}, {:changed => ["app/assets/print.scss"]}, {:deleted => ["app/assets/videos.scss"]}], @payload) - end - - def test_more_blocks - payload = [] - @watcher.watch %r{^config/routes\.rb$} do |pay| - payload << pay - trigger - end - - create "config/routes.rb" - assert_equal [{:created => ["config/routes.rb"]}], payload - assert_equal [], @payload - end - - def test_overlapping_watchers - payload = [] - @watcher.watch %r{^app/assets/main\.scss$} do |pay| - payload << pay - trigger - end - - change "app/assets/main.scss" - change "app/assets/print.scss" - assert_equal [{:changed => ["app/assets/main.scss"]}], payload - assert_equal [{:changed => ["app/assets/main.scss"]}, {:changed => ["app/assets/print.scss"]}], @payload - end -end diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index 1670d9ee7d..95f18126d4 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -255,12 +255,21 @@ class InflectorTest < Test::Unit::TestCase end def test_clear_all - cached_values = ActiveSupport::Inflector.inflections.plurals, ActiveSupport::Inflector.inflections.singulars, ActiveSupport::Inflector.inflections.uncountables, ActiveSupport::Inflector.inflections.humans - ActiveSupport::Inflector.inflections.clear :all - assert ActiveSupport::Inflector.inflections.plurals.empty? - assert ActiveSupport::Inflector.inflections.singulars.empty? - assert ActiveSupport::Inflector.inflections.uncountables.empty? - assert ActiveSupport::Inflector.inflections.humans.empty? + cached_values = ActiveSupport::Inflector.inflections.plurals.dup, ActiveSupport::Inflector.inflections.singulars.dup, ActiveSupport::Inflector.inflections.uncountables.dup, ActiveSupport::Inflector.inflections.humans.dup + ActiveSupport::Inflector.inflections do |inflect| + # ensure any data is present + inflect.plural(/(quiz)$/i, '\1zes') + inflect.singular(/(database)s$/i, '\1') + inflect.uncountable('series') + inflect.human("col_rpted_bugs", "Reported bugs") + + inflect.clear :all + + assert inflect.plurals.empty? + assert inflect.singulars.empty? + assert inflect.uncountables.empty? + assert inflect.humans.empty? + end 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] @@ -268,12 +277,21 @@ class InflectorTest < Test::Unit::TestCase end def test_clear_with_default - cached_values = ActiveSupport::Inflector.inflections.plurals, ActiveSupport::Inflector.inflections.singulars, ActiveSupport::Inflector.inflections.uncountables, ActiveSupport::Inflector.inflections.humans - ActiveSupport::Inflector.inflections.clear - assert ActiveSupport::Inflector.inflections.plurals.empty? - assert ActiveSupport::Inflector.inflections.singulars.empty? - assert ActiveSupport::Inflector.inflections.uncountables.empty? - assert ActiveSupport::Inflector.inflections.humans.empty? + cached_values = ActiveSupport::Inflector.inflections.plurals.dup, ActiveSupport::Inflector.inflections.singulars.dup, ActiveSupport::Inflector.inflections.uncountables.dup, ActiveSupport::Inflector.inflections.humans.dup + ActiveSupport::Inflector.inflections do |inflect| + # ensure any data is present + inflect.plural(/(quiz)$/i, '\1zes') + inflect.singular(/(database)s$/i, '\1') + inflect.uncountable('series') + inflect.human("col_rpted_bugs", "Reported bugs") + + inflect.clear + + assert inflect.plurals.empty? + assert inflect.singulars.empty? + assert inflect.uncountables.empty? + assert inflect.humans.empty? + end 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] diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index 88cf97de7e..6ccffa59b1 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -56,12 +56,9 @@ class TestJSONDecoding < ActiveSupport::TestCase %q({"a":"Line1\u000aLine2"}) => {"a"=>"Line1\nLine2"} } - # load the default JSON backend - ActiveSupport::JSON.backend = 'Yaml' - - backends = %w(Yaml) - backends << "JSONGem" if defined?(::JSON) - backends << "Yajl" if defined?(::Yajl) + backends = [:ok_json] + backends << :json_gem if defined?(::JSON) + backends << :yajl if defined?(::Yajl) backends.each do |backend| TESTS.each do |json, expected| diff --git a/activesupport/test/ordered_options_test.rb b/activesupport/test/ordered_options_test.rb index e48425ca25..b215b60df3 100644 --- a/activesupport/test/ordered_options_test.rb +++ b/activesupport/test/ordered_options_test.rb @@ -50,4 +50,30 @@ class OrderedOptionsTest < Test::Unit::TestCase assert_equal 2, a.size assert_equal 56, a.else_where end + + def test_inheritable_options_continues_lookup_in_parent + parent = ActiveSupport::OrderedOptions.new + parent[:foo] = true + + child = ActiveSupport::InheritableOptions.new(parent) + assert child.foo + end + + def test_inheritable_options_can_override_parent + parent = ActiveSupport::OrderedOptions.new + parent[:foo] = :bar + + child = ActiveSupport::InheritableOptions.new(parent) + child[:foo] = :baz + + assert_equal :baz, child.foo + end + + def test_inheritable_options_inheritable_copy + original = ActiveSupport::InheritableOptions.new + copy = original.inheritable_copy + + assert copy.kind_of?(original.class) + assert_not_equal copy.object_id, original.object_id + end end diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb index b054855d08..08e11d4f38 100644 --- a/activesupport/test/transliterate_test.rb +++ b/activesupport/test/transliterate_test.rb @@ -1,6 +1,7 @@ # encoding: utf-8 require 'abstract_unit' require 'active_support/inflector/transliterate' +require 'active_support/core_ext/object/inclusion' class TransliterateTest < Test::Unit::TestCase @@ -15,7 +16,7 @@ class TransliterateTest < Test::Unit::TestCase # create string with range of Unicode"s western characters with # diacritics, excluding the division and multiplication signs which for # some reason or other are floating in the middle of all the letters. - string = (0xC0..0x17E).to_a.reject {|c| [0xD7, 0xF7].include? c}.pack("U*") + string = (0xC0..0x17E).to_a.reject {|c| c.in?([0xD7, 0xF7])}.pack("U*") string.each_char do |char| assert_match %r{^[a-zA-Z']*$}, ActiveSupport::Inflector.transliterate(string) end |