multipart_body 0.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.
@@ -0,0 +1,2 @@
1
+ require 'multipart_body/multipart_body'
2
+ require 'multipart_body/part'
@@ -0,0 +1,26 @@
1
+ class MultipartBody
2
+ attr_accessor :parts, :boundary
3
+
4
+ def initialize(parts = nil, boundary = nil)
5
+ @parts = []
6
+ @boundary = boundary || "----multipart-boundary-#{rand(1000000)}"
7
+
8
+ if parts.is_a? Hash
9
+ @parts = parts.map {|name, body| Part.new(:name => name, :body => body) }
10
+ elsif parts.is_a?(Array) && parts.first.is_a?(Part)
11
+ @parts = parts
12
+ end
13
+
14
+ self
15
+ end
16
+
17
+ def self.from_hash(parts_hash)
18
+ multipart = self.new(parts_hash)
19
+ end
20
+
21
+ def to_s
22
+ output = "--#{@boundary}\r\n"
23
+ output << @parts.join("\r\n--#{@boundary}\r\n")
24
+ output << "\r\n--#{@boundary}--"
25
+ end
26
+ end
@@ -0,0 +1,43 @@
1
+ class Part < Struct.new(:name, :body, :filename, :content_type, :encoding)
2
+ def initialize(*args)
3
+ if args.flatten.first.is_a? Hash
4
+ from_hash(args.flatten.first)
5
+ elsif args.length > 0
6
+ from_args(*args)
7
+ end
8
+ end
9
+
10
+ def from_hash(hash)
11
+ hash.each_pair do |k, v|
12
+ self[k] = v
13
+ end
14
+ end
15
+
16
+ def from_args(name, body, filename=nil)
17
+ self[:name] = name
18
+ self[:body] = body
19
+ self[:filename] = filename
20
+ end
21
+
22
+ def header
23
+ header = "Content-Disposition: form-data; name=\"#{name}\""
24
+ header << "; filename=\"#{filename}\"" if filename
25
+ header << "\r\nContent-Type: #{content_type}" if content_type
26
+ header << "\r\nContent-Transfer-Encoding: #{encoding}" if encoding
27
+ header
28
+ end
29
+
30
+ # TODO: Implement encodings
31
+ def encoded_body
32
+ case encoding
33
+ when nil
34
+ body
35
+ else
36
+ raise "Encodings have not been implemented"
37
+ end
38
+ end
39
+
40
+ def to_s
41
+ "#{header}\r\n\r\n#{encoded_body}"
42
+ end
43
+ end
@@ -0,0 +1,30 @@
1
+ # MultipartBody
2
+ The multipart body is an attempt to bring consistency to multipart content in Ruby. When developing CloudMailin we struggled to find a gem to help us create multipart bodies. Many different libraries had implemented multipart bodies through their own implementation but non could be used independently.
3
+
4
+ The aim of MultipartBody is to ensure consistency when creating (and parsing in future) multipart content
5
+
6
+ ## Usage
7
+
8
+ require 'multipart_body'
9
+
10
+ # From a hash
11
+ multipart = Multipart.new(:field1 => 'content', :field2 => 'something else')
12
+
13
+ # With parts
14
+ part = Part.new(:name => 'name', :body => 'body', :filename => 'f.txt', :content_type => 'text/plain', :encoding => :base64)
15
+
16
+ # or to specify just the name, body and optional filename
17
+ part = Part.new('name', 'content', 'file.txt')
18
+ multipart = Multipart.new([part])
19
+
20
+ # Output
21
+ part.to_s #=> The part with headers and content
22
+ multipart.to_s #=> The full list of parts joined by boundaries
23
+
24
+ ## TODO
25
+ * Implement Parsing
26
+ * Add different encodings
27
+ * Add the ability to automatically add files and have the filename set
28
+
29
+ ## License
30
+ Copyright 2010 by Steve Smith ([CloudMailin](http://cloudmailin.com)) and is released under the MIT license.
@@ -0,0 +1,122 @@
1
+ require File.join(File.dirname(__FILE__), 'test_helper')
2
+
3
+ class MultipartBodyTest < Test::Unit::TestCase
4
+ context "MultipartBodyBody" do
5
+ setup do
6
+ @hash = {:test => 'test', :two => 'two'}
7
+ @parts = [Part.new('name', 'value'), Part.new('name2', 'value2')]
8
+ end
9
+
10
+ should "return a new multipart when sent #from_hash" do
11
+ multipart = MultipartBody.from_hash(@hash)
12
+ assert_equal MultipartBody, multipart.class
13
+ end
14
+
15
+ should "create a list of parts from the hash when sent #from_hash" do
16
+ multipart = MultipartBody.from_hash(@hash)
17
+ assert_equal @hash, Hash[multipart.parts.map{|part| [part.name, part.body] }]
18
+ end
19
+
20
+ should "add to the list of parts when sent #new with a hash" do
21
+ multipart = MultipartBody.new(@hash)
22
+ assert_equal @hash, Hash[multipart.parts.map{|part| [part.name, part.body] }]
23
+ end
24
+
25
+ should "correctly add parts sent #new with parts" do
26
+ multipart = MultipartBody.new(@parts)
27
+ assert_same_elements @parts, multipart.parts
28
+ end
29
+
30
+ should "assign a boundary if it is not given" do
31
+ multpart = MultipartBody.new()
32
+ assert_match /[\w\d-]{10,}/, multpart.boundary
33
+ end
34
+
35
+ should "use the boundary provided if given" do
36
+ multipart = MultipartBody.new(nil, "my-boundary")
37
+ assert_equal "my-boundary", multipart.boundary
38
+ end
39
+
40
+ should "starts with a boundary when sent #to_s" do
41
+ multipart = MultipartBody.new(@parts)
42
+ assert_match /^--#{multipart.boundary}/i, multipart.to_s
43
+ end
44
+
45
+ should "end with a boundary when sent #to_s" do
46
+ multipart = MultipartBody.new(@parts)
47
+ assert_match /--#{multipart.boundary}--\z/i, multipart.to_s
48
+ end
49
+
50
+ should "contain the parts joined by a boundary when sent #to_s" do
51
+ multipart = MultipartBody.new(@parts)
52
+ assert_match multipart.parts.join("\r\n--#{multipart.boundary}\r\n"), multipart.to_s
53
+ end
54
+ end
55
+
56
+ context "a Part" do
57
+ setup do
58
+ @part = Part
59
+ end
60
+
61
+ should "assign values when sent #new with a hash" do
62
+ part = Part.new(:name => 'test', :body => 'content', :filename => 'name')
63
+ assert_equal 'test', part.name
64
+ assert_equal 'content', part.body
65
+ assert_equal 'name', part.filename
66
+ end
67
+
68
+ should "assign values when sent #new with values" do
69
+ part = Part.new('test', 'content', 'name')
70
+ assert_equal 'test', part.name
71
+ assert_equal 'content', part.body
72
+ assert_equal 'name', part.filename
73
+ end
74
+
75
+ should "be happy when sent #new with args without a filename" do
76
+ part = Part.new('test', 'content')
77
+ assert_equal 'test', part.name
78
+ assert_equal 'content', part.body
79
+ assert_equal nil, part.filename
80
+ end
81
+
82
+ should "create an empty part when sent #new with nothing" do
83
+ part = Part.new()
84
+ assert_equal nil, part.name
85
+ assert_equal nil, part.body
86
+ assert_equal nil, part.filename
87
+ end
88
+
89
+ should "include a content disposition when sent #header name" do
90
+ part = Part.new(:name => 'key', :body => 'content')
91
+ assert_match /content-disposition: form-data; name="key"/i, part.header
92
+ end
93
+
94
+ should "include no filename when sent #header and a filename is not set" do
95
+ part = Part.new(:name => 'key', :body => 'content')
96
+ assert_no_match /content-disposition: .+; name=".+"; filename="?.*"?/i, part.header
97
+ end
98
+
99
+ should "include a filename when sent #header and a filename is set" do
100
+ part = Part.new(:name => 'key', :body => 'content', :filename => 'file.jpg')
101
+ assert_match /content-disposition: .+; name=".+"; filename="file.jpg"/i, part.header
102
+ end
103
+
104
+ should "return the original body if encoding is not set" do
105
+ part = Part.new(:name => 'key', :body => 'content')
106
+ assert_equal 'content', part.encoded_body
107
+ end
108
+
109
+ # TODO: Implement encoding tests
110
+ should "raise an exception when an encoding is passed" do
111
+ part = Part.new(:name => 'key', :body => 'content', :encoding => :base64)
112
+ assert_raises RuntimeError do
113
+ part.encoded_body
114
+ end
115
+ end
116
+
117
+ should "output the header and body when sent #to_s" do
118
+ part = Part.new(:name => 'key', :body => 'content')
119
+ assert_equal "#{part.header}\r\n\r\n#{part.body}", part.to_s
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'multipart_body'
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: multipart_body
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Steve Smith
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-17 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: A ruby library to create multipart bodies.
23
+ email: gems@scsworld.co.uk
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - readme.md
32
+ - lib/multipart_body/multipart_body.rb
33
+ - lib/multipart_body/part.rb
34
+ - lib/multipart_body.rb
35
+ - test/test.rb
36
+ - test/test_helper.rb
37
+ has_rdoc: true
38
+ homepage: http://github.com/cloudmailin/multipart_body
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ hash: 3
52
+ segments:
53
+ - 0
54
+ version: "0"
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ hash: 3
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ requirements: []
65
+
66
+ rubyforge_project:
67
+ rubygems_version: 1.3.7
68
+ signing_key:
69
+ specification_version: 3
70
+ summary: MultipartBody allows you to create consistant multipart bodies
71
+ test_files:
72
+ - test/test.rb
73
+ - test/test_helper.rb