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