multipart-post 0.1 → 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.
|