From 36dc94a6a1e742848c5a80975b8bf5d216f54022 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 16 Jun 2006 10:07:13 +0000 Subject: Added Hash.create_from_xml(string) which will create a hash from a XML string and even typecast if possible [DHH] git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4453 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- .../lib/action_controller/cgi_ext/cgi_methods.rb | 208 ++++++++------------- 1 file changed, 74 insertions(+), 134 deletions(-) (limited to 'actionpack/lib/action_controller/cgi_ext/cgi_methods.rb') diff --git a/actionpack/lib/action_controller/cgi_ext/cgi_methods.rb b/actionpack/lib/action_controller/cgi_ext/cgi_methods.rb index be5923e8de..1af837c39a 100755 --- a/actionpack/lib/action_controller/cgi_ext/cgi_methods.rb +++ b/actionpack/lib/action_controller/cgi_ext/cgi_methods.rb @@ -1,14 +1,13 @@ require 'cgi' -require 'action_controller/vendor/xml_simple' require 'action_controller/vendor/xml_node' # Static methods for parsing the query and request parameters that can be used in # a CGI extension class or testing in isolation. class CGIMethods #:nodoc: - public + class << self # Returns a hash with the pairs from the query string. The implicit hash construction that is done in # parse_request_params is not done here. - def CGIMethods.parse_query_parameters(query_string) + def parse_query_parameters(query_string) parsed_params = {} query_string.split(/[&;]/).each { |p| @@ -41,7 +40,7 @@ class CGIMethods #:nodoc: # Returns the request (POST/GET) parameters in a parsed form where pairs such as "customer[address][street]" / # "Somewhere cool!" are translated into a full hash hierarchy, like # { "customer" => { "address" => { "street" => "Somewhere cool!" } } } - def CGIMethods.parse_request_parameters(params) + def parse_request_parameters(params) parsed_params = {} for key, value in params @@ -59,162 +58,103 @@ class CGIMethods #:nodoc: parsed_params end - def self.parse_formatted_request_parameters(mime_type, raw_post_data) - params = case strategy = ActionController::Base.param_parsers[mime_type] + def parse_formatted_request_parameters(mime_type, raw_post_data) + case strategy = ActionController::Base.param_parsers[mime_type] when Proc strategy.call(raw_post_data) when :xml_simple - raw_post_data.blank? ? nil : - typecast_xml_value(XmlSimple.xml_in(raw_post_data, - 'forcearray' => false, - 'forcecontent' => true, - 'keeproot' => true, - 'contentkey' => '__content__')) + raw_post_data.blank? ? {} : Hash.create_from_xml(raw_post_data) when :yaml YAML.load(raw_post_data) when :xml_node node = XmlNode.from_xml(raw_post_data) { node.node_name => node } end - - dasherize_keys(params || {}) rescue Object => e { "exception" => "#{e.message} (#{e.class})", "backtrace" => e.backtrace, "raw_post_data" => raw_post_data, "format" => mime_type } end - def self.typecast_xml_value(value) - case value - when Hash - if value.has_key?("__content__") - content = translate_xml_entities(value["__content__"]) - case value["type"] - when "integer" then content.to_i - when "boolean" then content == "true" - when "datetime" then Time.parse(content) - when "date" then Date.parse(content) - else content - end - else - value.empty? ? nil : value.inject({}) do |h,(k,v)| - h[k] = typecast_xml_value(v) - h - end - 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 - else - raise "can't typecast #{value.inspect}" - end - end - - private - - def self.translate_xml_entities(value) - value.gsub(/</, "<"). - gsub(/>/, ">"). - gsub(/"/, '"'). - gsub(/'/, "'"). - gsub(/&/, "&") - end - - def self.dasherize_keys(params) - case params.class.to_s - when "Hash" - params.inject({}) do |h,(k,v)| - h[k.to_s.tr("-", "_")] = dasherize_keys(v) - h - end - when "Array" - params.map { |v| dasherize_keys(v) } - else - params - end - end - - # Splits the given key into several pieces. Example keys are 'name', 'person[name]', - # 'person[name][first]', and 'people[]'. In each instance, an Array instance is returned. - # 'person[name][first]' produces ['person', 'name', 'first']; 'people[]' produces ['people', ''] - def CGIMethods.split_key(key) - if /^([^\[]+)((?:\[[^\]]*\])+)$/ =~ key - keys = [$1] + private + # Splits the given key into several pieces. Example keys are 'name', 'person[name]', + # 'person[name][first]', and 'people[]'. In each instance, an Array instance is returned. + # 'person[name][first]' produces ['person', 'name', 'first']; 'people[]' produces ['people', ''] + def split_key(key) + if /^([^\[]+)((?:\[[^\]]*\])+)$/ =~ key + keys = [$1] - keys.concat($2[1..-2].split('][')) - keys << '' if key[-2..-1] == '[]' # Have to add it since split will drop empty strings + keys.concat($2[1..-2].split('][')) + keys << '' if key[-2..-1] == '[]' # Have to add it since split will drop empty strings - keys - else - [key] + keys + else + [key] + end end - end - def CGIMethods.get_typed_value(value) - # test most frequent case first - if value.is_a?(String) - value - elsif value.respond_to?(:content_type) && ! value.content_type.blank? - # Uploaded file - unless value.respond_to?(:full_original_filename) - class << value - alias_method :full_original_filename, :original_filename - - # Take the basename of the upload's original filename. - # This handles the full Windows paths given by Internet Explorer - # (and perhaps other broken user agents) without affecting - # those which give the lone filename. - # The Windows regexp is adapted from Perl's File::Basename. - def original_filename - if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename) - md.captures.first - else - File.basename full_original_filename + def get_typed_value(value) + # test most frequent case first + if value.is_a?(String) + value + elsif value.respond_to?(:content_type) && ! value.content_type.blank? + # Uploaded file + unless value.respond_to?(:full_original_filename) + class << value + alias_method :full_original_filename, :original_filename + + # Take the basename of the upload's original filename. + # This handles the full Windows paths given by Internet Explorer + # (and perhaps other broken user agents) without affecting + # those which give the lone filename. + # The Windows regexp is adapted from Perl's File::Basename. + def original_filename + if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename) + md.captures.first + else + File.basename full_original_filename + end end end end - end - # Return the same value after overriding original_filename. - value + # Return the same value after overriding original_filename. + value - elsif value.respond_to?(:read) - # Value as part of a multipart request - result = value.read - value.rewind - result - elsif value.class == Array - value.collect { |v| CGIMethods.get_typed_value(v) } - else - # other value (neither string nor a multipart request) - value.to_s + elsif value.respond_to?(:read) + # Value as part of a multipart request + result = value.read + value.rewind + result + elsif value.class == Array + value.collect { |v| get_typed_value(v) } + else + # other value (neither string nor a multipart request) + value.to_s + end end - end - PARAMS_HASH_RE = /^([^\[]+)(\[.*\])?(.)?.*$/ - def CGIMethods.get_levels(key) - all, main, bracketed, trailing = PARAMS_HASH_RE.match(key).to_a - if main.nil? - [] - elsif trailing - [key] - elsif bracketed - [main] + bracketed.slice(1...-1).split('][') - else - [main] + PARAMS_HASH_RE = /^([^\[]+)(\[.*\])?(.)?.*$/ + def get_levels(key) + all, main, bracketed, trailing = PARAMS_HASH_RE.match(key).to_a + if main.nil? + [] + elsif trailing + [key] + elsif bracketed + [main] + bracketed.slice(1...-1).split('][') + else + [main] + end end - end - def CGIMethods.build_deep_hash(value, hash, levels) - if levels.length == 0 - value - elsif hash.nil? - { levels.first => CGIMethods.build_deep_hash(value, nil, levels[1..-1]) } - else - hash.update({ levels.first => CGIMethods.build_deep_hash(value, hash[levels.first], levels[1..-1]) }) + def build_deep_hash(value, hash, levels) + if levels.length == 0 + value + elsif hash.nil? + { levels.first => build_deep_hash(value, nil, levels[1..-1]) } + else + hash.update({ levels.first => build_deep_hash(value, hash[levels.first], levels[1..-1]) }) + end end - end + end end -- cgit v1.2.3