diff options
Diffstat (limited to 'activesupport')
128 files changed, 1585 insertions, 611 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index e208f56455..27d26b2ba5 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,26 @@ *Edge* +* Allow MemCacheStore to be initialized with a MemCache-like object instead of addresses and options [Bryan Helmkamp] + +* Change spelling of Kyev timezone to Kyiv #2613 [Alexander Dymo] + +* Add ActiveSupport.parse_json_times to disable time parsing in JSON backends that don't support it or don't need it. [rick] + +* Add pluggable JSON backends with support for the JSON gem. [rick] + Example: ActiveSupport::JSON.backend = "JSONGem" + + All internal Rails JSON encoding is now handled by ActiveSupport::JSON.encode(). Use of #to_json is not recommended, as it may clash with other libraries that overwrite it. However, you can recover Rails specific functionality + if you really want to use #to_json. + + gem 'json' + ActiveSupport::JSON.backend = "JSONGem" + + class ActiveRecord::Base + alias to_json rails_to_json + end + +* require 'active_support' no longer orders the whole menu of core extensions. Ask for just what you need: e.g. require 'active_support/core/time' to use timezones, durations, and stdlib date/time extensions. [Jeremy Kemper] + * Removed rarely-used DRb cache store. [Jeremy Kemper] * TimeWithZone.name returns 'Time', to further thwart type checking [Geoff Buesing] diff --git a/activesupport/Rakefile b/activesupport/Rakefile index f7fd52c7d8..c3ea09d424 100644 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile @@ -21,6 +21,12 @@ Rake::TestTask.new { |t| t.verbose = true t.warning = true } +task :isolated_test do + ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) + Dir['test/**/*_test.rb'].all? do |file| + system(ruby, '-Ilib:test', file) + end or raise "Failures" +end # Create compressed packages dist_dirs = [ "lib", "test"] diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 21b730fa0c..a20635ba62 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -22,39 +22,19 @@ #++ module ActiveSupport - def self.load_all! - [Dependencies, Deprecation, Gzip, MessageVerifier, Multibyte, SecureRandom] + Core.load_all! + class << self + attr_accessor :load_all_hooks + def on_load_all(&hook) load_all_hooks << hook end + def load_all!; load_all_hooks.each { |hook| hook.call } end end + self.load_all_hooks = [] - autoload :BacktraceCleaner, 'active_support/backtrace_cleaner' - autoload :Base64, 'active_support/base64' - autoload :BasicObject, 'active_support/basic_object' - autoload :BufferedLogger, 'active_support/buffered_logger' - autoload :Cache, 'active_support/cache' - autoload :Callbacks, 'active_support/callbacks' - autoload :NewCallbacks, 'active_support/new_callbacks' - autoload :ConcurrentHash, 'active_support/concurrent_hash' - autoload :Deprecation, 'active_support/deprecation' - autoload :Gzip, 'active_support/gzip' - autoload :Inflector, 'active_support/inflector' - autoload :Memoizable, 'active_support/memoizable' - autoload :MessageEncryptor, 'active_support/message_encryptor' - autoload :MessageVerifier, 'active_support/message_verifier' - autoload :Multibyte, 'active_support/multibyte' - autoload :OptionMerger, 'active_support/option_merger' - autoload :OrderedHash, 'active_support/ordered_hash' - autoload :OrderedOptions, 'active_support/ordered_options' - autoload :Rescuable, 'active_support/rescuable' - autoload :SecureRandom, 'active_support/secure_random' - autoload :StringInquirer, 'active_support/string_inquirer' - autoload :XmlMini, 'active_support/xml_mini' + on_load_all do + [Dependencies, Deprecation, Gzip, MessageVerifier, Multibyte, SecureRandom] + end end -require 'active_support/core/all' - +require 'active_support/autoload' require 'active_support/vendor' -require 'active_support/core_ext' -require 'active_support/dependencies' -require 'active_support/json' I18n.load_path << "#{File.dirname(__FILE__)}/active_support/locale/en.yml" diff --git a/activesupport/lib/active_support/all.rb b/activesupport/lib/active_support/all.rb new file mode 100644 index 0000000000..f537818300 --- /dev/null +++ b/activesupport/lib/active_support/all.rb @@ -0,0 +1,3 @@ +require 'active_support' +require 'active_support/time' +require 'active_support/core_ext' diff --git a/activesupport/lib/active_support/autoload.rb b/activesupport/lib/active_support/autoload.rb new file mode 100644 index 0000000000..75706855d6 --- /dev/null +++ b/activesupport/lib/active_support/autoload.rb @@ -0,0 +1,26 @@ +module ActiveSupport + autoload :BacktraceCleaner, 'active_support/backtrace_cleaner' + autoload :Base64, 'active_support/base64' + autoload :BasicObject, 'active_support/basic_object' + autoload :BufferedLogger, 'active_support/buffered_logger' + autoload :Cache, 'active_support/cache' + autoload :Callbacks, 'active_support/callbacks' + autoload :Concern, 'active_support/concern' + autoload :ConcurrentHash, 'active_support/concurrent_hash' + autoload :DependencyModule, 'active_support/dependency_module' + autoload :Deprecation, 'active_support/deprecation' + autoload :Gzip, 'active_support/gzip' + autoload :Inflector, 'active_support/inflector' + autoload :Memoizable, 'active_support/memoizable' + autoload :MessageEncryptor, 'active_support/message_encryptor' + autoload :MessageVerifier, 'active_support/message_verifier' + autoload :Multibyte, 'active_support/multibyte' + autoload :NewCallbacks, 'active_support/new_callbacks' + autoload :OptionMerger, 'active_support/option_merger' + autoload :OrderedHash, 'active_support/ordered_hash' + autoload :OrderedOptions, 'active_support/ordered_options' + autoload :Rescuable, 'active_support/rescuable' + autoload :SecureRandom, 'active_support/secure_random' + autoload :StringInquirer, 'active_support/string_inquirer' + autoload :XmlMini, 'active_support/xml_mini' +end diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 4b2eebb007..feb6b1f2cf 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -2,6 +2,7 @@ require 'benchmark' require 'active_support/core_ext/benchmark' require 'active_support/core_ext/exception' require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext' # FIXME: pulling in all to_param extensions module ActiveSupport # See ActiveSupport::Cache::Store for documentation. diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb index 4d8e1fdd67..38b3409ca6 100644 --- a/activesupport/lib/active_support/cache/mem_cache_store.rb +++ b/activesupport/lib/active_support/cache/mem_cache_store.rb @@ -23,7 +23,12 @@ module ActiveSupport DELETED = "DELETED\r\n" end - attr_reader :addresses + def self.build_mem_cache(*addresses) + addresses = addresses.flatten + options = addresses.extract_options! + addresses = ["localhost"] if addresses.empty? + MemCache.new(addresses, options) + end # Creates a new MemCacheStore object, with the given memcached server # addresses. Each address is either a host name, or a host-with-port string @@ -34,15 +39,20 @@ module ActiveSupport # If no addresses are specified, then MemCacheStore will connect to # localhost port 11211 (the default memcached port). def initialize(*addresses) - addresses = addresses.flatten - options = addresses.extract_options! - addresses = ["localhost"] if addresses.empty? - @addresses = addresses - @data = MemCache.new(addresses, options) + if addresses.first.respond_to?(:get) + @data = addresses.first + else + @data = self.class.build_mem_cache(*addresses) + end extend Strategy::LocalCache end + # Reads multiple keys from the cache. + def read_multi(*keys) + @data.get_multi keys + end + def read(key, options = nil) # :nodoc: super @data.get(key, raw?(options)) diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index 84d9a0e6d8..3b5fccc737 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/duplicable' + module ActiveSupport module Cache module Strategy @@ -43,7 +45,7 @@ module ActiveSupport elsif value.nil? value = super local_cache.write(key, value || NULL) if local_cache - value + value.duplicable? ? value.dup : value else # forcing the value to be immutable value.duplicable? ? value.dup : value diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 4bac8292e2..f049189b9a 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/extract_options' + module ActiveSupport # Callbacks are hooks into the lifecycle of an object that allow you to trigger logic # before or after an alteration of the object state. diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb new file mode 100644 index 0000000000..154f8807f7 --- /dev/null +++ b/activesupport/lib/active_support/concern.rb @@ -0,0 +1,22 @@ +require 'active_support/dependency_module' + +module ActiveSupport + module Concern + include DependencyModule + + def append_features(base) + if super + base.extend const_get("ClassMethods") if const_defined?("ClassMethods") + base.class_eval(&@_included_block) if instance_variable_defined?("@_included_block") + end + end + + def included(base = nil, &block) + if base.nil? + @_included_block = block + else + super + end + end + end +end diff --git a/activesupport/lib/active_support/core.rb b/activesupport/lib/active_support/core.rb deleted file mode 100644 index ad8db94941..0000000000 --- a/activesupport/lib/active_support/core.rb +++ /dev/null @@ -1,7 +0,0 @@ -module ActiveSupport - module Core - def self.load_all! - [TimeWithZone] - end - end -end diff --git a/activesupport/lib/active_support/core/all.rb b/activesupport/lib/active_support/core/all.rb deleted file mode 100644 index f397f48e9c..0000000000 --- a/activesupport/lib/active_support/core/all.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'active_support/core' -Dir["#{File.dirname(__FILE__)}/*.rb"].sort.each do |path| - require "active_support/core/#{File.basename(path, '.rb')}" -end diff --git a/activesupport/lib/active_support/core/time.rb b/activesupport/lib/active_support/core/time.rb deleted file mode 100644 index 43e13b5e58..0000000000 --- a/activesupport/lib/active_support/core/time.rb +++ /dev/null @@ -1,4 +0,0 @@ -require 'active_support/core/time/autoload' -require 'active_support/core_ext/time' -require 'active_support/core_ext/date' -require 'active_support/core_ext/date_time' diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 81e466779e..5f1ce4142f 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -1,3 +1,7 @@ +require 'active_support/core_ext/hash/keys' +require 'active_support/core_ext/hash/reverse_merge' +require 'active_support/inflector' + class Array # Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options: # * <tt>:words_connector</tt> - The sign or word used to join the elements in arrays with two or more elements (default: ", ") @@ -155,7 +159,7 @@ class Array raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml } require 'builder' unless defined?(Builder) - options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? first.class.to_s.underscore.pluralize : "records" + options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? ActiveSupport::Inflector.pluralize(ActiveSupport::Inflector.underscore(first.class.name)) : "records" options[:children] ||= options[:root].singularize options[:indent] ||= 2 options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) diff --git a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb index 75e481fc54..74ce85a1c2 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute_accessors.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/extract_options' + # Extends the class object with class and instance accessors for class attributes, # just like the native attr* accessors for instance attributes. # diff --git a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb index da798c67e7..fd029544c3 100644 --- a/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/delegating_attributes.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + class Class def superclass_delegating_reader(*names) class_name_to_stop_searching_on = superclass.name.blank? ? "Object" : superclass.name diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb index 2f18666ab9..cca93a0b9f 100644 --- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/duplicable' + # Retain for backward compatibility. Methods are now included in Class. module ClassInheritableAttributes # :nodoc: end @@ -216,4 +218,4 @@ class Class extlib_inheritable_reader(*syms) extlib_inheritable_writer(*syms) end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/core_ext/class/removal.rb b/activesupport/lib/active_support/core_ext/class/removal.rb index 10660edb2c..2dea3c24d5 100644 --- a/activesupport/lib/active_support/core_ext/class/removal.rb +++ b/activesupport/lib/active_support/core_ext/class/removal.rb @@ -1,3 +1,6 @@ +require 'active_support/core_ext/object/extending' +require 'active_support/core_ext/module/introspection' + class Class #:nodoc: # Unassociates the class with its subclasses and removes the subclasses diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 59efdbd1b6..1fe4ffb8e1 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -1,3 +1,5 @@ +require 'active_support/duration' + class Date class << self # Returns a new Date representing the date 1 day ago (i.e. yesterday's date). @@ -130,7 +132,7 @@ class Date # Short-hand for years_since(1) def next_year years_since(1) - end + end unless method_defined?(:next_year) # Short-hand for months_ago(1) def last_month @@ -140,7 +142,7 @@ class Date # Short-hand for months_since(1) def next_month months_since(1) - end + end unless method_defined?(:next_month) # Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00) def beginning_of_week diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index f6c870035b..54facf4430 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -1,4 +1,6 @@ require 'active_support/inflector' +require 'active_support/core_ext/time/conversions' +require 'active_support/core_ext/date_time/conversions' class Date DATE_FORMATS = { diff --git a/activesupport/lib/active_support/core_ext/date_time.rb b/activesupport/lib/active_support/core_ext/date_time.rb index 83a11da1c7..004fd0ad29 100644 --- a/activesupport/lib/active_support/core_ext/date_time.rb +++ b/activesupport/lib/active_support/core_ext/date_time.rb @@ -1,5 +1,4 @@ -require 'date' - +require 'active_support/core_ext/time' require 'active_support/core_ext/date_time/acts_like' require 'active_support/core_ext/date_time/calculations' require 'active_support/core_ext/date_time/conversions' diff --git a/activesupport/lib/active_support/core_ext/date_time/conversions.rb b/activesupport/lib/active_support/core_ext/date_time/conversions.rb index ddfa8d610d..5f01bc4fd6 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -58,7 +58,7 @@ class DateTime # Converts self to a Ruby Date object; time portion is discarded def to_date ::Date.new(year, month, day) - end + end unless method_defined?(:to_date) # Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class # If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time @@ -69,12 +69,12 @@ class DateTime # To be able to keep Times, Dates and DateTimes interchangeable on conversions def to_datetime self - end + end unless method_defined?(:to_datetime) # Converts datetime to an appropriate format for use in XML def xmlschema strftime("%Y-%m-%dT%H:%M:%S%Z") - end if RUBY_VERSION < '1.9' + end unless method_defined?(:xmlschema) # Converts self to a floating-point number of seconds since the Unix epoch def to_f diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index fa171720f9..2a34874d08 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -1,6 +1,7 @@ -require 'date' +require 'active_support/time' require 'active_support/core_ext/object/conversions' require 'active_support/core_ext/array/conversions' +require 'active_support/core_ext/hash/reverse_merge' class Hash # This module exists to decorate files deserialized using Hash.from_xml with @@ -215,7 +216,7 @@ class Hash case params.class.to_s when "Hash" params.inject({}) do |h,(k,v)| - h[k.to_s.underscore.tr("-", "_")] = unrename_keys(v) + h[k.to_s.tr("-", "_")] = unrename_keys(v) h end when "Array" diff --git a/activesupport/lib/active_support/core_ext/integer/even_odd.rb b/activesupport/lib/active_support/core_ext/integer/even_odd.rb index 6d005268a3..8f9a97b44c 100644 --- a/activesupport/lib/active_support/core_ext/integer/even_odd.rb +++ b/activesupport/lib/active_support/core_ext/integer/even_odd.rb @@ -7,10 +7,10 @@ class Integer # Is the integer a multiple of 2? def even? multiple_of? 2 - end if RUBY_VERSION < '1.9' + end unless method_defined?(:even?) # Is the integer not a multiple of 2? def odd? !even? - end if RUBY_VERSION < '1.9' + end unless method_defined?(:odd?) end diff --git a/activesupport/lib/active_support/core_ext/kernel/requires.rb b/activesupport/lib/active_support/core_ext/kernel/requires.rb index 323fea49fe..d2238898d6 100644 --- a/activesupport/lib/active_support/core_ext/kernel/requires.rb +++ b/activesupport/lib/active_support/core_ext/kernel/requires.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/kernel/reporting' + module Kernel # Require a library with fallback to RubyGems. Warnings during library # loading are silenced to increase signal/noise for application warnings. @@ -21,4 +23,4 @@ module Kernel end end end -end
\ No newline at end of file +end diff --git a/activesupport/lib/active_support/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb index 858da7aa07..22749229a3 100644 --- a/activesupport/lib/active_support/core_ext/logger.rb +++ b/activesupport/lib/active_support/core_ext/logger.rb @@ -1,6 +1,7 @@ -# Adds the 'around_level' method to Logger. +require 'active_support/core_ext/class/attribute_accessors' -class Logger +# Adds the 'around_level' method to Logger. +class Logger #:nodoc: def self.define_around_helper(level) module_eval <<-end_eval def around_#{level}(before_message, after_message, &block) # def around_debug(before_message, after_message, &block) diff --git a/activesupport/lib/active_support/core_ext/module.rb b/activesupport/lib/active_support/core_ext/module.rb index fee91534e7..215c47b114 100644 --- a/activesupport/lib/active_support/core_ext/module.rb +++ b/activesupport/lib/active_support/core_ext/module.rb @@ -9,4 +9,3 @@ require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/module/loading' require 'active_support/core_ext/module/model_naming' require 'active_support/core_ext/module/synchronization' -require 'active_support/core_ext/module/setup' diff --git a/activesupport/lib/active_support/core_ext/module/attr_internal.rb b/activesupport/lib/active_support/core_ext/module/attr_internal.rb index b66c0d7500..d052bfed2d 100644 --- a/activesupport/lib/active_support/core_ext/module/attr_internal.rb +++ b/activesupport/lib/active_support/core_ext/module/attr_internal.rb @@ -22,11 +22,11 @@ class Module alias_method :attr_internal, :attr_internal_accessor - private - mattr_accessor :attr_internal_naming_format - self.attr_internal_naming_format = '@_%s' + class << self; attr_accessor :attr_internal_naming_format end + self.attr_internal_naming_format = '@_%s' + private def attr_internal_ivar_name(attr) - attr_internal_naming_format % attr + Module.attr_internal_naming_format % attr end end diff --git a/activesupport/lib/active_support/core_ext/module/delegation.rb b/activesupport/lib/active_support/core_ext/module/delegation.rb index fb4b5f0f3c..a44344806d 100644 --- a/activesupport/lib/active_support/core_ext/module/delegation.rb +++ b/activesupport/lib/active_support/core_ext/module/delegation.rb @@ -110,8 +110,10 @@ class Module allow_nil = options[:allow_nil] && "#{to} && " + file, line = caller[0].split(":") + methods.each do |method| - module_eval(<<-EOS, "(__DELEGATION__)", 1) + module_eval(<<-EOS, file, line.to_i) def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block) #{allow_nil}#{to}.__send__(#{method.inspect}, *args, &block) # client && client.__send__(:name, *args, &block) end # end diff --git a/activesupport/lib/active_support/core_ext/module/introspection.rb b/activesupport/lib/active_support/core_ext/module/introspection.rb index 7708d573fa..23a1063901 100644 --- a/activesupport/lib/active_support/core_ext/module/introspection.rb +++ b/activesupport/lib/active_support/core_ext/module/introspection.rb @@ -1,3 +1,5 @@ +require 'active_support/inflector' + class Module # Returns the name of the module containing this one. # @@ -26,7 +28,7 @@ class Module # p Module.new.parent # => Object # def parent - parent_name ? parent_name.constantize : Object + parent_name ? ActiveSupport::Inflector.constantize(parent_name) : Object end # Returns all the parents of this module according to its name, ordered from @@ -47,7 +49,7 @@ class Module if parent_name parts = parent_name.split('::') until parts.empty? - parents << (parts * '::').constantize + parents << ActiveSupport::Inflector.constantize(parts * '::') parts.pop end end diff --git a/activesupport/lib/active_support/core_ext/module/model_naming.rb b/activesupport/lib/active_support/core_ext/module/model_naming.rb index 004b96a3c1..36fde87b23 100644 --- a/activesupport/lib/active_support/core_ext/module/model_naming.rb +++ b/activesupport/lib/active_support/core_ext/module/model_naming.rb @@ -1,13 +1,15 @@ +require 'active_support/inflector' + module ActiveSupport class ModelName < String attr_reader :singular, :plural, :cache_key, :partial_path def initialize(name) super - @singular = underscore.tr('/', '_').freeze - @plural = @singular.pluralize.freeze + @singular = ActiveSupport::Inflector.underscore(self).tr('/', '_').freeze + @plural = ActiveSupport::Inflector.pluralize(@singular).freeze @cache_key = tableize.freeze - @partial_path = "#{@cache_key}/#{demodulize.underscore}".freeze + @partial_path = "#{@cache_key}/#{ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self))}".freeze end end end diff --git a/activesupport/lib/active_support/core_ext/module/setup.rb b/activesupport/lib/active_support/core_ext/module/setup.rb deleted file mode 100644 index e6dfd0cf56..0000000000 --- a/activesupport/lib/active_support/core_ext/module/setup.rb +++ /dev/null @@ -1,26 +0,0 @@ -class Module - attr_accessor :_setup_block - attr_accessor :_dependencies - - def setup(&blk) - @_setup_block = blk - end - - def use(mod) - return if self < mod - - (mod._dependencies || []).each do |dep| - use dep - end - # raise "Circular dependencies" if self < mod - include mod - extend mod.const_get("ClassMethods") if mod.const_defined?("ClassMethods") - class_eval(&mod._setup_block) if mod._setup_block - end - - def depends_on(mod) - return if self < mod - @_dependencies ||= [] - @_dependencies << mod - end -end
\ No newline at end of file diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb index 069db3fed0..f72d512340 100644 --- a/activesupport/lib/active_support/core_ext/module/synchronization.rb +++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/module/aliasing' + class Module # Synchronize access around a method, delegating synchronization to a # particular mutex. A mutex (either a Mutex, or any object that responds to diff --git a/activesupport/lib/active_support/core_ext/numeric/time.rb b/activesupport/lib/active_support/core_ext/numeric/time.rb index 2955e8ff1d..d1062805c5 100644 --- a/activesupport/lib/active_support/core_ext/numeric/time.rb +++ b/activesupport/lib/active_support/core_ext/numeric/time.rb @@ -1,3 +1,5 @@ +require 'active_support/duration' + class Numeric # Enables the use of time calculations and declarations, like 45.minutes + 2.hours + 4.years. # diff --git a/activesupport/lib/active_support/core_ext/object.rb b/activesupport/lib/active_support/core_ext/object.rb index 96385d2b87..04e8f06b3d 100644 --- a/activesupport/lib/active_support/core_ext/object.rb +++ b/activesupport/lib/active_support/core_ext/object.rb @@ -1,3 +1,4 @@ +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' diff --git a/activesupport/lib/active_support/core_ext/object/conversions.rb b/activesupport/lib/active_support/core_ext/object/conversions.rb index 278b856c45..638f0decc1 100644 --- a/activesupport/lib/active_support/core_ext/object/conversions.rb +++ b/activesupport/lib/active_support/core_ext/object/conversions.rb @@ -1,3 +1,6 @@ +require 'active_support/core_ext/array/conversions' +require 'active_support/core_ext/hash/conversions' + class Object # Alias of <tt>to_s</tt>. def to_param diff --git a/activesupport/lib/active_support/core_ext/object/misc.rb b/activesupport/lib/active_support/core_ext/object/misc.rb index fb1bcdb98f..80011dfbed 100644 --- a/activesupport/lib/active_support/core_ext/object/misc.rb +++ b/activesupport/lib/active_support/core_ext/object/misc.rb @@ -1,80 +1,3 @@ -class Object - # Returns +value+ after yielding +value+ to the block. This simplifies the - # process of constructing an object, performing work on the object, and then - # returning the object from a method. It is a Ruby-ized realization of the K - # combinator, courtesy of Mikael Brockman. - # - # ==== Examples - # - # # Without returning - # def foo - # values = [] - # values << "bar" - # values << "baz" - # return values - # end - # - # foo # => ['bar', 'baz'] - # - # # returning with a local variable - # def foo - # returning values = [] do - # values << 'bar' - # values << 'baz' - # end - # end - # - # foo # => ['bar', 'baz'] - # - # # returning with a block argument - # def foo - # returning [] do |values| - # values << 'bar' - # values << 'baz' - # end - # end - # - # foo # => ['bar', 'baz'] - def returning(value) - yield(value) - value - end - - # Yields <code>x</code> to the block, and then returns <code>x</code>. - # The primary purpose of this method is to "tap into" a method chain, - # in order to perform operations on intermediate results within the chain. - # - # (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a. - # tap { |x| puts "array: #{x.inspect}" }. - # select { |x| x%2 == 0 }. - # tap { |x| puts "evens: #{x.inspect}" }. - # map { |x| x*x }. - # tap { |x| puts "squares: #{x.inspect}" } - def tap - yield self - self - end unless Object.respond_to?(:tap) - - # An elegant way to factor duplication out of options passed to a series of - # method calls. Each method called in the block, with the block variable as - # the receiver, will have its options merged with the default +options+ hash - # provided. Each method called on the block variable must take an options - # hash as its final argument. - # - # with_options :order => 'created_at', :class_name => 'Comment' do |post| - # post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all - # post.has_many :unapproved_comments, :conditions => ['approved = ?', false] - # post.has_many :all_comments - # end - # - # Can also be used with an explicit receiver: - # - # map.with_options :controller => "people" do |people| - # people.connect "/people", :action => "index" - # people.connect "/people/:id", :action => "show" - # end - # - def with_options(options) - yield ActiveSupport::OptionMerger.new(self, options) - end -end +require 'active_support/core_ext/object/returning' +require 'active_support/core_ext/object/tap' +require 'active_support/core_ext/object/with_options' diff --git a/activesupport/lib/active_support/core_ext/object/returning.rb b/activesupport/lib/active_support/core_ext/object/returning.rb new file mode 100644 index 0000000000..0dc2e1266a --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/returning.rb @@ -0,0 +1,42 @@ +class Object + # Returns +value+ after yielding +value+ to the block. This simplifies the + # process of constructing an object, performing work on the object, and then + # returning the object from a method. It is a Ruby-ized realization of the K + # combinator, courtesy of Mikael Brockman. + # + # ==== Examples + # + # # Without returning + # def foo + # values = [] + # values << "bar" + # values << "baz" + # return values + # end + # + # foo # => ['bar', 'baz'] + # + # # returning with a local variable + # def foo + # returning values = [] do + # values << 'bar' + # values << 'baz' + # end + # end + # + # foo # => ['bar', 'baz'] + # + # # returning with a block argument + # def foo + # returning [] do |values| + # values << 'bar' + # values << 'baz' + # end + # end + # + # foo # => ['bar', 'baz'] + def returning(value) + yield(value) + value + end +end diff --git a/activesupport/lib/active_support/core_ext/object/tap.rb b/activesupport/lib/active_support/core_ext/object/tap.rb new file mode 100644 index 0000000000..db7e715e2d --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/tap.rb @@ -0,0 +1,16 @@ +class Object + # Yields <code>x</code> to the block, and then returns <code>x</code>. + # The primary purpose of this method is to "tap into" a method chain, + # in order to perform operations on intermediate results within the chain. + # + # (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a. + # tap { |x| puts "array: #{x.inspect}" }. + # select { |x| x%2 == 0 }. + # tap { |x| puts "evens: #{x.inspect}" }. + # map { |x| x*x }. + # tap { |x| puts "squares: #{x.inspect}" } + def tap + yield self + self + end unless Object.respond_to?(:tap) +end diff --git a/activesupport/lib/active_support/core_ext/object/with_options.rb b/activesupport/lib/active_support/core_ext/object/with_options.rb new file mode 100644 index 0000000000..dd38b7d261 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/object/with_options.rb @@ -0,0 +1,24 @@ +class Object + # An elegant way to factor duplication out of options passed to a series of + # method calls. Each method called in the block, with the block variable as + # the receiver, will have its options merged with the default +options+ hash + # provided. Each method called on the block variable must take an options + # hash as its final argument. + # + # with_options :order => 'created_at', :class_name => 'Comment' do |post| + # post.has_many :comments, :conditions => ['approved = ?', true], :dependent => :delete_all + # post.has_many :unapproved_comments, :conditions => ['approved = ?', false] + # post.has_many :all_comments + # end + # + # Can also be used with an explicit receiver: + # + # map.with_options :controller => "people" do |people| + # people.connect "/people", :action => "index" + # people.connect "/people/:id", :action => "show" + # end + # + def with_options(options) + yield ActiveSupport::OptionMerger.new(self, options) + end +end diff --git a/activesupport/lib/active_support/core_ext/range/blockless_step.rb b/activesupport/lib/active_support/core_ext/range/blockless_step.rb index f4792d03b7..db42ef5c47 100644 --- a/activesupport/lib/active_support/core_ext/range/blockless_step.rb +++ b/activesupport/lib/active_support/core_ext/range/blockless_step.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/module/aliasing' + class Range begin (1..2).step diff --git a/activesupport/lib/active_support/core_ext/string/conversions.rb b/activesupport/lib/active_support/core_ext/string/conversions.rb index 39c2b1b8ed..2b9f8c70f6 100644 --- a/activesupport/lib/active_support/core_ext/string/conversions.rb +++ b/activesupport/lib/active_support/core_ext/string/conversions.rb @@ -1,10 +1,11 @@ require 'date' +require 'active_support/core_ext/time/calculations' class String # 'a'.ord == 'a'[0] for Ruby 1.9 forward compatibility. def ord self[0] - end if RUBY_VERSION < '1.9' + end unless method_defined?(:ord) # Form can be either :utc (default) or :local. def to_time(form = :utc) diff --git a/activesupport/lib/active_support/core_ext/string/iterators.rb b/activesupport/lib/active_support/core_ext/string/iterators.rb index 472de7bc24..2f8aa84024 100644 --- a/activesupport/lib/active_support/core_ext/string/iterators.rb +++ b/activesupport/lib/active_support/core_ext/string/iterators.rb @@ -1,11 +1,10 @@ unless '1.9'.respond_to?(:each_char) - autoload :StringScanner, 'strscan' unless defined? :StringScanner - class String # Yields a single-character string for each character in the string. # When $KCODE = 'UTF8', multi-byte characters are yielded appropriately. def each_char - scanner, char = StringScanner.new(self), /./mu + require 'strscan' unless defined? ::StringScanner + scanner, char = ::StringScanner.new(self), /./mu while c = scanner.scan(char) yield c end diff --git a/activesupport/lib/active_support/core_ext/string/multibyte.rb b/activesupport/lib/active_support/core_ext/string/multibyte.rb index 0f0dfb2443..13208c6ee2 100644 --- a/activesupport/lib/active_support/core_ext/string/multibyte.rb +++ b/activesupport/lib/active_support/core_ext/string/multibyte.rb @@ -1,4 +1,5 @@ # encoding: utf-8 +require 'active_support/multibyte' class String unless '1.9'.respond_to?(:force_encoding) diff --git a/activesupport/lib/active_support/core_ext/symbol.rb b/activesupport/lib/active_support/core_ext/symbol.rb index 520369452b..c103cd9dcf 100644 --- a/activesupport/lib/active_support/core_ext/symbol.rb +++ b/activesupport/lib/active_support/core_ext/symbol.rb @@ -1,14 +1 @@ -class Symbol - # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples: - # - # # The same as people.collect { |p| p.name } - # people.collect(&:name) - # - # # The same as people.select { |p| p.manager? }.collect { |p| p.salary } - # people.select(&:manager?).collect(&:salary) - # - # This is a builtin method in Ruby 1.8.7 and later. - def to_proc - Proc.new { |*args| args.shift.__send__(self, *args) } - end unless :to_proc.respond_to?(:to_proc) -end +require 'active_support/core_ext/symbol/to_proc' diff --git a/activesupport/lib/active_support/core_ext/symbol/to_proc.rb b/activesupport/lib/active_support/core_ext/symbol/to_proc.rb new file mode 100644 index 0000000000..520369452b --- /dev/null +++ b/activesupport/lib/active_support/core_ext/symbol/to_proc.rb @@ -0,0 +1,14 @@ +class Symbol + # Turns the symbol into a simple proc, which is especially useful for enumerations. Examples: + # + # # The same as people.collect { |p| p.name } + # people.collect(&:name) + # + # # The same as people.select { |p| p.manager? }.collect { |p| p.salary } + # people.select(&:manager?).collect(&:salary) + # + # This is a builtin method in Ruby 1.8.7 and later. + def to_proc + Proc.new { |*args| args.shift.__send__(self, *args) } + end unless :to_proc.respond_to?(:to_proc) +end diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb index 18261e139f..6d9c080442 100644 --- a/activesupport/lib/active_support/core_ext/time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/time/conversions.rb @@ -63,13 +63,13 @@ class Time # your_time.to_date # => Tue, 13 Jan 2009 def to_date ::Date.new(year, month, day) - end + end unless method_defined?(:to_date) # A method to keep Time, Date and DateTime instances interchangeable on conversions. # In this case, it simply returns +self+. def to_time self - end + end unless method_defined?(:to_time) # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset. # @@ -80,5 +80,5 @@ class Time # your_time.to_datetime # => Tue, 13 Jan 2009 13:13:03 -0500 def to_datetime ::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400)) - end + end unless method_defined?(:to_datetime) end diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 41b7e00c0c..855b720ef1 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -1,10 +1,13 @@ require 'set' -require 'active_support/inflector' -require 'active_support/core_ext/name_error' -require 'active_support/core_ext/object/blank' +require 'thread' require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/module/introspection' +require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/load_error' +require 'active_support/core_ext/name_error' +require 'active_support/core_ext/string/starts_ends_with' +require 'active_support/inflector' module ActiveSupport #:nodoc: module Dependencies #:nodoc: diff --git a/activesupport/lib/active_support/dependency_module.rb b/activesupport/lib/active_support/dependency_module.rb new file mode 100644 index 0000000000..6847c0f86a --- /dev/null +++ b/activesupport/lib/active_support/dependency_module.rb @@ -0,0 +1,17 @@ +module ActiveSupport + module DependencyModule + def append_features(base) + return false if base < self + (@_dependencies ||= []).each { |dep| base.send(:include, dep) } + super + end + + def depends_on(*mods) + mods.each do |mod| + next if self < mod + @_dependencies ||= [] + @_dependencies << mod + end + end + end +end diff --git a/activesupport/lib/active_support/deprecation/method_wrappers.rb b/activesupport/lib/active_support/deprecation/method_wrappers.rb index b35d4daf9a..b9eb539aa7 100644 --- a/activesupport/lib/active_support/deprecation/method_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/method_wrappers.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/module/deprecation' +require 'active_support/core_ext/module/aliasing' module ActiveSupport class << Deprecation diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index 1c268d0d9c..dec56715be 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -1,7 +1,9 @@ +require 'active_support/inflector' + module ActiveSupport module Deprecation class DeprecationProxy #:nodoc: - instance_methods.each { |m| undef_method m unless m =~ /^__/ } + instance_methods.each { |m| undef_method m unless m =~ /^__|^object_id$/ } # Don't give a deprecation warning on inspect since test/unit and error # logs rely on it for diagnostics. @@ -61,7 +63,7 @@ module ActiveSupport private def target - @new_const.to_s.constantize + ActiveSupport::Inflector.constantize(@new_const.to_s) end def warn(callstack, called, args) diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index f64661c5b1..a33586f77f 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -1,4 +1,5 @@ require 'active_support/basic_object' +require 'active_support/core_ext/array/conversions' module ActiveSupport # Provides accurate date and time measurements using Date#advance and diff --git a/activesupport/lib/active_support/inflector.rb b/activesupport/lib/active_support/inflector.rb index 614afa1bd6..ff70d6d76e 100644 --- a/activesupport/lib/active_support/inflector.rb +++ b/activesupport/lib/active_support/inflector.rb @@ -1,6 +1,7 @@ # encoding: utf-8 require 'iconv' - +require 'active_support/core_ext/object/blank' +require 'active_support/core_ext/string/access' require 'active_support/core_ext/string/multibyte' module ActiveSupport diff --git a/activesupport/lib/active_support/json.rb b/activesupport/lib/active_support/json.rb index 2bdb4a7b11..6d845182fb 100644 --- a/activesupport/lib/active_support/json.rb +++ b/activesupport/lib/active_support/json.rb @@ -1,11 +1,74 @@ +require 'active_support/core_ext/module/delegation' +require 'active_support/core_ext/module/attribute_accessors' + module ActiveSupport # If true, use ISO 8601 format for dates and times. Otherwise, fall back to the Active Support legacy format. mattr_accessor :use_standard_json_time_format + # Look for and parse json strings that look like ISO 8601 times. + mattr_accessor :parse_json_times - class << self - def escape_html_entities_in_json - @escape_html_entities_in_json + module JSON + # matches YAML-formatted dates + DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?))$/ + + module Encoding #:nodoc: + mattr_accessor :escape_regex + + ESCAPED_CHARS = { + "\010" => '\b', + "\f" => '\f', + "\n" => '\n', + "\r" => '\r', + "\t" => '\t', + '"' => '\"', + '\\' => '\\\\', + '>' => '\u003E', + '<' => '\u003C', + '&' => '\u0026' + } + + def self.escape(string) + string = string.dup.force_encoding(::Encoding::BINARY) if string.respond_to?(:force_encoding) + json = string.gsub(escape_regex) { |s| ESCAPED_CHARS[s] }. + gsub(/([\xC0-\xDF][\x80-\xBF]| + [\xE0-\xEF][\x80-\xBF]{2}| + [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s| + s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&') + } + %("#{json}") + end + end + + class << self + delegate :decode, :to => :backend + + def backend + unless defined? @backend + self.backend = defined?(::JSON) ? "JSONGem" : "Yaml" + end + @backend + end + + def backend=(name) + if name.is_a?(Module) + @backend = name + else + require "active_support/json/backends/#{name.to_s.downcase}.rb" + @backend = ActiveSupport::JSON::Backends::const_get(name) + end + end + + def with_backend(name) + old_backend, self.backend = backend, name + yield + ensure + self.backend = old_backend + end end + end + + class << self + attr_reader :escape_html_entities_in_json def escape_html_entities_in_json=(value) ActiveSupport::JSON::Encoding.escape_regex = \ @@ -19,5 +82,6 @@ module ActiveSupport end end +ActiveSupport.escape_html_entities_in_json = true + require 'active_support/json/encoding' -require 'active_support/json/decoding' diff --git a/activesupport/lib/active_support/json/backends/jsongem.rb b/activesupport/lib/active_support/json/backends/jsongem.rb new file mode 100644 index 0000000000..649e6301d1 --- /dev/null +++ b/activesupport/lib/active_support/json/backends/jsongem.rb @@ -0,0 +1,41 @@ +require 'json' unless defined?(JSON) + +module ActiveSupport + module JSON + ParseError = ::JSON::ParserError unless const_defined?(:ParseError) + + module Backends + module JSONGem + 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 DATE_REGEX + DateTime.parse(data) + 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
\ No newline at end of file diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb new file mode 100644 index 0000000000..667016f45d --- /dev/null +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -0,0 +1,88 @@ +require 'active_support/core_ext/string/starts_ends_with' + +module ActiveSupport + module JSON + unless const_defined?(:ParseError) + class ParseError < StandardError + end + end + + module Backends + module Yaml + extend self + + # 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 ArgumentError => e + raise ParseError, "Invalid JSON string" + 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 json[pos..scanner.pos-2] =~ DATE_REGEX + # found a date, track the exact positions of the quotes so we can remove them later. + # oh, and increment them for each current mark, each one is an extra padded space that bumps + # the position in the final YAML output + total_marks = marks.size + times << pos+total_marks << scanner.pos+total_marks + end + quoting = false + end + when ":","," + marks << scanner.pos - 1 unless quoting + end + end + + if marks.empty? + json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do + ustr = $1 + if ustr.start_with?('u') + [ustr[1..-1].to_i(16)].pack("U") + 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 + output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do + ustr = $1 + if ustr.start_with?('u') + [ustr[1..-1].to_i(16)].pack("U") + elsif ustr == '\\' + '\\\\' + else + ustr + end + end + end + output = output * " " + + times.each { |i| output[i-1] = ' ' } + output.gsub!(/\\\//, '/') + output + end + end + end + end + end +end diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb deleted file mode 100644 index c66500aa9c..0000000000 --- a/activesupport/lib/active_support/json/decoding.rb +++ /dev/null @@ -1,84 +0,0 @@ -autoload :YAML, 'yaml' unless defined? YAML -autoload :StringScanner, 'strscan' unless defined? StringScanner - -require 'active_support/core_ext/string/starts_ends_with' - -module ActiveSupport - module JSON - class ParseError < StandardError - end - - class << self - # Converts a JSON string into a Ruby object. - def decode(json) - YAML.load(convert_json_to_yaml(json)) - rescue ArgumentError => e - raise ParseError, "Invalid JSON string" - end - - protected - # matches YAML-formatted dates - DATE_REGEX = /^(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[ \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)$/ - - # Ensure that ":" and "," are always followed by a space - def convert_json_to_yaml(json) #:nodoc: - 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 json[pos..scanner.pos-2] =~ DATE_REGEX - # found a date, track the exact positions of the quotes so we can remove them later. - # oh, and increment them for each current mark, each one is an extra padded space that bumps - # the position in the final YAML output - total_marks = marks.size - times << pos+total_marks << scanner.pos+total_marks - end - quoting = false - end - when ":","," - marks << scanner.pos - 1 unless quoting - end - end - - if marks.empty? - json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do - ustr = $1 - if ustr.start_with?('u') - [ustr[1..-1].to_i(16)].pack("U") - 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 - output << scanner.peek(right_pos[i] - scanner.pos + 1).gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do - ustr = $1 - if ustr.start_with?('u') - [ustr[1..-1].to_i(16)].pack("U") - elsif ustr == '\\' - '\\\\' - else - ustr - end - end - end - output = output * " " - - times.each { |i| output[i-1] = ' ' } - output.gsub!(/\\\//, '/') - output - end - end - end - end -end diff --git a/activesupport/lib/active_support/json/encoders/date.rb b/activesupport/lib/active_support/json/encoders/date.rb index cc84de1388..9adb3c20e2 100644 --- a/activesupport/lib/active_support/json/encoders/date.rb +++ b/activesupport/lib/active_support/json/encoders/date.rb @@ -1,21 +1,22 @@ class Date - # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the - # ISO 8601 format is used. - # - # ==== Examples - # - # # With ActiveSupport.use_standard_json_time_format = true - # Date.new(2005,2,1).to_json - # # => "2005-02-01" - # - # # With ActiveSupport.use_standard_json_time_format = false - # Date.new(2005,2,1).to_json - # # => "2005/02/01" - def to_json(options = nil) - if ActiveSupport.use_standard_json_time_format - %("#{strftime("%Y-%m-%d")}") - else - %("#{strftime("%Y/%m/%d")}") + private + # Returns a JSON string representing the date. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. + # + # ==== Examples + # + # # With ActiveSupport.use_standard_json_time_format = true + # Date.new(2005,2,1).to_json + # # => "2005-02-01" + # + # # With ActiveSupport.use_standard_json_time_format = false + # Date.new(2005,2,1).to_json + # # => "2005/02/01" + def rails_to_json(*) + if ActiveSupport.use_standard_json_time_format + %("#{strftime("%Y-%m-%d")}") + else + %("#{strftime("%Y/%m/%d")}") + end end - end end diff --git a/activesupport/lib/active_support/json/encoders/date_time.rb b/activesupport/lib/active_support/json/encoders/date_time.rb index 6c85824105..3a29292b24 100644 --- a/activesupport/lib/active_support/json/encoders/date_time.rb +++ b/activesupport/lib/active_support/json/encoders/date_time.rb @@ -1,21 +1,22 @@ class DateTime - # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the - # ISO 8601 format is used. - # - # ==== Examples - # - # # With ActiveSupport.use_standard_json_time_format = true - # DateTime.civil(2005,2,1,15,15,10).to_json - # # => "2005-02-01T15:15:10+00:00" - # - # # With ActiveSupport.use_standard_json_time_format = false - # DateTime.civil(2005,2,1,15,15,10).to_json - # # => "2005/02/01 15:15:10 +0000" - def to_json(options = nil) - if ActiveSupport.use_standard_json_time_format - xmlschema.inspect - else - strftime('"%Y/%m/%d %H:%M:%S %z"') + private + # Returns a JSON string representing the datetime. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. + # + # ==== Examples + # + # # With ActiveSupport.use_standard_json_time_format = true + # DateTime.civil(2005,2,1,15,15,10).to_json + # # => "2005-02-01T15:15:10+00:00" + # + # # With ActiveSupport.use_standard_json_time_format = false + # DateTime.civil(2005,2,1,15,15,10).to_json + # # => "2005/02/01 15:15:10 +0000" + def rails_to_json(*) + if ActiveSupport.use_standard_json_time_format + xmlschema.inspect + else + strftime('"%Y/%m/%d %H:%M:%S %z"') + end end - end end diff --git a/activesupport/lib/active_support/json/encoders/enumerable.rb b/activesupport/lib/active_support/json/encoders/enumerable.rb index 881b1d62c1..898990a59c 100644 --- a/activesupport/lib/active_support/json/encoders/enumerable.rb +++ b/activesupport/lib/active_support/json/encoders/enumerable.rb @@ -1,12 +1,13 @@ module Enumerable - # Returns a JSON string representing the enumerable. Any +options+ - # given will be passed on to its elements. For example: - # - # users = User.find(:all) - # # => users.to_json(:only => :name) - # - # will pass the <tt>:only => :name</tt> option to each user. - def to_json(options = {}) #:nodoc: - "[#{map { |value| ActiveSupport::JSON.encode(value, options) } * ', '}]" - end + private + # Returns a JSON string representing the enumerable. Any +options+ + # given will be passed on to its elements. For example: + # + # users = User.find(:all) + # # => users.to_json(:only => :name) + # + # will pass the <tt>:only => :name</tt> option to each user. + def rails_to_json(options = nil, *args) #:nodoc: + "[#{map { |value| ActiveSupport::JSON.encode(value, options, *args) } * ','}]" + end end diff --git a/activesupport/lib/active_support/json/encoders/false_class.rb b/activesupport/lib/active_support/json/encoders/false_class.rb index bf0844334b..eb975fe542 100644 --- a/activesupport/lib/active_support/json/encoders/false_class.rb +++ b/activesupport/lib/active_support/json/encoders/false_class.rb @@ -1,5 +1,6 @@ class FalseClass - def to_json(options = nil) #:nodoc: - 'false' - end + private + def rails_to_json(*) + 'false' + end end diff --git a/activesupport/lib/active_support/json/encoders/hash.rb b/activesupport/lib/active_support/json/encoders/hash.rb index d87b880743..4771484843 100644 --- a/activesupport/lib/active_support/json/encoders/hash.rb +++ b/activesupport/lib/active_support/json/encoders/hash.rb @@ -1,48 +1,51 @@ require 'active_support/core_ext/array/wrap' class Hash - # Returns a JSON string representing the hash. - # - # Without any +options+, the returned JSON string will include all - # the hash keys. For example: - # - # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json - # # => {"name": "Konata Izumi", "1": 2, "age": 16} - # - # The keys in the JSON string are unordered due to the nature of hashes. - # - # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the - # attributes included, and will accept 1 or more hash keys to include/exclude. - # - # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:only => [:name, 'age']) - # # => {"name": "Konata Izumi", "age": 16} - # - # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:except => 1) - # # => {"name": "Konata Izumi", "age": 16} - # - # The +options+ also filter down to any hash values. This is particularly - # useful for converting hashes containing ActiveRecord objects or any object - # that responds to options in their <tt>to_json</tt> method. For example: - # - # users = User.find(:all) - # { :users => users, :count => users.size }.to_json(:include => :posts) - # - # would pass the <tt>:include => :posts</tt> option to <tt>users</tt>, - # allowing the posts association in the User model to be converted to JSON - # as well. - def to_json(options = {}) #:nodoc: - hash_keys = self.keys + private + # Returns a JSON string representing the hash. + # + # Without any +options+, the returned JSON string will include all + # the hash keys. For example: + # + # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json + # # => {"name": "Konata Izumi", "1": 2, "age": 16} + # + # The keys in the JSON string are unordered due to the nature of hashes. + # + # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the + # attributes included, and will accept 1 or more hash keys to include/exclude. + # + # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:only => [:name, 'age']) + # # => {"name": "Konata Izumi", "age": 16} + # + # { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json(:except => 1) + # # => {"name": "Konata Izumi", "age": 16} + # + # The +options+ also filter down to any hash values. This is particularly + # useful for converting hashes containing ActiveRecord objects or any object + # that responds to options in their <tt>to_json</tt> method. For example: + # + # users = User.find(:all) + # { :users => users, :count => users.size }.to_json(:include => :posts) + # + # would pass the <tt>:include => :posts</tt> option to <tt>users</tt>, + # allowing the posts association in the User model to be converted to JSON + # as well. + def rails_to_json(options = nil, *args) #:nodoc: + hash_keys = self.keys - if except = options[:except] - hash_keys = hash_keys - Array.wrap(except) - elsif only = options[:only] - hash_keys = hash_keys & Array.wrap(only) - end + if options + if except = options[:except] + hash_keys = hash_keys - Array.wrap(except) + elsif only = options[:only] + hash_keys = hash_keys & Array.wrap(only) + end + end - result = '{' - result << hash_keys.map do |key| - "#{ActiveSupport::JSON.encode(key.to_s)}: #{ActiveSupport::JSON.encode(self[key], options)}" - end * ', ' - result << '}' - end + result = '{' + result << hash_keys.map do |key| + "#{ActiveSupport::JSON.encode(key.to_s)}:#{ActiveSupport::JSON.encode(self[key], options, *args)}" + end * ',' + result << '}' + end end diff --git a/activesupport/lib/active_support/json/encoders/nil_class.rb b/activesupport/lib/active_support/json/encoders/nil_class.rb index 4763471ac4..8c51dba384 100644 --- a/activesupport/lib/active_support/json/encoders/nil_class.rb +++ b/activesupport/lib/active_support/json/encoders/nil_class.rb @@ -1,5 +1,6 @@ class NilClass - def to_json(options = nil) #:nodoc: - 'null' - end + private + def rails_to_json(*) + 'null' + end end diff --git a/activesupport/lib/active_support/json/encoders/numeric.rb b/activesupport/lib/active_support/json/encoders/numeric.rb index 38713fb369..c7cd0df1d7 100644 --- a/activesupport/lib/active_support/json/encoders/numeric.rb +++ b/activesupport/lib/active_support/json/encoders/numeric.rb @@ -1,5 +1,6 @@ class Numeric - def to_json(options = nil) #:nodoc: - to_s - end + private + def rails_to_json(*) + to_s + end end diff --git a/activesupport/lib/active_support/json/encoders/object.rb b/activesupport/lib/active_support/json/encoders/object.rb index 0475967aee..9cc12d91ac 100644 --- a/activesupport/lib/active_support/json/encoders/object.rb +++ b/activesupport/lib/active_support/json/encoders/object.rb @@ -2,7 +2,12 @@ require 'active_support/core_ext/object/instance_variables' class Object # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. - def to_json(options = {}) - ActiveSupport::JSON.encode(instance_values, options) + def to_json(options = nil) + ActiveSupport::JSON.encode(self, options) end + + private + def rails_to_json(*args) + ActiveSupport::JSON.encode(instance_values, *args) + end end diff --git a/activesupport/lib/active_support/json/encoders/regexp.rb b/activesupport/lib/active_support/json/encoders/regexp.rb index b6116b70b5..ee42db4d02 100644 --- a/activesupport/lib/active_support/json/encoders/regexp.rb +++ b/activesupport/lib/active_support/json/encoders/regexp.rb @@ -1,5 +1,6 @@ class Regexp - def to_json(options = nil) #:nodoc: - inspect - end + private + def rails_to_json(*) + inspect + end end diff --git a/activesupport/lib/active_support/json/encoders/string.rb b/activesupport/lib/active_support/json/encoders/string.rb index 5ef797955a..4a6b21c1c0 100644 --- a/activesupport/lib/active_support/json/encoders/string.rb +++ b/activesupport/lib/active_support/json/encoders/string.rb @@ -1,36 +1,6 @@ -module ActiveSupport - module JSON - module Encoding - mattr_accessor :escape_regex - - ESCAPED_CHARS = { - "\010" => '\b', - "\f" => '\f', - "\n" => '\n', - "\r" => '\r', - "\t" => '\t', - '"' => '\"', - '\\' => '\\\\', - '>' => '\u003E', - '<' => '\u003C', - '&' => '\u0026' - } - end - end -end - -ActiveSupport.escape_html_entities_in_json = true - class String - def to_json(options = nil) #:nodoc: - json = '"' + gsub(ActiveSupport::JSON::Encoding.escape_regex) { |s| - ActiveSupport::JSON::Encoding::ESCAPED_CHARS[s] - } - json.force_encoding('ascii-8bit') if respond_to?(:force_encoding) - json.gsub(/([\xC0-\xDF][\x80-\xBF]| - [\xE0-\xEF][\x80-\xBF]{2}| - [\xF0-\xF7][\x80-\xBF]{3})+/nx) { |s| - s.unpack("U*").pack("n*").unpack("H*")[0].gsub(/.{4}/, '\\\\u\&') - } + '"' - end + private + def rails_to_json(*) + ActiveSupport::JSON::Encoding.escape(self) + end end diff --git a/activesupport/lib/active_support/json/encoders/symbol.rb b/activesupport/lib/active_support/json/encoders/symbol.rb index 485112f97c..d575350a4e 100644 --- a/activesupport/lib/active_support/json/encoders/symbol.rb +++ b/activesupport/lib/active_support/json/encoders/symbol.rb @@ -1,5 +1,6 @@ class Symbol - def to_json(options = {}) #:nodoc: - ActiveSupport::JSON.encode(to_s, options) - end + private + def rails_to_json(*args) + ActiveSupport::JSON.encode(to_s, *args) + end end diff --git a/activesupport/lib/active_support/json/encoders/time.rb b/activesupport/lib/active_support/json/encoders/time.rb index f45a0059e8..d434b9aace 100644 --- a/activesupport/lib/active_support/json/encoders/time.rb +++ b/activesupport/lib/active_support/json/encoders/time.rb @@ -1,21 +1,24 @@ +require 'active_support/core_ext/time/conversions' + class Time - # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the - # ISO 8601 format is used. - # - # ==== Examples - # - # # With ActiveSupport.use_standard_json_time_format = true - # Time.utc(2005,2,1,15,15,10).to_json - # # => "2005-02-01T15:15:10Z" - # - # # With ActiveSupport.use_standard_json_time_format = false - # Time.utc(2005,2,1,15,15,10).to_json - # # => "2005/02/01 15:15:10 +0000" - def to_json(options = nil) - if ActiveSupport.use_standard_json_time_format - xmlschema.inspect - else - %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") + private + # Returns a JSON string representing the time. If ActiveSupport.use_standard_json_time_format is set to true, the + # ISO 8601 format is used. + # + # ==== Examples + # + # # With ActiveSupport.use_standard_json_time_format = true + # Time.utc(2005,2,1,15,15,10).to_json + # # => "2005-02-01T15:15:10Z" + # + # # With ActiveSupport.use_standard_json_time_format = false + # Time.utc(2005,2,1,15,15,10).to_json + # # => "2005/02/01 15:15:10 +0000" + def rails_to_json(*) + if ActiveSupport.use_standard_json_time_format + xmlschema.inspect + else + %("#{strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") + end end - end end diff --git a/activesupport/lib/active_support/json/encoders/true_class.rb b/activesupport/lib/active_support/json/encoders/true_class.rb index 037d812b3f..bc25a6db78 100644 --- a/activesupport/lib/active_support/json/encoders/true_class.rb +++ b/activesupport/lib/active_support/json/encoders/true_class.rb @@ -1,5 +1,6 @@ class TrueClass - def to_json(options = nil) #:nodoc: - 'true' - end + private + def rails_to_json(*) + 'true' + end end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index aaaa3cdfd2..5fefe5b88b 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -4,11 +4,13 @@ module ActiveSupport end # Converts a Ruby object into a JSON string. - def self.encode(value, options = {}) - seen = (options[:seen] ||= []) - raise CircularReferenceError, 'object references itself' if seen.include?(value) + def self.encode(value, options = nil, seen = nil) + seen ||= [] + if seen.any? { |object| object.equal?(value) } + raise CircularReferenceError, 'object references itself' + end seen << value - value.send(:to_json, options) + value.__send__(:rails_to_json, options, seen) ensure seen.pop end diff --git a/activesupport/lib/active_support/json/variable.rb b/activesupport/lib/active_support/json/variable.rb index 7fd23b0a9e..daa7449b71 100644 --- a/activesupport/lib/active_support/json/variable.rb +++ b/activesupport/lib/active_support/json/variable.rb @@ -2,9 +2,10 @@ module ActiveSupport module JSON # A string that returns itself as its JSON-encoded form. class Variable < String - def to_json(options=nil) - self - end + private + def rails_to_json(*) + self + end end end end diff --git a/activesupport/lib/active_support/memoizable.rb b/activesupport/lib/active_support/memoizable.rb index 2b85fd7be4..fa6db683d4 100644 --- a/activesupport/lib/active_support/memoizable.rb +++ b/activesupport/lib/active_support/memoizable.rb @@ -1,3 +1,6 @@ +require 'active_support/core_ext/object/metaclass' +require 'active_support/core_ext/module/aliasing' + module ActiveSupport module SafelyMemoizable def safely_memoize(*symbols) diff --git a/activesupport/lib/active_support/multibyte.rb b/activesupport/lib/active_support/multibyte.rb index 65a96af49a..d8d58f3bce 100644 --- a/activesupport/lib/active_support/multibyte.rb +++ b/activesupport/lib/active_support/multibyte.rb @@ -4,6 +4,8 @@ require 'active_support/multibyte/chars' require 'active_support/multibyte/exceptions' require 'active_support/multibyte/unicode_database' +require 'active_support/core_ext/module/attribute_accessors' + module ActiveSupport #:nodoc: module Multibyte # A list of all available normalization forms. See http://www.unicode.org/reports/tr15/tr15-29.html for more diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 60f082bcc1..96ed35f0e0 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -1,4 +1,5 @@ # encoding: utf-8 +require 'active_support/core_ext/string/behavior' module ActiveSupport #:nodoc: module Multibyte #:nodoc: diff --git a/activesupport/lib/active_support/new_callbacks.rb b/activesupport/lib/active_support/new_callbacks.rb index 356d70b650..f1d2ea7bb1 100644 --- a/activesupport/lib/active_support/new_callbacks.rb +++ b/activesupport/lib/active_support/new_callbacks.rb @@ -1,3 +1,6 @@ +require 'active_support/core_ext/array/wrap' +require 'active_support/core_ext/class/inheritable_attributes' + module ActiveSupport # Callbacks are hooks into the lifecycle of an object that allow you to trigger logic # before or after an alteration of the object state. @@ -284,6 +287,14 @@ module ActiveSupport when Proc @klass.send(:define_method, method_name, &filter) method_name << (filter.arity == 1 ? "(self)" : "") + when Method + @klass.send(:define_method, "#{method_name}_method") { filter } + @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def #{method_name}(&blk) + #{method_name}_method.call(self, &blk) + end + RUBY_EVAL + method_name when String @klass.class_eval <<-RUBY_EVAL def #{method_name} @@ -293,23 +304,39 @@ module ActiveSupport method_name else kind, name = @kind, @name - @klass.send(:define_method, method_name) do - filter.send("#{kind}_#{name}", self) + @klass.send(:define_method, "#{method_name}_object") { filter } + + if kind == :around + @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def #{method_name}(&blk) + if :#{kind} == :around && #{method_name}_object.respond_to?(:filter) + #{method_name}_object.send("filter", self, &blk) + # TODO: Deprecate this + elsif #{method_name}_object.respond_to?(:before) && #{method_name}_object.respond_to?(:after) + should_continue = #{method_name}_object.before(self) + yield if should_continue + #{method_name}_object.after(self) + else + #{method_name}_object.send("#{kind}_#{name}", self, &blk) + end + end + RUBY_EVAL + else + @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def #{method_name}(&blk) + if #{method_name}_object.respond_to?(:#{kind}) + #{method_name}_object.#{kind}(self, &blk) + else + #{method_name}_object.send("#{kind}_#{name}", self, &blk) + end + end + RUBY_EVAL end method_name end end end - # This method_missing is supplied to catch callbacks with keys and create - # the appropriate callback for future use. - def method_missing(meth, *args, &blk) - if meth.to_s =~ /_run__([\w:]+)__(\w+)__(\w+)__callbacks/ - return self.class._create_and_run_keyed_callback($1, $2.to_sym, $3.to_sym, self, &blk) - end - super - end - # An Array with a compile method class CallbackChain < Array def initialize(symbol) @@ -322,7 +349,7 @@ module ActiveSupport each do |callback| method << callback.start(key, options) end - method << "yield self if block_given?" + method << "yield self if block_given? && !halted" reverse_each do |callback| method << callback.end(key, options) end @@ -353,6 +380,7 @@ module ActiveSupport str = <<-RUBY_EVAL def _run_#{symbol}_callbacks(key = nil) if key + key = key.hash.to_s.gsub(/-/, '_') name = "_run__\#{self.class.name.split("::").last}__#{symbol}__\#{key}__callbacks" if respond_to?(name) @@ -445,7 +473,9 @@ module ActiveSupport self._#{symbol}_callbacks.delete_if {|c| c.matches?(type, :#{symbol}, filter)} Callback.new(filter, type, options.dup, self, :#{symbol}) end - self._#{symbol}_callbacks.push(*filters) + options[:prepend] ? + self._#{symbol}_callbacks.unshift(*filters) : + self._#{symbol}_callbacks.push(*filters) _define_runner(:#{symbol}, self._#{symbol}_callbacks.compile(nil, :terminator => _#{symbol}_terminator), options) diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index fed8094a24..8d1c0f5160 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -10,6 +10,16 @@ module ActiveSupport @keys = [] end + def self.[](*args) + ordered_hash = new + args.each_with_index { |val,ind| + # Only every second value is a key. + next if ind % 2 != 0 + ordered_hash[val] = args[ind + 1] + } + ordered_hash + end + def initialize_copy(other) super # make a deep copy of keys @@ -57,6 +67,10 @@ module ActiveSupport self end + def to_a + @keys.map { |key| [ key, self[key] ] } + end + def each_key @keys.each { |key| yield key } end diff --git a/activesupport/lib/active_support/ruby/shim.rb b/activesupport/lib/active_support/ruby/shim.rb new file mode 100644 index 0000000000..37c57c485a --- /dev/null +++ b/activesupport/lib/active_support/ruby/shim.rb @@ -0,0 +1,24 @@ +# Backported Ruby builtins so you can code with the latest & greatest +# but still run on any Ruby 1.8.x. +# +# Date next_year, next_month +# DateTime to_date, to_datetime, xmlschema +# Enumerable group_by, each_with_object, none? +# Integer even?, odd? +# Object tap +# Process Process.daemon +# REXML security fix +# String ord +# Symbol to_proc +# Time to_date, to_time, to_datetime +require 'active_support' +require 'active_support/core_ext/date/calculations' +require 'active_support/core_ext/date_time/conversions' +require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/integer/even_odd' +require 'active_support/core_ext/object/tap' +require 'active_support/core_ext/process/daemon' +require 'active_support/core_ext/string/conversions' +require 'active_support/core_ext/rexml' +require 'active_support/core_ext/symbol/to_proc' +require 'active_support/core_ext/time/conversions' diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb index 50e25ef740..bab2a401eb 100644 --- a/activesupport/lib/active_support/test_case.rb +++ b/activesupport/lib/active_support/test_case.rb @@ -32,6 +32,11 @@ module ActiveSupport include ActiveSupport::Testing::Default end + $tags = {} + def self.for_tag(tag) + yield if $tags[tag] + end + include ActiveSupport::Testing::SetupAndTeardown include ActiveSupport::Testing::Assertions include ActiveSupport::Testing::Deprecation diff --git a/activesupport/lib/active_support/testing/assertions.rb b/activesupport/lib/active_support/testing/assertions.rb index ca51adba1e..c529b92240 100644 --- a/activesupport/lib/active_support/testing/assertions.rb +++ b/activesupport/lib/active_support/testing/assertions.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/wrap' + module ActiveSupport module Testing module Assertions diff --git a/activesupport/lib/active_support/testing/deprecation.rb b/activesupport/lib/active_support/testing/deprecation.rb index e9220605bd..0135185a47 100644 --- a/activesupport/lib/active_support/testing/deprecation.rb +++ b/activesupport/lib/active_support/testing/deprecation.rb @@ -1,3 +1,5 @@ +require 'active_support/deprecation' + module ActiveSupport module Testing module Deprecation #:nodoc: @@ -35,21 +37,19 @@ end begin require 'test/unit/error' - +rescue LoadError + # Using miniunit, ignore. +else module Test module Unit - class Error # :nodoc: + class Error #:nodoc: # Silence warnings when reporting test errors. def message_with_silenced_deprecation - ActiveSupport::Deprecation.silence do - message_without_silenced_deprecation - end + ActiveSupport::Deprecation.silence { message_without_silenced_deprecation } end - - alias_method_chain :message, :silenced_deprecation + alias_method :message_without_silenced_deprecation, :message + alias_method :message, :message_with_silenced_deprecation end end end -rescue LoadError - # Using miniunit, ignore. end diff --git a/activesupport/lib/active_support/testing/pending.rb b/activesupport/lib/active_support/testing/pending.rb index d945c7e476..21134ff9e2 100644 --- a/activesupport/lib/active_support/testing/pending.rb +++ b/activesupport/lib/active_support/testing/pending.rb @@ -11,6 +11,11 @@ module ActiveSupport @@at_exit = false def pending(description = "", &block) + if description.is_a?(Symbol) + is_pending = $tags[description] + return block.call unless is_pending + end + if block_given? failed = false diff --git a/activesupport/lib/active_support/time.rb b/activesupport/lib/active_support/time.rb new file mode 100644 index 0000000000..d36a683601 --- /dev/null +++ b/activesupport/lib/active_support/time.rb @@ -0,0 +1,14 @@ +require 'active_support' +require 'active_support/core_ext/time' +require 'active_support/core_ext/date' +require 'active_support/core_ext/date_time' + +module ActiveSupport + autoload :Duration, 'active_support/duration' + autoload :TimeWithZone, 'active_support/time_with_zone' + autoload :TimeZone, 'active_support/values/time_zone' + + on_load_all do + [Duration, TimeWithZone, TimeZone] + end +end diff --git a/activesupport/lib/active_support/core/time/autoload.rb b/activesupport/lib/active_support/time/autoload.rb index c9a7731b39..c9a7731b39 100644 --- a/activesupport/lib/active_support/core/time/autoload.rb +++ b/activesupport/lib/active_support/time/autoload.rb diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index aa448debda..069842c6c3 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -118,26 +118,6 @@ module ActiveSupport end alias_method :iso8601, :xmlschema - # Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to - # true, the ISO 8601 format is used. - # - # ==== Examples - # - # # With ActiveSupport.use_standard_json_time_format = true - # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json - # # => "2005-02-01T15:15:10Z" - # - # # With ActiveSupport.use_standard_json_time_format = false - # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json - # # => "2005/02/01 15:15:10 +0000" - def to_json(options = nil) - if ActiveSupport.use_standard_json_time_format - xmlschema.inspect - else - %("#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") - end - end - def to_yaml(options = {}) if options.kind_of?(YAML::Emitter) utc.to_yaml(options) @@ -321,6 +301,26 @@ module ActiveSupport end private + # Returns a JSON string representing the TimeWithZone. If ActiveSupport.use_standard_json_time_format is set to + # true, the ISO 8601 format is used. + # + # ==== Examples + # + # # With ActiveSupport.use_standard_json_time_format = true + # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json + # # => "2005-02-01T15:15:10Z" + # + # # With ActiveSupport.use_standard_json_time_format = false + # Time.utc(2005,2,1,15,15,10).in_time_zone.to_json + # # => "2005/02/01 15:15:10 +0000" + def rails_to_json(*) + if !ActiveSupport.respond_to?(:use_standard_json_time_format) || ActiveSupport.use_standard_json_time_format + xmlschema.inspect + else + %("#{time.strftime("%Y/%m/%d %H:%M:%S")} #{formatted_offset(false)}") + end + end + def get_period_and_ensure_valid_local_time # we don't want a Time.local instance enforcing its own DST rules as well, # so transfer time values to a utc constructor if necessary diff --git a/activesupport/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index e2d759aa50..b37dae1c2a 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -92,7 +92,7 @@ module ActiveSupport "Bucharest" => "Europe/Bucharest", "Cairo" => "Africa/Cairo", "Helsinki" => "Europe/Helsinki", - "Kyev" => "Europe/Kiev", + "Kyiv" => "Europe/Kiev", "Riga" => "Europe/Riga", "Sofia" => "Europe/Sofia", "Tallinn" => "Europe/Tallinn", @@ -336,7 +336,7 @@ module ActiveSupport "Copenhagen", "Madrid", "Paris", "Amsterdam", "Berlin", "Bern", "Rome", "Stockholm", "Vienna", "West Central Africa" ], - [ 7_200, "Bucharest", "Cairo", "Helsinki", "Kyev", "Riga", "Sofia", + [ 7_200, "Bucharest", "Cairo", "Helsinki", "Kyiv", "Riga", "Sofia", "Tallinn", "Vilnius", "Athens", "Istanbul", "Minsk", "Jerusalem", "Harare", "Pretoria" ], [ 10_800, "Moscow", "St. Petersburg", "Volgograd", "Kuwait", "Riyadh", diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb index ccd1349491..f22fbcc0e1 100644 --- a/activesupport/lib/active_support/xml_mini.rb +++ b/activesupport/lib/active_support/xml_mini.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/module/delegation' + module ActiveSupport # = XmlMini # diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb new file mode 100644 index 0000000000..1cd714d864 --- /dev/null +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -0,0 +1,166 @@ +raise "JRuby is required to use the JDOM backend for XmlMini" unless RUBY_PLATFORM =~ /java/ + +require 'jruby' +include Java + +import javax.xml.parsers.DocumentBuilder unless defined? DocumentBuilder +import javax.xml.parsers.DocumentBuilderFactory unless defined? DocumentBuilderFactory +import java.io.StringReader unless defined? StringReader +import org.xml.sax.InputSource unless defined? InputSource +import org.xml.sax.Attributes unless defined? Attributes +import org.w3c.dom.Node unless defined? Node + +# = XmlMini JRuby JDOM implementation +module ActiveSupport + module XmlMini_JDOM #:nodoc: + extend self + + CONTENT_KEY = '__content__'.freeze + + NODE_TYPE_NAMES = %w{ATTRIBUTE_NODE CDATA_SECTION_NODE COMMENT_NODE DOCUMENT_FRAGMENT_NODE + DOCUMENT_NODE DOCUMENT_TYPE_NODE ELEMENT_NODE ENTITY_NODE ENTITY_REFERENCE_NODE NOTATION_NODE + PROCESSING_INSTRUCTION_NODE TEXT_NODE} + + node_type_map = {} + NODE_TYPE_NAMES.each { |type| node_type_map[Node.send(type)] = type } + + # Parse an XML Document string or IO into a simple hash using Java's jdom. + # data:: + # XML Document string or IO to parse + def parse(data) + if data.respond_to?(:read) + data = data.read + end + + if data.blank? + {} + else + @dbf = DocumentBuilderFactory.new_instance + xml_string_reader = StringReader.new(data) + xml_input_source = InputSource.new(xml_string_reader) + doc = @dbf.new_document_builder.parse(xml_input_source) + merge_element!({}, doc.document_element) + end + end + + private + + # Convert an XML element and merge into the hash + # + # hash:: + # Hash to merge the converted element into. + # element:: + # XML element to merge into hash + def merge_element!(hash, element) + merge!(hash, element.tag_name, collapse(element)) + end + + # Actually converts an XML document element into a data structure. + # + # element:: + # The document element to be collapsed. + def collapse(element) + hash = get_attributes(element) + + child_nodes = element.child_nodes + if child_nodes.length > 0 + for i in 0...child_nodes.length + child = child_nodes.item(i) + merge_element!(hash, child) unless child.node_type == Node.TEXT_NODE + end + merge_texts!(hash, element) unless empty_content?(element) + hash + else + merge_texts!(hash, element) + end + end + + # Merge all the texts of an element into the hash + # + # hash:: + # Hash to add the converted emement to. + # element:: + # XML element whose texts are to me merged into the hash + def merge_texts!(hash, element) + text_children = texts(element) + if text_children.join.empty? + hash + else + # must use value to prevent double-escaping + merge!(hash, CONTENT_KEY, text_children.join) + end + end + + # Adds a new key/value pair to an existing Hash. If the key to be added + # already exists and the existing value associated with key is not + # an Array, it will be wrapped in an Array. Then the new value is + # appended to that Array. + # + # hash:: + # Hash to add key/value pair to. + # key:: + # Key to be added. + # value:: + # Value to be associated with key. + def merge!(hash, key, value) + if hash.has_key?(key) + if hash[key].instance_of?(Array) + hash[key] << value + else + hash[key] = [hash[key], value] + end + elsif value.instance_of?(Array) + hash[key] = [value] + else + hash[key] = value + end + hash + end + + # Converts the attributes array of an XML element into a hash. + # Returns an empty Hash if node has no attributes. + # + # element:: + # XML element to extract attributes from. + def get_attributes(element) + attribute_hash = {} + attributes = element.attributes + for i in 0...attributes.length + attribute_hash[attributes.item(i).name] = attributes.item(i).value + end + attribute_hash + end + + # Determines if a document element has text content + # + # element:: + # XML element to be checked. + def texts(element) + texts = [] + child_nodes = element.child_nodes + for i in 0...child_nodes.length + item = child_nodes.item(i) + if item.node_type == Node.TEXT_NODE + texts << item.get_data + end + end + texts + end + + # Determines if a document element has text content + # + # element:: + # XML element to be checked. + def empty_content?(element) + text = '' + child_nodes = element.child_nodes + for i in 0...child_nodes.length + item = child_nodes.item(i) + if item.node_type == Node.TEXT_NODE + text << item.get_data.strip + end + end + text.strip.length == 0 + end + end +end diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb index 370205409a..d4c4dc7be5 100644 --- a/activesupport/lib/active_support/xml_mini/libxml.rb +++ b/activesupport/lib/active_support/xml_mini/libxml.rb @@ -5,16 +5,20 @@ module ActiveSupport module XmlMini_LibXML #:nodoc: extend self - # Parse an XML Document string into a simple hash using libxml. - # string:: - # XML Document string to parse - def parse(string) + # Parse an XML Document string or IO into a simple hash using libxml. + # data:: + # XML Document string or IO to parse + def parse(data) + if data.respond_to?(:read) + data = data.read + end + LibXML::XML.default_keep_blanks = false - if string.blank? + if data.blank? {} else - LibXML::XML::Parser.string(string.strip).parse.to_hash + LibXML::XML::Parser.string(data.strip).parse.to_hash end end diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index 8f9676e4f6..7337c143c9 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -5,14 +5,18 @@ module ActiveSupport module XmlMini_Nokogiri #:nodoc: extend self - # Parse an XML Document string into a simple hash using libxml / nokogiri. - # string:: - # XML Document string to parse - def parse(string) - if string.blank? + # Parse an XML Document string or IO into a simple hash using libxml / nokogiri. + # data:: + # XML Document string or IO to parse + def parse(data) + if data.respond_to?(:read) + data = data.read + end + + if data.blank? {} else - doc = Nokogiri::XML(string) + doc = Nokogiri::XML(data) raise doc.errors.first if doc.errors.length > 0 doc.to_hash end diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index 771109514c..1184d2d6c9 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/object/blank' + # = XmlMini ReXML implementation module ActiveSupport module XmlMini_REXML #:nodoc: @@ -5,16 +7,20 @@ module ActiveSupport CONTENT_KEY = '__content__'.freeze - # Parse an XML Document string into a simple hash + # Parse an XML Document string or IO into a simple hash # # Same as XmlSimple::xml_in but doesn't shoot itself in the foot, # and uses the defaults from ActiveSupport # - # string:: - # XML Document string to parse - def parse(string) + # data:: + # XML Document string or IO to parse + def parse(data) + if data.respond_to?(:read) + data = data.read + end + require 'rexml/document' unless defined?(REXML::Document) - doc = REXML::Document.new(string) + doc = REXML::Document.new(data) merge_element!({}, doc.root) end diff --git a/activesupport/test/buffered_logger_test.rb b/activesupport/test/buffered_logger_test.rb index d48c7fcc54..7a0ec2e910 100644 --- a/activesupport/test/buffered_logger_test.rb +++ b/activesupport/test/buffered_logger_test.rb @@ -4,11 +4,13 @@ require 'fileutils' require 'active_support/buffered_logger' class BufferedLoggerTest < Test::Unit::TestCase + Logger = ActiveSupport::BufferedLogger + def setup @message = "A debug message" @integer_message = 12345 @output = StringIO.new - @logger = ActiveSupport::BufferedLogger.new(@output) + @logger = Logger.new(@output) end def test_should_log_debugging_message_when_debugging @@ -76,9 +78,9 @@ class BufferedLoggerTest < Test::Unit::TestCase define_method "test_disabling_auto_flush_with_#{disable.inspect}_should_flush_at_max_buffer_size_as_failsafe" do @logger.auto_flushing = disable - assert_equal ActiveSupport::BufferedLogger::MAX_BUFFER_SIZE, @logger.auto_flushing + assert_equal Logger::MAX_BUFFER_SIZE, @logger.auto_flushing - (ActiveSupport::BufferedLogger::MAX_BUFFER_SIZE - 1).times do + (Logger::MAX_BUFFER_SIZE - 1).times do @logger.info 'wait for it..' assert @output.string.empty?, @output.string end @@ -89,8 +91,8 @@ class BufferedLoggerTest < Test::Unit::TestCase end def test_should_know_if_its_loglevel_is_below_a_given_level - ActiveSupport::BufferedLogger::Severity.constants.each do |level| - @logger.level = ActiveSupport::BufferedLogger::Severity.const_get(level) - 1 + Logger::Severity.constants.each do |level| + @logger.level = Logger::Severity.const_get(level) - 1 assert @logger.send("#{level.downcase}?"), "didn't know if it was #{level.downcase}? or below" end end @@ -111,7 +113,7 @@ class BufferedLoggerTest < Test::Unit::TestCase tmp_directory = File.join(File.dirname(__FILE__), "tmp") log_file = File.join(tmp_directory, "development.log") assert !File.exist?(tmp_directory) - @logger = ActiveSupport::BufferedLogger.new(log_file) + @logger = Logger.new(log_file) assert File.exist?(tmp_directory) ensure FileUtils.rm_rf(tmp_directory) diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 8bb0c155cf..51d04d9388 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -15,22 +15,34 @@ class CacheStoreSettingTest < ActiveSupport::TestCase end def test_mem_cache_fragment_cache_store + MemCache.expects(:new).with(%w[localhost], {}) store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost" assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) - assert_equal %w(localhost), store.addresses + end + + def test_mem_cache_fragment_cache_store_with_given_mem_cache + mem_cache = MemCache.new + MemCache.expects(:new).never + store = ActiveSupport::Cache.lookup_store :mem_cache_store, mem_cache + assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) + end + + def test_mem_cache_fragment_cache_store_with_given_mem_cache_like_object + MemCache.expects(:new).never + store = ActiveSupport::Cache.lookup_store :mem_cache_store, stub("memcache", :get => true) + assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) end def test_mem_cache_fragment_cache_store_with_multiple_servers + MemCache.expects(:new).with(%w[localhost 192.168.1.1], {}) store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1' assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) - assert_equal %w(localhost 192.168.1.1), store.addresses end def test_mem_cache_fragment_cache_store_with_options + MemCache.expects(:new).with(%w[localhost 192.168.1.1], { :namespace => "foo" }) store = ActiveSupport::Cache.lookup_store :mem_cache_store, "localhost", '192.168.1.1', :namespace => 'foo' assert_kind_of(ActiveSupport::Cache::MemCacheStore, store) - assert_equal %w(localhost 192.168.1.1), store.addresses - assert_equal 'foo', store.instance_variable_get('@data').instance_variable_get('@namespace') end def test_object_assigned_fragment_cache_store @@ -168,6 +180,15 @@ uses_memcached 'memcached backed store' do end end + def test_stored_objects_should_not_be_frozen + @cache.with_local_cache do + @cache.write('foo', 'bar') + end + @cache.with_local_cache do + assert !@cache.read('foo').frozen? + end + end + def test_write_should_return_true_on_success @cache.with_local_cache do result = @cache.write('foo', 'bar') @@ -251,6 +272,15 @@ uses_memcached 'memcached backed store' do end end + def test_multi_get + @cache.with_local_cache do + @cache.write('foo', 1) + @cache.write('goo', 2) + result = @cache.read_multi('foo', 'goo') + assert_equal({'foo' => 1, 'goo' => 2}, result) + end + end + def test_middleware app = lambda { |env| result = @cache.write('foo', 'bar') diff --git a/activesupport/test/concern_test.rb b/activesupport/test/concern_test.rb new file mode 100644 index 0000000000..22f7ec2064 --- /dev/null +++ b/activesupport/test/concern_test.rb @@ -0,0 +1,88 @@ +require 'abstract_unit' +require 'active_support/concern' + +class ConcernTest < Test::Unit::TestCase + module Baz + extend ActiveSupport::Concern + + module ClassMethods + def baz + "baz" + end + + def included_ran=(value) + @@included_ran = value + end + + def included_ran + @@included_ran + end + end + + included do + self.included_ran = true + end + + def baz + "baz" + end + end + + module Bar + extend ActiveSupport::Concern + + depends_on Baz + + def bar + "bar" + end + + def baz + "bar+" + super + end + end + + module Foo + extend ActiveSupport::Concern + + depends_on Bar, Baz + end + + def setup + @klass = Class.new + end + + def test_module_is_included_normally + @klass.send(:include, Baz) + assert_equal "baz", @klass.new.baz + assert_equal ConcernTest::Baz, @klass.included_modules[0] + + @klass.send(:include, Baz) + assert_equal "baz", @klass.new.baz + assert_equal ConcernTest::Baz, @klass.included_modules[0] + end + + def test_class_methods_are_extended + @klass.send(:include, Baz) + assert_equal "baz", @klass.baz + assert_equal ConcernTest::Baz::ClassMethods, (class << @klass; self.included_modules; end)[0] + end + + def test_included_block_is_ran + @klass.send(:include, Baz) + assert_equal true, @klass.included_ran + end + + def test_modules_dependencies_are_met + @klass.send(:include, Bar) + assert_equal "bar", @klass.new.bar + assert_equal "bar+baz", @klass.new.baz + assert_equal "baz", @klass.baz + assert_equal [ConcernTest::Bar, ConcernTest::Baz], @klass.included_modules[0..1] + end + + def test_depends_on_with_multiple_modules + @klass.send(:include, Foo) + assert_equal [ConcernTest::Foo, ConcernTest::Bar, ConcernTest::Baz], @klass.included_modules[0..2] + end +end diff --git a/activesupport/test/core_ext/array_ext_test.rb b/activesupport/test/core_ext/array_ext_test.rb index 112cb998b1..24d33896ce 100644 --- a/activesupport/test/core_ext/array_ext_test.rb +++ b/activesupport/test/core_ext/array_ext_test.rb @@ -1,6 +1,9 @@ require 'abstract_unit' require 'active_support/core_ext/array' require 'active_support/core_ext/big_decimal' +require 'active_support/core_ext/object/conversions' + +require 'active_support/core_ext' # FIXME: pulling in all to_xml extensions class ArrayExtAccessTests < Test::Unit::TestCase def test_from diff --git a/activesupport/test/core_ext/blank_test.rb b/activesupport/test/core_ext/blank_test.rb index 00fea74639..1dbbf3ff30 100644 --- a/activesupport/test/core_ext/blank_test.rb +++ b/activesupport/test/core_ext/blank_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/object/blank' class EmptyTrue def empty?() true; end diff --git a/activesupport/test/core_ext/class/attribute_accessor_test.rb b/activesupport/test/core_ext/class/attribute_accessor_test.rb index 85d0dd89e2..2214ba9894 100644 --- a/activesupport/test/core_ext/class/attribute_accessor_test.rb +++ b/activesupport/test/core_ext/class/attribute_accessor_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/class/attribute_accessors' class ClassAttributeAccessorTest < Test::Unit::TestCase def setup diff --git a/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb b/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb index 7f859772e7..eeda468d9c 100644 --- a/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb +++ b/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/class/inheritable_attributes' class ClassInheritableAttributesTest < Test::Unit::TestCase def setup diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 45eb52c720..a7b179b2be 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/date_time' class DateTimeExtCalculationsTest < Test::Unit::TestCase def test_to_s diff --git a/activesupport/test/core_ext/duplicable_test.rb b/activesupport/test/core_ext/duplicable_test.rb index 8b6127f31e..6e1f876959 100644 --- a/activesupport/test/core_ext/duplicable_test.rb +++ b/activesupport/test/core_ext/duplicable_test.rb @@ -1,4 +1,6 @@ require 'abstract_unit' +require 'bigdecimal' +require 'active_support/core_ext/object/duplicable' class DuplicableTest < Test::Unit::TestCase NO = [nil, false, true, :symbol, 1, 2.3, BigDecimal.new('4.56'), Class.new] diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index d4a4627e3e..6f16621ae5 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/time' class DurationTest < ActiveSupport::TestCase def test_inspect @@ -41,23 +42,23 @@ class DurationTest < ActiveSupport::TestCase end def test_since_and_ago_with_fractional_days - Time.stubs(:now).returns Time.local(2000) + t = Time.local(2000) # since - assert_equal 36.hours.since, 1.5.days.since - assert_equal((24 * 1.7).hours.since, 1.7.days.since) + assert_equal 36.hours.since(t), 1.5.days.since(t) + assert_in_delta((24 * 1.7).hours.since(t), 1.7.days.since(t), 1) # ago - assert_equal 36.hours.ago, 1.5.days.ago - assert_equal((24 * 1.7).hours.ago, 1.7.days.ago) + assert_equal 36.hours.ago(t), 1.5.days.ago(t) + assert_in_delta((24 * 1.7).hours.ago(t), 1.7.days.ago(t), 1) end def test_since_and_ago_with_fractional_weeks - Time.stubs(:now).returns Time.local(2000) + t = Time.local(2000) # since - assert_equal((7 * 36).hours.since, 1.5.weeks.since) - assert_equal((7 * 24 * 1.7).hours.since, 1.7.weeks.since) + assert_equal((7 * 36).hours.since(t), 1.5.weeks.since(t)) + assert_in_delta((7 * 24 * 1.7).hours.since(t), 1.7.weeks.since(t), 1) # ago - assert_equal((7 * 36).hours.ago, 1.5.weeks.ago) - assert_equal((7 * 24 * 1.7).hours.ago, 1.7.weeks.ago) + assert_equal((7 * 36).hours.ago(t), 1.5.weeks.ago(t)) + assert_in_delta((7 * 24 * 1.7).hours.ago(t), 1.7.weeks.ago(t), 1) end def test_since_and_ago_anchored_to_time_now_when_time_zone_default_not_set diff --git a/activesupport/test/core_ext/exception_test.rb b/activesupport/test/core_ext/exception_test.rb index dabd8c7c06..e63842c0bd 100644 --- a/activesupport/test/core_ext/exception_test.rb +++ b/activesupport/test/core_ext/exception_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/exception' class ExceptionExtTests < Test::Unit::TestCase diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index bbb1c631f9..ece5466abb 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -1,5 +1,7 @@ require 'abstract_unit' require 'active_support/core_ext/hash' +require 'bigdecimal' +require 'active_support/core_ext/string/access' class HashExtTest < Test::Unit::TestCase def setup @@ -644,6 +646,22 @@ class HashToXmlTest < Test::Unit::TestCase assert_equal expected_topic_hash, Hash.from_xml(topic_xml)["rsp"]["photos"]["photo"] end + def test_all_caps_key_from_xml + test_xml = <<-EOT + <ABC3XYZ> + <TEST>Lorem Ipsum</TEST> + </ABC3XYZ> + EOT + + expected_hash = { + "ABC3XYZ" => { + "TEST" => "Lorem Ipsum" + } + } + + assert_equal expected_hash, Hash.from_xml(test_xml) + end + def test_empty_array_from_xml blog_xml = <<-XML <blog> diff --git a/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb b/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb index 7c59348af9..7c0d0bb242 100644 --- a/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb +++ b/activesupport/test/core_ext/module/attr_accessor_with_default_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/module/attr_accessor_with_default' class AttrAccessorWithDefaultTest < Test::Unit::TestCase def setup diff --git a/activesupport/test/core_ext/module/attr_internal_test.rb b/activesupport/test/core_ext/module/attr_internal_test.rb index 52833019e7..93578c9610 100644 --- a/activesupport/test/core_ext/module/attr_internal_test.rb +++ b/activesupport/test/core_ext/module/attr_internal_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/module/attr_internal' class AttrInternalTest < Test::Unit::TestCase def setup @@ -37,8 +38,8 @@ class AttrInternalTest < Test::Unit::TestCase end def test_naming_format - assert_equal '@_%s', @target.attr_internal_naming_format - assert_nothing_raised { @target.attr_internal_naming_format = '@abc%sdef' } + assert_equal '@_%s', Module.attr_internal_naming_format + assert_nothing_raised { Module.attr_internal_naming_format = '@abc%sdef' } @target.attr_internal :foo assert !@instance.instance_variable_defined?('@_foo') @@ -47,6 +48,6 @@ class AttrInternalTest < Test::Unit::TestCase assert !@instance.instance_variable_defined?('@_foo') assert @instance.instance_variable_defined?('@abcfoodef') ensure - @target.attr_internal_naming_format = '@_%s' + Module.attr_internal_naming_format = '@_%s' end end diff --git a/activesupport/test/core_ext/module/attribute_accessor_test.rb b/activesupport/test/core_ext/module/attribute_accessor_test.rb index 96975085cf..bd9461e62c 100644 --- a/activesupport/test/core_ext/module/attribute_accessor_test.rb +++ b/activesupport/test/core_ext/module/attribute_accessor_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/module/attribute_accessors' class ModuleAttributeAccessorTest < Test::Unit::TestCase def setup diff --git a/activesupport/test/core_ext/module/attribute_aliasing_test.rb b/activesupport/test/core_ext/module/attribute_aliasing_test.rb index 570ac3b8d5..f17d031662 100644 --- a/activesupport/test/core_ext/module/attribute_aliasing_test.rb +++ b/activesupport/test/core_ext/module/attribute_aliasing_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/module/aliasing' module AttributeAliasing class Content diff --git a/activesupport/test/core_ext/module/model_naming_test.rb b/activesupport/test/core_ext/module/model_naming_test.rb index d08349dd97..da3b6c4932 100644 --- a/activesupport/test/core_ext/module/model_naming_test.rb +++ b/activesupport/test/core_ext/module/model_naming_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/module/model_naming' class ModelNamingTest < Test::Unit::TestCase def setup diff --git a/activesupport/test/core_ext/name_error_test.rb b/activesupport/test/core_ext/name_error_test.rb index bae004809f..10913e2ade 100644 --- a/activesupport/test/core_ext/name_error_test.rb +++ b/activesupport/test/core_ext/name_error_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/name_error' class NameErrorTest < Test::Unit::TestCase def test_name_error_should_set_missing_name diff --git a/activesupport/test/core_ext/numeric_ext_test.rb b/activesupport/test/core_ext/numeric_ext_test.rb index 9cca4d3aaf..992ec60302 100644 --- a/activesupport/test/core_ext/numeric_ext_test.rb +++ b/activesupport/test/core_ext/numeric_ext_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' - -require 'active_support/core_ext/numeric/bytes' +require 'active_support/time' +require 'active_support/core_ext/numeric' +require 'active_support/core_ext/integer' class NumericExtTimeAndDateTimeTest < Test::Unit::TestCase def setup diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb index c919698c0f..f0121b862d 100644 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ b/activesupport/test/core_ext/object_and_class_ext_test.rb @@ -1,5 +1,7 @@ require 'abstract_unit' +require 'active_support/time' require 'active_support/core_ext/object' +require 'active_support/core_ext/class/removal' class ClassA; end class ClassB < ClassA; end diff --git a/activesupport/test/core_ext/proc_test.rb b/activesupport/test/core_ext/proc_test.rb index 29f85371de..dc7b2c957d 100644 --- a/activesupport/test/core_ext/proc_test.rb +++ b/activesupport/test/core_ext/proc_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/proc' class ProcTests < Test::Unit::TestCase def test_bind_returns_method_with_changed_self diff --git a/activesupport/test/core_ext/range_ext_test.rb b/activesupport/test/core_ext/range_ext_test.rb index 76e05e9954..2565c56b8a 100644 --- a/activesupport/test/core_ext/range_ext_test.rb +++ b/activesupport/test/core_ext/range_ext_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'active_support/core_ext/range' +require 'active_support/core_ext/date/conversions' class RangeTest < Test::Unit::TestCase def test_to_s_from_dates diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index a357ba5852..237a843f9a 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -4,6 +4,7 @@ require 'abstract_unit' require 'inflector_test_cases' require 'active_support/core_ext/string' +require 'active_support/core_ext/time' class StringInflectionsTest < Test::Unit::TestCase include InflectorTestCases diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index 8ee4904036..1c2d0fbce4 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/time' class TimeExtCalculationsTest < Test::Unit::TestCase def test_seconds_since_midnight @@ -423,10 +424,10 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_equal Time.local(2005,6,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(:months => 4) assert_equal Time.local(2005,3,21,15,15,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3) assert_equal Time.local(2005,3,25,3,15,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3.5) - assert_equal Time.local(2005,3,26,12,51,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3.7) + assert_in_delta Time.local(2005,3,26,12,51,10), Time.local(2005,2,28,15,15,10).advance(:weeks => 3.7), 1 assert_equal Time.local(2005,3,5,15,15,10), Time.local(2005,2,28,15,15,10).advance(:days => 5) assert_equal Time.local(2005,3,6,3,15,10), Time.local(2005,2,28,15,15,10).advance(:days => 5.5) - assert_equal Time.local(2005,3,6,8,3,10), Time.local(2005,2,28,15,15,10).advance(:days => 5.7) + assert_in_delta Time.local(2005,3,6,8,3,10), Time.local(2005,2,28,15,15,10).advance(:days => 5.7), 1 assert_equal Time.local(2012,9,28,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 7) assert_equal Time.local(2013,10,3,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :days => 5) assert_equal Time.local(2013,10,17,15,15,10), Time.local(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5) @@ -445,10 +446,10 @@ class TimeExtCalculationsTest < Test::Unit::TestCase assert_equal Time.utc(2005,6,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:months => 4) assert_equal Time.utc(2005,3,21,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3) assert_equal Time.utc(2005,3,25,3,15,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3.5) - assert_equal Time.utc(2005,3,26,12,51,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3.7) + assert_in_delta Time.utc(2005,3,26,12,51,10), Time.utc(2005,2,28,15,15,10).advance(:weeks => 3.7), 1 assert_equal Time.utc(2005,3,5,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5) assert_equal Time.utc(2005,3,6,3,15,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5.5) - assert_equal Time.utc(2005,3,6,8,3,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5.7) + assert_in_delta Time.utc(2005,3,6,8,3,10), Time.utc(2005,2,28,15,15,10).advance(:days => 5.7), 1 assert_equal Time.utc(2012,9,22,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:years => 7, :months => 7) assert_equal Time.utc(2013,10,3,15,15,10), Time.utc(2005,2,22,15,15,10).advance(:years => 7, :months => 19, :days => 11) assert_equal Time.utc(2013,10,17,15,15,10), Time.utc(2005,2,28,15,15,10).advance(:years => 7, :months => 19, :weeks => 2, :days => 5) diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 3dec4c95f4..03ae70d420 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'active_support/time_with_zone' +require 'active_support/json' class TimeWithZoneTest < Test::Unit::TestCase @@ -56,12 +57,12 @@ class TimeWithZoneTest < Test::Unit::TestCase end def test_to_json - assert_equal "\"1999/12/31 19:00:00 -0500\"", @twz.to_json + assert_equal "\"1999/12/31 19:00:00 -0500\"", ActiveSupport::JSON.encode(@twz) 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 + assert_equal "\"1999-12-31T19:00:00-05:00\"", ActiveSupport::JSON.encode(@twz) ensure ActiveSupport.use_standard_json_time_format = old end @@ -91,7 +92,7 @@ class TimeWithZoneTest < Test::Unit::TestCase end def test_xmlschema_with_fractional_seconds - @twz += 0.123456 # advance the time by a fraction of a second + @twz += 0.1234560001 # advance the time by a fraction of a second assert_equal "1999-12-31T19:00:00.123-05:00", @twz.xmlschema(3) assert_equal "1999-12-31T19:00:00.123456-05:00", @twz.xmlschema(6) assert_equal "1999-12-31T19:00:00.123456-05:00", @twz.xmlschema(12) diff --git a/activesupport/test/dependencies_test.rb b/activesupport/test/dependencies_test.rb index a21f09403f..99c53924c2 100644 --- a/activesupport/test/dependencies_test.rb +++ b/activesupport/test/dependencies_test.rb @@ -1,5 +1,8 @@ require 'abstract_unit' require 'pp' +require 'active_support/dependencies' +require 'active_support/core_ext/module/loading' +require 'active_support/core_ext/kernel/reporting' module ModuleWithMissing mattr_accessor :missing_count diff --git a/activesupport/test/i18n_test.rb b/activesupport/test/i18n_test.rb index 7535f4ad7a..9868f1e87d 100644 --- a/activesupport/test/i18n_test.rb +++ b/activesupport/test/i18n_test.rb @@ -1,4 +1,6 @@ require 'abstract_unit' +require 'active_support/time' +require 'active_support/core_ext/array/conversions' class I18nTest < Test::Unit::TestCase def setup diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index 6b9fbd3156..8c4d831a39 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -1,4 +1,6 @@ require 'abstract_unit' +require 'active_support/inflector' + require 'inflector_test_cases' module Ace diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb index 8fe40557d6..09fd0d09ba 100644 --- a/activesupport/test/json/decoding_test.rb +++ b/activesupport/test/json/decoding_test.rb @@ -1,48 +1,78 @@ # encoding: UTF-8 require 'abstract_unit' +require 'active_support/json' +require 'active_support/core_ext/kernel/reporting' -class TestJSONDecoding < Test::Unit::TestCase +class TestJSONDecoding < ActiveSupport::TestCase TESTS = { %q({"returnTo":{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}}, - %q({returnTo:{"\/categories":"\/"}}) => {"returnTo" => {"/categories" => "/"}}, %q({"return\\"To\\":":{"\/categories":"\/"}}) => {"return\"To\":" => {"/categories" => "/"}}, %q({"returnTo":{"\/categories":1}}) => {"returnTo" => {"/categories" => 1}}, %({"returnTo":[1,"a"]}) => {"returnTo" => [1, "a"]}, %({"returnTo":[1,"\\"a\\",", "b"]}) => {"returnTo" => [1, "\"a\",", "b"]}, - %({a: "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"}, - %({a: "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"}, + %({"a": "'", "b": "5,000"}) => {"a" => "'", "b" => "5,000"}, + %({"a": "a's, b's and c's", "b": "5,000"}) => {"a" => "a's, b's and c's", "b" => "5,000"}, # multibyte %({"matzue": "松江", "asakusa": "浅草"}) => {"matzue" => "松江", "asakusa" => "浅草"}, - %({a: "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)}, - %({a: "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)}, + %({"a": "2007-01-01"}) => {'a' => Date.new(2007, 1, 1)}, + %({"a": "2007-01-01 01:12:34 Z"}) => {'a' => Time.utc(2007, 1, 1, 1, 12, 34)}, # no time zone - %({a: "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"}, + %({"a": "2007-01-01 01:12:34"}) => {'a' => "2007-01-01 01:12:34"}, # needs to be *exact* - %({a: " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "}, - %({a: "2007-01-01 : it's your birthday"}) => {'a' => "2007-01-01 : it's your birthday"}, + %({"a": " 2007-01-01 01:12:34 Z "}) => {'a' => " 2007-01-01 01:12:34 Z "}, + %({"a": "2007-01-01 : it's your birthday"}) => {'a' => "2007-01-01 : it's your birthday"}, %([]) => [], %({}) => {}, - %(1) => 1, - %("") => "", - %("\\"") => "\"", - %(null) => nil, - %(true) => true, - %(false) => false, - %q("http:\/\/test.host\/posts\/1") => "http://test.host/posts/1", - %q("\u003cunicode\u0020escape\u003e") => "<unicode escape>", - %q("\\\\u0020skip double backslashes") => "\\u0020skip double backslashes", - %q({a: "\u003cbr /\u003e"}) => {'a' => "<br />"}, - %q({b:["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["<i>","<b>","<u>"]} + %({"a":1}) => {"a" => 1}, + %({"a": ""}) => {"a" => ""}, + %({"a":"\\""}) => {"a" => "\""}, + %({"a": null}) => {"a" => nil}, + %({"a": true}) => {"a" => true}, + %({"a": false}) => {"a" => false}, + %q({"a": "http:\/\/test.host\/posts\/1"}) => {"a" => "http://test.host/posts/1"}, + %q({"a": "\u003cunicode\u0020escape\u003e"}) => {"a" => "<unicode escape>"}, + %q({"a": "\\\\u0020skip double backslashes"}) => {"a" => "\\u0020skip double backslashes"}, + %q({"a": "\u003cbr /\u003e"}) => {'a' => "<br />"}, + %q({"b":["\u003ci\u003e","\u003cb\u003e","\u003cu\u003e"]}) => {'b' => ["<i>","<b>","<u>"]} } - - TESTS.each do |json, expected| - define_method :"test_json_decoding_#{json}" do - assert_nothing_raised do - assert_equal expected, ActiveSupport::JSON.decode(json) + + # load the default JSON backend + ActiveSupport::JSON.backend + + backends = %w(Yaml) + begin + gem 'json', '>= 1.1' + require 'json' + backends << "JSONGem" + rescue Gem::LoadError + # Skip JSON gem tests + end + + backends.each do |backend| + TESTS.each do |json, expected| + test "json decodes #{json} with the #{backend} backend" do + ActiveSupport.parse_json_times = true + silence_warnings do + ActiveSupport::JSON.with_backend backend do + assert_nothing_raised do + assert_equal expected, ActiveSupport::JSON.decode(json) + end + end + end end end end - + + if backends.include?("JSONGem") + test "json decodes time json with time parsing disabled" do + ActiveSupport.parse_json_times = false + expected = {"a" => "2007-01-01 01:12:34 Z"} + ActiveSupport::JSON.with_backend "JSONGem" do + assert_equal expected, ActiveSupport::JSON.decode(%({"a": "2007-01-01 01:12:34 Z"})) + end + end + end + def test_failed_json_decoding assert_raise(ActiveSupport::JSON::ParseError) { ActiveSupport::JSON.decode(%({: 1})) } end diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index 9e6b4fa501..1a0e6d543c 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -19,14 +19,14 @@ class TestJSONEncoding < Test::Unit::TestCase [ 'a "string" with quotes & an ampersand', %("a \\"string\\" with quotes \\u0026 an ampersand") ], [ 'http://test.host/posts/1', %("http://test.host/posts/1")]] - ArrayTests = [[ ['a', 'b', 'c'], %([\"a\", \"b\", \"c\"]) ], - [ [1, 'a', :b, nil, false], %([1, \"a\", \"b\", null, false]) ]] + ArrayTests = [[ ['a', 'b', 'c'], %([\"a\",\"b\",\"c\"]) ], + [ [1, 'a', :b, nil, false], %([1,\"a\",\"b\",null,false]) ]] SymbolTests = [[ :a, %("a") ], [ :this, %("this") ], [ :"a b", %("a b") ]] - ObjectTests = [[ Foo.new(1, 2), %({\"a\": 1, \"b\": 2}) ]] + ObjectTests = [[ Foo.new(1, 2), %({\"a\":1,\"b\":2}) ]] VariableTests = [[ ActiveSupport::JSON::Variable.new('foo'), 'foo'], [ ActiveSupport::JSON::Variable.new('alert("foo")'), 'alert("foo")']] @@ -47,7 +47,7 @@ class TestJSONEncoding < Test::Unit::TestCase ActiveSupport.escape_html_entities_in_json = class_tests !~ /^Standard/ ActiveSupport.use_standard_json_time_format = class_tests =~ /^Standard/ self.class.const_get(class_tests).each do |pair| - assert_equal pair.last, pair.first.to_json + assert_equal pair.last, ActiveSupport::JSON.encode(pair.first) end ensure ActiveSupport.escape_html_entities_in_json = false @@ -57,45 +57,45 @@ class TestJSONEncoding < Test::Unit::TestCase end def test_hash_encoding - assert_equal %({\"a\": \"b\"}), { :a => :b }.to_json - assert_equal %({\"a\": 1}), { 'a' => 1 }.to_json - assert_equal %({\"a\": [1, 2]}), { 'a' => [1,2] }.to_json - assert_equal %({"1": 2}), { 1 => 2 }.to_json + assert_equal %({\"a\":\"b\"}), ActiveSupport::JSON.encode(:a => :b) + assert_equal %({\"a\":1}), ActiveSupport::JSON.encode('a' => 1) + assert_equal %({\"a\":[1,2]}), ActiveSupport::JSON.encode('a' => [1,2]) + assert_equal %({"1":2}), ActiveSupport::JSON.encode(1 => 2) - sorted_json = '{' + {:a => :b, :c => :d}.to_json[1..-2].split(', ').sort.join(', ') + '}' - assert_equal %({\"a\": \"b\", \"c\": \"d\"}), sorted_json + sorted_json = '{' + ActiveSupport::JSON.encode(:a => :b, :c => :d)[1..-2].split(',').sort.join(',') + '}' + assert_equal %({\"a\":\"b\",\"c\":\"d\"}), sorted_json end def test_utf8_string_encoded_properly_when_kcode_is_utf8 with_kcode 'UTF8' do - assert_equal '"\\u20ac2.99"', '€2.99'.to_json - assert_equal '"\\u270e\\u263a"', '✎☺'.to_json + assert_equal '"\\u20ac2.99"', ActiveSupport::JSON.encode('€2.99') + assert_equal '"\\u270e\\u263a"', ActiveSupport::JSON.encode('✎☺') end end def test_exception_raised_when_encoding_circular_reference a = [1] a << a - assert_raise(ActiveSupport::JSON::CircularReferenceError) { a.to_json } + assert_raise(ActiveSupport::JSON::CircularReferenceError) { ActiveSupport::JSON.encode(a) } end def test_hash_key_identifiers_are_always_quoted values = {0 => 0, 1 => 1, :_ => :_, "$" => "$", "a" => "a", :A => :A, :A0 => :A0, "A0B" => "A0B"} - assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(values.to_json) + assert_equal %w( "$" "A" "A0" "A0B" "_" "a" "0" "1" ).sort, object_keys(ActiveSupport::JSON.encode(values)) end def test_hash_should_allow_key_filtering_with_only - assert_equal %({"a": 1}), { 'a' => 1, :b => 2, :c => 3 }.to_json(:only => 'a') + assert_equal %({"a":1}), ActiveSupport::JSON.encode({'a' => 1, :b => 2, :c => 3}, :only => 'a') end def test_hash_should_allow_key_filtering_with_except - assert_equal %({"b": 2}), { 'foo' => 'bar', :b => 2, :c => 3 }.to_json(:except => ['foo', :c]) + assert_equal %({"b":2}), ActiveSupport::JSON.encode({'foo' => 'bar', :b => 2, :c => 3}, :except => ['foo', :c]) end def test_time_to_json_includes_local_offset ActiveSupport.use_standard_json_time_format = true with_env_tz 'US/Eastern' do - assert_equal %("2005-02-01T15:15:10-05:00"), Time.local(2005,2,1,15,15,10).to_json + assert_equal %("2005-02-01T15:15:10-05:00"), ActiveSupport::JSON.encode(Time.local(2005,2,1,15,15,10)) end ensure ActiveSupport.use_standard_json_time_format = false @@ -109,7 +109,7 @@ class TestJSONEncoding < Test::Unit::TestCase :latitude => 123.234 } } - result = hash.to_json + result = ActiveSupport::JSON.encode(hash) end end @@ -134,6 +134,6 @@ class JsonOptionsTests < Test::Unit::TestCase ActiveSupport::JSON.expects(:encode).with(2, json_options) ActiveSupport::JSON.expects(:encode).with('foo', json_options) - [1, 2, 'foo'].to_json(json_options) + [1, 2, 'foo'].send(:rails_to_json, json_options) end end diff --git a/activesupport/test/new_callbacks_test.rb b/activesupport/test/new_callbacks_test.rb index 5cde078b65..7bec47224d 100644 --- a/activesupport/test/new_callbacks_test.rb +++ b/activesupport/test/new_callbacks_test.rb @@ -19,11 +19,11 @@ module NewCallbacksTest class << self def callback_symbol(callback_method) - returning(:"#{callback_method}_method") do |method_name| - define_method(method_name) do - history << [callback_method, :symbol] - end + method_name = :"#{callback_method}_method" + define_method(method_name) do + history << [callback_method, :symbol] end + method_name end def callback_string(callback_method) @@ -255,6 +255,26 @@ module NewCallbacksTest end end + class HyphenatedCallbacks + include ActiveSupport::NewCallbacks + define_callbacks :save + attr_reader :stuff + + save_callback :before, :omg, :per_key => {:if => :yes} + + def yes() true end + + def omg + @stuff = "OMG" + end + + def save + _run_save_callbacks("hyphen-ated") do + @stuff + end + end + end + class AroundCallbacksTest < Test::Unit::TestCase def test_save_around around = AroundPerson.new @@ -345,7 +365,7 @@ module NewCallbacksTest save_callback :after, :third - attr_reader :history + attr_reader :history, :saved def initialize @history = [] end @@ -370,8 +390,72 @@ module NewCallbacksTest end def save - _run_save_callbacks + _run_save_callbacks do + @saved = true + end + end + end + + class CallbackObject + def before_save(caller) + caller.record << "before" + end + + def around_save(caller) + caller.record << "around before" + yield + caller.record << "around after" + end + end + + class UsingObjectBefore + include ActiveSupport::NewCallbacks + + define_callbacks :save + save_callback :before, CallbackObject.new + + attr_accessor :record + def initialize + @record = [] + end + + def save + _run_save_callbacks do + @record << "yielded" + end + end + end + + class UsingObjectAround + include ActiveSupport::NewCallbacks + + define_callbacks :save + save_callback :around, CallbackObject.new + + attr_accessor :record + def initialize + @record = [] + end + + def save + _run_save_callbacks do + @record << "yielded" + end + end + end + + class UsingObjectTest < Test::Unit::TestCase + def test_before_object + u = UsingObjectBefore.new + u.save + assert_equal ["before", "yielded"], u.record end + + def test_around_object + u = UsingObjectAround.new + u.save + assert_equal ["around before", "yielded", "around after"], u.record + end end class CallbackTerminatorTest < Test::Unit::TestCase @@ -380,5 +464,19 @@ module NewCallbacksTest terminator.save assert_equal ["first", "second", "third", "second", "first"], terminator.history end + + def test_block_never_called_if_terminated + obj = CallbackTerminator.new + obj.save + assert !obj.saved + end end -end
\ No newline at end of file + + class HyphenatedKeyTest < Test::Unit::TestCase + def test_save + obj = HyphenatedCallbacks.new + obj.save + assert_equal obj.stuff, "OMG" + end + end +end diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 7cd8c8a8f4..647938dd87 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -51,6 +51,10 @@ class OrderedHashTest < Test::Unit::TestCase assert_same @ordered_hash, @ordered_hash.to_hash end + def test_to_a + assert_equal @keys.zip(@values), @ordered_hash.to_a + end + def test_has_key assert_equal true, @ordered_hash.has_key?('blue') assert_equal true, @ordered_hash.key?('blue') @@ -158,4 +162,10 @@ class OrderedHashTest < Test::Unit::TestCase def test_inspect assert @ordered_hash.inspect.include?(@hash.inspect) end + + def test_alternate_initialization + alternate = ActiveSupport::OrderedHash[1,2,3,4] + assert_kind_of ActiveSupport::OrderedHash, alternate + assert_equal [1, 3], alternate.keys + end end diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb index d250b10822..40d3d612e7 100644 --- a/activesupport/test/test_test.rb +++ b/activesupport/test/test_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/core_ext/kernel/reporting' class AssertDifferenceTest < ActiveSupport::TestCase def setup diff --git a/activesupport/test/time_zone_test.rb b/activesupport/test/time_zone_test.rb index 7e8047566e..99c4310854 100644 --- a/activesupport/test/time_zone_test.rb +++ b/activesupport/test/time_zone_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'active_support/time' class TimeZoneTest < Test::Unit::TestCase def test_utc_to_local diff --git a/activesupport/test/xml_mini/jdom_engine_test.rb b/activesupport/test/xml_mini/jdom_engine_test.rb new file mode 100644 index 0000000000..b745228994 --- /dev/null +++ b/activesupport/test/xml_mini/jdom_engine_test.rb @@ -0,0 +1,153 @@ +require 'abstract_unit' +require 'active_support/xml_mini' + +if RUBY_PLATFORM =~ /java/ + +class JDOMEngineTest < Test::Unit::TestCase + include ActiveSupport + + def setup + @default_backend = XmlMini.backend + XmlMini.backend = 'JDOM' + end + + def teardown + XmlMini.backend = @default_backend + end + + # def test_file_from_xml + # hash = Hash.from_xml(<<-eoxml) + # <blog> + # <logo type="file" name="logo.png" content_type="image/png"> + # </logo> + # </blog> + # eoxml + # assert hash.has_key?('blog') + # assert hash['blog'].has_key?('logo') + # + # file = hash['blog']['logo'] + # assert_equal 'logo.png', file.original_filename + # assert_equal 'image/png', file.content_type + # end + + def test_exception_thrown_on_expansion_attack + assert_raise NativeException do + attack_xml = <<-EOT + <?xml version="1.0" encoding="UTF-8"?> + <!DOCTYPE member [ + <!ENTITY a "&b;&b;&b;&b;&b;&b;&b;&b;&b;&b;"> + <!ENTITY b "&c;&c;&c;&c;&c;&c;&c;&c;&c;&c;"> + <!ENTITY c "&d;&d;&d;&d;&d;&d;&d;&d;&d;&d;"> + <!ENTITY d "&e;&e;&e;&e;&e;&e;&e;&e;&e;&e;"> + <!ENTITY e "&f;&f;&f;&f;&f;&f;&f;&f;&f;&f;"> + <!ENTITY f "&g;&g;&g;&g;&g;&g;&g;&g;&g;&g;"> + <!ENTITY g "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"> + ]> + <member> + &a; + </member> + EOT + Hash.from_xml(attack_xml) + end + end + + def test_setting_JDOM_as_backend + XmlMini.backend = 'JDOM' + assert_equal XmlMini_JDOM, XmlMini.backend + end + + def test_blank_returns_empty_hash + assert_equal({}, XmlMini.parse(nil)) + assert_equal({}, XmlMini.parse('')) + end + + def test_array_type_makes_an_array + assert_equal_rexml(<<-eoxml) + <blog> + <posts type="array"> + <post>a post</post> + <post>another post</post> + </posts> + </blog> + eoxml + end + + def test_one_node_document_as_hash + assert_equal_rexml(<<-eoxml) + <products/> + eoxml + end + + def test_one_node_with_attributes_document_as_hash + assert_equal_rexml(<<-eoxml) + <products foo="bar"/> + eoxml + end + + def test_products_node_with_book_node_as_hash + assert_equal_rexml(<<-eoxml) + <products> + <book name="awesome" id="12345" /> + </products> + eoxml + end + + def test_products_node_with_two_book_nodes_as_hash + assert_equal_rexml(<<-eoxml) + <products> + <book name="awesome" id="12345" /> + <book name="america" id="67890" /> + </products> + eoxml + end + + def test_single_node_with_content_as_hash + assert_equal_rexml(<<-eoxml) + <products> + hello world + </products> + eoxml + end + + def test_children_with_children + assert_equal_rexml(<<-eoxml) + <root> + <products> + <book name="america" id="67890" /> + </products> + </root> + eoxml + end + + def test_children_with_text + assert_equal_rexml(<<-eoxml) + <root> + <products> + hello everyone + </products> + </root> + eoxml + end + + def test_children_with_non_adjacent_text + assert_equal_rexml(<<-eoxml) + <root> + good + <products> + hello everyone + </products> + morning + </root> + eoxml + end + + private + def assert_equal_rexml(xml) + hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } + assert_equal(hash, XmlMini.parse(xml)) + end +end + +else + # don't run these test because we aren't running in JRuby +end diff --git a/activesupport/test/xml_mini/nokogiri_engine_test.rb b/activesupport/test/xml_mini/nokogiri_engine_test.rb index e5174a0b57..7c3a591e63 100644 --- a/activesupport/test/xml_mini/nokogiri_engine_test.rb +++ b/activesupport/test/xml_mini/nokogiri_engine_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'active_support/xml_mini' +require 'active_support/core_ext/hash/conversions' begin gem 'nokogiri', '>= 1.1.1' @@ -147,6 +148,19 @@ class NokogiriEngineTest < Test::Unit::TestCase eoxml end + def test_parse_from_io + io = StringIO.new(<<-eoxml) + <root> + good + <products> + hello everyone + </products> + morning + </root> + eoxml + XmlMini.parse(io) + end + private def assert_equal_rexml(xml) hash = XmlMini.with_backend('REXML') { XmlMini.parse(xml) } diff --git a/activesupport/test/xml_mini/rexml_engine_test.rb b/activesupport/test/xml_mini/rexml_engine_test.rb index a412d8ca05..57bb35254a 100644 --- a/activesupport/test/xml_mini/rexml_engine_test.rb +++ b/activesupport/test/xml_mini/rexml_engine_test.rb @@ -12,4 +12,18 @@ class REXMLEngineTest < Test::Unit::TestCase XmlMini.backend = 'REXML' assert_equal XmlMini_REXML, XmlMini.backend end + + def test_parse_from_io + XmlMini.backend = 'REXML' + io = StringIO.new(<<-eoxml) + <root> + good + <products> + hello everyone + </products> + morning + </root> + eoxml + XmlMini.parse(io) + end end |