aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Heinemeier Hansson <david@loudthinking.com>2007-04-25 17:25:44 +0000
committerDavid Heinemeier Hansson <david@loudthinking.com>2007-04-25 17:25:44 +0000
commit6a85955642606aa3159ea8e4d24fbc77a1fc5e94 (patch)
tree616c37e17c1b14fbece4a319fc41ee63dba78072
parent54bc5ca8d97b7ef8640686e50168ba25d7f23d27 (diff)
downloadrails-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-xactionpack/test/controller/cgi_test.rb30
-rw-r--r--activesupport/CHANGELOG16
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb43
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) }