diff options
Diffstat (limited to 'activesupport')
58 files changed, 565 insertions, 459 deletions
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index 3b6a29f7d6..6e8cce0d27 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -4,6 +4,10 @@ * Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! [Andrew White] +*Rails 3.0.2 (unreleased)* + +* Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! [Andrew White] + *Rails 3.0.1 (October 15, 2010)* * No Changes, just a version bump. diff --git a/activesupport/Rakefile b/activesupport/Rakefile index d117ca6356..d117ca6356 100644..100755 --- a/activesupport/Rakefile +++ b/activesupport/Rakefile diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 9098ffbfec..b4f0c42e37 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -210,11 +210,11 @@ module ActiveSupport # be specified as an option to the construction in which call all entries will be # affected. Or it can be supplied to the +fetch+ or +write+ method for just one entry. # - # cache = ActiveSupport::Cache::MemoryStore.new(:expire_in => 5.minutes) - # cache.write(key, value, :expire_in => 1.minute) # Set a lower value for one entry + # cache = ActiveSupport::Cache::MemoryStore.new(:expires_in => 5.minutes) + # cache.write(key, value, :expires_in => 1.minute) # Set a lower value for one entry # # Setting <tt>:race_condition_ttl</tt> is very useful in situations where a cache entry - # is used very frequently unver heavy load. If a cache expires and due to heavy load + # is used very frequently and is under heavy load. If a cache expires and due to heavy load # seven different processes will try to read data natively and then they all will try to # write to cache. To avoid that case the first process to find an expired cache entry will # bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>. Yes diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb index 84f6f29572..18182bbb40 100644 --- a/activesupport/lib/active_support/cache/file_store.rb +++ b/activesupport/lib/active_support/cache/file_store.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/file/atomic' require 'active_support/core_ext/string/conversions' +require 'rack/utils' module ActiveSupport module Cache @@ -11,8 +12,6 @@ module ActiveSupport attr_reader :cache_path DIR_FORMATTER = "%03X" - ESCAPE_FILENAME_CHARS = /[^a-z0-9_.-]/i - UNESCAPE_FILENAME_CHARS = /%[0-9A-F]{2}/ def initialize(cache_path, options = nil) super(options) @@ -136,7 +135,7 @@ module ActiveSupport # Translate a key into a file path. def key_file_path(key) - fname = key.to_s.gsub(ESCAPE_FILENAME_CHARS){|match| "%#{match.ord.to_s(16).upcase}"} + fname = Rack::Utils.escape(key) hash = Zlib.adler32(fname) hash, dir_1 = hash.divmod(0x1000) dir_2 = hash.modulo(0x1000) @@ -156,7 +155,7 @@ module ActiveSupport # Translate a file path into a key. def file_path_key(path) fname = path[cache_path.size, path.size].split(File::SEPARATOR, 4).last - fname.gsub(UNESCAPE_FILENAME_CHARS){|match| $1.ord.to_s(16)} + Rack::Utils.unescape(fname) end # Delete empty directories in the cache. diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 8f8def5922..32ebea8571 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -1,6 +1,6 @@ require 'active_support/descendants_tracker' require 'active_support/core_ext/array/wrap' -require 'active_support/core_ext/class/inheritable_attributes' +require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/singleton_class' @@ -178,49 +178,48 @@ module ActiveSupport # options[0] is the compiled form of supplied conditions # options[1] is the "end" for the conditional # - if @kind == :before || @kind == :around - if @kind == :before - # if condition # before_save :filter_name, :if => :condition - # filter_name - # end - filter = <<-RUBY_EVAL - unless halted - result = #{@filter} - halted = (#{chain.config[:terminator]}) - end - RUBY_EVAL - - [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n") - else - # Compile around filters with conditions into proxy methods - # that contain the conditions. - # - # For `around_save :filter_name, :if => :condition': - # - # def _conditional_callback_save_17 - # if condition - # filter_name do - # yield self - # end - # else - # yield self - # end - # end - # - name = "_conditional_callback_#{@kind}_#{next_id}" - @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def #{name}(halted) - #{@compiled_options[0] || "if true"} && !halted - #{@filter} do - yield self - end - else + case @kind + when :before + # if condition # before_save :filter_name, :if => :condition + # filter_name + # end + filter = <<-RUBY_EVAL + unless halted + result = #{@filter} + halted = (#{chain.config[:terminator]}) + end + RUBY_EVAL + + [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n") + when :around + # Compile around filters with conditions into proxy methods + # that contain the conditions. + # + # For `around_save :filter_name, :if => :condition': + # + # def _conditional_callback_save_17 + # if condition + # filter_name do + # yield self + # end + # else + # yield self + # end + # end + # + name = "_conditional_callback_#{@kind}_#{next_id}" + @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 + def #{name}(halted) + #{@compiled_options[0] || "if true"} && !halted + #{@filter} do yield self end + else + yield self end - RUBY_EVAL - "#{name}(halted) do" - end + end + RUBY_EVAL + "#{name}(halted) do" end end @@ -229,15 +228,14 @@ module ActiveSupport def end(key=nil, object=nil) return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?") - if @kind == :around || @kind == :after + case @kind + when :after # if condition # after_save :filter_name, :if => :condition # filter_name # end - if @kind == :after - [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n") - else - "end" - end + [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n") + when :around + "end" end end @@ -388,7 +386,7 @@ module ActiveSupport # key. See #define_callbacks for more information. # def __define_runner(symbol) #:nodoc: - body = send("_#{symbol}_callbacks").compile(nil) + body = send("_#{symbol}_callbacks").compile silence_warnings do undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks") @@ -437,7 +435,7 @@ module ActiveSupport ([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target| chain = target.send("_#{name}_callbacks") - yield chain, type, filters, options + yield target, chain.dup, type, filters, options target.__define_runner(name) end end @@ -449,6 +447,10 @@ module ActiveSupport # set_callback :save, :after, :after_meth, :if => :condition # set_callback :save, :around, lambda { |r| stuff; yield; stuff } # + # If the second argument is not :before, :after or :around then an implicit :before is assumed. + # It means the first example mentioned above can also be written as: + # set_callback :save, :before_meth + # # Use skip_callback to skip any defined one. # # When creating or skipping callbacks, you can specify conditions that @@ -473,7 +475,7 @@ module ActiveSupport def set_callback(name, *filter_list, &block) mapped = nil - __update_callbacks(name, filter_list, block) do |chain, type, filters, options| + __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options| mapped ||= filters.map do |filter| Callback.new(chain, filter, type, options.dup, self) end @@ -483,6 +485,8 @@ module ActiveSupport end options[:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped) + + target.send("_#{name}_callbacks=", chain) end end @@ -493,7 +497,7 @@ module ActiveSupport # end # def skip_callback(name, *filter_list, &block) - __update_callbacks(name, filter_list, block) do |chain, type, filters, options| + __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options| filters.each do |filter| filter = chain.find {|c| c.matches?(type, filter) } @@ -505,6 +509,7 @@ module ActiveSupport chain.delete(filter) end + target.send("_#{name}_callbacks=", chain) end end @@ -514,12 +519,14 @@ module ActiveSupport callbacks = send("_#{symbol}_callbacks") ActiveSupport::DescendantsTracker.descendants(self).each do |target| - chain = target.send("_#{symbol}_callbacks") + chain = target.send("_#{symbol}_callbacks").dup callbacks.each { |c| chain.delete(c) } + target.send("_#{symbol}_callbacks=", chain) target.__define_runner(symbol) end - callbacks.clear + self.send("_#{symbol}_callbacks=", callbacks.dup.clear) + __define_runner(symbol) end @@ -589,9 +596,8 @@ module ActiveSupport def define_callbacks(*callbacks) config = callbacks.last.is_a?(Hash) ? callbacks.pop : {} callbacks.each do |callback| - extlib_inheritable_reader("_#{callback}_callbacks") do - CallbackChain.new(callback, config) - end + class_attribute "_#{callback}_callbacks" + send("_#{callback}_callbacks=", CallbackChain.new(callback, config)) __define_runner(callback) end end diff --git a/activesupport/lib/active_support/concern.rb b/activesupport/lib/active_support/concern.rb index ac94d12e5e..81fb859334 100644 --- a/activesupport/lib/active_support/concern.rb +++ b/activesupport/lib/active_support/concern.rb @@ -3,7 +3,7 @@ module ActiveSupport # # module M # def self.included(base) - # base.extend, ClassMethods + # base.extend ClassMethods # base.send(:include, InstanceMethods) # scope :disabled, where(:disabled => true) # end diff --git a/activesupport/lib/active_support/configurable.rb b/activesupport/lib/active_support/configurable.rb index 58ed37b018..644db0b205 100644 --- a/activesupport/lib/active_support/configurable.rb +++ b/activesupport/lib/active_support/configurable.rb @@ -38,6 +38,18 @@ module ActiveSupport yield config end + # Allows you to add shortcut so that you don't have to refer to attribute through config. + # Also look at the example for config to contrast. + # + # class User + # include ActiveSupport::Configurable + # config_accessor :allowed_access + # end + # + # user = User.new + # user.allowed_access = true + # user.allowed_access # => true + # def config_accessor(*names) names.each do |name| code, line = <<-RUBY, __LINE__ + 1 diff --git a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb index f7f03f4d95..3720dbb8b8 100644 --- a/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb +++ b/activesupport/lib/active_support/core_ext/big_decimal/conversions.rb @@ -18,6 +18,10 @@ class BigDecimal end end + def to_d + self + end + DEFAULT_STRING_FORMAT = 'F' def to_formatted_s(format = DEFAULT_STRING_FORMAT) _original_to_s(format) diff --git a/activesupport/lib/active_support/core_ext/cgi.rb b/activesupport/lib/active_support/core_ext/cgi.rb deleted file mode 100644 index 7279a3d4da..0000000000 --- a/activesupport/lib/active_support/core_ext/cgi.rb +++ /dev/null @@ -1 +0,0 @@ -require 'active_support/core_ext/cgi/escape_skipping_slashes' diff --git a/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb b/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb deleted file mode 100644 index d3c3575748..0000000000 --- a/activesupport/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'cgi' - -class CGI #:nodoc: - if RUBY_VERSION >= '1.9' - def self.escape_skipping_slashes(str) - str = str.join('/') if str.respond_to? :join - str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do - "%#{$1.unpack('H2' * $1.bytesize).join('%').upcase}" - end.tr(' ', '+') - end - else - def self.escape_skipping_slashes(str) - str = str.join('/') if str.respond_to? :join - str.gsub(/([^ \/a-zA-Z0-9_.-])/n) do - "%#{$1.unpack('H2').first.upcase}" - end.tr(' ', '+') - end - end -end 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 af30bfc13a..ca3db2349e 100644 --- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb +++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb @@ -1,8 +1,10 @@ require 'active_support/core_ext/object/duplicable' require 'active_support/core_ext/array/extract_options' +require 'active_support/deprecation' # Retained for backward compatibility. Methods are now included in Class. module ClassInheritableAttributes # :nodoc: + DEPRECATION_WARNING_MESSAGE = "class_inheritable_attribute is deprecated, please use class_attribute method instead. Notice their behavior are slightly different, so refer to class_attribute documentation first" end # It is recommended to use <tt>class_attribute</tt> over methods defined in this file. Please @@ -36,6 +38,7 @@ end # Person.new.hair_colors # => NoMethodError class Class # :nodoc: def class_inheritable_reader(*syms) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE options = syms.extract_options! syms.each do |sym| next if sym.is_a?(Hash) @@ -54,6 +57,7 @@ class Class # :nodoc: end def class_inheritable_writer(*syms) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE options = syms.extract_options! syms.each do |sym| class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@ -71,6 +75,7 @@ class Class # :nodoc: end def class_inheritable_array_writer(*syms) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE options = syms.extract_options! syms.each do |sym| class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@ -88,6 +93,7 @@ class Class # :nodoc: end def class_inheritable_hash_writer(*syms) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE options = syms.extract_options! syms.each do |sym| class_eval(<<-EOS, __FILE__, __LINE__ + 1) @@ -124,6 +130,7 @@ class Class # :nodoc: end def write_inheritable_attribute(key, value) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES) @inheritable_attributes = {} end @@ -141,10 +148,12 @@ class Class # :nodoc: end def read_inheritable_attribute(key) + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE inheritable_attributes[key] end def reset_inheritable_attributes + ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE @inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES end @@ -169,86 +178,3 @@ class Class # :nodoc: alias inherited_without_inheritable_attributes inherited alias inherited inherited_with_inheritable_attributes end - -class Class - # Defines class-level inheritable attribute reader. Attributes are available to subclasses, - # each subclass has a copy of parent's attribute. - # - # @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for. - # @return <Array[#to_s]> Array of attributes converted into inheritable_readers. - # - # @api public - # - # @todo Do we want to block instance_reader via :instance_reader => false - # @todo It would be preferable that we do something with a Hash passed in - # (error out or do the same as other methods above) instead of silently - # moving on). In particular, this makes the return value of this function - # less useful. - def extlib_inheritable_reader(*ivars, &block) - options = ivars.extract_options! - - ivars.each do |ivar| - self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def self.#{ivar} - return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar}) - ivar = superclass.#{ivar} - return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}") - @#{ivar} = ivar.duplicable? ? ivar.dup : ivar - end - RUBY - unless options[:instance_reader] == false - self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{ivar} - self.class.#{ivar} - end - RUBY - end - instance_variable_set(:"@#{ivar}", yield) if block_given? - end - end - - # Defines class-level inheritable attribute writer. Attributes are available to subclasses, - # each subclass has a copy of parent's attribute. - # - # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to - # define inheritable writer for. - # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined. - # @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers. - # - # @api public - # - # @todo We need a style for class_eval <<-HEREDOC. I'd like to make it - # class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere. - def extlib_inheritable_writer(*ivars) - options = ivars.extract_options! - - ivars.each do |ivar| - self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def self.#{ivar}=(obj) - @#{ivar} = obj - end - RUBY - unless options[:instance_writer] == false - self.class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{ivar}=(obj) self.class.#{ivar} = obj end - RUBY - end - - self.send("#{ivar}=", yield) if block_given? - end - end - - # Defines class-level inheritable attribute accessor. Attributes are available to subclasses, - # each subclass has a copy of parent's attribute. - # - # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to - # define inheritable accessor for. - # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined. - # @return <Array[#to_s]> An Array of attributes turned into inheritable accessors. - # - # @api public - def extlib_inheritable_accessor(*syms, &block) - extlib_inheritable_reader(*syms) - extlib_inheritable_writer(*syms, &block) - end -end diff --git a/activesupport/lib/active_support/core_ext/class/subclasses.rb b/activesupport/lib/active_support/core_ext/class/subclasses.rb index 3e5d1a2a42..46e9daaa8f 100644 --- a/activesupport/lib/active_support/core_ext/class/subclasses.rb +++ b/activesupport/lib/active_support/core_ext/class/subclasses.rb @@ -2,49 +2,35 @@ require 'active_support/core_ext/module/anonymous' require 'active_support/core_ext/module/reachable' class Class #:nodoc: - # Rubinius - if defined?(Class.__subclasses__) - alias :subclasses :__subclasses__ + begin + ObjectSpace.each_object(Class.new) {} def descendants descendants = [] - __subclasses__.each do |k| - descendants << k - descendants.concat k.descendants + ObjectSpace.each_object(class << self; self; end) do |k| + descendants.unshift k unless k == self end descendants end - else # MRI - begin - ObjectSpace.each_object(Class.new) {} - - def descendants - descendants = [] - ObjectSpace.each_object(class << self; self; end) do |k| - descendants.unshift k unless k == self - end - descendants - end - rescue StandardError # JRuby - def descendants - descendants = [] - ObjectSpace.each_object(Class) do |k| - descendants.unshift k if k < self - end - descendants.uniq! - descendants + rescue StandardError # JRuby + def descendants + descendants = [] + ObjectSpace.each_object(Class) do |k| + descendants.unshift k if k < self end + descendants.uniq! + descendants end + end - # Returns an array with the direct children of +self+. - # - # Integer.subclasses # => [Bignum, Fixnum] - def subclasses - subclasses, chain = [], descendants - chain.each do |k| - subclasses << k unless chain.any? { |c| c > k } - end - subclasses + # Returns an array with the direct children of +self+. + # + # Integer.subclasses # => [Bignum, Fixnum] + def subclasses + subclasses, chain = [], descendants + chain.each do |k| + subclasses << k unless chain.any? { |c| c > k } end + subclasses end end diff --git a/activesupport/lib/active_support/core_ext/date/conversions.rb b/activesupport/lib/active_support/core_ext/date/conversions.rb index 092f936961..06d868a3b0 100644 --- a/activesupport/lib/active_support/core_ext/date/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date/conversions.rb @@ -1,5 +1,5 @@ require 'date' -require 'active_support/inflector' +require 'active_support/inflector/methods' require 'active_support/core_ext/date/zones' class Date 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 8e267c76c4..029b8c41b4 100644 --- a/activesupport/lib/active_support/core_ext/date_time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/date_time/conversions.rb @@ -1,4 +1,4 @@ -require 'active_support/inflector' +require 'active_support/inflector/methods' require 'active_support/core_ext/time/conversions' require 'active_support/core_ext/date_time/calculations' require 'active_support/values/time_zone' diff --git a/activesupport/lib/active_support/core_ext/hash.rb b/activesupport/lib/active_support/core_ext/hash.rb index 501483498d..fd1cda991e 100644 --- a/activesupport/lib/active_support/core_ext/hash.rb +++ b/activesupport/lib/active_support/core_ext/hash.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/hash/conversions' require 'active_support/core_ext/hash/deep_merge' +require 'active_support/core_ext/hash/deep_dup' require 'active_support/core_ext/hash/diff' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/hash/indifferent_access' diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 4e8ec5a3a8..3005fef44c 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -26,10 +26,22 @@ class Hash # # * If +value+ is a callable object it must expect one or two arguments. Depending # on the arity, the callable is invoked with the +options+ hash as first argument - # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. Its - # return value becomes a new node. + # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The + # callable can add nodes by using <tt>options[:builder]</tt>. + # + # "foo".to_xml(lambda { |options, key| options[:builder].b(key) }) + # # => "<b>foo</b>" # # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>. + # + # class Foo + # def to_xml(options) + # options[:builder].bar "fooing!" + # end + # end + # + # {:foo => Foo.new}.to_xml(:skip_instruct => true) + # # => "<hash><bar>fooing!</bar></hash>" # # * Otherwise, a node with +key+ as tag is created with a string representation of # +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added. diff --git a/activesupport/lib/active_support/core_ext/hash/deep_dup.rb b/activesupport/lib/active_support/core_ext/hash/deep_dup.rb new file mode 100644 index 0000000000..447142605c --- /dev/null +++ b/activesupport/lib/active_support/core_ext/hash/deep_dup.rb @@ -0,0 +1,11 @@ +class Hash + # Returns a deep copy of hash. + def deep_dup + duplicate = self.dup + duplicate.each_pair do |k,v| + tv = duplicate[k] + duplicate[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_dup : v + end + duplicate + end +end diff --git a/activesupport/lib/active_support/core_ext/module/synchronization.rb b/activesupport/lib/active_support/core_ext/module/synchronization.rb index 38ce55f26e..ed16c2f71b 100644 --- a/activesupport/lib/active_support/core_ext/module/synchronization.rb +++ b/activesupport/lib/active_support/core_ext/module/synchronization.rb @@ -1,3 +1,4 @@ +require 'thread' require 'active_support/core_ext/module/aliasing' require 'active_support/core_ext/array/extract_options' diff --git a/activesupport/lib/active_support/core_ext/object/instance_variables.rb b/activesupport/lib/active_support/core_ext/object/instance_variables.rb index 77a3cfc21d..eda9694614 100644 --- a/activesupport/lib/active_support/core_ext/object/instance_variables.rb +++ b/activesupport/lib/active_support/core_ext/object/instance_variables.rb @@ -30,35 +30,4 @@ class Object else alias_method :instance_variable_names, :instance_variables end - - # Copies the instance variables of +object+ into +self+. - # - # Instance variable names in the +exclude+ array are ignored. If +object+ - # responds to <tt>protected_instance_variables</tt> the ones returned are - # also ignored. For example, Rails controllers implement that method. - # - # In both cases strings and symbols are understood, and they have to include - # the at sign. - # - # class C - # def initialize(x, y, z) - # @x, @y, @z = x, y, z - # end - # - # def protected_instance_variables - # %w(@z) - # end - # end - # - # a = C.new(0, 1, 2) - # b = C.new(3, 4, 5) - # - # a.copy_instance_variables_from(b, [:@y]) - # # a is now: @x = 3, @y = 1, @z = 2 - def copy_instance_variables_from(object, exclude = []) #:nodoc: - exclude += object.protected_instance_variables if object.respond_to? :protected_instance_variables - - vars = object.instance_variables.map(&:to_s) - exclude.map(&:to_s) - vars.each { |name| instance_variable_set(name, object.instance_variable_get(name)) } - end end diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb index ecb2bca82c..593f376159 100644 --- a/activesupport/lib/active_support/core_ext/object/to_param.rb +++ b/activesupport/lib/active_support/core_ext/object/to_param.rb @@ -1,5 +1,3 @@ - - class Object # Alias of <tt>to_s</tt>. def to_param @@ -41,7 +39,7 @@ class Hash # ==== Examples # { :name => 'David', :nationality => 'Danish' }.to_param # => "name=David&nationality=Danish" # - # { :name => 'David', :nationality => 'Danish' }.to_param('user') # => "user[name]=David&user[nationality]=Danish" + # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish" def to_param(namespace = nil) collect do |key, value| value.to_query(namespace ? "#{namespace}[#{key}]" : key) diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb index c9981895b4..3f1540f685 100644 --- a/activesupport/lib/active_support/core_ext/object/to_query.rb +++ b/activesupport/lib/active_support/core_ext/object/to_query.rb @@ -7,7 +7,7 @@ class Object # Note: This method is defined as a default implementation for all Objects for Hash#to_query to work. def to_query(key) require 'cgi' unless defined?(CGI) && defined?(CGI::escape) - "#{CGI.escape(key.to_s).gsub(/%(5B|5D)/n) { [$1].pack('H*') }}=#{CGI.escape(to_param.to_s)}" + "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}" end end @@ -15,7 +15,7 @@ class Array # Converts an array into a string suitable for use as a URL query string, # using the given +key+ as the param name. # - # ['Rails', 'coding'].to_query('hobbies') # => "hobbies[]=Rails&hobbies[]=coding" + # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding" def to_query(key) prefix = "#{key}[]" collect { |value| value.to_query(prefix) }.join '&' diff --git a/activesupport/lib/active_support/core_ext/object/try.rb b/activesupport/lib/active_support/core_ext/object/try.rb index 48e9d04787..ff812234e3 100644 --- a/activesupport/lib/active_support/core_ext/object/try.rb +++ b/activesupport/lib/active_support/core_ext/object/try.rb @@ -1,13 +1,12 @@ class Object - ## - # :method: try - # # Invokes the method identified by the symbol +method+, passing it any arguments # and/or the block specified, just like the regular Ruby <tt>Object#send</tt> does. # # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised # and +nil+ will be returned instead, if the receiving object is a +nil+ object or NilClass. # + # If try is called without a method to call, it will yield any given block with the object. + # # ==== Examples # # Without try @@ -21,10 +20,18 @@ class Object # +try+ also accepts arguments and/or a block, for the method it is trying # Person.try(:find, 1) # @people.try(:collect) {|p| p.name} + # + # Without a method argument try will yield to the block unless the reciever is nil. + # @person.try { |p| "#{p.first_name} #{p.last_name}" } #-- # +try+ behaves like +Object#send+, unless called on +NilClass+. - - alias_method :try, :__send__ + def try(*a, &b) + if a.empty? && block_given? + yield self + else + __send__(*a, &b) + end + end end class NilClass #:nodoc: diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb index 37c206ea3c..bb0f747960 100644 --- a/activesupport/lib/active_support/core_ext/string/output_safety.rb +++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb @@ -33,21 +33,23 @@ class ERB singleton_class.send(:remove_method, :html_escape) module_function :html_escape - # A utility method for escaping HTML entities in JSON strings. - # This method is also aliased as <tt>j</tt>. + # A utility method for escaping HTML entities in JSON strings + # using \uXXXX JavaScript escape sequences for string literals: # - # Note that after this operation is performed the output is not - # a valid JSON. + # json_escape("is a > 0 & a < 10?") + # # => is a \u003E 0 \u0026 a \u003C 10? # - # In your ERb templates, use this method to escape any HTML entities: - # <%=j @person.to_json %> + # Note that after this operation is performed the output is not + # valid JSON. In particular double quotes are removed: # - # ==== Example: - # puts json_escape("{\"name\":\"john\",\"created_at\":\"2010-04-28T01:39:31Z\",\"id\":1}") + # json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}') # # => {name:john,created_at:2010-04-28T01:39:31Z,id:1} # - # puts json_escape("is a > 0 & a < 10?") - # # => is a \u003E 0 \u0026 a \u003C 10? + # This method is also aliased as +j+, and available as a helper + # in Rails templates: + # + # <%=j @person.to_json %> + # def json_escape(s) s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] } end diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb index d4ae3131ec..49ac18d245 100644 --- a/activesupport/lib/active_support/core_ext/time/conversions.rb +++ b/activesupport/lib/active_support/core_ext/time/conversions.rb @@ -1,4 +1,4 @@ -require 'active_support/inflector' +require 'active_support/inflector/methods' require 'active_support/core_ext/time/publicize_conversion_methods' require 'active_support/values/time_zone' diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 787437c49a..dab6fdbac6 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -331,7 +331,7 @@ module ActiveSupport #:nodoc: if load? log "loading #{file_name}" - # Enable warnings iff this file has not been loaded before and + # Enable warnings if this file has not been loaded before and # warnings_on_first_load is set. load_args = ["#{file_name}.rb"] load_args << const_path unless const_path.nil? diff --git a/activesupport/lib/active_support/deprecation.rb b/activesupport/lib/active_support/deprecation.rb index e1b8211d68..ce0775a690 100644 --- a/activesupport/lib/active_support/deprecation.rb +++ b/activesupport/lib/active_support/deprecation.rb @@ -4,12 +4,12 @@ require 'active_support/deprecation/method_wrappers' require 'active_support/deprecation/proxy_wrappers' module ActiveSupport - module Deprecation #:nodoc: + module Deprecation class << self # The version the deprecated behavior will be removed, by default. attr_accessor :deprecation_horizon end - self.deprecation_horizon = '3.0' + self.deprecation_horizon = '3.1' # By default, warnings are not silenced and debugging is off. self.silenced = false diff --git a/activesupport/lib/active_support/deprecation/behaviors.rb b/activesupport/lib/active_support/deprecation/behaviors.rb index f54f65dcf0..da4af339fc 100644 --- a/activesupport/lib/active_support/deprecation/behaviors.rb +++ b/activesupport/lib/active_support/deprecation/behaviors.rb @@ -7,10 +7,17 @@ module ActiveSupport # Whether to print a backtrace along with the warning. attr_accessor :debug + # Returns the set behaviour or if one isn't set, defaults to +:stderr+ def behavior @behavior ||= [DEFAULT_BEHAVIORS[:stderr]] end + # Sets the behaviour to the specified value. Can be a single value or an array. + # + # Examples + # + # ActiveSupport::Deprecation.behavior = :stderr + # ActiveSupport::Deprecation.behavior = [:stderr, :log] def behavior=(behavior) @behavior = Array.wrap(behavior).map { |b| DEFAULT_BEHAVIORS[b] || b } end diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index 970536a594..a65fcafb44 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -1,4 +1,4 @@ -require 'active_support/inflector' +require 'active_support/inflector/methods' module ActiveSupport module Deprecation diff --git a/activesupport/lib/active_support/deprecation/reporting.rb b/activesupport/lib/active_support/deprecation/reporting.rb index 6a7b11c7e0..ced08b8783 100644 --- a/activesupport/lib/active_support/deprecation/reporting.rb +++ b/activesupport/lib/active_support/deprecation/reporting.rb @@ -3,6 +3,12 @@ module ActiveSupport class << self attr_accessor :silenced + # Outputs a deprecation warning to the output configured by <tt>ActiveSupport::Deprecation.behavior</tt> + # + # Example: + # + # ActiveSupport::Deprecation.warn("something broke!") + # #=> "DEPRECATION WARNING: something broke! (called from your_code.rb:1)" def warn(message = nil, callstack = caller) return if silenced deprecation_message(callstack, message).tap do |m| diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index de3ded1e1f..00c67a470d 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -80,6 +80,10 @@ module ActiveSupport parts.to_sentence(:locale => :en) end + def as_json(options = nil) #:nodoc: + to_i + end + protected def sum(sign, time = ::Time.current) #:nodoc: diff --git a/activesupport/lib/active_support/file_update_checker.rb b/activesupport/lib/active_support/file_update_checker.rb index cd658fe173..a97e9d7daf 100644 --- a/activesupport/lib/active_support/file_update_checker.rb +++ b/activesupport/lib/active_support/file_update_checker.rb @@ -8,7 +8,7 @@ module ActiveSupport # I18n.reload! # end # - # ActionDispatch::Callbacks.to_prepare do + # ActionDispatch::Reloader.to_prepare do # i18n_reloader.execute_if_updated # end # diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index c406dd3c2e..6a344867ee 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -28,7 +28,7 @@ module ActiveSupport end def self.new_from_hash_copying_default(hash) - ActiveSupport::HashWithIndifferentAccess.new(hash).tap do |new_hash| + new(hash).tap do |new_hash| new_hash.default = hash.default end end @@ -97,7 +97,9 @@ module ActiveSupport # Returns an exact copy of the hash. def dup - HashWithIndifferentAccess.new(self) + self.class.new(self).tap do |new_hash| + new_hash.default = default + end end # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash @@ -138,11 +140,10 @@ module ActiveSupport end def convert_value(value) - case value - when Hash + if value.class == Hash self.class.new_from_hash_copying_default(value) - when Array - value.collect { |e| e.is_a?(Hash) ? self.class.new_from_hash_copying_default(e) : e } + elsif value.is_a?(Array) + value.dup.replace(value.map { |e| convert_value(e) }) else value end diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index 4d33f597d9..00ea8813dd 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -7,4 +7,3 @@ rescue LoadError => e end I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" -ActiveSupport.run_load_hooks(:i18n) diff --git a/activesupport/lib/active_support/i18n_railtie.rb b/activesupport/lib/active_support/i18n_railtie.rb index f8a5616a76..282337d373 100644 --- a/activesupport/lib/active_support/i18n_railtie.rb +++ b/activesupport/lib/active_support/i18n_railtie.rb @@ -19,7 +19,7 @@ module I18n # on to_prepare callbacks. This will only happen on the config.after_initialize # callback below. initializer "i18n.callbacks" do - ActionDispatch::Callbacks.to_prepare do + ActionDispatch::Reloader.to_prepare do I18n::Railtie.reloader.execute_if_updated end end diff --git a/activesupport/lib/active_support/inflector/inflections.rb b/activesupport/lib/active_support/inflector/inflections.rb index 3caf78bc7d..e136e4c5b3 100644 --- a/activesupport/lib/active_support/inflector/inflections.rb +++ b/activesupport/lib/active_support/inflector/inflections.rb @@ -148,7 +148,7 @@ module ActiveSupport def singularize(word) result = word.to_s.dup - if inflections.uncountables.any? { |inflection| result =~ /#{inflection}\Z/i } + if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i } result else inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) } diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb index 4cb9d01077..2e389b5c12 100644 --- a/activesupport/lib/active_support/json/backends/yaml.rb +++ b/activesupport/lib/active_support/json/backends/yaml.rb @@ -7,13 +7,20 @@ module ActiveSupport ParseError = ::StandardError extend self + EXCEPTIONS = [::ArgumentError] # :nodoc: + begin + require 'psych' + EXCEPTIONS << Psych::SyntaxError + rescue LoadError + end + # Parses a JSON string or IO and converts it into an object def decode(json) if json.respond_to?(:read) json = json.read end YAML.load(convert_json_to_yaml(json)) - rescue ArgumentError + rescue *EXCEPTIONS raise ParseError, "Invalid JSON string" end diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index c8cac52027..b2ea196003 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -153,6 +153,12 @@ class Object end end +class Struct + def as_json(options = nil) #:nodoc: + Hash[members.zip(values)] + end +end + class TrueClass AS_JSON = ActiveSupport::JSON::Variable.new('true').freeze def as_json(options = nil) AS_JSON end #:nodoc: diff --git a/activesupport/lib/active_support/ordered_hash.rb b/activesupport/lib/active_support/ordered_hash.rb index 7f148dc853..5b8c342f4f 100644 --- a/activesupport/lib/active_support/ordered_hash.rb +++ b/activesupport/lib/active_support/ordered_hash.rb @@ -20,9 +20,17 @@ module ActiveSupport "!tag:yaml.org,2002:omap" end + def encode_with(coder) + coder.represent_seq '!omap', map { |k,v| { k => v } } + end + def to_yaml(opts = {}) + if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? + return super + end + YAML.quick_emit(self, opts) do |out| - out.seq(taguri, to_yaml_style) do |seq| + out.seq(taguri) do |seq| each do |k, v| seq.add(k => v) end @@ -137,6 +145,8 @@ module ActiveSupport alias_method :each_pair, :each + alias_method :select, :find_all + def clear super @keys.clear diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 93f5d5a0cc..8137f8e17e 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -73,7 +73,7 @@ module ActiveSupport # Returns a <tt>Time.local()</tt> instance of the simultaneous time in your system's <tt>ENV['TZ']</tt> zone def localtime - utc.getlocal + utc.respond_to?(:getlocal) ? utc.getlocal : utc.to_time.getlocal end alias_method :getlocal, :localtime @@ -138,11 +138,7 @@ module ActiveSupport end def to_yaml(options = {}) - if options.kind_of?(YAML::Emitter) - utc.to_yaml(options) - else - time.to_yaml(options).gsub('Z', formatted_offset(true, 'Z')) - end + utc.to_yaml(options) end def httpdate diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb index 9e8b3d7888..690fc7f0fc 100644 --- a/activesupport/lib/active_support/version.rb +++ b/activesupport/lib/active_support/version.rb @@ -3,8 +3,8 @@ module ActiveSupport MAJOR = 3 MINOR = 1 TINY = 0 - BUILD = "beta" + PRE = "beta" - STRING = [MAJOR, MINOR, TINY, BUILD].join('.') + STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.') end end diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb index b6a8cf3caf..cddfcddb57 100644 --- a/activesupport/lib/active_support/xml_mini.rb +++ b/activesupport/lib/active_support/xml_mini.rb @@ -126,9 +126,11 @@ module ActiveSupport end def rename_key(key, options = {}) - camelize = options.has_key?(:camelize) && options[:camelize] + camelize = options[:camelize] dasherize = !options.has_key?(:dasherize) || options[:dasherize] - key = key.camelize if camelize + if camelize + key = true == camelize ? key.camelize : key.camelize(camelize) + end key = _dasherize(key) if dasherize key end diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb index 38c8685390..25afbfcd1c 100644 --- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb +++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb @@ -38,8 +38,7 @@ module ActiveSupport end def start_element(name, attrs = []) - new_hash = { CONTENT_KEY => '' } - new_hash[attrs.shift] = attrs.shift while attrs.length > 0 + new_hash = { CONTENT_KEY => '' }.merge(Hash[attrs]) new_hash[HASH_SIZE_KEY] = new_hash.size + 1 case current_hash[name] diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 28ef695a4b..579d5dad24 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -356,9 +356,13 @@ module CacheDeleteMatchedBehavior def test_delete_matched @cache.write("foo", "bar") @cache.write("fu", "baz") + @cache.write("foo/bar", "baz") + @cache.write("fu/baz", "bar") @cache.delete_matched(/oo/) assert_equal false, @cache.exist?("foo") assert_equal true, @cache.exist?("fu") + assert_equal false, @cache.exist?("foo/bar") + assert_equal true, @cache.exist?("fu/baz") end end @@ -509,6 +513,11 @@ class FileStoreTest < ActiveSupport::TestCase assert_nil old_cache.read('foo') end end + + def test_key_transformation + key = @cache.send(:key_file_path, "views/index?id=1") + assert_equal "views/index?id=1", @cache.send(:file_path_key, key) + end end class MemoryStoreTest < ActiveSupport::TestCase diff --git a/activesupport/test/callbacks_test.rb b/activesupport/test/callbacks_test.rb index 51b28b6a43..c89b03e243 100644 --- a/activesupport/test/callbacks_test.rb +++ b/activesupport/test/callbacks_test.rb @@ -3,6 +3,27 @@ require 'test/unit' require 'active_support' module CallbacksTest + class Phone + include ActiveSupport::Callbacks + define_callbacks :save, :rescuable => true + + set_callback :save, :before, :before_save1 + set_callback :save, :after, :after_save1 + + def before_save1; self.history << :before; end + def after_save1; self.history << :after; end + + def save + self.send(:_run_save_callbacks) do + raise 'boom' + end + end + + def history + @history ||= [] + end + end + class Record include ActiveSupport::Callbacks @@ -338,6 +359,14 @@ module CallbacksTest end class CallbacksTest < Test::Unit::TestCase + def test_save_phone + phone = Phone.new + assert_raise RuntimeError do + phone.save + end + assert_equal [:before, :after], phone.history + end + def test_save_person person = Person.new assert_equal [], person.history @@ -573,5 +602,5 @@ module CallbacksTest ], writer.history end end - + end diff --git a/activesupport/test/core_ext/bigdecimal.rb b/activesupport/test/core_ext/bigdecimal_test.rb index 9faad9146f..d592973d7a 100644 --- a/activesupport/test/core_ext/bigdecimal.rb +++ b/activesupport/test/core_ext/bigdecimal_test.rb @@ -1,10 +1,17 @@ require 'abstract_unit' +require 'bigdecimal' +require 'active_support/core_ext/big_decimal' class BigDecimalTest < Test::Unit::TestCase def test_to_yaml assert_equal("--- 100000.30020320320000000000000000000000000000001\n", BigDecimal.new('100000.30020320320000000000000000000000000000001').to_yaml) - assert_equal("--- .Inf\n", BigDecimal.new('Infinity').to_yaml) - assert_equal("--- .NaN\n", BigDecimal.new('NaN').to_yaml) + assert_equal("--- .Inf\n", BigDecimal.new('Infinity').to_yaml) + assert_equal("--- .NaN\n", BigDecimal.new('NaN').to_yaml) assert_equal("--- -.Inf\n", BigDecimal.new('-Infinity').to_yaml) end -end
\ No newline at end of file + + def test_to_d + bd = BigDecimal.new '10' + assert_equal bd, bd.to_d + end +end diff --git a/activesupport/test/core_ext/cgi_ext_test.rb b/activesupport/test/core_ext/cgi_ext_test.rb deleted file mode 100644 index c80362e382..0000000000 --- a/activesupport/test/core_ext/cgi_ext_test.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'abstract_unit' -require 'active_support/core_ext/cgi' - -class EscapeSkippingSlashesTest < Test::Unit::TestCase - def test_array - assert_equal 'hello/world', CGI.escape_skipping_slashes(%w(hello world)) - assert_equal 'hello+world/how/are/you', CGI.escape_skipping_slashes(['hello world', 'how', 'are', 'you']) - end - - def test_typical - assert_equal 'hi', CGI.escape_skipping_slashes('hi') - assert_equal 'hi/world', CGI.escape_skipping_slashes('hi/world') - assert_equal 'hi/world+you+funky+thing', CGI.escape_skipping_slashes('hi/world you funky thing') - end -end 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 b284e5ee1c..020dfce56a 100644 --- a/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb +++ b/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb @@ -3,9 +3,14 @@ require 'active_support/core_ext/class/inheritable_attributes' class ClassInheritableAttributesTest < Test::Unit::TestCase def setup + ActiveSupport::Deprecation.silenced = true @klass = Class.new end + def teardown + ActiveSupport::Deprecation.silenced = false + end + def test_reader_declaration assert_nothing_raised do @klass.class_inheritable_reader :a diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index bb453b8d7f..6a01eeed6b 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'active_support/time' +require 'active_support/json' class DurationTest < ActiveSupport::TestCase def test_is_a @@ -138,6 +139,10 @@ class DurationTest < ActiveSupport::TestCase assert_equal counter, 60 end + def test_to_json + assert_equal '172800', 2.days.to_json + end + protected def with_env_tz(new_tz = 'US/Eastern') old_tz, ENV['TZ'] = ENV['TZ'], new_tz diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index 545fed2684..a0479d45ac 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -4,8 +4,18 @@ require 'bigdecimal' require 'active_support/core_ext/string/access' require 'active_support/ordered_hash' require 'active_support/core_ext/object/conversions' +require 'active_support/inflections' class HashExtTest < Test::Unit::TestCase + class IndifferentHash < HashWithIndifferentAccess + end + + class SubclassingArray < Array + end + + class SubclassingHash < Hash + end + def setup @strings = { 'a' => 1, 'b' => 2 } @symbols = { :a => 1, :b => 2 } @@ -99,6 +109,11 @@ class HashExtTest < Test::Unit::TestCase assert_equal @strings, @mixed.with_indifferent_access.dup.stringify_keys! end + def test_hash_subclass + flash = { "foo" => SubclassingHash.new.tap { |h| h["bar"] = "baz" } }.with_indifferent_access + assert_kind_of SubclassingHash, flash["foo"] + end + def test_indifferent_assorted @strings = @strings.with_indifferent_access @symbols = @symbols.with_indifferent_access @@ -248,6 +263,20 @@ class HashExtTest < Test::Unit::TestCase hash = { "urls" => { "url" => [ { "address" => "1" }, { "address" => "2" } ] }}.with_indifferent_access assert_equal "1", hash[:urls][:url].first[:address] end + + def test_should_preserve_array_subclass_when_value_is_array + array = SubclassingArray.new + array << { "address" => "1" } + hash = { "urls" => { "url" => array }}.with_indifferent_access + assert_equal SubclassingArray, hash[:urls][:url].class + end + + def test_should_preserve_array_class_when_hash_value_is_frozen_array + array = SubclassingArray.new + array << { "address" => "1" } + hash = { "urls" => { "url" => array.freeze }}.with_indifferent_access + assert_equal SubclassingArray, hash[:urls][:url].class + end def test_stringify_and_symbolize_keys_on_indifferent_preserves_hash h = HashWithIndifferentAccess.new @@ -267,7 +296,6 @@ class HashExtTest < Test::Unit::TestCase assert_equal 1, h['first'] end - def test_indifferent_subhashes h = {'user' => {'id' => 5}}.with_indifferent_access ['user', :user].each {|user| [:id, 'id'].each {|id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5"}} @@ -276,6 +304,17 @@ class HashExtTest < Test::Unit::TestCase ['user', :user].each {|user| [:id, 'id'].each {|id| assert_equal 5, h[user][id], "h[#{user.inspect}][#{id.inspect}] should be 5"}} end + def test_indifferent_duplication + # Should preserve default value + h = HashWithIndifferentAccess.new + h.default = '1234' + assert_equal h.default, h.dup.default + + # Should preserve class for subclasses + h = IndifferentHash.new + assert_equal h.class, h.dup.class + end + def test_assert_valid_keys assert_nothing_raised do { :failure => "stuff", :funny => "business" }.assert_valid_keys([ :failure, :funny ]) @@ -316,6 +355,21 @@ class HashExtTest < Test::Unit::TestCase assert_equal expected, hash_1 end + def test_deep_dup + hash = { :a => { :b => 'b' } } + dup = hash.deep_dup + dup[:a][:c] = 'c' + assert_equal nil, hash[:a][:c] + assert_equal 'c', dup[:a][:c] + end + + def test_deep_dup_initialize + zero_hash = Hash.new 0 + hash = { :a => zero_hash } + dup = hash.deep_dup + assert_equal 0, dup[:a][44] + end + def test_store_on_indifferent_access hash = HashWithIndifferentAccess.new hash.store(:test1, 1) @@ -486,7 +540,7 @@ class HashExtToParamTests < Test::Unit::TestCase def test_to_param_hash_escapes_its_keys_and_values assert_equal 'param+1=A+string+with+%2F+characters+%26+that+should+be+%3F+escaped', { 'param 1' => 'A string with / characters & that should be ? escaped' }.to_param end - + def test_to_param_orders_by_key_in_ascending_order assert_equal 'a=2&b=1&c=0', ActiveSupport::OrderedHash[*%w(b 1 c 0 a 2)].to_param end @@ -525,6 +579,13 @@ class HashToXmlTest < Test::Unit::TestCase assert xml.include?(%(<Name>David</Name>)) end + def test_one_level_camelize_lower + xml = { :name => "David", :street_name => "Paulina" }.to_xml(@xml_options.merge(:camelize => :lower)) + assert_equal "<person>", xml.first(8) + assert xml.include?(%(<streetName>Paulina</streetName>)) + assert xml.include?(%(<name>David</name>)) + end + def test_one_level_with_types xml = { :name => "David", :street => "Paulina", :age => 26, :age_in_millis => 820497600000, :moved_on => Date.new(2005, 11, 15), :resident => :yes }.to_xml(@xml_options) assert_equal "<person>", xml.first(8) diff --git a/activesupport/test/core_ext/module/synchronization_test.rb b/activesupport/test/core_ext/module/synchronization_test.rb index eb850893f0..6c407e2260 100644 --- a/activesupport/test/core_ext/module/synchronization_test.rb +++ b/activesupport/test/core_ext/module/synchronization_test.rb @@ -1,3 +1,4 @@ +require 'thread' require 'abstract_unit' require 'active_support/core_ext/class/attribute_accessors' diff --git a/activesupport/test/core_ext/object/to_query_test.rb b/activesupport/test/core_ext/object/to_query_test.rb index e28b4cd493..84da52f4bf 100644 --- a/activesupport/test/core_ext/object/to_query_test.rb +++ b/activesupport/test/core_ext/object/to_query_test.rb @@ -18,22 +18,22 @@ class ToQueryTest < Test::Unit::TestCase end def test_nested_conversion - assert_query_equal 'person[login]=seckar&person[name]=Nicholas', + assert_query_equal 'person%5Blogin%5D=seckar&person%5Bname%5D=Nicholas', :person => ActiveSupport::OrderedHash[:login, 'seckar', :name, 'Nicholas'] end def test_multiple_nested - assert_query_equal 'account[person][id]=20&person[id]=10', + assert_query_equal 'account%5Bperson%5D%5Bid%5D=20&person%5Bid%5D=10', ActiveSupport::OrderedHash[:account, {:person => {:id => 20}}, :person, {:id => 10}] end def test_array_values - assert_query_equal 'person[id][]=10&person[id][]=20', + assert_query_equal 'person%5Bid%5D%5B%5D=10&person%5Bid%5D%5B%5D=20', :person => {:id => [10, 20]} end def test_array_values_are_not_sorted - assert_query_equal 'person[id][]=20&person[id][]=10', + assert_query_equal 'person%5Bid%5D%5B%5D=20&person%5Bid%5D%5B%5D=10', :person => {:id => [20, 10]} end diff --git a/activesupport/test/core_ext/object_and_class_ext_test.rb b/activesupport/test/core_ext/object_and_class_ext_test.rb index 3ccf18f473..5d68b198f2 100644 --- a/activesupport/test/core_ext/object_and_class_ext_test.rb +++ b/activesupport/test/core_ext/object_and_class_ext_test.rb @@ -28,18 +28,6 @@ module Nested end end -module Bar - def bar; end -end - -module Baz - def baz; end -end - -class Foo - include Bar -end - class ObjectTests < ActiveSupport::TestCase class DuckTime def acts_like_time? @@ -82,37 +70,6 @@ class ObjectInstanceVariableTest < Test::Unit::TestCase assert_equal %w(@bar @baz), @source.instance_variable_names.sort end - def test_copy_instance_variables_from_without_explicit_excludes - assert_equal [], @dest.instance_variables - @dest.copy_instance_variables_from(@source) - - assert_equal %w(@bar @baz), @dest.instance_variables.sort.map(&:to_s) - %w(@bar @baz).each do |name| - assert_equal @source.instance_variable_get(name).object_id, - @dest.instance_variable_get(name).object_id - end - end - - def test_copy_instance_variables_from_with_explicit_excludes - @dest.copy_instance_variables_from(@source, ['@baz']) - assert !@dest.instance_variable_defined?('@baz') - assert_equal 'bar', @dest.instance_variable_get('@bar') - end - - def test_copy_instance_variables_automatically_excludes_protected_instance_variables - @source.instance_variable_set(:@quux, 'quux') - class << @source - def protected_instance_variables - ['@bar', :@quux] - end - end - - @dest.copy_instance_variables_from(@source) - assert !@dest.instance_variable_defined?('@bar') - assert !@dest.instance_variable_defined?('@quux') - assert_equal 'baz', @dest.instance_variable_get('@baz') - end - def test_instance_values object = Object.new object.instance_variable_set :@a, 1 @@ -165,4 +122,14 @@ class ObjectTryTest < Test::Unit::TestCase def test_false_try assert_equal 'false', false.try(:to_s) end + + def test_try_only_block + assert_equal @string.reverse, @string.try { |s| s.reverse } + end + + def test_try_only_block_nil + ran = false + nil.try { ran = true } + assert_equal false, ran + end end diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index 8be65c99f2..bb865cae91 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -321,75 +321,6 @@ class CoreExtStringMultibyteTest < ActiveSupport::TestCase end end -=begin - string.rb - Interpolation for String. - - Copyright (C) 2005-2009 Masao Mutoh - - You may redistribute it and/or modify it under the same - license terms as Ruby. -=end -class TestGetTextString < Test::Unit::TestCase - def test_sprintf - assert_equal("foo is a number", "%{msg} is a number" % {:msg => "foo"}) - assert_equal("bar is a number", "%s is a number" % ["bar"]) - assert_equal("bar is a number", "%s is a number" % "bar") - assert_equal("1, test", "%{num}, %{record}" % {:num => 1, :record => "test"}) - assert_equal("test, 1", "%{record}, %{num}" % {:num => 1, :record => "test"}) - assert_equal("1, test", "%d, %s" % [1, "test"]) - assert_equal("test, 1", "%2$s, %1$d" % [1, "test"]) - assert_raise(ArgumentError) { "%-%" % [1] } - end - - def test_percent - assert_equal("% 1", "%% %<num>d" % {:num => 1.0}) - assert_equal("%{num} %<num>d 1", "%%{num} %%<num>d %<num>d" % {:num => 1}) - end - - def test_sprintf_percent_in_replacement - assert_equal("%<not_translated>s", "%{msg}" % { :msg => '%<not_translated>s', :not_translated => 'should not happen' }) - end - - def test_sprintf_lack_argument - assert_raises(KeyError) { "%{num}, %{record}" % {:record => "test"} } - assert_raises(KeyError) { "%{record}" % {:num => 1} } - end - - def test_no_placeholder - # Causes a "too many arguments for format string" warning - # on 1.8.7 and 1.9 but we still want to make sure the behavior works - silence_warnings do - assert_equal("aaa", "aaa" % {:num => 1}) - assert_equal("bbb", "bbb" % [1]) - end - end - - def test_sprintf_ruby19_style - assert_equal("1", "%<num>d" % {:num => 1}) - assert_equal("0b1", "%<num>#b" % {:num => 1}) - assert_equal("foo", "%<msg>s" % {:msg => "foo"}) - assert_equal("1.000000", "%<num>f" % {:num => 1.0}) - assert_equal(" 1", "%<num>3.0f" % {:num => 1.0}) - assert_equal("100.00", "%<num>2.2f" % {:num => 100.0}) - assert_equal("0x64", "%<num>#x" % {:num => 100.0}) - assert_raise(ArgumentError) { "%<num>,d" % {:num => 100} } - assert_raise(ArgumentError) { "%<num>/d" % {:num => 100} } - end - - def test_sprintf_old_style - assert_equal("foo 1.000000", "%s %f" % ["foo", 1.0]) - end - - def test_sprintf_mix_unformatted_and_formatted_named_placeholders - assert_equal("foo 1.000000", "%{name} %<num>f" % {:name => "foo", :num => 1.0}) - end - - def test_string_interpolation_raises_an_argument_error_when_mixing_named_and_unnamed_placeholders - assert_raises(ArgumentError) { "%{name} %f" % [1.0] } - assert_raises(ArgumentError) { "%{name} %f" % [1.0, 2.0] } - end -end - class OutputSafetyTest < ActiveSupport::TestCase def setup @string = "hello" diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index 0bb2c4a39e..2b86da67fa 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -36,6 +36,10 @@ class TimeWithZoneTest < Test::Unit::TestCase assert_equal @twz.object_id, @twz.in_time_zone(ActiveSupport::TimeZone['Eastern Time (US & Canada)']).object_id end + def test_localtime + assert_equal @twz.localtime, @twz.utc.getlocal + end + def test_utc? assert_equal false, @twz.utc? assert_equal true, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']).utc? @@ -102,7 +106,7 @@ class TimeWithZoneTest < Test::Unit::TestCase end def test_to_yaml - assert_equal "--- 1999-12-31 19:00:00 -05:00\n", @twz.to_yaml + assert_equal "--- 2000-01-01 00:00:00 Z\n", @twz.to_yaml end def test_ruby_to_yaml @@ -763,6 +767,13 @@ class TimeWithZoneMethodsForTimeAndDateTimeTest < Test::Unit::TestCase end end + def test_localtime + Time.zone_default = ActiveSupport::TimeZone['Eastern Time (US & Canada)'] + assert_equal @dt.in_time_zone.localtime, @dt.in_time_zone.utc.to_time.getlocal + ensure + Time.zone_default = nil + end + def test_use_zone Time.zone = 'Alaska' Time.use_zone 'Hawaii' do diff --git a/activesupport/test/inflector_test.rb b/activesupport/test/inflector_test.rb index 2990177bed..60714a152d 100644 --- a/activesupport/test/inflector_test.rb +++ b/activesupport/test/inflector_test.rb @@ -22,6 +22,34 @@ class InflectorTest < Test::Unit::TestCase assert_equal "", ActiveSupport::Inflector.pluralize("") end + ActiveSupport::Inflector.inflections.uncountable.each do |word| + define_method "test_uncountability_of_#{word}" do + assert_equal word, ActiveSupport::Inflector.singularize(word) + assert_equal word, ActiveSupport::Inflector.pluralize(word) + assert_equal ActiveSupport::Inflector.pluralize(word), ActiveSupport::Inflector.singularize(word) + end + end + + def test_uncountable_word_is_not_greedy + uncountable_word = "ors" + countable_word = "sponsor" + + cached_uncountables = ActiveSupport::Inflector.inflections.uncountables + + ActiveSupport::Inflector.inflections.uncountable << uncountable_word + + assert_equal uncountable_word, ActiveSupport::Inflector.singularize(uncountable_word) + assert_equal uncountable_word, ActiveSupport::Inflector.pluralize(uncountable_word) + assert_equal ActiveSupport::Inflector.pluralize(uncountable_word), ActiveSupport::Inflector.singularize(uncountable_word) + + assert_equal "sponsor", ActiveSupport::Inflector.singularize(countable_word) + assert_equal "sponsors", ActiveSupport::Inflector.pluralize(countable_word) + assert_equal "sponsor", ActiveSupport::Inflector.singularize(ActiveSupport::Inflector.pluralize(countable_word)) + + ensure + ActiveSupport::Inflector.inflections.instance_variable_set :@uncountables, cached_uncountables + end + SingularToPlural.each do |singular, plural| define_method "test_pluralize_#{singular}" do assert_equal(plural, ActiveSupport::Inflector.pluralize(singular)) diff --git a/activesupport/test/json/encoding_test.rb b/activesupport/test/json/encoding_test.rb index e0494de6e4..d5fcbf15b7 100644 --- a/activesupport/test/json/encoding_test.rb +++ b/activesupport/test/json/encoding_test.rb @@ -1,5 +1,6 @@ # encoding: utf-8 require 'abstract_unit' +require 'active_support/core_ext/string/inflections' require 'active_support/json' class TestJSONEncoding < Test::Unit::TestCase @@ -214,6 +215,36 @@ class TestJSONEncoding < Test::Unit::TestCase assert_equal(%([{"address":{"city":"London"}},{"address":{"city":"Paris"}}]), json) end + def test_struct_encoding + Struct.new('UserNameAndEmail', :name, :email) + Struct.new('UserNameAndDate', :name, :date) + Struct.new('Custom', :name, :sub) + user_email = Struct::UserNameAndEmail.new 'David', 'sample@example.com' + user_birthday = Struct::UserNameAndDate.new 'David', Date.new(2010, 01, 01) + custom = Struct::Custom.new 'David', user_birthday + + + json_strings = "" + json_string_and_date = "" + json_custom = "" + + assert_nothing_raised do + json_strings = user_email.to_json + json_string_and_date = user_birthday.to_json + json_custom = custom.to_json + end + + assert_equal({"name" => "David", + "sub" => { + "name" => "David", + "date" => "2010/01/01" }}, JSON.parse(json_custom)) + + assert_equal({"name" => "David", "email" => "sample@example.com"}, + JSON.parse(json_strings)) + + assert_equal({"name" => "David", "date" => "2010/01/01"}, + JSON.parse(json_string_and_date)) + end protected diff --git a/activesupport/test/ordered_hash_test.rb b/activesupport/test/ordered_hash_test.rb index 50778b5864..09203465c3 100644 --- a/activesupport/test/ordered_hash_test.rb +++ b/activesupport/test/ordered_hash_test.rb @@ -109,6 +109,14 @@ class OrderedHashTest < Test::Unit::TestCase assert_equal @keys, keys end + def test_find_all + assert_equal @keys, @ordered_hash.find_all { true }.map(&:first) + end + + def test_select + assert_equal @keys, @ordered_hash.select { true }.map(&:first) + end + def test_delete_if copy = @ordered_hash.dup copy.delete('pink') @@ -231,14 +239,14 @@ class OrderedHashTest < Test::Unit::TestCase def test_each_after_yaml_serialization values = [] - @deserialized_ordered_hash = YAML::load(YAML::dump(@ordered_hash)) + @deserialized_ordered_hash = YAML.load(YAML.dump(@ordered_hash)) @deserialized_ordered_hash.each {|key, value| values << value} assert_equal @values, values end def test_order_after_yaml_serialization - @deserialized_ordered_hash = YAML::load(YAML::dump(@ordered_hash)) + @deserialized_ordered_hash = YAML.load(YAML.dump(@ordered_hash)) assert_equal @keys, @deserialized_ordered_hash.keys assert_equal @values, @deserialized_ordered_hash.values @@ -247,12 +255,34 @@ class OrderedHashTest < Test::Unit::TestCase def test_order_after_yaml_serialization_with_nested_arrays @ordered_hash[:array] = %w(a b c) - @deserialized_ordered_hash = YAML::load(YAML::dump(@ordered_hash)) + @deserialized_ordered_hash = YAML.load(YAML.dump(@ordered_hash)) assert_equal @ordered_hash.keys, @deserialized_ordered_hash.keys assert_equal @ordered_hash.values, @deserialized_ordered_hash.values end + begin + require 'psych' + + def test_psych_serialize + @deserialized_ordered_hash = Psych.load(Psych.dump(@ordered_hash)) + + values = @deserialized_ordered_hash.map { |_, value| value } + assert_equal @values, values + end + + def test_psych_serialize_tag + yaml = Psych.dump(@ordered_hash) + assert_match '!omap', yaml + end + rescue LoadError + end + + def test_has_yaml_tag + @ordered_hash[:array] = %w(a b c) + assert_match '!omap', YAML.dump(@ordered_hash) + end + def test_update_sets_keys @updated_ordered_hash = ActiveSupport::OrderedHash.new @updated_ordered_hash.update(:name => "Bob") diff --git a/activesupport/test/test_xml_mini.rb b/activesupport/test/test_xml_mini.rb index 585eb15c6e..6dbcd1f40b 100644 --- a/activesupport/test/test_xml_mini.rb +++ b/activesupport/test/test_xml_mini.rb @@ -1,49 +1,100 @@ require 'abstract_unit' require 'active_support/xml_mini' +require 'active_support/builder' -class XmlMiniTest < Test::Unit::TestCase - def test_rename_key_dasherizes_by_default - assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key") - end +module XmlMiniTest + class RenameKeyTest < Test::Unit::TestCase + def test_rename_key_dasherizes_by_default + assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key") + end - def test_rename_key_does_nothing_with_dasherize_true - assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key", :dasherize => true) - end + def test_rename_key_does_nothing_with_dasherize_true + assert_equal "my-key", ActiveSupport::XmlMini.rename_key("my_key", :dasherize => true) + end - def test_rename_key_does_nothing_with_dasherize_false - assert_equal "my_key", ActiveSupport::XmlMini.rename_key("my_key", :dasherize => false) - end + def test_rename_key_does_nothing_with_dasherize_false + assert_equal "my_key", ActiveSupport::XmlMini.rename_key("my_key", :dasherize => false) + end - def test_rename_key_camelizes_with_camelize_true - assert_equal "MyKey", ActiveSupport::XmlMini.rename_key("my_key", :camelize => true) - end + def test_rename_key_camelizes_with_camelize_false + assert_equal "my_key", ActiveSupport::XmlMini.rename_key("my_key", :camelize => false) + end - def test_rename_key_camelizes_with_camelize_true - assert_equal "MyKey", ActiveSupport::XmlMini.rename_key("my_key", :camelize => true) - end + def test_rename_key_camelizes_with_camelize_nil + assert_equal "my_key", ActiveSupport::XmlMini.rename_key("my_key", :camelize => nil) + end - def test_rename_key_does_not_dasherize_leading_underscores - assert_equal "_id", ActiveSupport::XmlMini.rename_key("_id") - end + def test_rename_key_camelizes_with_camelize_true + assert_equal "MyKey", ActiveSupport::XmlMini.rename_key("my_key", :camelize => true) + end - def test_rename_key_with_leading_underscore_dasherizes_interior_underscores - assert_equal "_my-key", ActiveSupport::XmlMini.rename_key("_my_key") - end + def test_rename_key_lower_camelizes_with_camelize_lower + assert_equal "myKey", ActiveSupport::XmlMini.rename_key("my_key", :camelize => :lower) + end - def test_rename_key_does_not_dasherize_trailing_underscores - assert_equal "id_", ActiveSupport::XmlMini.rename_key("id_") - end + def test_rename_key_lower_camelizes_with_camelize_upper + assert_equal "MyKey", ActiveSupport::XmlMini.rename_key("my_key", :camelize => :upper) + end - def test_rename_key_with_trailing_underscore_dasherizes_interior_underscores - assert_equal "my-key_", ActiveSupport::XmlMini.rename_key("my_key_") - end + def test_rename_key_does_not_dasherize_leading_underscores + assert_equal "_id", ActiveSupport::XmlMini.rename_key("_id") + end - def test_rename_key_does_not_dasherize_multiple_leading_underscores - assert_equal "__id", ActiveSupport::XmlMini.rename_key("__id") - end + def test_rename_key_with_leading_underscore_dasherizes_interior_underscores + assert_equal "_my-key", ActiveSupport::XmlMini.rename_key("_my_key") + end + + def test_rename_key_does_not_dasherize_trailing_underscores + assert_equal "id_", ActiveSupport::XmlMini.rename_key("id_") + end - def test_rename_key_does_not_dasherize_multiple_leading_underscores - assert_equal "id__", ActiveSupport::XmlMini.rename_key("id__") + def test_rename_key_with_trailing_underscore_dasherizes_interior_underscores + assert_equal "my-key_", ActiveSupport::XmlMini.rename_key("my_key_") + end + + def test_rename_key_does_not_dasherize_multiple_leading_underscores + assert_equal "__id", ActiveSupport::XmlMini.rename_key("__id") + end + + def test_rename_key_does_not_dasherize_multiple_leading_underscores + assert_equal "id__", ActiveSupport::XmlMini.rename_key("id__") + end end + class ToTagTest < ActiveSupport::TestCase + def assert_xml(xml) + assert_equal xml, @options[:builder].target! + end + + setup do + @xml = ActiveSupport::XmlMini + @options = {:skip_instruct => true, :builder => Builder::XmlMarkup.new} + end + + test "#to_tag accepts a callable object and passes options with the builder" do + @xml.to_tag(:some_tag, lambda {|o| o[:builder].br }, @options) + assert_xml "<br/>" + end + + test "#to_tag accepts a callable object and passes options and tag name" do + @xml.to_tag(:tag, lambda {|o, t| o[:builder].b(t) }, @options) + assert_xml "<b>tag</b>" + end + + test "#to_tag accepts an object responding to #to_xml and passes the options, where :root is key" do + obj = Object.new + obj.instance_eval do + def to_xml(options) options[:builder].yo(options[:root].to_s) end + end + + @xml.to_tag(:tag, obj, @options) + assert_xml "<yo>tag</yo>" + end + + test "#to_tag accepts arbitrary objects responding to #to_str" do + @xml.to_tag(:b, "Howdy", @options) + assert_xml "<b>Howdy</b>" + end + # TODO: test the remaining functions hidden in #to_tag. + end end |