batch_api2 0.3.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.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +30 -0
  4. data/changelog.md +74 -0
  5. data/lib/batch_api.rb +28 -0
  6. data/lib/batch_api/batch_error.rb +41 -0
  7. data/lib/batch_api/configuration.rb +36 -0
  8. data/lib/batch_api/error_wrapper.rb +44 -0
  9. data/lib/batch_api/internal_middleware.rb +87 -0
  10. data/lib/batch_api/internal_middleware/decode_json_body.rb +28 -0
  11. data/lib/batch_api/internal_middleware/response_filter.rb +27 -0
  12. data/lib/batch_api/operation.rb +2 -0
  13. data/lib/batch_api/operation/rack.rb +76 -0
  14. data/lib/batch_api/operation/rails.rb +42 -0
  15. data/lib/batch_api/processor.rb +113 -0
  16. data/lib/batch_api/processor/executor.rb +18 -0
  17. data/lib/batch_api/processor/sequential.rb +29 -0
  18. data/lib/batch_api/rack_middleware.rb +37 -0
  19. data/lib/batch_api/response.rb +38 -0
  20. data/lib/batch_api/utils.rb +17 -0
  21. data/lib/batch_api/version.rb +3 -0
  22. data/lib/tasks/batch_api_tasks.rake +4 -0
  23. data/readme.md +243 -0
  24. data/spec/dummy/Gemfile +1 -0
  25. data/spec/dummy/Gemfile.lock +8 -0
  26. data/spec/dummy/README.rdoc +261 -0
  27. data/spec/dummy/Rakefile +15 -0
  28. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  29. data/spec/dummy/app/assets/javascripts/endpoints.js +2 -0
  30. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  31. data/spec/dummy/app/assets/stylesheets/endpoints.css +4 -0
  32. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  33. data/spec/dummy/app/controllers/endpoints_controller.rb +36 -0
  34. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  35. data/spec/dummy/app/helpers/endpoints_helper.rb +2 -0
  36. data/spec/dummy/app/views/endpoints/get.html.erb +2 -0
  37. data/spec/dummy/app/views/endpoints/post.html.erb +2 -0
  38. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  39. data/spec/dummy/bin/bundle +3 -0
  40. data/spec/dummy/bin/rails +4 -0
  41. data/spec/dummy/bin/rake +4 -0
  42. data/spec/dummy/bin/setup +29 -0
  43. data/spec/dummy/config.ru +4 -0
  44. data/spec/dummy/config/application.rb +32 -0
  45. data/spec/dummy/config/boot.rb +3 -0
  46. data/spec/dummy/config/database.yml +25 -0
  47. data/spec/dummy/config/environment.rb +5 -0
  48. data/spec/dummy/config/environments/development.rb +41 -0
  49. data/spec/dummy/config/environments/production.rb +79 -0
  50. data/spec/dummy/config/environments/test.rb +42 -0
  51. data/spec/dummy/config/initializers/assets.rb +11 -0
  52. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  53. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  54. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  55. data/spec/dummy/config/initializers/inflections.rb +16 -0
  56. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  57. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  58. data/spec/dummy/config/initializers/session_store.rb +3 -0
  59. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  60. data/spec/dummy/config/locales/en.yml +23 -0
  61. data/spec/dummy/config/routes.rb +64 -0
  62. data/spec/dummy/config/secrets.yml +22 -0
  63. data/spec/dummy/db/test.sqlite3 +0 -0
  64. data/spec/dummy/public/404.html +26 -0
  65. data/spec/dummy/public/422.html +26 -0
  66. data/spec/dummy/public/500.html +25 -0
  67. data/spec/dummy/public/favicon.ico +0 -0
  68. data/spec/dummy/script/rails +6 -0
  69. data/spec/dummy/test/functional/endpoints_controller_test.rb +14 -0
  70. data/spec/dummy/test/unit/helpers/endpoints_helper_test.rb +4 -0
  71. data/spec/lib/batch_api_spec.rb +20 -0
  72. data/spec/lib/batch_error_spec.rb +23 -0
  73. data/spec/lib/configuration_spec.rb +30 -0
  74. data/spec/lib/error_wrapper_spec.rb +68 -0
  75. data/spec/lib/internal_middleware/decode_json_body_spec.rb +44 -0
  76. data/spec/lib/internal_middleware/response_filter_spec.rb +61 -0
  77. data/spec/lib/internal_middleware_spec.rb +93 -0
  78. data/spec/lib/operation/rack_spec.rb +246 -0
  79. data/spec/lib/operation/rails_spec.rb +100 -0
  80. data/spec/lib/processor/executor_spec.rb +22 -0
  81. data/spec/lib/processor/sequential_spec.rb +39 -0
  82. data/spec/lib/processor_spec.rb +136 -0
  83. data/spec/lib/rack_middleware_spec.rb +103 -0
  84. data/spec/lib/response_spec.rb +53 -0
  85. data/spec/rack-integration/rails_spec.rb +10 -0
  86. data/spec/rack-integration/shared_examples.rb +273 -0
  87. data/spec/rack-integration/sinatra_integration_spec.rb +19 -0
  88. data/spec/spec_helper.rb +42 -0
  89. data/spec/support/sinatra_app.rb +54 -0
  90. data/spec/support/sinatra_xhr.rb +13 -0
  91. metadata +214 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5555904a32bd830b6afdb63a282135a2a33cd995
4
+ data.tar.gz: 81cd01446df080d2ce53b865eaa8e24ceedeff71
5
+ SHA512:
6
+ metadata.gz: ebdd0d77a56b9d44b1e3872d95fc017c72fbaa8ec3b4daaee9bfbcdb1a6ea78e88ed98c6989c7a5c57c9446ad6637c1386c8f8487e97e258bf6e37c295dae0ae
7
+ data.tar.gz: a74c28e3dc240b0c1ed2baa01fa274a86fef87100ca0793d5357135f09b5f6ef40345c9c5da9ba805a2ebe29a63328337a8656636eb8b0e48afc9478b5511a49
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'BatchApi'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ Bundler::GemHelper.install_tasks
24
+
25
+ require 'rspec/core/rake_task'
26
+ RSpec::Core::RakeTask.new do |t|
27
+ t.rspec_opts = ["--color", '--format doc', '--order rand']
28
+ end
29
+
30
+ task :default => :spec
@@ -0,0 +1,74 @@
1
+ v0.3.0
2
+
3
+ New features:
4
+
5
+ * guard against nil REQUEST_URI (thanks, pbendersky and dlackty!)
6
+ * don't parse empty bodies as JSON (thanks, trungpham and dlackty!)
7
+
8
+ Testing improvements:
9
+
10
+ * modernize test infrastructure and test against newer Ruby versions (thanks, dwaller, pmq20 and dlackty!)
11
+ * update test Rails app version to 4.2
12
+ * modernize RSpec syntax using transpec
13
+
14
+
15
+ v0.2.2
16
+ * Update documentation to remove old options and add installation section
17
+ * Update gems
18
+
19
+ v0.2.1
20
+ * Default the method to GET if no method is specified
21
+
22
+ v0.2.0
23
+ * Refactor app to use internal middlewares for handling operations
24
+ * Refactor JSON decoding to a middleware
25
+ * Remove timestamp option
26
+
27
+ v0.1.4
28
+ * Refactor errors into ErrorWrapper/BatchError
29
+ * Allow specification of custom status codes raised for errors
30
+
31
+ v0.1.3
32
+ * Refactor config to use a struct
33
+ * Update readme to cover HTTP pipelining
34
+
35
+ v0.1.2
36
+ * Rewrite the readme
37
+ * Add travis icon
38
+
39
+ v0.1.1
40
+ * Fix dumb error
41
+
42
+ v0.1.0
43
+ * Add direct support for Rails for path params
44
+ * Fix spec bugs
45
+
46
+ v0.0.8
47
+ * Return the results wrapped in a hash, rather than a raw array
48
+ * Add process_start timestamp option
49
+
50
+ v0.0.7
51
+ * Return more specific error codes to alert clients to param errors
52
+
53
+ v0.0.6
54
+ * Refactor Rack middleware to be Sinatra-compatible
55
+
56
+ v0.0.5
57
+ * Add setting to decode JSON responses before sending batch results
58
+
59
+ v0.0.4
60
+ * Switch from Rails-based process to a Rack middleware
61
+ * Improve tests
62
+
63
+ v0.0.3
64
+ * Encapsulate processing into a Processor module
65
+ * Prepare for parallel processing in the future
66
+ * Add specific errors
67
+ * Allow controlling the routing target
68
+
69
+ v0.0.2
70
+ * Add config module
71
+ * Add options for operation limit, endpoint, and verb
72
+
73
+ v0.0.1
74
+ * Initial build
@@ -0,0 +1,28 @@
1
+ require 'batch_api/configuration'
2
+ require 'batch_api/version'
3
+ require 'batch_api/utils'
4
+ require 'batch_api/processor'
5
+
6
+ require 'batch_api/internal_middleware'
7
+ require 'batch_api/rack_middleware'
8
+
9
+ require 'batch_api/error_wrapper'
10
+ require 'batch_api/batch_error'
11
+
12
+ module BatchApi
13
+
14
+ # Public: access the main Batch API configuration object.
15
+ #
16
+ # Returns a BatchApi::Configuration instance
17
+ def self.config
18
+ @config ||= Configuration.new
19
+ end
20
+
21
+ # Public: are we in Rails? This partly exists just so that you
22
+ # can stub it in the tests.
23
+ #
24
+ # Returns true if Rails is a defined constant, false otherwise.
25
+ def self.rails?
26
+ defined?(Rails)
27
+ end
28
+ end
@@ -0,0 +1,41 @@
1
+ module BatchApi
2
+ module Errors
3
+ # Public: a module that tags Batch API errors and provides a default
4
+ # status.
5
+ module BatchError
6
+ # Public: the status code for this type of error.
7
+ # Subclasses can change this as desired.
8
+ def status_code; 500; end
9
+ end
10
+
11
+ # Public: an error thrown if an invalid option is
12
+ # specificed.
13
+ class BadOptionError < ArgumentError
14
+ include BatchError
15
+ # Public: the status code for this error.
16
+ def status_code; 422; end
17
+ end
18
+
19
+ # Public: an error thrown if too many operations are provided.
20
+ class OperationLimitExceeded < ArgumentError
21
+ include BatchError
22
+ # Public: the status code for this error.
23
+ def status_code; 422; end
24
+ end
25
+
26
+ # Public: an error thrown if no operations are provided.
27
+ class NoOperationsError < ArgumentError
28
+ include BatchError
29
+ # Public: the status code for this error.
30
+ def status_code; 422; end
31
+ end
32
+
33
+ # Public: an error thrown if one of the batch operations
34
+ # is somehow invalid (missing key parameters, etc.).
35
+ class MalformedOperationError < ArgumentError
36
+ include BatchError
37
+ # Public: the status code for this error.
38
+ def status_code; 422; end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,36 @@
1
+ require 'batch_api/internal_middleware'
2
+
3
+ module BatchApi
4
+ # Public: configuration options.
5
+ # Currently, you can set:
6
+ # - endpoint: (URL) through which the Batch API will be exposed (default
7
+ # "/batch)
8
+ # - verb: through which it's accessed (default "POST")
9
+ # - limit: how many requests can be processed in a single request
10
+ # (default 50)
11
+ #
12
+ # There are also two middleware-related options -- check out middleware.rb
13
+ # for more information.
14
+ # - global_middleware: any middlewares to use round the entire batch request
15
+ # (such as authentication, etc.)
16
+ # - per_op_middleware: any middlewares to run around each individual request
17
+ # (adding headers, decoding JSON, etc.)
18
+ CONFIGURATION_OPTIONS = {
19
+ verb: :post,
20
+ endpoint: "/batch",
21
+ limit: 50,
22
+ batch_middleware: InternalMiddleware::DEFAULT_BATCH_MIDDLEWARE,
23
+ operation_middleware: InternalMiddleware::DEFAULT_OPERATION_MIDDLEWARE
24
+ }
25
+
26
+ # Batch API Configuration
27
+ class Configuration < Struct.new(*CONFIGURATION_OPTIONS.keys)
28
+ # Public: initialize a new configuration option and apply the defaults.
29
+ def initialize
30
+ super
31
+ CONFIGURATION_OPTIONS.each {|k, v| self[k] = v}
32
+ end
33
+ end
34
+ end
35
+
36
+
@@ -0,0 +1,44 @@
1
+ module BatchApi
2
+ # Public: wrap an error thrown during a batch operation.
3
+ # This has a body class and a cookies accessor and can
4
+ # function in place of a regular BatchResponse object.
5
+ class ErrorWrapper
6
+ # Public: create a new ErrorWrapper from an error object.
7
+ def initialize(error)
8
+ @error = error
9
+ @status_code = error.status_code if error.respond_to?(:status_code)
10
+ end
11
+
12
+ # Public: the error details as a hash, which can be returned
13
+ # to clients as JSON.
14
+ def body
15
+ message = if self.class.expose_backtrace?
16
+ {
17
+ message: @error.message,
18
+ backtrace: @error.backtrace
19
+ }
20
+ else
21
+ { message: @error.message }
22
+ end
23
+ { error: message }
24
+ end
25
+
26
+ # Public: turn the error body into a Rack-compatible body component.
27
+ #
28
+ # Returns: an Array with the error body represented as JSON.
29
+ def render
30
+ [status_code, RackMiddleware.content_type, [body.to_json]]
31
+ end
32
+
33
+ # Public: the status code to return for the given error.
34
+ def status_code
35
+ @status_code || 500
36
+ end
37
+
38
+ # Internal: whether the backtrace should be exposed in the response.
39
+ # Currently Rails-specific, needs to be generalized (to ENV["RACK_ENV"])?
40
+ def self.expose_backtrace?
41
+ !Rails.env.production?
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,87 @@
1
+ require 'middleware'
2
+ require 'batch_api/processor/sequential'
3
+ require 'batch_api/processor/executor'
4
+ require 'batch_api/internal_middleware/decode_json_body'
5
+ require 'batch_api/internal_middleware/response_filter'
6
+
7
+ module BatchApi
8
+ # Public: the internal middleware system used to process batch requests.
9
+ # (Not to be confused with RackMiddleware, which handles incoming Rack
10
+ # request.) Based on Mitchel Hashimoto's middleware gem.
11
+ #
12
+ # There are actually two internal middleware stacks, one run around the
13
+ # entire request and the other run around each individual operation.
14
+ #
15
+ # The batch_middleware stack consists of two parts:
16
+ # 1) Custom middlewares - these middlewares will be run around the entire
17
+ # sequence of batch requests. Useful for global processing on a batch
18
+ # request; for instance, the timestamp middleware adds a timestamp based on
19
+ # when the original request was started. This should return an array of
20
+ # BatchApi::Response objects.
21
+ #
22
+ # 2) Processor - this automatically-provided middleware will execute all
23
+ # batch requests either sequentially (currently the only option) or in
24
+ # parallel (in the future). This will return an array of
25
+ # BatchApi::Response objects.
26
+ #
27
+ # The operation_middleware stack also consists of two parts:
28
+ # 1) Operation - these middlewares will run once per each operation, giving
29
+ # you a chance to alter the results or details of an op -- for instance,
30
+ # decoding the body if it's JSON. This should return an individual
31
+ # BatchApi::Response object.
32
+ #
33
+ # 2) Executor - this automatically-provided middleware actually executes
34
+ # the individual Rack request, and returns a BatchApi::Response object.
35
+ #
36
+ # Symetry, right?
37
+ #
38
+ # All middlewares#call will receive the following as an env hash:
39
+ # {
40
+ # # for batch middlewares
41
+ # ops: [], # the total set of operations
42
+ # # for operation middlewares
43
+ # op: obj, # the specific operation being executed
44
+ # # all middlewares get the following:
45
+ # rack_env: {}, # the Rack environment
46
+ # rack_app: app # the Rack application
47
+ # }
48
+ #
49
+ # All middlewares should return the result of their individual operation or
50
+ # the array of operation results, depending on where they are in the chain.
51
+ # (See above.)
52
+ module InternalMiddleware
53
+ # Public: the default internal middlewares to be run around the entire
54
+ # operation.
55
+ DEFAULT_BATCH_MIDDLEWARE = Proc.new {}
56
+
57
+ # Public: the default internal middlewares to be run around each batch
58
+ # operation.
59
+ DEFAULT_OPERATION_MIDDLEWARE = Proc.new do
60
+ # Decode JSON response bodies, so they're not double-encoded.
61
+ use InternalMiddleware::ResponseFilter
62
+ use InternalMiddleware::DecodeJsonBody
63
+ end
64
+
65
+ # Public: the middleware stack to use for processing the batch request as a
66
+ # whole..
67
+ def self.batch_stack(processor)
68
+ Middleware::Builder.new do
69
+ # evaluate these in the context of the middleware stack
70
+ self.instance_eval &BatchApi.config.batch_middleware
71
+ # for now, everything's sequential, but that will change
72
+ use processor.strategy
73
+ end
74
+ end
75
+ #
76
+ # Public: the middleware stack to use for processing each batch operation.
77
+ def self.operation_stack
78
+ Middleware::Builder.new do
79
+ # evaluate the operation middleware in the context of the middleware
80
+ # stack
81
+ self.instance_eval &BatchApi.config.operation_middleware
82
+ # and end with actually executing the batch request
83
+ use Processor::Executor
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,28 @@
1
+ module BatchApi
2
+ module InternalMiddleware
3
+ # Public: a middleware that decodes the body of any individual batch
4
+ # operation if it's JSON.
5
+ class DecodeJsonBody
6
+ # Public: initialize the middleware.
7
+ def initialize(app)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ @app.call(env).tap do |result|
13
+ if should_decode?(result)
14
+ result.body = MultiJson.load(result.body)
15
+ end
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def should_decode?(result)
22
+ result.headers["Content-Type"] =~ /^application\/json/ &&
23
+ # don't try to decode an empty response
24
+ result.body.present?
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ module BatchApi
2
+ module InternalMiddleware
3
+ # Public: a batch middleware which surpresses the response from a call. If you
4
+ # know you don't need a response (for instance, for a POST or PUT), you can
5
+ # add silent: true (or any truthy value, like 1) to your operation to
6
+ # surpress all output for successful requests. Failed requests (status !=
7
+ # 2xx) will still return information.
8
+ class ResponseFilter
9
+ # Public: init the middleware.
10
+ def initialize(app)
11
+ @app = app
12
+ end
13
+
14
+ # Public: execute the call. If env[:op].options[:silent] is true, it'll
15
+ # remove any output for a successful response.
16
+ def call(env)
17
+ @app.call(env).tap do |result|
18
+ if env[:op].options["silent"] && (200...299).include?(result.status)
19
+ # we have success and a request for silence
20
+ # so remove all the content before proceeding
21
+ result.status = result.body = result.headers = nil
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,2 @@
1
+ require 'batch_api/operation/rack'
2
+ require 'batch_api/operation/rails' if defined?(Rails)
@@ -0,0 +1,76 @@
1
+ require 'batch_api/response'
2
+
3
+ module BatchApi
4
+ # Public: an individual batch operation.
5
+ module Operation
6
+ class Rack
7
+ attr_accessor :method, :url, :params, :headers
8
+ attr_accessor :env, :app, :result, :options
9
+
10
+ # Public: create a new Batch Operation given the specifications for a batch
11
+ # operation (as defined above) and the request environment for the main
12
+ # batch request.
13
+ def initialize(op, base_env, app)
14
+ @op = op
15
+
16
+ @method = op["method"] || "get"
17
+ @url = op["url"]
18
+ @params = op["params"] || {}
19
+ @headers = op["headers"] || {}
20
+ @options = op
21
+
22
+ raise Errors::MalformedOperationError,
23
+ "BatchAPI operation must include method (received #{@method.inspect}) " +
24
+ "and url (received #{@url.inspect})" unless @method && @url
25
+
26
+ @app = app
27
+ # deep_dup to avoid unwanted changes across requests
28
+ @env = BatchApi::Utils.deep_dup(base_env)
29
+ end
30
+
31
+ # Execute a batch request, returning a BatchResponse object. If an error
32
+ # occurs, it returns the same results as Rails would.
33
+ def execute
34
+ process_env
35
+ begin
36
+ response = @app.call(@env)
37
+ rescue => err
38
+ response = BatchApi::ErrorWrapper.new(err).render
39
+ end
40
+ BatchApi::Response.new(response)
41
+ end
42
+
43
+ # Internal: customize the request environment. This is currently done
44
+ # manually and feels clunky and brittle, but is mostly likely fine, though
45
+ # there are one or two environment parameters not yet adjusted.
46
+ def process_env
47
+ path, qs = @url.split("?")
48
+
49
+ # Headers
50
+ headrs = (@headers || {}).inject({}) do |heads, (k, v)|
51
+ heads.tap {|h| h["HTTP_" + k.gsub(/\-/, "_").upcase] = v}
52
+ end
53
+ # preserve original headers unless explicitly overridden
54
+ @env.merge!(headrs)
55
+
56
+ # method
57
+ @env["REQUEST_METHOD"] = @method.upcase
58
+
59
+ # path and query string
60
+ if @env["REQUEST_URI"]
61
+ # not all servers provide REQUEST_URI -- Pow, for instance, doesn't
62
+ @env["REQUEST_URI"] = @env["REQUEST_URI"].gsub(/#{BatchApi.config.endpoint}.*/, @url)
63
+ end
64
+ @env["REQUEST_PATH"] = path
65
+ @env["ORIGINAL_FULLPATH"] = @env["PATH_INFO"] = @url
66
+
67
+ @env["rack.request.query_string"] = qs
68
+ @env["QUERY_STRING"] = qs
69
+
70
+ # parameters
71
+ @env["rack.request.form_hash"] = @params
72
+ @env["rack.request.query_hash"] = @method == "get" ? @params : nil
73
+ end
74
+ end
75
+ end
76
+ end