http-form_data 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: