multipart-post 0.1 → 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +2 -0
- data/README.txt +2 -3
- data/Rakefile +3 -1
- data/lib/multipartable.rb +13 -0
- data/lib/net/http/post/multipart.rb +9 -84
- data/lib/parts.rb +66 -0
- data/test/net/http/post/test_multipart.rb +8 -2
- metadata +7 -5
data/Manifest.txt
CHANGED
data/README.txt
CHANGED
@@ -33,14 +33,13 @@ None
|
|
33
33
|
|
34
34
|
== INSTALL:
|
35
35
|
|
36
|
-
|
37
|
-
gem install pkg/multipart-form*.gem
|
36
|
+
gem install multipart-post
|
38
37
|
|
39
38
|
== LICENSE:
|
40
39
|
|
41
40
|
(The MIT License)
|
42
41
|
|
43
|
-
Copyright (c) 2007-
|
42
|
+
Copyright (c) 2007-2009 Nick Sieger <nick@nicksieger.com>
|
44
43
|
|
45
44
|
Permission is hereby granted, free of charge, to any person obtaining
|
46
45
|
a copy of this software and associated documentation files (the
|
data/Rakefile
CHANGED
@@ -2,7 +2,9 @@ begin
|
|
2
2
|
require 'rubygems'
|
3
3
|
require 'hoe'
|
4
4
|
|
5
|
-
|
5
|
+
require 'lib/multipart_post'
|
6
|
+
|
7
|
+
hoe = Hoe.new("multipart-post", MultipartPost::VERSION) do |p|
|
6
8
|
p.rubyforge_name = "caldersphere"
|
7
9
|
p.author = "Nick Sieger"
|
8
10
|
p.url = "http://github.com/nicksieger/multipart-post"
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'parts'
|
2
|
+
module Multipartable
|
3
|
+
DEFAULT_BOUNDARY = "-----------RubyMultipartPost"
|
4
|
+
def initialize(path, params, headers={}, boundary = DEFAULT_BOUNDARY)
|
5
|
+
super(path, headers)
|
6
|
+
parts = params.map {|k,v| Parts::Part.new(boundary, k, v)}
|
7
|
+
parts << Parts::EpiloguePart.new(boundary)
|
8
|
+
ios = parts.map{|p| p.to_io }
|
9
|
+
self.set_content_type("multipart/form-data", { "boundary" => boundary })
|
10
|
+
self.content_length = parts.inject(0) {|sum,i| sum + i.length }
|
11
|
+
self.body_stream = CompositeReadIO.new(*ios)
|
12
|
+
end
|
13
|
+
end
|
@@ -8,95 +8,20 @@ require 'net/http'
|
|
8
8
|
require 'stringio'
|
9
9
|
require 'cgi'
|
10
10
|
require 'composite_io'
|
11
|
+
require 'multipartable'
|
12
|
+
require 'parts'
|
11
13
|
|
12
14
|
module Net #:nodoc:
|
13
15
|
class HTTP #:nodoc:
|
14
|
-
class
|
15
|
-
|
16
|
-
|
17
|
-
if value.respond_to? :content_type
|
18
|
-
FilePart.new(boundary, name, value)
|
19
|
-
else
|
20
|
-
ParamPart.new(boundary, name, value)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def length
|
25
|
-
@part.length
|
26
|
-
end
|
27
|
-
|
28
|
-
def to_io
|
29
|
-
@io
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# Represents a part to be filled with a string name/value pair.
|
34
|
-
class ParamPart
|
35
|
-
include Part
|
36
|
-
def initialize(boundary, name, value)
|
37
|
-
@part = build_part(boundary, name, value)
|
38
|
-
@io = StringIO.new(@part)
|
39
|
-
end
|
40
|
-
|
41
|
-
def build_part(boundary, name, value)
|
42
|
-
part = ''
|
43
|
-
part << "--#{boundary}\r\n"
|
44
|
-
part << "Content-Disposition: form-data; name=\"#{name.to_s}\"\r\n"
|
45
|
-
part << "\r\n"
|
46
|
-
part << "#{value}\r\n"
|
47
|
-
end
|
16
|
+
class Put
|
17
|
+
class Multipart < Put
|
18
|
+
include Multipartable
|
48
19
|
end
|
49
|
-
|
50
|
-
|
51
|
-
class FilePart
|
52
|
-
include Part
|
53
|
-
attr_reader :length
|
54
|
-
def initialize(boundary, name, io)
|
55
|
-
@head = build_head(boundary, name, io.original_filename, io.content_type)
|
56
|
-
file_length = if io.respond_to? :length
|
57
|
-
io.length
|
58
|
-
else
|
59
|
-
File.size(io.local_path)
|
60
|
-
end
|
61
|
-
@length = @head.length + file_length
|
62
|
-
@io = CompositeReadIO.new(StringIO.new(@head), io, StringIO.new("\r\n"))
|
63
|
-
end
|
64
|
-
|
65
|
-
def build_head(boundary, name, filename, type)
|
66
|
-
part = ''
|
67
|
-
part << "--#{boundary}\r\n"
|
68
|
-
part << "Content-Disposition: form-data; name=\"#{name.to_s}\"; filename=\"#{filename}\"\r\n"
|
69
|
-
part << "Content-Type: #{type}\r\n"
|
70
|
-
part << "Content-Transfer-Encoding: binary\r\n"
|
71
|
-
part << "\r\n"
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
# Represents the epilogue or closing boundary.
|
76
|
-
class EpiloguePart
|
77
|
-
include Part
|
78
|
-
def initialize(boundary)
|
79
|
-
@part = "--#{boundary}--\r\n"
|
80
|
-
@io = StringIO.new(@part)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
DEFAULT_BOUNDARY = "-----------RubyMultipartPost"
|
85
|
-
|
86
|
-
# Extension to the Net::HTTP::Post class that builds a post body
|
87
|
-
# consisting of a multipart mime stream based on the parameters given.
|
88
|
-
# See README.txt for synopsis and details.
|
20
|
+
end
|
21
|
+
class Post #:nodoc:
|
89
22
|
class Multipart < Post
|
90
|
-
|
91
|
-
super(path)
|
92
|
-
parts = params.map {|k,v| Part.new(boundary, k, v)}
|
93
|
-
parts << EpiloguePart.new(boundary)
|
94
|
-
ios = parts.map{|p| p.to_io }
|
95
|
-
self.set_content_type("multipart/form-data", { "boundary" => boundary })
|
96
|
-
self.content_length = parts.inject(0) {|sum,i| sum + i.length }
|
97
|
-
self.body_stream = CompositeReadIO.new(*ios)
|
98
|
-
end
|
23
|
+
include Multipartable
|
99
24
|
end
|
100
25
|
end
|
101
26
|
end
|
102
|
-
end
|
27
|
+
end
|
data/lib/parts.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
module Parts
|
2
|
+
module Part #:nodoc:
|
3
|
+
def self.new(boundary, name, value)
|
4
|
+
if value.respond_to? :content_type
|
5
|
+
FilePart.new(boundary, name, value)
|
6
|
+
else
|
7
|
+
ParamPart.new(boundary, name, value)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def length
|
12
|
+
@part.length
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_io
|
16
|
+
@io
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class ParamPart
|
21
|
+
include Part
|
22
|
+
def initialize(boundary, name, value)
|
23
|
+
@part = build_part(boundary, name, value)
|
24
|
+
@io = StringIO.new(@part)
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_part(boundary, name, value)
|
28
|
+
part = ''
|
29
|
+
part << "--#{boundary}\r\n"
|
30
|
+
part << "Content-Disposition: form-data; name=\"#{name.to_s}\"\r\n"
|
31
|
+
part << "\r\n"
|
32
|
+
part << "#{value}\r\n"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Represents a part to be filled from file IO.
|
37
|
+
class FilePart
|
38
|
+
include Part
|
39
|
+
attr_reader :length
|
40
|
+
def initialize(boundary, name, io)
|
41
|
+
file_length = io.respond_to?(:length) ? io.length : File.size(io.local_path)
|
42
|
+
@head = build_head(boundary, name, io.original_filename, io.content_type, file_length)
|
43
|
+
@length = @head.length + file_length
|
44
|
+
@io = CompositeReadIO.new(StringIO.new(@head), io, StringIO.new("\r\n"))
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_head(boundary, name, filename, type, content_len)
|
48
|
+
part = ''
|
49
|
+
part << "--#{boundary}\r\n"
|
50
|
+
part << "Content-Disposition: form-data; name=\"#{name.to_s}\"; filename=\"#{filename}\"\r\n"
|
51
|
+
part << "Content-Length: #{content_len}\r\n"
|
52
|
+
part << "Content-Type: #{type}\r\n"
|
53
|
+
part << "Content-Transfer-Encoding: binary\r\n"
|
54
|
+
part << "\r\n"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Represents the epilogue or closing boundary.
|
59
|
+
class EpiloguePart
|
60
|
+
include Part
|
61
|
+
def initialize(boundary)
|
62
|
+
@part = "--#{boundary}--\r\n"
|
63
|
+
@io = StringIO.new(@part)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -26,6 +26,12 @@ class Net::HTTP::Post::MultiPartTest < Test::Unit::TestCase
|
|
26
26
|
UploadIO.convert! @io, "text/plain", TEMP_FILE, TEMP_FILE
|
27
27
|
assert_results Net::HTTP::Post::Multipart.new("/foo/bar", :foo => 'bar', :file => @io)
|
28
28
|
end
|
29
|
+
def test_form_multipart_body_put
|
30
|
+
File.open(TEMP_FILE, "w") {|f| f << "1234567890"}
|
31
|
+
@io = File.open(TEMP_FILE)
|
32
|
+
UploadIO.convert! @io, "text/plain", TEMP_FILE, TEMP_FILE
|
33
|
+
assert_results Net::HTTP::Put::Multipart.new("/foo/bar", :foo => 'bar', :file => @io)
|
34
|
+
end
|
29
35
|
|
30
36
|
def test_form_multipart_body_with_stringio
|
31
37
|
@io = StringIO.new("1234567890")
|
@@ -36,9 +42,9 @@ class Net::HTTP::Post::MultiPartTest < Test::Unit::TestCase
|
|
36
42
|
def assert_results(post)
|
37
43
|
assert post.content_length && post.content_length > 0
|
38
44
|
assert post.body_stream
|
39
|
-
assert_equal "multipart/form-data; boundary=#{
|
45
|
+
assert_equal "multipart/form-data; boundary=#{Multipartable::DEFAULT_BOUNDARY}", post['content-type']
|
40
46
|
body = post.body_stream.read
|
41
|
-
boundary_regex = Regexp.quote
|
47
|
+
boundary_regex = Regexp.quote Multipartable::DEFAULT_BOUNDARY
|
42
48
|
assert body =~ /1234567890/
|
43
49
|
# ensure there is at least one boundary
|
44
50
|
assert body =~ /^--#{boundary_regex}\r\n/
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: multipart-post
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0
|
4
|
+
version: "1.0"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sieger
|
@@ -9,18 +9,18 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-02-12 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: hoe
|
17
|
-
type: :
|
17
|
+
type: :development
|
18
18
|
version_requirement:
|
19
19
|
version_requirements: !ruby/object:Gem::Requirement
|
20
20
|
requirements:
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 1.
|
23
|
+
version: 1.8.2
|
24
24
|
version:
|
25
25
|
description: "Use with Net::HTTP to do multipart form posts. IO values that have #content_type, #original_filename, and #local_path will be posted as a binary file."
|
26
26
|
email: nick@nicksieger.com
|
@@ -33,6 +33,8 @@ extra_rdoc_files:
|
|
33
33
|
- README.txt
|
34
34
|
files:
|
35
35
|
- lib/composite_io.rb
|
36
|
+
- lib/multipartable.rb
|
37
|
+
- lib/parts.rb
|
36
38
|
- lib/net/http/post/multipart.rb
|
37
39
|
- Manifest.txt
|
38
40
|
- Rakefile
|
@@ -62,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
64
|
requirements: []
|
63
65
|
|
64
66
|
rubyforge_project: caldersphere
|
65
|
-
rubygems_version: 1.
|
67
|
+
rubygems_version: 1.3.1
|
66
68
|
signing_key:
|
67
69
|
specification_version: 2
|
68
70
|
summary: Creates a multipart form post accessory for Net::HTTP.
|