From 4e75cc59e70947b794c96894d39c015f9e1cb96c Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 28 Apr 2010 15:20:04 -0700 Subject: object_id may be negative, producing an invalid symbol. h/t Markus Schirp --- activesupport/lib/active_support/cache/strategy/local_cache.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activesupport/lib/active_support') diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index 8942587ac8..81002da0a8 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -140,7 +140,7 @@ module ActiveSupport private def thread_local_key - @thread_local_key ||= "#{self.class.name.underscore}_local_cache_#{self.object_id}".gsub("/", "_").to_sym + @thread_local_key ||= "#{self.class.name.underscore}_local_cache_#{object_id}".gsub(/[\/-]/, '_').to_sym end def local_cache -- cgit v1.2.3 From 1b816d502444ce2b3153d8c689d0057f1c257eee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 29 Apr 2010 08:39:44 +0200 Subject: The rake task :environment now loads config/environment.rb instead of initializing the application on its own. This fixes [#4492 state:resolved] and also avoids the application being initialized twice in some rake tasks. --- .../lib/active_support/cache/strategy/local_cache.rb | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'activesupport/lib/active_support') diff --git a/activesupport/lib/active_support/cache/strategy/local_cache.rb b/activesupport/lib/active_support/cache/strategy/local_cache.rb index 81002da0a8..efb5ad26ab 100644 --- a/activesupport/lib/active_support/cache/strategy/local_cache.rb +++ b/activesupport/lib/active_support/cache/strategy/local_cache.rb @@ -56,6 +56,13 @@ module ActiveSupport @middleware ||= begin klass = Class.new klass.class_eval(<<-EOS, __FILE__, __LINE__ + 1) + class << self + def name + "ActiveSupport::Cache::Strategy::LocalCache" + end + alias :to_s :name + end + def initialize(app) @app = app end @@ -67,11 +74,6 @@ module ActiveSupport Thread.current[:#{thread_local_key}] = nil end EOS - - def klass.to_s - "ActiveSupport::Cache::Strategy::LocalCache" - end - klass end end -- cgit v1.2.3 From 580dd3b052e471aa680b80c0e1d8ebe7bd7a3831 Mon Sep 17 00:00:00 2001 From: Neeraj Singh Date: Tue, 27 Apr 2010 16:45:26 -0400 Subject: array.to_xml should be able to handle all types of data elements [#4490 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- activesupport/lib/active_support/core_ext/array.rb | 1 + .../active_support/core_ext/array/conversions.rb | 31 +++++------- activesupport/lib/active_support/core_ext/hash.rb | 1 + .../active_support/core_ext/hash/conversions.rb | 55 ++++------------------ .../core_ext/hash/conversions_xml_value.rb | 51 ++++++++++++++++++++ 5 files changed, 74 insertions(+), 65 deletions(-) create mode 100644 activesupport/lib/active_support/core_ext/hash/conversions_xml_value.rb (limited to 'activesupport/lib/active_support') diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb index 4688468a8f..d20b725701 100644 --- a/activesupport/lib/active_support/core_ext/array.rb +++ b/activesupport/lib/active_support/core_ext/array.rb @@ -5,3 +5,4 @@ require 'active_support/core_ext/array/conversions' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/array/grouping' require 'active_support/core_ext/array/random_access' +require 'active_support/core_ext/hash/conversions_xml_value' diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 5d8e78e6e5..b9ef8c0ee1 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/hash/keys' +require 'active_support/core_ext/hash/conversions_xml_value' require 'active_support/core_ext/hash/reverse_merge' require 'active_support/inflector' @@ -51,6 +52,8 @@ class Array alias_method :to_default_s, :to_s alias_method :to_s, :to_formatted_s + include Hash::XmlValue + # Returns a string that represents this array in XML by sending +to_xml+ # to each element. Active Record collections delegate their representation # in XML to this method. @@ -127,34 +130,26 @@ class Array # # def to_xml(options = {}) - raise "Not all elements respond to to_xml" unless all? { |e| e.respond_to? :to_xml } require 'builder' unless defined?(Builder) options = options.dup - options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? ActiveSupport::Inflector.pluralize(ActiveSupport::Inflector.underscore(first.class.name)).tr('/', '_') : "records" - options[:children] ||= options[:root].singularize options[:indent] ||= 2 - options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) + options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]) }) - root = options.delete(:root).to_s - children = options.delete(:children) + options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? ActiveSupport::Inflector.pluralize(ActiveSupport::Inflector.underscore(first.class.name)).tr('/', '_') : "objects" - if !options.has_key?(:dasherize) || options[:dasherize] - root = root.dasherize - end options[:builder].instruct! unless options.delete(:skip_instruct) + root = rename_key(options[:root].to_s, options) - opts = options.merge({ :root => children }) + options[:children] ||= options[:root].singularize + attributes = options[:skip_types] ? {} : {:type => "array"} + return options[:builder].tag!(root, attributes) if empty? - xml = options[:builder] - if empty? - xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) - else - xml.tag!(root, options[:skip_types] ? {} : {:type => "array"}) { - yield xml if block_given? - each { |e| e.to_xml(opts.merge({ :skip_instruct => true })) } - } + options[:builder].__send__(:method_missing, root, attributes) do + each { |value| xml_value(options[:children], value, options) } + yield options[:builder] if block_given? end end + end diff --git a/activesupport/lib/active_support/core_ext/hash.rb b/activesupport/lib/active_support/core_ext/hash.rb index 501483498d..2e7bdce360 100644 --- a/activesupport/lib/active_support/core_ext/hash.rb +++ b/activesupport/lib/active_support/core_ext/hash.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/hash/conversions' +require 'active_support/core_ext/hash/conversions_xml_value' require 'active_support/core_ext/hash/deep_merge' require 'active_support/core_ext/hash/diff' require 'active_support/core_ext/hash/except' diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index c882434f78..1b2f69f573 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -3,6 +3,7 @@ require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/hash/reverse_merge' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/inflections' +require 'active_support/core_ext/hash/conversions_xml_value' class Hash # This module exists to decorate files deserialized using Hash.from_xml with @@ -19,6 +20,8 @@ class Hash end end + include XmlValue + XML_TYPE_NAMES = { "Symbol" => "symbol", "Fixnum" => "integer", @@ -29,7 +32,9 @@ class Hash "FalseClass" => "boolean", "Date" => "date", "DateTime" => "datetime", - "Time" => "datetime" + "Time" => "datetime", + "Array" => "array", + "Hash" => "hash" } unless defined?(XML_TYPE_NAMES) XML_FORMATTING = { @@ -135,57 +140,13 @@ class Hash :root => "hash" }) options[:builder].instruct! unless options.delete(:skip_instruct) root = rename_key(options[:root].to_s, options) - + # common upto this point options[:builder].__send__(:method_missing, root) do each do |key, value| - case value - when ::Hash - value.to_xml(options.merge({ :root => key, :skip_instruct => true })) - when ::Array - value.to_xml(options.merge({ :root => key, :children => key.to_s.singularize, :skip_instruct => true})) - when ::Method, ::Proc - # If the Method or Proc takes two arguments, then - # pass the suggested child element name. This is - # used if the Method or Proc will be operating over - # multiple records and needs to create an containing - # element that will contain the objects being - # serialized. - if 1 == value.arity - value.call(options.merge({ :root => key, :skip_instruct => true })) - else - value.call(options.merge({ :root => key, :skip_instruct => true }), key.to_s.singularize) - end - else - if value.respond_to?(:to_xml) - value.to_xml(options.merge({ :root => key, :skip_instruct => true })) - else - type_name = XML_TYPE_NAMES[value.class.name] - - key = rename_key(key.to_s, options) - - attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name } - if value.nil? - attributes[:nil] = true - end - - options[:builder].tag!(key, - XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value, - attributes - ) - end - end + xml_value(key, value, options) end - yield options[:builder] if block_given? end - - end - - def rename_key(key, options = {}) - camelize = options.has_key?(:camelize) && options[:camelize] - dasherize = !options.has_key?(:dasherize) || options[:dasherize] - key = key.camelize if camelize - dasherize ? key.dasherize : key end class << self diff --git a/activesupport/lib/active_support/core_ext/hash/conversions_xml_value.rb b/activesupport/lib/active_support/core_ext/hash/conversions_xml_value.rb new file mode 100644 index 0000000000..fac8f90122 --- /dev/null +++ b/activesupport/lib/active_support/core_ext/hash/conversions_xml_value.rb @@ -0,0 +1,51 @@ +class Hash + module XmlValue + def xml_value(key, value, options) + case value + when ::Hash + value.to_xml(options.merge({ :root => key, :skip_instruct => true })) + when ::Array + value.to_xml(options.merge({ :root => key, :children => key.to_s.singularize, :skip_instruct => true})) + when ::Method, ::Proc + # If the Method or Proc takes two arguments, then + # pass the suggested child element name. This is + # used if the Method or Proc will be operating over + # multiple records and needs to create an containing + # element that will contain the objects being + # serialized. + if 1 == value.arity + value.call(options.merge({ :root => key, :skip_instruct => true })) + else + value.call(options.merge({ :root => key, :skip_instruct => true }), key.to_s.singularize) + end + else + if value.respond_to?(:to_xml) + value.to_xml(options.merge({ :root => key, :skip_instruct => true })) + else + type_name = XML_TYPE_NAMES[value.class.name] + + key = rename_key(key.to_s, options) + + attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name } + if value.nil? + attributes[:nil] = true + end + + options[:builder].tag!(key, + XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value, + attributes + ) + end + end + #yield options[:builder] if block_given? + end + + def rename_key(key, options = {}) + camelize = options.has_key?(:camelize) && options[:camelize] + dasherize = !options.has_key?(:dasherize) || options[:dasherize] + key = key.camelize if camelize + dasherize ? key.dasherize : key + end + end +end + -- cgit v1.2.3 From 2e9af3638d950ef840e1287f99e323887ec6a4c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 29 Apr 2010 12:27:25 +0200 Subject: Move several configuration values from Hash to ActiveSupport::XmlMini, which both Hash and Array depends on. Also, refactored ActiveModel serializers to just use ActiveSupport::XmlMini.to_tag. As consequence, if a serialized attribute is an array or a hash, it's not encoded as yaml, but as a hash or array. --- activesupport/lib/active_support/core_ext/array.rb | 1 - .../active_support/core_ext/array/conversions.rb | 33 +++--- activesupport/lib/active_support/core_ext/hash.rb | 1 - .../active_support/core_ext/hash/conversions.rb | 116 +++---------------- .../core_ext/hash/conversions_xml_value.rb | 51 --------- activesupport/lib/active_support/xml_mini.rb | 127 ++++++++++++++++++++- 6 files changed, 160 insertions(+), 169 deletions(-) delete mode 100644 activesupport/lib/active_support/core_ext/hash/conversions_xml_value.rb (limited to 'activesupport/lib/active_support') diff --git a/activesupport/lib/active_support/core_ext/array.rb b/activesupport/lib/active_support/core_ext/array.rb index d20b725701..4688468a8f 100644 --- a/activesupport/lib/active_support/core_ext/array.rb +++ b/activesupport/lib/active_support/core_ext/array.rb @@ -5,4 +5,3 @@ require 'active_support/core_ext/array/conversions' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/array/grouping' require 'active_support/core_ext/array/random_access' -require 'active_support/core_ext/hash/conversions_xml_value' diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index b9ef8c0ee1..2b07f05d27 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -1,7 +1,7 @@ +require 'active_support/xml_mini' require 'active_support/core_ext/hash/keys' -require 'active_support/core_ext/hash/conversions_xml_value' require 'active_support/core_ext/hash/reverse_merge' -require 'active_support/inflector' +require 'active_support/core_ext/string/inflections' class Array # Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options: @@ -52,8 +52,6 @@ class Array alias_method :to_default_s, :to_s alias_method :to_s, :to_formatted_s - include Hash::XmlValue - # Returns a string that represents this array in XML by sending +to_xml+ # to each element. Active Record collections delegate their representation # in XML to this method. @@ -133,22 +131,27 @@ class Array require 'builder' unless defined?(Builder) options = options.dup - options[:indent] ||= 2 - options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]) }) - - options[:root] ||= all? { |e| e.is_a?(first.class) && first.class.to_s != "Hash" } ? ActiveSupport::Inflector.pluralize(ActiveSupport::Inflector.underscore(first.class.name)).tr('/', '_') : "objects" + options[:indent] ||= 2 + options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) + options[:root] ||= if first.class.to_s != "Hash" && all? { |e| e.is_a?(first.class) } + underscored = ActiveSupport::Inflector.underscore(first.class.name) + ActiveSupport::Inflector.pluralize(underscored).tr('/', '_') + else + "objects" + end + builder = options[:builder] + builder.instruct! unless options.delete(:skip_instruct) - options[:builder].instruct! unless options.delete(:skip_instruct) - root = rename_key(options[:root].to_s, options) + root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options) + children = options.delete(:children) || root.singularize - options[:children] ||= options[:root].singularize attributes = options[:skip_types] ? {} : {:type => "array"} - return options[:builder].tag!(root, attributes) if empty? + return builder.tag!(root, attributes) if empty? - options[:builder].__send__(:method_missing, root, attributes) do - each { |value| xml_value(options[:children], value, options) } - yield options[:builder] if block_given? + builder.__send__(:method_missing, root, attributes) do + each { |value| ActiveSupport::XmlMini.to_tag(children, value, options) } + yield builder if block_given? end end diff --git a/activesupport/lib/active_support/core_ext/hash.rb b/activesupport/lib/active_support/core_ext/hash.rb index 2e7bdce360..501483498d 100644 --- a/activesupport/lib/active_support/core_ext/hash.rb +++ b/activesupport/lib/active_support/core_ext/hash.rb @@ -1,5 +1,4 @@ require 'active_support/core_ext/hash/conversions' -require 'active_support/core_ext/hash/conversions_xml_value' require 'active_support/core_ext/hash/deep_merge' require 'active_support/core_ext/hash/diff' require 'active_support/core_ext/hash/except' diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 1b2f69f573..14e5d2f8ac 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -1,88 +1,11 @@ +require 'active_support/xml_mini' require 'active_support/time' require 'active_support/core_ext/array/wrap' require 'active_support/core_ext/hash/reverse_merge' require 'active_support/core_ext/object/blank' require 'active_support/core_ext/string/inflections' -require 'active_support/core_ext/hash/conversions_xml_value' class Hash - # This module exists to decorate files deserialized using Hash.from_xml with - # the original_filename and content_type methods. - module FileLike #:nodoc: - attr_writer :original_filename, :content_type - - def original_filename - @original_filename || 'untitled' - end - - def content_type - @content_type || 'application/octet-stream' - end - end - - include XmlValue - - XML_TYPE_NAMES = { - "Symbol" => "symbol", - "Fixnum" => "integer", - "Bignum" => "integer", - "BigDecimal" => "decimal", - "Float" => "float", - "TrueClass" => "boolean", - "FalseClass" => "boolean", - "Date" => "date", - "DateTime" => "datetime", - "Time" => "datetime", - "Array" => "array", - "Hash" => "hash" - } unless defined?(XML_TYPE_NAMES) - - XML_FORMATTING = { - "symbol" => Proc.new { |symbol| symbol.to_s }, - "date" => Proc.new { |date| date.to_s(:db) }, - "datetime" => Proc.new { |time| time.xmlschema }, - "binary" => Proc.new { |binary| ActiveSupport::Base64.encode64(binary) }, - "yaml" => Proc.new { |yaml| yaml.to_yaml } - } unless defined?(XML_FORMATTING) - - # TODO: use Time.xmlschema instead of Time.parse; - # use regexp instead of Date.parse - unless defined?(XML_PARSING) - XML_PARSING = { - "symbol" => Proc.new { |symbol| symbol.to_sym }, - "date" => Proc.new { |date| ::Date.parse(date) }, - "datetime" => Proc.new { |time| ::Time.parse(time).utc rescue ::DateTime.parse(time).utc }, - "integer" => Proc.new { |integer| integer.to_i }, - "float" => Proc.new { |float| float.to_f }, - "decimal" => Proc.new { |number| BigDecimal(number) }, - "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) }, - "string" => Proc.new { |string| string.to_s }, - "yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml }, - "base64Binary" => Proc.new { |bin| ActiveSupport::Base64.decode64(bin) }, - "binary" => Proc.new do |bin, entity| - case entity['encoding'] - when 'base64' - ActiveSupport::Base64.decode64(bin) - # TODO: Add support for other encodings - else - bin - end - end, - "file" => Proc.new do |file, entity| - f = StringIO.new(ActiveSupport::Base64.decode64(file)) - f.extend(FileLike) - f.original_filename = entity['name'] - f.content_type = entity['content_type'] - f - end - } - - XML_PARSING.update( - "double" => XML_PARSING["float"], - "dateTime" => XML_PARSING["datetime"] - ) - end - # Returns a string containing an XML representation of its receiver: # # {"foo" => 1, "bar" => 2}.to_xml @@ -135,17 +58,18 @@ class Hash require 'builder' unless defined?(Builder) options = options.dup - options[:indent] ||= 2 - options.reverse_merge!({ :builder => Builder::XmlMarkup.new(:indent => options[:indent]), - :root => "hash" }) - options[:builder].instruct! unless options.delete(:skip_instruct) - root = rename_key(options[:root].to_s, options) - # common upto this point - options[:builder].__send__(:method_missing, root) do - each do |key, value| - xml_value(key, value, options) - end - yield options[:builder] if block_given? + options[:indent] ||= 2 + options[:root] ||= "hash" + options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent]) + + builder = options[:builder] + builder.instruct! unless options.delete(:skip_instruct) + + root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options) + + builder.__send__(:method_missing, root) do + each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) } + yield builder if block_given? end end @@ -174,12 +98,8 @@ class Hash end elsif value.has_key?("__content__") content = value["__content__"] - if parser = XML_PARSING[value["type"]] - if parser.arity == 2 - XML_PARSING[value["type"]].call(content, value) - else - XML_PARSING[value["type"]].call(content) - end + if parser = ActiveSupport::XmlMini::PARSING[value["type"]] + parser.arity == 1 ? parser.call(content) : parser.call(content, value) else content end @@ -205,11 +125,7 @@ class Hash end when 'Array' value.map! { |i| typecast_xml_value(i) } - case value.length - when 0 then nil - when 1 then value.first - else value - end + value.length > 1 ? value : value.first when 'String' value else diff --git a/activesupport/lib/active_support/core_ext/hash/conversions_xml_value.rb b/activesupport/lib/active_support/core_ext/hash/conversions_xml_value.rb deleted file mode 100644 index fac8f90122..0000000000 --- a/activesupport/lib/active_support/core_ext/hash/conversions_xml_value.rb +++ /dev/null @@ -1,51 +0,0 @@ -class Hash - module XmlValue - def xml_value(key, value, options) - case value - when ::Hash - value.to_xml(options.merge({ :root => key, :skip_instruct => true })) - when ::Array - value.to_xml(options.merge({ :root => key, :children => key.to_s.singularize, :skip_instruct => true})) - when ::Method, ::Proc - # If the Method or Proc takes two arguments, then - # pass the suggested child element name. This is - # used if the Method or Proc will be operating over - # multiple records and needs to create an containing - # element that will contain the objects being - # serialized. - if 1 == value.arity - value.call(options.merge({ :root => key, :skip_instruct => true })) - else - value.call(options.merge({ :root => key, :skip_instruct => true }), key.to_s.singularize) - end - else - if value.respond_to?(:to_xml) - value.to_xml(options.merge({ :root => key, :skip_instruct => true })) - else - type_name = XML_TYPE_NAMES[value.class.name] - - key = rename_key(key.to_s, options) - - attributes = options[:skip_types] || value.nil? || type_name.nil? ? { } : { :type => type_name } - if value.nil? - attributes[:nil] = true - end - - options[:builder].tag!(key, - XML_FORMATTING[type_name] ? XML_FORMATTING[type_name].call(value) : value, - attributes - ) - end - end - #yield options[:builder] if block_given? - end - - def rename_key(key, options = {}) - camelize = options.has_key?(:camelize) && options[:camelize] - dasherize = !options.has_key?(:dasherize) || options[:dasherize] - key = key.camelize if camelize - dasherize ? key.dasherize : key - end - end -end - diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb index f22fbcc0e1..7594d7b68b 100644 --- a/activesupport/lib/active_support/xml_mini.rb +++ b/activesupport/lib/active_support/xml_mini.rb @@ -9,6 +9,71 @@ module ActiveSupport module XmlMini extend self + # This module exists to decorate files deserialized using Hash.from_xml with + # the original_filename and content_type methods. + module FileLike #:nodoc: + attr_writer :original_filename, :content_type + + def original_filename + @original_filename || 'untitled' + end + + def content_type + @content_type || 'application/octet-stream' + end + end + + DEFAULT_ENCODINGS = { + "binary" => "base64" + } unless defined?(TYPE_NAMES) + + TYPE_NAMES = { + "Symbol" => "symbol", + "Fixnum" => "integer", + "Bignum" => "integer", + "BigDecimal" => "decimal", + "Float" => "float", + "TrueClass" => "boolean", + "FalseClass" => "boolean", + "Date" => "date", + "DateTime" => "datetime", + "Time" => "datetime", + "Array" => "array", + "Hash" => "hash" + } unless defined?(TYPE_NAMES) + + FORMATTING = { + "symbol" => Proc.new { |symbol| symbol.to_s }, + "date" => Proc.new { |date| date.to_s(:db) }, + "datetime" => Proc.new { |time| time.xmlschema }, + "binary" => Proc.new { |binary| ActiveSupport::Base64.encode64(binary) }, + "yaml" => Proc.new { |yaml| yaml.to_yaml } + } unless defined?(FORMATTING) + + # TODO: use Time.xmlschema instead of Time.parse; + # use regexp instead of Date.parse + unless defined?(PARSING) + PARSING = { + "symbol" => Proc.new { |symbol| symbol.to_sym }, + "date" => Proc.new { |date| ::Date.parse(date) }, + "datetime" => Proc.new { |time| ::Time.parse(time).utc rescue ::DateTime.parse(time).utc }, + "integer" => Proc.new { |integer| integer.to_i }, + "float" => Proc.new { |float| float.to_f }, + "decimal" => Proc.new { |number| BigDecimal(number) }, + "boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) }, + "string" => Proc.new { |string| string.to_s }, + "yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml }, + "base64Binary" => Proc.new { |bin| ActiveSupport::Base64.decode64(bin) }, + "binary" => Proc.new { |bin, entity| _parse_binary(bin, entity) }, + "file" => Proc.new { |file, entity| _parse_file(file, entity) } + } + + PARSING.update( + "double" => PARSING["float"], + "dateTime" => PARSING["datetime"] + ) + end + attr_reader :backend delegate :parse, :to => :backend @@ -16,7 +81,7 @@ module ActiveSupport if name.is_a?(Module) @backend = name else - require "active_support/xml_mini/#{name.to_s.downcase}.rb" + require "active_support/xml_mini/#{name.to_s.downcase}" @backend = ActiveSupport.const_get("XmlMini_#{name}") end end @@ -27,6 +92,66 @@ module ActiveSupport ensure self.backend = old_backend end + + def to_tag(key, value, options) + type_name = options.delete(:type) + merged_options = options.merge(:root => key, :skip_instruct => true) + + if value.is_a?(::Method) || value.is_a?(::Proc) + if value.arity == 1 + value.call(merged_options) + else + value.call(merged_options, key.to_s.singularize) + end + elsif value.respond_to?(:to_xml) + value.to_xml(merged_options) + else + type_name ||= TYPE_NAMES[value.class.name] + type_name ||= value.class.name if value && !value.respond_to?(:to_str) + type_name = type_name.to_s if type_name + + key = rename_key(key.to_s, options) + + attributes = options[:skip_types] || type_name.nil? ? { } : { :type => type_name } + attributes[:nil] = true if value.nil? + + encoding = options[:encoding] || DEFAULT_ENCODINGS[type_name] + attributes[:encoding] = encoding if encoding + + formatted_value = FORMATTING[type_name] && !value.nil? ? + FORMATTING[type_name].call(value) : value + + options[:builder].tag!(key, formatted_value, attributes) + end + end + + def rename_key(key, options = {}) + camelize = options.has_key?(:camelize) && options[:camelize] + dasherize = !options.has_key?(:dasherize) || options[:dasherize] + key = key.camelize if camelize + key = key.dasherize if dasherize + key + end + + protected + + # TODO: Add support for other encodings + def _parse_binary(bin, entity) #:nodoc: + case entity['encoding'] + when 'base64' + ActiveSupport::Base64.decode64(bin) + else + bin + end + end + + def _parse_file(file, entity) + f = StringIO.new(ActiveSupport::Base64.decode64(file)) + f.extend(FileLike) + f.original_filename = entity['name'] + f.content_type = entity['content_type'] + f + end end XmlMini.backend = 'REXML' -- cgit v1.2.3 From f0e754e7136f92398495590908162b2501de5869 Mon Sep 17 00:00:00 2001 From: Norman Clarke Date: Wed, 14 Apr 2010 11:12:07 -0300 Subject: Delegate Inflector.transliterate to i18n. [#4508 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ancillary changes: Moved Chars#normalize into a class method; removed unused UTF_PAT constant. Signed-off-by: José Valim --- .../lib/active_support/inflector/transliterate.rb | 94 +++++++++++++--------- .../lib/active_support/multibyte/chars.rb | 45 ++++++----- 2 files changed, 83 insertions(+), 56 deletions(-) (limited to 'activesupport/lib/active_support') diff --git a/activesupport/lib/active_support/inflector/transliterate.rb b/activesupport/lib/active_support/inflector/transliterate.rb index 9c99dcfb01..5ec87372d0 100644 --- a/activesupport/lib/active_support/inflector/transliterate.rb +++ b/activesupport/lib/active_support/inflector/transliterate.rb @@ -3,45 +3,62 @@ require 'active_support/core_ext/string/multibyte' module ActiveSupport module Inflector - extend self - # UTF-8 byte => ASCII approximate UTF-8 byte(s) - ASCII_APPROXIMATIONS = { - 198 => [65, 69], # Æ => AE - 208 => 68, # Ð => D - 216 => 79, # Ø => O - 222 => [84, 104], # Þ => Þ - 223 => [115, 115], # ß => ss - 230 => [97, 101], # æ => ae - 240 => 100, # ð => d - 248 => 111, # ø => o - 254 => [116, 104], # þ => th - 272 => 68, # Đ => D - 273 => 100, # đ => đ - 294 => 72, # Ħ => H - 295 => 104, # ħ => h - 305 => 105, # ı => i - 306 => [73, 74], # IJ =>IJ - 307 => [105, 106], # ij => ij - 312 => 107, # ĸ => k - 319 => 76, # Ŀ => L - 320 => 108, # ŀ => l - 321 => 76, # Ł => L - 322 => 108, # ł => l - 329 => 110, # ʼn => n - 330 => [78, 71], # Ŋ => NG - 331 => [110, 103], # ŋ => ng - 338 => [79, 69], # Œ => OE - 339 => [111, 101], # œ => oe - 358 => 84, # Ŧ => T - 359 => 116 # ŧ => t - } - - # Replaces accented characters with an ASCII approximation, or deletes it if none exsits. - def transliterate(string) - ActiveSupport::Multibyte::Chars.new(string).tidy_bytes.normalize(:d).unpack("U*").map do |char| - ASCII_APPROXIMATIONS[char] || (char if char < 128) - end.compact.flatten.pack("U*") + # Replaces non-ASCII characters with an ASCII approximation, or if none + # exists, a replacement character which defaults to "?". + # + # transliterate("Ærøskøbing") + # # => "AEroskobing" + # + # Default approximations are provided for Western/Latin characters, + # e.g, "ø", "ñ", "é", "ß", etc. + # + # This method is I18n aware, so you can set up custom approximations for a + # locale. This can be useful, for example, to transliterate German's "ü" + # and "ö" to "ue" and "oe", or to add support for transliterating Russian + # to ASCII. + # + # In order to make your custom transliterations available, you must set + # them as the i18n.transliterate.rule i18n key: + # + # # Store the transliterations in locales/de.yml + # i18n: + # transliterate: + # ü: "ue" + # ö: "oe" + # + # # Or set them using Ruby + # I18n.backend.store_translations(:de, :i18n => { + # :transliterate => { + # :rule => { + # "ü" => "ue", + # "ö" => "oe" + # } + # } + # }) + # + # The value for i18n.transliterate.rule can be a simple Hash that maps + # characters to ASCII approximations as shown above, or, for more complex + # requirements, a Proc: + # + # I18n.backend.store_translations(:de, :i18n => { + # :transliterate => { + # :rule => lambda {|string| MyTransliterator.transliterate(string)} + # } + # }) + # + # Now you can have different transliterations for each locale: + # + # I18n.locale = :en + # transliterate("Jürgen") + # # => "Jurgen" + # + # I18n.locale = :de + # transliterate("Jürgen") + # # => "Juergen" + def transliterate(string, replacement = "?") + I18n.transliterate(Multibyte::Chars.normalize( + Multibyte::Chars.tidy_bytes(string), :c), :replacement => replacement) end # Replaces special characters in a string so that it may be used as part of a 'pretty' URL. @@ -73,5 +90,6 @@ module ActiveSupport end parameterized_string.downcase end + end end diff --git a/activesupport/lib/active_support/multibyte/chars.rb b/activesupport/lib/active_support/multibyte/chars.rb index 4ade1158fd..cca30d1141 100644 --- a/activesupport/lib/active_support/multibyte/chars.rb +++ b/activesupport/lib/active_support/multibyte/chars.rb @@ -75,8 +75,6 @@ module ActiveSupport #:nodoc: UNICODE_TRAILERS_PAT = /(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+\Z/u UNICODE_LEADERS_PAT = /\A(#{codepoints_to_pattern(UNICODE_LEADERS_AND_TRAILERS)})+/u - UTF8_PAT = ActiveSupport::Multibyte::VALID_CHARACTER['UTF-8'] - attr_reader :wrapped_string alias to_s wrapped_string alias to_str wrapped_string @@ -409,25 +407,11 @@ module ActiveSupport #:nodoc: # Returns the KC normalization of the string by default. NFKC is considered the best normalization form for # passing strings to databases and validations. # - # * str - The string to perform normalization on. # * form - The form you want to normalize in. Should be one of the following: # :c, :kc, :d, or :kd. Default is # ActiveSupport::Multibyte.default_normalization_form def normalize(form=ActiveSupport::Multibyte.default_normalization_form) - # See http://www.unicode.org/reports/tr15, Table 1 - codepoints = self.class.u_unpack(@wrapped_string) - chars(case form - when :d - self.class.reorder_characters(self.class.decompose_codepoints(:canonical, codepoints)) - when :c - self.class.compose_codepoints(self.class.reorder_characters(self.class.decompose_codepoints(:canonical, codepoints))) - when :kd - self.class.reorder_characters(self.class.decompose_codepoints(:compatability, codepoints)) - when :kc - self.class.compose_codepoints(self.class.reorder_characters(self.class.decompose_codepoints(:compatability, codepoints))) - else - raise ArgumentError, "#{form} is not a valid normalization variant", caller - end.pack('U*')) + chars(self.class.normalize(@wrapped_string, form)) end # Performs canonical decomposition on all the characters. @@ -659,7 +643,7 @@ module ActiveSupport #:nodoc: # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent resulting in a valid UTF-8 string. # - # Passing +true+ will forcibly tidy all bytes, assuming that the string's encoding is entirely CP-1252 or ISO-8859-1. + # Passing +true+ will forcibly tidy all bytes, assuming that the string's encoding is entirely CP1252 or ISO-8859-1. def tidy_bytes(string, force = false) if force return string.unpack("C*").map do |b| @@ -708,6 +692,31 @@ module ActiveSupport #:nodoc: end bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*") end + + # Returns the KC normalization of the string by default. NFKC is considered the best normalization form for + # passing strings to databases and validations. + # + # * string - The string to perform normalization on. + # * form - The form you want to normalize in. Should be one of the following: + # :c, :kc, :d, or :kd. Default is + # ActiveSupport::Multibyte.default_normalization_form + def normalize(string, form=ActiveSupport::Multibyte.default_normalization_form) + # See http://www.unicode.org/reports/tr15, Table 1 + codepoints = u_unpack(string) + case form + when :d + reorder_characters(decompose_codepoints(:canonical, codepoints)) + when :c + compose_codepoints(reorder_characters(decompose_codepoints(:canonical, codepoints))) + when :kd + reorder_characters(decompose_codepoints(:compatability, codepoints)) + when :kc + compose_codepoints(reorder_characters(decompose_codepoints(:compatability, codepoints))) + else + raise ArgumentError, "#{form} is not a valid normalization variant", caller + end.pack('U*') + end + end protected -- cgit v1.2.3