grape-batch 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5e7ee2f0355c89ffe06fa9619f2f702b0936e47d
4
+ data.tar.gz: 236ce32d1c3dde93ed7682a77151a335382c552d
5
+ SHA512:
6
+ metadata.gz: 7bddd90def48672d79f5ba3edc3a8dbe330f6ac87d31efb93b5f6faf8fd61b1a67f60096ab5f6d0df7dbb17c62b41a5ac35cf2efb945a6ad097b17b780e658e8
7
+ data.tar.gz: 45382d61dedfb0c418c6c6b184854a0fc8b328e2f3d961ca2c43e4a179f6b8837898903d6d846f201d26dfa5e6166f675b39c54c4dd3db354b671ae28a82896b
data/.gitignore ADDED
@@ -0,0 +1,28 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
23
+
24
+ # RubyMine
25
+ .idea
26
+
27
+ # OSX
28
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --require spec_helper
3
+ --format documentation
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ grape-batch
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in grape-batch.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Lionel Oto
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.
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # Grape::Batch
2
+
3
+ Rack middleware which extends Grape::API to support request batching.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'grape-batch'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install grape-batch
18
+
19
+ ## Usage
20
+ ### General considerations
21
+ This middleware is intended to be used with JSON Grape::API only.
22
+
23
+ ### Rails apps
24
+ 1. Create an initializer 'config/initializers/grape-batch.rb'
25
+ 2. Add the middleware to the stack
26
+ ```ruby
27
+ # grape-batch.rb
28
+ Rails.application.configure do
29
+ config.middleware.insert_before Rack::Sendfile, Grape::Batch::Base
30
+ end
31
+ ```
32
+
33
+ ### Sinatra and other Rack apps
34
+ ```ruby
35
+ # config.ru
36
+ require 'grape/batch'
37
+ use Grape::Batch::Base
38
+ ```
39
+
40
+ ### Settings
41
+ You can customize the middleware with a hash.
42
+
43
+ | Argument | Type | Default | Description
44
+ | :---: | :---: | :---: | :---:
45
+ | :limit | integer | 10 | Maximum number of batched requests allowed by the middleware
46
+ | :path | string | /batch | Route on which the middleware is mounted on
47
+ | :formatter | class | Grape::Batch::Response | The response formatter to use
48
+
49
+ #### Response formatter
50
+ #####Default format (success)
51
+ ```ruby
52
+ {success: RESOURCE_RESPONSE}
53
+ ```
54
+
55
+ #####Default format (failure)
56
+ ```ruby
57
+ {code: HTTP_STATUS_CODE, error: ERROR_MESSAGE}
58
+ ```
59
+
60
+ Can be inherited easily.
61
+ ```ruby
62
+ class MyFormatter < Grape::Batch::Response
63
+ def self.format(status, headers, body)
64
+ # My awesome formatting
65
+ end
66
+ end
67
+ ```
68
+
69
+ ### Input format
70
+ POST http request on the default URL with a similar body:
71
+ ```ruby
72
+ {
73
+ requests:
74
+ [
75
+ {
76
+ method: 'GET',
77
+ path: '/api/v1/users'
78
+ },
79
+ {
80
+ method: 'POST',
81
+ path: '/api/v1/login',
82
+ body: { token: 'nrg55xwrd45' }
83
+ }
84
+ ]
85
+ }
86
+ ```
87
+
88
+ 'body' is optional.
89
+
90
+ ## Contributing
91
+
92
+ 1. Fork it ( https://github.com/c4mprod/grape-batch/fork )
93
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
94
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
95
+ 4. Push to the branch (`git push origin my-new-feature`)
96
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'grape/batch/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'grape-batch'
8
+ spec.version = Grape::Batch::VERSION
9
+ spec.authors = ['Lionel Oto', 'Cédric Darné']
10
+ spec.email = ['lionel.oto@c4mprod.com', 'cedric.darne@c4mprod.com']
11
+ spec.summary = %q{Extends Grape::API to support request batching }
12
+ spec.homepage = ''
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_runtime_dependency 'rack', '~> 1.5'
21
+ spec.add_runtime_dependency 'grape', '~> 0.9.0'
22
+ spec.add_runtime_dependency 'multi_json', '>= 1.0'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.6'
25
+ spec.add_development_dependency 'rake', '~> 10.3.2'
26
+ spec.add_development_dependency 'rspec', '~> 3.1.0'
27
+ spec.add_development_dependency 'rack-test', '~> 0.6.2'
28
+ end
@@ -0,0 +1,40 @@
1
+ module Grape
2
+ module Batch
3
+ class RequestBodyError < ArgumentError; end
4
+ class TooManyRequestsError < StandardError; end
5
+
6
+ class Validator
7
+ def self.parse(env, limit)
8
+ rack_input = env['rack.input'].read
9
+ raise RequestBodyError.new('Request body is blank') unless rack_input.length > 0
10
+
11
+ begin
12
+ batch_body = MultiJson.decode(rack_input)
13
+ rescue MultiJson::ParseError
14
+ raise RequestBodyError.new('Request body is not valid JSON')
15
+ end
16
+
17
+ raise RequestBodyError.new('Request body is nil') unless batch_body
18
+ raise RequestBodyError.new('Request body is not well formatted') unless batch_body.is_a?(Hash)
19
+
20
+ batch_requests = batch_body['requests']
21
+ raise RequestBodyError.new("'requests' object is missing in request body") unless batch_requests
22
+ raise RequestBodyError.new("'requests' is not well formatted") unless batch_requests.is_a?(Array)
23
+ raise TooManyRequestsError.new('Batch requests limit exceeded') if batch_requests.count > limit
24
+
25
+ batch_requests.each do |request|
26
+ raise RequestBodyError.new("'method' is missing in one of request objects") unless request['method']
27
+ raise RequestBodyError.new("'method' is invalid in one of request objects") unless request['method'].is_a?(String)
28
+ unless ['GET', 'POST', 'PUT', 'DELETE'].include?(request['method'])
29
+ raise RequestBodyError.new("'method' is invalid in one of request objects")
30
+ end
31
+
32
+ raise RequestBodyError.new("'path' is missing in one of request objects") unless request['path']
33
+ raise RequestBodyError.new("'path' is invalid in one of request objects") unless request['path'].is_a?(String)
34
+ end
35
+
36
+ batch_requests
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,14 @@
1
+ module Grape
2
+ module Batch
3
+ class Response
4
+ def self.format(status, headers, response)
5
+ if response
6
+ body = response.respond_to?(:body) ? response.body.join : response.join
7
+ result = MultiJson.decode(body)
8
+ end
9
+
10
+ (200..299).include?(status) ? {success: result} : {code: status, error: result['error']}
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ module Grape
2
+ module Batch
3
+ VERSION = '1.0.0'
4
+ end
5
+ end
@@ -0,0 +1,69 @@
1
+ require 'grape/batch/parser'
2
+ require 'grape/batch/response'
3
+ require 'grape/batch/version'
4
+ require 'multi_json'
5
+
6
+ module Grape
7
+ module Batch
8
+ class Base
9
+ def initialize(app, opt = {})
10
+ @app = app
11
+ @limit = opt[:limit] || 10
12
+ @path = opt[:path] || '/batch'
13
+ @response_klass = opt[:formatter] || Grape::Batch::Response
14
+ end
15
+
16
+ def call(env)
17
+ return @app.call(env) unless is_batch_request?(env)
18
+ batch_call(env)
19
+ end
20
+
21
+ def batch_call(env)
22
+ status = 200
23
+ headers = {'Content-Type' => 'application/json'}
24
+
25
+ begin
26
+ batch_requests = Grape::Batch::Validator::parse(env, @limit)
27
+ result = dispatch(env, batch_requests)
28
+ body = MultiJson.encode(result)
29
+ rescue Grape::Batch::RequestBodyError, Grape::Batch::TooManyRequestsError => e
30
+ e.class == TooManyRequestsError ? status = 429 : status = 400
31
+ body = [e.message]
32
+ end
33
+
34
+ [status, headers, body]
35
+ end
36
+
37
+ private
38
+
39
+ def is_batch_request?(env)
40
+ env['PATH_INFO'].start_with?(@path) &&
41
+ env['REQUEST_METHOD'] == 'POST' &&
42
+ env['CONTENT_TYPE'] == 'application/json'
43
+ end
44
+
45
+ def dispatch(env, batch_requests)
46
+ request_env = env.dup
47
+
48
+ batch_requests.map do |request|
49
+ method = request['method']
50
+ path = request['path']
51
+ body = request['body'].is_a?(Hash) ? request['body'] : {}
52
+
53
+ request_env['REQUEST_METHOD'] = method
54
+ request_env['PATH_INFO'] = path
55
+ if method == 'GET'
56
+ request_env['rack.input'] = StringIO.new(MultiJson.encode({}))
57
+ request_env['QUERY_STRING'] = URI.encode_www_form(body.to_a)
58
+ else
59
+ request_env['rack.input'] = StringIO.new(MultiJson.encode(body))
60
+ end
61
+
62
+ status, headers, response = @app.call(request_env)
63
+
64
+ @response_klass::format(status, headers, response)
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
data/spec/api.rb ADDED
@@ -0,0 +1,57 @@
1
+ module Twitter
2
+ class API < Grape::API
3
+ version 'v1', using: :path
4
+ format :json
5
+ prefix 'api'
6
+
7
+ resource :hello do
8
+ get do
9
+ 'world'
10
+ end
11
+
12
+ post do
13
+ 'world'
14
+ end
15
+ end
16
+
17
+ resource :failure do
18
+ desc 'Failure'
19
+ get do
20
+ error!('Failed as expected', 503)
21
+ end
22
+ end
23
+
24
+ resource :user do
25
+ params do
26
+ requires :id, type: Integer, desc: "User id."
27
+ end
28
+ route_param :id do
29
+ get do
30
+ "user #{params[:id]}"
31
+ end
32
+ end
33
+ end
34
+
35
+ resource :status do
36
+ params do
37
+ requires :id, type: Integer, desc: "User id."
38
+ end
39
+ get do
40
+ "status #{params[:id]}"
41
+ end
42
+
43
+ params do
44
+ requires :id, type: Integer, desc: "User id."
45
+ end
46
+ post do
47
+ "status #{params[:id]}"
48
+ end
49
+ end
50
+
51
+ # 404
52
+ #
53
+ route :any, '*path' do
54
+ error!("#{@env['PATH_INFO']} not found", 404)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+ require 'rack/test'
3
+ require 'grape/batch'
4
+ require 'grape'
5
+ require 'api'
6
+
7
+ RSpec.describe Grape::Batch::Base do
8
+ let(:app) { Twitter::API.new }
9
+ let(:stack) { Grape::Batch::Base.new(app) }
10
+ let(:request) { Rack::MockRequest.new(stack) }
11
+
12
+ def encode(message)
13
+ MultiJson.encode(message)
14
+ end
15
+
16
+ def decode(message)
17
+ MultiJson.decode(message)
18
+ end
19
+
20
+ describe '/api' do
21
+ describe 'GET /hello' do
22
+ let(:response) { request.get('/api/v1/hello') }
23
+
24
+ it { expect(response.status).to eq(200) }
25
+ it { expect(response.body).to eq(encode('world')) }
26
+ end
27
+
28
+ describe 'GET /failure' do
29
+ let(:response) { request.get('/api/v1/failure') }
30
+
31
+ it { expect(response.status).to eq(503) }
32
+ it { expect(response.body).to eq(encode({error: 'Failed as expected'})) }
33
+ end
34
+ end
35
+
36
+ describe '/batch' do
37
+ let(:request_body) { nil }
38
+ let(:response) { request.post('/batch', {'CONTENT_TYPE' => 'application/json', input: request_body}) }
39
+
40
+ context 'with invalid body' do
41
+ it { expect(response.status).to eq(400) }
42
+
43
+ context 'when body == nil' do
44
+ it { expect(response.body).to eq('Request body is blank') }
45
+ end
46
+
47
+ context 'when body is empty' do
48
+ let(:request_body) { '' }
49
+ it { expect(response.body).to eq('Request body is blank') }
50
+ end
51
+
52
+ context 'when body is not valid JSON' do
53
+ let(:request_body) { 'ads[}' }
54
+ it { expect(response.body).to eq('Request body is not valid JSON') }
55
+ end
56
+
57
+ context 'when body == null' do
58
+ let(:request_body) { 'null' }
59
+ it { expect(response.body).to eq('Request body is nil') }
60
+ end
61
+
62
+ context 'when body is not a hash' do
63
+ let(:request_body) { '[1, 2, 3]' }
64
+ it { expect(response.body).to eq('Request body is not well formatted') }
65
+ end
66
+
67
+ context "when body['requests'] == nil" do
68
+ let(:request_body) { '{}' }
69
+ it { expect(response.body).to eq("'requests' object is missing in request body") }
70
+ end
71
+
72
+ context "when body['requests'] is not an array" do
73
+ let(:request_body) { encode({requests: 'request'}) }
74
+ it { expect(response.body).to eq("'requests' is not well formatted") }
75
+ end
76
+
77
+ context 'when request limit is exceeded' do
78
+ let(:request_body) { encode({requests: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]}) }
79
+ it { expect(response.body).to eq('Batch requests limit exceeded') }
80
+ end
81
+
82
+ describe 'method attribute in request object' do
83
+ context 'method is missing' do
84
+ let(:request_body) { encode({requests: [{}]}) }
85
+ it { expect(response.body).to eq("'method' is missing in one of request objects") }
86
+ end
87
+
88
+ context 'method is not a String' do
89
+ let(:request_body) { encode({requests: [{method: true}]}) }
90
+ it { expect(response.body).to eq("'method' is invalid in one of request objects") }
91
+ end
92
+
93
+ context 'method is invalid' do
94
+ let(:request_body) { encode({requests: [{method: 'TRACE'}]}) }
95
+ it { expect(response.body).to eq("'method' is invalid in one of request objects") }
96
+ end
97
+ end
98
+
99
+ describe 'path attribute in request object' do
100
+ context 'path is missing' do
101
+ let(:request_body) { encode({requests: [{method: 'GET'}]}) }
102
+ it { expect(response.body).to eq("'path' is missing in one of request objects") }
103
+ end
104
+
105
+ context 'path is not a String' do
106
+ let(:request_body) { encode({requests: [{method: 'GET', path: 123}]}) }
107
+ it { expect(response.body).to eq("'path' is invalid in one of request objects") }
108
+ end
109
+ end
110
+ end
111
+
112
+ describe 'GET' do
113
+ context 'with no parameters' do
114
+ let(:request_body) { encode({requests: [{method: 'GET', path: '/api/v1/hello'}]}) }
115
+ it { expect(response.status).to eq(200) }
116
+ it { expect(response.body).to eq(encode([{success: 'world'}])) }
117
+ end
118
+
119
+ context 'with parameters' do
120
+ let(:request_body) { encode({requests: [{method: 'GET', path: '/api/v1/user/856'}]}) }
121
+ it { expect(response.status).to eq(200) }
122
+ it { expect(response.body).to eq(encode([{success: 'user 856'}])) }
123
+ end
124
+
125
+ context 'with a body' do
126
+ let(:request_body) { encode({requests: [{method: 'GET', path: '/api/v1/status', body: {id: 856}}]}) }
127
+ it { expect(response.status).to eq(200) }
128
+ it { expect(response.body).to eq(encode([{success: 'status 856'}])) }
129
+ end
130
+
131
+ describe '404 errors' do
132
+ let(:request_body) { encode({requests: [{method: 'GET', path: '/api/v1/unknown'}]}) }
133
+ it { expect(response.status).to eq(200) }
134
+ it { expect(response.body).to eq(encode([{code: 404, message: '/api/v1/unknown not found'}])) }
135
+ end
136
+ end
137
+
138
+ describe 'POST' do
139
+ context 'with no parameters' do
140
+ let(:request_body) { encode({requests: [{method: 'POST', path: '/api/v1/hello'}]}) }
141
+ it { expect(response.status).to eq(200) }
142
+ it { expect(response.body).to eq(encode([{success: 'world'}])) }
143
+ end
144
+
145
+ context 'with a body' do
146
+ let(:request_body) { encode({requests: [{method: 'POST', path: '/api/v1/status', body: {id: 856}}]}) }
147
+ it { expect(response.status).to eq(200) }
148
+ it { expect(response.body).to eq(encode([{success: 'status 856'}])) }
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,85 @@
1
+ # This file was generated by the `rails generate rspec:install` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
4
+ # file to always be loaded, without a need to explicitly require it in any files.
5
+ #
6
+ # Given that it is always loaded, you are encouraged to keep this file as
7
+ # light-weight as possible. Requiring heavyweight dependencies from this file
8
+ # will add to the boot time of your test suite on EVERY test run, even for an
9
+ # individual file that may not need all of that loaded. Instead, consider making
10
+ # a separate helper file that requires the additional dependencies and performs
11
+ # the additional setup, and require it from the spec files that actually need it.
12
+ #
13
+ # The `.rspec` file also contains a few flags that are not defaults but that
14
+ # users commonly want.
15
+ #
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+ RSpec.configure do |config|
18
+ # rspec-expectations config goes here. You can use an alternate
19
+ # assertion/expectation library such as wrong or the stdlib/minitest
20
+ # assertions if you prefer.
21
+ config.expect_with :rspec do |expectations|
22
+ # This option will default to `true` in RSpec 4. It makes the `description`
23
+ # and `failure_message` of custom matchers include text for helper methods
24
+ # defined using `chain`, e.g.:
25
+ # be_bigger_than(2).and_smaller_than(4).description
26
+ # # => "be bigger than 2 and smaller than 4"
27
+ # ...rather than:
28
+ # # => "be bigger than 2"
29
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
30
+ end
31
+
32
+ # rspec-mocks config goes here. You can use an alternate test double
33
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
34
+ config.mock_with :rspec do |mocks|
35
+ # Prevents you from mocking or stubbing a method that does not exist on
36
+ # a real object. This is generally recommended, and will default to
37
+ # `true` in RSpec 4.
38
+ mocks.verify_partial_doubles = true
39
+ end
40
+
41
+ # The settings below are suggested to provide a good initial experience
42
+ # with RSpec, but feel free to customize to your heart's content.
43
+ =begin
44
+ # These two settings work together to allow you to limit a spec run
45
+ # to individual examples or groups you care about by tagging them with
46
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
47
+ # get run.
48
+ config.filter_run :focus
49
+ config.run_all_when_everything_filtered = true
50
+
51
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
52
+ # For more details, see:
53
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
54
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
55
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
56
+ config.disable_monkey_patching!
57
+
58
+ # Many RSpec users commonly either run the entire suite or an individual
59
+ # file, and it's useful to allow more verbose output when running an
60
+ # individual spec file.
61
+ if config.files_to_run.one?
62
+ # Use the documentation formatter for detailed output,
63
+ # unless a formatter has already been configured
64
+ # (e.g. via a command-line flag).
65
+ config.default_formatter = 'doc'
66
+ end
67
+
68
+ # Print the 10 slowest examples and example groups at the
69
+ # end of the spec run, to help surface which specs are running
70
+ # particularly slow.
71
+ config.profile_examples = 10
72
+
73
+ # Run specs in random order to surface order dependencies. If you find an
74
+ # order dependency and want to debug it, you can fix the order by providing
75
+ # the seed, which is printed after each run.
76
+ # --seed 1234
77
+ config.order = :random
78
+
79
+ # Seed global randomization in this process using the `--seed` CLI option.
80
+ # Setting this allows you to use `--seed` to deterministically reproduce
81
+ # test failures related to randomization by passing the same `--seed` value
82
+ # as the one that triggered the failure.
83
+ Kernel.srand config.seed
84
+ =end
85
+ end
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grape-batch
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Lionel Oto
8
+ - Cédric Darné
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-11-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rack
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '1.5'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '1.5'
28
+ - !ruby/object:Gem::Dependency
29
+ name: grape
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 0.9.0
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 0.9.0
42
+ - !ruby/object:Gem::Dependency
43
+ name: multi_json
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '1.0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '1.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: bundler
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '1.6'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.6'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rake
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: 10.3.2
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: 10.3.2
84
+ - !ruby/object:Gem::Dependency
85
+ name: rspec
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: 3.1.0
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: 3.1.0
98
+ - !ruby/object:Gem::Dependency
99
+ name: rack-test
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: 0.6.2
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: 0.6.2
112
+ description:
113
+ email:
114
+ - lionel.oto@c4mprod.com
115
+ - cedric.darne@c4mprod.com
116
+ executables: []
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".gitignore"
121
+ - ".rspec"
122
+ - ".ruby-gemset"
123
+ - ".ruby-version"
124
+ - Gemfile
125
+ - LICENSE.txt
126
+ - README.md
127
+ - Rakefile
128
+ - grape-batch.gemspec
129
+ - lib/grape/batch.rb
130
+ - lib/grape/batch/parser.rb
131
+ - lib/grape/batch/response.rb
132
+ - lib/grape/batch/version.rb
133
+ - spec/api.rb
134
+ - spec/requests_spec.rb
135
+ - spec/spec_helper.rb
136
+ homepage: ''
137
+ licenses:
138
+ - MIT
139
+ metadata: {}
140
+ post_install_message:
141
+ rdoc_options: []
142
+ require_paths:
143
+ - lib
144
+ required_ruby_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ requirements: []
155
+ rubyforge_project:
156
+ rubygems_version: 2.2.2
157
+ signing_key:
158
+ specification_version: 4
159
+ summary: Extends Grape::API to support request batching
160
+ test_files:
161
+ - spec/api.rb
162
+ - spec/requests_spec.rb
163
+ - spec/spec_helper.rb