http-form_data 1.0.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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6b6f78da2bcad8f8c3d16a66a0a3b2d95446e5b9
4
+ data.tar.gz: 821cfd73800cc9cf8ee056638be1c4e2ae4181de
5
+ SHA512:
6
+ metadata.gz: 058ac0d84ee6e846dca8143b270b9ac0196bc12a51894772239004577d93f36064f609cadc851f628311339e85d0c5dfb06b0d11ee39ef358fdbf7186f481f1d
7
+ data.tar.gz: 3e9b0000a763a4c14f825ed58d1606cbbeb4fe94031fa32a22a530d2345500860540581ef8233b88b1efd8e1ffe978f719b7148c60b59e4e1c0bc083b348461e
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
@@ -0,0 +1,68 @@
1
+ ## Styles ######################################################################
2
+
3
+ # See: http://erniemiller.org/2014/10/23/in-defense-of-alias/
4
+ Style/Alias:
5
+ Enabled: false
6
+
7
+ Style/AlignParameters:
8
+ EnforcedStyle: with_fixed_indentation
9
+
10
+ Style/BracesAroundHashParameters:
11
+ Enabled: false
12
+
13
+ # Broken (2014-12-15). Use `yardstick` gem instead.
14
+ # See: https://github.com/bbatsov/rubocop/issues/947
15
+ # TODO: Enable back once cop is fixed.
16
+ Style/Documentation:
17
+ Enabled: false
18
+
19
+ Style/EmptyLineBetweenDefs:
20
+ AllowAdjacentOneLineDefs: true
21
+
22
+ Style/Encoding:
23
+ EnforcedStyle: when_needed
24
+
25
+ Style/HashSyntax:
26
+ EnforcedStyle: hash_rockets
27
+
28
+ Style/IndentHash:
29
+ EnforcedStyle: consistent
30
+
31
+ # New lambda syntax is as ugly to me as new syntax of Hash.
32
+ Style/Lambda:
33
+ Enabled: false
34
+
35
+ Style/MultilineOperationIndentation:
36
+ EnforcedStyle: indented
37
+
38
+ # A bit useless restriction, that makes impossible aligning code like this:
39
+ #
40
+ # redis do |conn|
41
+ # conn.hset :k1, now
42
+ # conn.hincrby :k2, 123
43
+ # end
44
+ SingleSpaceBeforeFirstArg:
45
+ Enabled: false
46
+
47
+ Style/StringLiterals:
48
+ EnforcedStyle: double_quotes
49
+
50
+ # Not all trivial readers/writers can be defined with attr_* methods
51
+ #
52
+ # class Example < SimpleDelegator
53
+ # def __getobj__
54
+ # @obj
55
+ # end
56
+ #
57
+ # def __setobj__(obj)
58
+ # @obj = obj
59
+ # end
60
+ # end
61
+ Style/TrivialAccessors:
62
+ Enabled: false
63
+
64
+ ## Metrics #####################################################################
65
+
66
+ Metrics/MethodLength:
67
+ CountComments: false
68
+ Max: 15
@@ -0,0 +1,20 @@
1
+ bundler_args: --without development doc
2
+ env:
3
+ global:
4
+ - JRUBY_OPTS="$JRUBY_OPTS --debug"
5
+ language: ruby
6
+ rvm:
7
+ - 1.9.3
8
+ - 2.0.0
9
+ - 2.1
10
+ - 2.2
11
+ - jruby-19mode
12
+ - jruby-head
13
+ - rbx-2
14
+ - ruby-head
15
+ matrix:
16
+ allow_failures:
17
+ - rvm: jruby-head
18
+ - rvm: ruby-head
19
+ fast_finish: true
20
+ sudo: false
@@ -0,0 +1,2 @@
1
+ --markup-provider=redcarpet
2
+ --markup=markdown
@@ -0,0 +1,15 @@
1
+ ## 1.0.0 (2015-01-04)
2
+
3
+ * Gem renamed to `http-form_data` as `FormData` is not top-level citizen
4
+ anymore: `FormData -> HTTP::FormData`.
5
+
6
+
7
+ ## 0.1.0 (2015-01-02)
8
+
9
+ * Move repo under `httprb` organization on GitHub.
10
+ * Add `nil` support to `FormData#ensure_hash`.
11
+
12
+
13
+ ## 0.0.1 (2014-12-15)
14
+
15
+ * First release ever!
data/Gemfile ADDED
@@ -0,0 +1,24 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem "rake"
4
+
5
+ group :development do
6
+ gem "pry"
7
+ gem "guard"
8
+ gem "guard-rspec", :require => false
9
+ end
10
+
11
+ group :test do
12
+ gem "coveralls"
13
+ gem "rspec", "~> 3.1"
14
+ gem "simplecov", ">= 0.9"
15
+ gem "rubocop", "~> 0.28.0"
16
+ end
17
+
18
+ group :doc do
19
+ gem "yard"
20
+ gem "redcarpet"
21
+ end
22
+
23
+ # Specify your gem's dependencies in form_data.gemspec
24
+ gemspec
@@ -0,0 +1,14 @@
1
+ guard :rspec, :cmd => "bundle exec rspec" do
2
+ require "guard/rspec/dsl"
3
+ dsl = Guard::RSpec::Dsl.new(self)
4
+
5
+ # RSpec files
6
+ rspec = dsl.rspec
7
+ watch(rspec.spec_helper) { rspec.spec_dir }
8
+ watch(rspec.spec_support) { rspec.spec_dir }
9
+ watch(rspec.spec_files)
10
+
11
+ # Ruby files
12
+ ruby = dsl.ruby
13
+ dsl.watch_spec_files_for(ruby.lib_files)
14
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Aleksey V Zapparov
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,88 @@
1
+ # FormData
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/http-form_data.png)](http://rubygems.org/gems/http-form_data)
4
+ [![Build Status](https://secure.travis-ci.org/httprb/form_data.rb.png?branch=master)](http://travis-ci.org/httprb/form_data.rb)
5
+ [![Code Climate](https://codeclimate.com/github/httprb/form_data.rb.png)](https://codeclimate.com/github/httprb/form_data.rb)
6
+ [![Coverage Status](https://coveralls.io/repos/httprb/form_data.rb/badge.png?branch=master)](https://coveralls.io/r/httprb/form_data.rb)
7
+
8
+ Utility-belt to build form data request bodies.
9
+
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'http-form_data'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install http-form_data
26
+
27
+
28
+ ## Usage
29
+
30
+ ``` ruby
31
+ require "http/form_data"
32
+
33
+ form = HTTP::FormData.create({
34
+ :username => "ixti",
35
+ :avatar_file => HTTP::FormData::File.new("/home/ixti/avatar.png")
36
+ })
37
+
38
+ # Assuming socket is an open socket to some HTTP server
39
+ socket << "POST /some-url HTTP/1.1\r\n"
40
+ socket << "Host: example.com\r\n"
41
+ socket << "Content-Type: #{form.content_type}\r\n"
42
+ socket << "Content-Length: #{form.content_length}\r\n"
43
+ socket << "\r\n"
44
+ socket << form.to_s
45
+ ```
46
+
47
+
48
+ ## Supported Ruby Versions
49
+
50
+ This library aims to support and is [tested against][ci] the following Ruby
51
+ versions:
52
+
53
+ * Ruby 1.9.3
54
+ * Ruby 2.0.0
55
+ * Ruby 2.1.x
56
+ * Ruby 2.2.x
57
+
58
+ If something doesn't work on one of these versions, it's a bug.
59
+
60
+ This library may inadvertently work (or seem to work) on other Ruby versions,
61
+ however support will only be provided for the versions listed above.
62
+
63
+ If you would like this library to support another Ruby version or
64
+ implementation, you may volunteer to be a maintainer. Being a maintainer
65
+ entails making sure all tests run and pass on that implementation. When
66
+ something breaks on your implementation, you will be responsible for providing
67
+ patches in a timely fashion. If critical issues for a particular implementation
68
+ exist at the time of a major release, support for that Ruby version may be
69
+ dropped.
70
+
71
+
72
+ ## Contributing
73
+
74
+ 1. Fork it ( https://github.com/httprb/form_data.rb/fork )
75
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
76
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
77
+ 4. Push to the branch (`git push origin my-new-feature`)
78
+ 5. Create a new Pull Request
79
+
80
+
81
+ ## Copyright
82
+
83
+ Copyright (c) 2015 Aleksey V Zapparov.
84
+ See [LICENSE.txt][license] for further details.
85
+
86
+
87
+ [ci]: http://travis-ci.org/httprb/form_data.rb
88
+ [license]: https://github.com/httprb/form_data.rb/blob/master/LICENSE.txt
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require "bundler/gem_tasks"
4
+
5
+ require "rspec/core/rake_task"
6
+ RSpec::Core::RakeTask.new
7
+
8
+ begin
9
+ require "rubocop/rake_task"
10
+ RuboCop::RakeTask.new
11
+ rescue LoadError
12
+ task :rubocop do
13
+ $stderr.puts "RuboCop is disabled"
14
+ end
15
+ end
16
+
17
+ task :default => [:spec, :rubocop]
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "http/form_data/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "http-form_data"
8
+ spec.version = HTTP::FormData::VERSION
9
+ spec.homepage = "https://github.com/httprb/form_data.rb"
10
+ spec.authors = ["Aleksey V Zapparov"]
11
+ spec.email = ["ixti@member.fsf.org"]
12
+ spec.license = "MIT"
13
+ spec.summary = "http-form_data-#{HTTP::FormData::VERSION}"
14
+ spec.description = <<-DESC.gsub(/^\s+> /m, "").gsub("\n", " ").strip
15
+ > Utility-belt to build form data request bodies.
16
+ > Provides support for `application/x-www-form-urlencoded` and
17
+ > `multipart/form-data` types.
18
+ DESC
19
+
20
+ spec.files = `git ls-files -z`.split("\x0")
21
+ spec.executables = spec.files.grep(/^bin\//).map { |f| File.basename(f) }
22
+ spec.test_files = spec.files.grep(/^(test|spec|features)\//)
23
+ spec.require_paths = ["lib"]
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.7"
26
+ end
@@ -0,0 +1,76 @@
1
+ require "http/form_data/file"
2
+ require "http/form_data/multipart"
3
+ require "http/form_data/urlencoded"
4
+ require "http/form_data/version"
5
+
6
+ # http.rb namespace.
7
+ # @see https://github.com/httprb/http.rb
8
+ module HTTP
9
+ # Utility-belt to build form data request bodies.
10
+ # Provides support for `application/x-www-form-urlencoded` and
11
+ # `multipart/form-data` types.
12
+ #
13
+ # @example Usage
14
+ #
15
+ # form = FormData.create({
16
+ # :username => "ixti",
17
+ # :avatar_file => FormData::File.new("/home/ixti/avatar.png")
18
+ # })
19
+ #
20
+ # # Assuming socket is an open socket to some HTTP server
21
+ # socket << "POST /some-url HTTP/1.1\r\n"
22
+ # socket << "Host: example.com\r\n"
23
+ # socket << "Content-Type: #{form.content_type}\r\n"
24
+ # socket << "Content-Length: #{form.content_length}\r\n"
25
+ # socket << "\r\n"
26
+ # socket << form.to_s
27
+ module FormData
28
+ # CRLF
29
+ CRLF = "\r\n".freeze
30
+
31
+ # Generic FormData error.
32
+ class Error < StandardError; end
33
+
34
+ class << self
35
+ # FormData factory. Automatically selects best type depending on given
36
+ # `data` Hash.
37
+ #
38
+ # @param [#to_h, Hash] data
39
+ # @return [Multipart] if any of values is a {FormData::File}
40
+ # @return [Urlencoded] otherwise
41
+ def create(data)
42
+ data = ensure_hash data
43
+ klass = multipart?(data) ? Multipart : Urlencoded
44
+
45
+ klass.new data
46
+ end
47
+
48
+ # Coerce `obj` to Hash.
49
+ #
50
+ # @note Internal usage helper, to workaround lack of `#to_h` on Ruby < 2.1
51
+ # @raise [Error] `obj` can't be coerced.
52
+ # @return [Hash]
53
+ def ensure_hash(obj)
54
+ case
55
+ when obj.nil? then {}
56
+ when obj.is_a?(Hash) then obj
57
+ when obj.respond_to?(:to_h) then obj.to_h
58
+ else fail Error, "#{obj.inspect} is neither Hash nor responds to :to_h"
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ # Tells whenever data contains multipart data or not.
65
+ #
66
+ # @param [Hash] data
67
+ # @return [Boolean]
68
+ def multipart?(data)
69
+ data.any? do |_, v|
70
+ next true if v.is_a? FormData::File
71
+ v.respond_to?(:to_ary) && v.to_ary.any? { |e| e.is_a? FormData::File }
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,75 @@
1
+ module HTTP
2
+ module FormData
3
+ # Represents file form param.
4
+ #
5
+ # @example Usage with StringIO
6
+ #
7
+ # io = StringIO.new "foo bar baz"
8
+ # FormData::File.new io, :filename => "foobar.txt"
9
+ #
10
+ # @example Usage with IO
11
+ #
12
+ # File.open "/home/ixti/avatar.png" do |io|
13
+ # FormData::File.new io
14
+ # end
15
+ #
16
+ # @example Usage with pathname
17
+ #
18
+ # FormData::File.new "/home/ixti/avatar.png"
19
+ class File
20
+ # Default MIME type
21
+ DEFAULT_MIME = "application/octet-stream".freeze
22
+
23
+ attr_reader :mime_type, :filename
24
+
25
+ # @see DEFAULT_MIME
26
+ # @param [String, StringIO, File] file_or_io Filename or IO instance.
27
+ # @param [#to_h] opts
28
+ # @option opts [#to_s] :mime_type (DEFAULT_MIME)
29
+ # @option opts [#to_s] :filename
30
+ # When `file` is a String, defaults to basename of `file`.
31
+ # When `file` is a File, defaults to basename of `file`.
32
+ # When `file` is a StringIO, defaults to `"stream-{object_id}"`
33
+ def initialize(file_or_io, opts = {})
34
+ @file_or_io = file_or_io
35
+
36
+ opts = FormData.ensure_hash opts
37
+
38
+ @mime_type = opts.fetch(:mime_type) { DEFAULT_MIME }
39
+ @filename = opts.fetch :filename do
40
+ case file_or_io
41
+ when String then ::File.basename file_or_io
42
+ when ::File then ::File.basename file_or_io.path
43
+ else "stream-#{file_or_io.object_id}"
44
+ end
45
+ end
46
+ end
47
+
48
+ # Returns content size.
49
+ #
50
+ # @return [Fixnum]
51
+ def size
52
+ with_io(&:size)
53
+ end
54
+
55
+ # Returns content of a file of IO.
56
+ #
57
+ # @return [String]
58
+ def to_s
59
+ with_io(&:read)
60
+ end
61
+
62
+ private
63
+
64
+ # @yield [io] Gives IO instance to the block
65
+ # @return result of yielded block
66
+ def with_io
67
+ if @file_or_io.is_a?(::File) || @file_or_io.is_a?(StringIO)
68
+ yield @file_or_io
69
+ else
70
+ ::File.open(@file_or_io) { |io| yield io }
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,64 @@
1
+ # stdlib
2
+ require "securerandom"
3
+
4
+ # internal
5
+ require "http/form_data/multipart/param"
6
+
7
+ module HTTP
8
+ module FormData
9
+ # `multipart/form-data` form data.
10
+ class Multipart
11
+ # @param [#to_h, Hash] data form data key-value Hash
12
+ def initialize(data)
13
+ @parts = Param.coerce FormData.ensure_hash data
14
+ @boundary = ("-" * 21) << SecureRandom.hex(21)
15
+ @content_length = nil
16
+ end
17
+
18
+ # Returns content to be used for HTTP request body.
19
+ #
20
+ # @return [String]
21
+ def to_s
22
+ head + @parts.map(&:to_s).join(glue) + tail
23
+ end
24
+
25
+ # Returns MIME type to be used for HTTP request `Content-Type` header.
26
+ #
27
+ # @return [String]
28
+ def content_type
29
+ "multipart/form-data; boundary=#{@boundary}"
30
+ end
31
+
32
+ # Returns form data content size to be used for HTTP request
33
+ # `Content-Length` header.
34
+ #
35
+ # @return [Fixnum]
36
+ def content_length
37
+ unless @content_length
38
+ @content_length = head.bytesize + tail.bytesize
39
+ @content_length += @parts.map(&:size).reduce(:+)
40
+ @content_length += (glue.bytesize * (@parts.count - 1))
41
+ end
42
+
43
+ @content_length
44
+ end
45
+
46
+ private
47
+
48
+ # @return [String]
49
+ def head
50
+ @head ||= "--#{@boundary}#{CRLF}"
51
+ end
52
+
53
+ # @return [String]
54
+ def glue
55
+ @glue ||= "#{CRLF}--#{@boundary}#{CRLF}"
56
+ end
57
+
58
+ # @return [String]
59
+ def tail
60
+ @tail ||= "#{CRLF}--#{@boundary}--"
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,82 @@
1
+ module HTTP
2
+ module FormData
3
+ class Multipart
4
+ # Utility class to represent multi-part chunks
5
+ class Param
6
+ # @param [#to_s] name
7
+ # @param [FormData::File, #to_s] value
8
+ def initialize(name, value)
9
+ @name, @value = name.to_s, value
10
+
11
+ @header = "Content-Disposition: form-data; name=#{@name.inspect}"
12
+
13
+ return unless file?
14
+
15
+ @header << "; filename=#{value.filename.inspect}"
16
+ @header << CRLF
17
+ @header << "Content-Type: #{value.mime_type}"
18
+ end
19
+
20
+ # Returns body part with headers and data.
21
+ #
22
+ # @example With {FormData::File} value
23
+ #
24
+ # Content-Disposition: form-data; name="avatar"; filename="avatar.png"
25
+ # Content-Type: application/octet-stream
26
+ #
27
+ # ...data of avatar.png...
28
+ #
29
+ # @example With non-{FormData::File} value
30
+ #
31
+ # Content-Disposition: form-data; name="username"
32
+ #
33
+ # ixti
34
+ #
35
+ # @return [String]
36
+ def to_s
37
+ "#{@header}#{CRLF * 2}#{@value}"
38
+ end
39
+
40
+ # Calculates size of a part (headers + body).
41
+ #
42
+ # @return [Fixnum]
43
+ def size
44
+ size = @header.bytesize + (CRLF.bytesize * 2)
45
+
46
+ if file?
47
+ size + @value.size
48
+ else
49
+ size + @value.to_s.bytesize
50
+ end
51
+ end
52
+
53
+ # Flattens given `data` Hash into an array of `Param`'s.
54
+ # Nested array are unwinded.
55
+ # Behavior is similar to `URL.encode_www_form`.
56
+ #
57
+ # @param [Hash] data
58
+ # @return [Array<FormData::MultiPart::Param>]
59
+ def self.coerce(data)
60
+ params = []
61
+
62
+ data.each do |name, values|
63
+ Array(values).each do |value|
64
+ params << new(name, value)
65
+ end
66
+ end
67
+
68
+ params
69
+ end
70
+
71
+ private
72
+
73
+ # Tells whenever value is a {FormData::File} or not.
74
+ #
75
+ # @return [Boolean]
76
+ def file?
77
+ @value.is_a? FormData::File
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,33 @@
1
+ module HTTP
2
+ module FormData
3
+ # `application/x-www-form-urlencoded` form data.
4
+ class Urlencoded
5
+ # @param [#to_h, Hash] data form data key-value Hash
6
+ def initialize(data)
7
+ @data = FormData.ensure_hash data
8
+ end
9
+
10
+ # Returns content to be used for HTTP request body.
11
+ #
12
+ # @return [String]
13
+ def to_s
14
+ URI.encode_www_form @data
15
+ end
16
+
17
+ # Returns MIME type to be used for HTTP request `Content-Type` header.
18
+ #
19
+ # @return [String]
20
+ def content_type
21
+ "application/x-www-form-urlencoded"
22
+ end
23
+
24
+ # Returns form data content size to be used for HTTP request
25
+ # `Content-Length` header.
26
+ #
27
+ # @return [Fixnum]
28
+ def content_length
29
+ to_s.bytesize
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,6 @@
1
+ module HTTP
2
+ module FormData
3
+ # Gem version.
4
+ VERSION = "1.0.0"
5
+ end
6
+ end
@@ -0,0 +1 @@
1
+ The HTTP Gem is an easy-to-use client library for making requests from Ruby.
@@ -0,0 +1,94 @@
1
+ # coding: utf-8
2
+
3
+ RSpec.describe HTTP::FormData::File do
4
+ let(:opts) { nil }
5
+
6
+ describe "#size" do
7
+ subject { described_class.new(file, opts).size }
8
+
9
+ context "when file given as a String" do
10
+ let(:file) { fixture("the-http-gem.info").to_s }
11
+ it { is_expected.to eq fixture("the-http-gem.info").size }
12
+ end
13
+
14
+ context "when file given as StringIO" do
15
+ let(:file) { StringIO.new "привет мир!" }
16
+ it { is_expected.to eq 20 }
17
+ end
18
+
19
+ context "when file given as File" do
20
+ let(:file) { fixture("the-http-gem.info").open }
21
+ after { file.close }
22
+ it { is_expected.to eq fixture("the-http-gem.info").size }
23
+ end
24
+ end
25
+
26
+ describe "#to_s" do
27
+ subject { described_class.new(file, opts).to_s }
28
+
29
+ context "when file given as a String" do
30
+ let(:file) { fixture("the-http-gem.info").to_s }
31
+ it { is_expected.to eq fixture("the-http-gem.info").read }
32
+ end
33
+
34
+ context "when file given as StringIO" do
35
+ let(:file) { StringIO.new "привет мир!" }
36
+ it { is_expected.to eq "привет мир!" }
37
+ end
38
+
39
+ context "when file given as File" do
40
+ let(:file) { fixture("the-http-gem.info").open }
41
+ after { file.close }
42
+ it { is_expected.to eq fixture("the-http-gem.info").read }
43
+ end
44
+ end
45
+
46
+ describe "#filename" do
47
+ subject { described_class.new(file, opts).filename }
48
+
49
+ context "when file given as a String" do
50
+ let(:file) { fixture("the-http-gem.info").to_s }
51
+
52
+ it { is_expected.to eq ::File.basename file }
53
+
54
+ context "and filename given with options" do
55
+ let(:opts) { { :filename => "foobar.txt" } }
56
+ it { is_expected.to eq "foobar.txt" }
57
+ end
58
+ end
59
+
60
+ context "when file given as StringIO" do
61
+ let(:file) { StringIO.new }
62
+
63
+ it { is_expected.to eq "stream-#{file.object_id}" }
64
+
65
+ context "and filename given with options" do
66
+ let(:opts) { { :filename => "foobar.txt" } }
67
+ it { is_expected.to eq "foobar.txt" }
68
+ end
69
+ end
70
+
71
+ context "when file given as File" do
72
+ let(:file) { fixture("the-http-gem.info").open }
73
+ after { file.close }
74
+
75
+ it { is_expected.to eq "the-http-gem.info" }
76
+
77
+ context "and filename given with options" do
78
+ let(:opts) { { :filename => "foobar.txt" } }
79
+ it { is_expected.to eq "foobar.txt" }
80
+ end
81
+ end
82
+ end
83
+
84
+ describe "#mime_type" do
85
+ subject { described_class.new(StringIO.new, opts).mime_type }
86
+
87
+ it { is_expected.to eq "application/octet-stream" }
88
+
89
+ context "when it was given with options" do
90
+ let(:opts) { { :mime_type => "application/json" } }
91
+ it { is_expected.to eq "application/json" }
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,40 @@
1
+ RSpec.describe HTTP::FormData::Multipart do
2
+ let(:file) { HTTP::FormData::File.new fixture "the-http-gem.info" }
3
+ let(:params) { { :foo => :bar, :baz => file } }
4
+ let(:boundary) { /-{21}[a-f0-9]{42}/ }
5
+ subject(:form_data) { HTTP::FormData::Multipart.new params }
6
+
7
+ describe "#content_type" do
8
+ subject { form_data.content_type }
9
+ it { is_expected.to match(/^multipart\/form-data; boundary=#{boundary}$/) }
10
+ end
11
+
12
+ describe "#content_length" do
13
+ subject { form_data.content_length }
14
+ it { is_expected.to eq form_data.to_s.bytesize }
15
+ end
16
+
17
+ describe "#to_s" do
18
+ def disposition(params)
19
+ params = params.map { |k, v| "#{k}=#{v.inspect}" }.join("; ")
20
+ "Content-Disposition: form-data; #{params}"
21
+ end
22
+
23
+ let(:crlf) { "\r\n" }
24
+
25
+ it "properly generates multipart data" do
26
+ boundary_value = form_data.content_type[/(#{boundary})$/, 1]
27
+
28
+ expect(form_data.to_s).to eq [
29
+ "--#{boundary_value}#{crlf}",
30
+ "#{disposition 'name' => 'foo'}#{crlf}",
31
+ "#{crlf}bar#{crlf}",
32
+ "--#{boundary_value}#{crlf}",
33
+ "#{disposition 'name' => 'baz', 'filename' => file.filename}#{crlf}",
34
+ "Content-Type: #{file.mime_type}#{crlf}",
35
+ "#{crlf}#{file}#{crlf}",
36
+ "--#{boundary_value}--"
37
+ ].join("")
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+
3
+ RSpec.describe HTTP::FormData::Urlencoded do
4
+ let(:data) { { "foo[bar]" => "test" } }
5
+ subject(:form_data) { HTTP::FormData::Urlencoded.new data }
6
+
7
+ describe "#content_type" do
8
+ subject { form_data.content_type }
9
+ it { is_expected.to eq "application/x-www-form-urlencoded" }
10
+ end
11
+
12
+ describe "#content_length" do
13
+ subject { form_data.content_length }
14
+ it { is_expected.to eq form_data.to_s.bytesize }
15
+
16
+ context "with unicode chars" do
17
+ let(:data) { { "foo[bar]" => "тест" } }
18
+ it { is_expected.to eq form_data.to_s.bytesize }
19
+ end
20
+ end
21
+
22
+ describe "#to_s" do
23
+ subject { form_data.to_s }
24
+ it { is_expected.to eq "foo%5Bbar%5D=test" }
25
+
26
+ context "with unicode chars" do
27
+ let(:data) { { "foo[bar]" => "тест" } }
28
+ it { is_expected.to eq "foo%5Bbar%5D=%D1%82%D0%B5%D1%81%D1%82" }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,48 @@
1
+ RSpec.describe HTTP::FormData do
2
+ describe ".create" do
3
+ subject { HTTP::FormData.create params }
4
+
5
+ context "when form has no files" do
6
+ let(:params) { { :foo => :bar } }
7
+ it { is_expected.to be_a HTTP::FormData::Urlencoded }
8
+ end
9
+
10
+ context "when form has at least one file param" do
11
+ let(:gemspec) { HTTP::FormData::File.new "gemspec" }
12
+ let(:params) { { :foo => :bar, :baz => gemspec } }
13
+ it { is_expected.to be_a HTTP::FormData::Multipart }
14
+ end
15
+
16
+ context "when form has file in an array param" do
17
+ let(:gemspec) { HTTP::FormData::File.new "gemspec" }
18
+ let(:params) { { :foo => :bar, :baz => [gemspec] } }
19
+ it { is_expected.to be_a HTTP::FormData::Multipart }
20
+ end
21
+ end
22
+
23
+ describe ".ensure_hash" do
24
+ subject(:ensure_hash) { HTTP::FormData.ensure_hash data }
25
+
26
+ context "when Hash given" do
27
+ let(:data) { { :foo => :bar } }
28
+ it { is_expected.to eq :foo => :bar }
29
+ end
30
+
31
+ context "when #to_h given" do
32
+ let(:data) { double(:to_h => { :foo => :bar }) }
33
+ it { is_expected.to eq :foo => :bar }
34
+ end
35
+
36
+ context "when nil given" do
37
+ let(:data) { nil }
38
+ it { is_expected.to eq({}) }
39
+ end
40
+
41
+ context "when neither Hash nor #to_h given" do
42
+ let(:data) { double }
43
+ it "fails with HTTP::FormData::Error" do
44
+ expect { ensure_hash }.to raise_error HTTP::FormData::Error
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,83 @@
1
+ # coding: utf-8
2
+
3
+ require "simplecov"
4
+ require "coveralls"
5
+
6
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
7
+ SimpleCov::Formatter::HTMLFormatter,
8
+ Coveralls::SimpleCov::Formatter
9
+ ]
10
+
11
+ SimpleCov.start { add_filter "/spec/" }
12
+
13
+ require "http/form_data"
14
+ require "support/fixtures_helper"
15
+
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+ RSpec.configure do |config|
18
+ config.expect_with :rspec do |expectations|
19
+ # This option will default to `true` in RSpec 4. It makes the `description`
20
+ # and `failure_message` of custom matchers include text for helper methods
21
+ # defined using `chain`, e.g.:
22
+ # be_bigger_than(2).and_smaller_than(4).description
23
+ # # => "be bigger than 2 and smaller than 4"
24
+ # ...rather than:
25
+ # # => "be bigger than 2"
26
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
27
+ end
28
+
29
+ config.mock_with :rspec do |mocks|
30
+ # Prevents you from mocking or stubbing a method that does not exist on
31
+ # a real object. This is generally recommended, and will default to
32
+ # `true` in RSpec 4.
33
+ mocks.verify_partial_doubles = true
34
+ end
35
+
36
+ # These two settings work together to allow you to limit a spec run
37
+ # to individual examples or groups you care about by tagging them with
38
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
39
+ # get run.
40
+ config.filter_run :focus
41
+ config.run_all_when_everything_filtered = true
42
+
43
+ # Limits the available syntax to the non-monkey patched syntax that is
44
+ # recommended. For more details, see:
45
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
46
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
47
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
48
+ config.disable_monkey_patching!
49
+
50
+ # This setting enables warnings. It's recommended, but in some cases may
51
+ # be too noisy due to issues in dependencies.
52
+ config.warnings = true
53
+
54
+ # Many RSpec users commonly either run the entire suite or an individual
55
+ # file, and it's useful to allow more verbose output when running an
56
+ # individual spec file.
57
+ if config.files_to_run.one?
58
+ # Use the documentation formatter for detailed output,
59
+ # unless a formatter has already been configured
60
+ # (e.g. via a command-line flag).
61
+ config.default_formatter = "doc"
62
+ end
63
+
64
+ # Print the 10 slowest examples and example groups at the
65
+ # end of the spec run, to help surface which specs are running
66
+ # particularly slow.
67
+ config.profile_examples = 10
68
+
69
+ # Run specs in random order to surface order dependencies. If you find an
70
+ # order dependency and want to debug it, you can fix the order by providing
71
+ # the seed, which is printed after each run.
72
+ # --seed 1234
73
+ config.order = :random
74
+
75
+ # Seed global randomization in this process using the `--seed` CLI option.
76
+ # Setting this allows you to use `--seed` to deterministically reproduce
77
+ # test failures related to randomization by passing the same `--seed` value
78
+ # as the one that triggered the failure.
79
+ Kernel.srand config.seed
80
+
81
+ # Include common helpers
82
+ config.include FixturesHelper
83
+ end
@@ -0,0 +1,13 @@
1
+ # coding: utf-8
2
+
3
+ require "pathname"
4
+
5
+ module FixturesHelper
6
+ def fixture(filename)
7
+ fixtures_root.join filename
8
+ end
9
+
10
+ def fixtures_root
11
+ @fixtures_root ||= Pathname.new(__FILE__).join("../../fixtures").realpath
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: http-form_data
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Aleksey V Zapparov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ description: Utility-belt to build form data request bodies. Provides support for
28
+ `application/x-www-form-urlencoded` and `multipart/form-data` types.
29
+ email:
30
+ - ixti@member.fsf.org
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - ".rspec"
37
+ - ".rubocop.yml"
38
+ - ".travis.yml"
39
+ - ".yardopts"
40
+ - CHANGES.md
41
+ - Gemfile
42
+ - Guardfile
43
+ - LICENSE.txt
44
+ - README.md
45
+ - Rakefile
46
+ - http-form_data.gemspec
47
+ - lib/http/form_data.rb
48
+ - lib/http/form_data/file.rb
49
+ - lib/http/form_data/multipart.rb
50
+ - lib/http/form_data/multipart/param.rb
51
+ - lib/http/form_data/urlencoded.rb
52
+ - lib/http/form_data/version.rb
53
+ - spec/fixtures/expected-multipart-body.tpl
54
+ - spec/fixtures/the-http-gem.info
55
+ - spec/lib/http/form_data/file_spec.rb
56
+ - spec/lib/http/form_data/multipart_spec.rb
57
+ - spec/lib/http/form_data/urlencoded_spec.rb
58
+ - spec/lib/http/form_data_spec.rb
59
+ - spec/spec_helper.rb
60
+ - spec/support/fixtures_helper.rb
61
+ homepage: https://github.com/httprb/form_data.rb
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.2.2
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: http-form_data-1.0.0
85
+ test_files:
86
+ - spec/fixtures/expected-multipart-body.tpl
87
+ - spec/fixtures/the-http-gem.info
88
+ - spec/lib/http/form_data/file_spec.rb
89
+ - spec/lib/http/form_data/multipart_spec.rb
90
+ - spec/lib/http/form_data/urlencoded_spec.rb
91
+ - spec/lib/http/form_data_spec.rb
92
+ - spec/spec_helper.rb
93
+ - spec/support/fixtures_helper.rb
94
+ has_rdoc: