diff options
author | David Heinemeier Hansson <david@loudthinking.com> | 2007-04-25 17:25:44 +0000 |
---|---|---|
committer | David Heinemeier Hansson <david@loudthinking.com> | 2007-04-25 17:25:44 +0000 |
commit | 6a85955642606aa3159ea8e4d24fbc77a1fc5e94 (patch) | |
tree | 616c37e17c1b14fbece4a319fc41ee63dba78072 | |
parent | 54bc5ca8d97b7ef8640686e50168ba25d7f23d27 (diff) | |
download | rails-6a85955642606aa3159ea8e4d24fbc77a1fc5e94.tar.gz rails-6a85955642606aa3159ea8e4d24fbc77a1fc5e94.tar.bz2 rails-6a85955642606aa3159ea8e4d24fbc77a1fc5e94.zip |
Added parsing of file type in Hash.xml_in so you can easily do file uploads with base64 from an API [DHH]
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@6578 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
-rwxr-xr-x | actionpack/test/controller/cgi_test.rb | 30 | ||||
-rw-r--r-- | activesupport/CHANGELOG | 16 | ||||
-rw-r--r-- | activesupport/lib/active_support/core_ext/hash/conversions.rb | 43 |
3 files changed, 76 insertions, 13 deletions
diff --git a/actionpack/test/controller/cgi_test.rb b/actionpack/test/controller/cgi_test.rb index d5c8ee0063..5be6cd2ea2 100755 --- a/actionpack/test/controller/cgi_test.rb +++ b/actionpack/test/controller/cgi_test.rb @@ -2,7 +2,6 @@ require File.dirname(__FILE__) + '/../abstract_unit' require 'action_controller/cgi_process' require 'action_controller/cgi_ext/cgi_ext' - require 'stringio' class CGITest < Test::Unit::TestCase @@ -278,6 +277,35 @@ class CGITest < Test::Unit::TestCase end +class XmlCGITest < Test::Unit::TestCase + def test_single_file + raw_post_data = + "<person><name>David</name><avatar type='file' name='me.jpg' content_type='image/jpg'>#{Base64.encode64('ABC')}</avatar></person>" + person = CGIMethods.parse_formatted_request_parameters(Mime::XML, raw_post_data) + assert_equal "image/jpg", person['person']['avatar'].content_type + assert_equal "me.jpg", person['person']['avatar'].original_filename + assert_equal "ABC", person['person']['avatar'].read + end + + def test_multiple_files + raw_post_data = + "<person><name>David</name><avatars>" + + "<avatar type='file' name='me.jpg' content_type='image/jpg'>#{Base64.encode64('ABC')}</avatar>" + + "<avatar type='file' name='you.gif' content_type='image/gif'>#{Base64.encode64('DEF')}</avatar>" + + "</avatars></person>" + person = CGIMethods.parse_formatted_request_parameters(Mime::XML, raw_post_data) + + assert_equal "image/jpg", person['person']['avatars']['avatar'].first.content_type + assert_equal "me.jpg", person['person']['avatars']['avatar'].first.original_filename + assert_equal "ABC", person['person']['avatars']['avatar'].first.read + + assert_equal "image/gif", person['person']['avatars']['avatar'].last.content_type + assert_equal "you.gif", person['person']['avatars']['avatar'].last.original_filename + assert_equal "DEF", person['person']['avatars']['avatar'].last.read + end +end + + class MultipartCGITest < Test::Unit::TestCase FIXTURE_PATH = File.dirname(__FILE__) + '/../fixtures/multipart' diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG index ea734dcddf..018d6e977c 100644 --- a/activesupport/CHANGELOG +++ b/activesupport/CHANGELOG @@ -1,5 +1,21 @@ *SVN* +* Added parsing of file type in Hash.xml_in so you can easily do file uploads with base64 from an API [DHH] + + <person> + <name>David</name> + <avatar type="file" name="me.jpg" content_type="image/jpg">R0lGODlhkACZAPUAAM5lcfjrtMQCG=\n</avatar> + </person> + + ...becomes: + + attributes = { :person => { :name => "David", :avatar => #<StringIO> } } + attributes[:person][:avatar].content_type # => "image/jpg" + attributes[:person][:avatar].original_filename # => "me.jpg" + attributes[:person][:avatar].read # => binary data of the file + + Which is duck-type compatible with the files that you get when doing multipart uploads through HTML. + * Improved multibyte performance by relying less on exception raising #8159 [Blaine] * Use XSD-compatible type names for Hash#to_xml and make the converters extendable #8047 [Tim Pope] diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb index 4fb3cbe4d3..a8f2ec4452 100644 --- a/activesupport/lib/active_support/core_ext/hash/conversions.rb +++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb @@ -45,15 +45,22 @@ module ActiveSupport #:nodoc: unless defined?(XML_PARSING) XML_PARSING = { - "date" => Proc.new { |date| ::Date.parse(date) }, - "datetime" => Proc.new { |time| ::Time.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| Base64.decode64(bin) } + "date" => Proc.new { |date| ::Date.parse(date) }, + "datetime" => Proc.new { |time| ::Time.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| Base64.decode64(bin) }, + # FIXME: Get rid of eval and institute a proper decorator here + "file" => Proc.new do |file, entity| + f = StringIO.new(Base64.decode64(file)) + eval "def f.original_filename() '#{entity["name"]}' || 'untitled' end" + eval "def f.content_type() '#{entity["content_type"]}' || 'application/octet-stream' end" + f + end } XML_PARSING.update( @@ -147,18 +154,30 @@ module ActiveSupport #:nodoc: when "Hash" if value.has_key?("__content__") content = translate_xml_entities(value["__content__"]) - if XML_PARSING[value["type"]] - XML_PARSING[value["type"]].call(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 else content end elsif value['type'] == 'string' && value['nil'] != 'true' "" else - (value.blank? || value['type'] || value['nil'] == 'true') ? nil : value.inject({}) do |h,(k,v)| + xml_value = (value.blank? || value['type'] || value['nil'] == 'true') ? nil : value.inject({}) do |h,(k,v)| h[k] = typecast_xml_value(v) h end + + # Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with + # how multipart uploaded files from HTML appear + if xml_value["file"].is_a?(StringIO) + xml_value["file"] + else + xml_value + end end when "Array" value.map! { |i| typecast_xml_value(i) } |