adavidev_batch_api 0.2.1.pre.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +30 -0
  4. data/changelog.md +60 -0
  5. data/lib/adavidev_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/decode_json_body.rb +24 -0
  10. data/lib/batch_api/internal_middleware/response_filter.rb +27 -0
  11. data/lib/batch_api/internal_middleware.rb +87 -0
  12. data/lib/batch_api/operation/rack.rb +74 -0
  13. data/lib/batch_api/operation/rails.rb +42 -0
  14. data/lib/batch_api/operation.rb +2 -0
  15. data/lib/batch_api/processor/executor.rb +18 -0
  16. data/lib/batch_api/processor/sequential.rb +29 -0
  17. data/lib/batch_api/processor.rb +114 -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/batch_api.rb +28 -0
  23. data/lib/tasks/batch_api_tasks.rake +4 -0
  24. data/readme.md +243 -0
  25. data/spec/dummy/README.rdoc +261 -0
  26. data/spec/dummy/Rakefile +15 -0
  27. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  28. data/spec/dummy/app/assets/javascripts/endpoints.js +2 -0
  29. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  30. data/spec/dummy/app/assets/stylesheets/endpoints.css +4 -0
  31. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  32. data/spec/dummy/app/controllers/endpoints_controller.rb +36 -0
  33. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  34. data/spec/dummy/app/helpers/endpoints_helper.rb +2 -0
  35. data/spec/dummy/app/views/endpoints/get.html.erb +2 -0
  36. data/spec/dummy/app/views/endpoints/post.html.erb +2 -0
  37. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/spec/dummy/config/application.rb +63 -0
  39. data/spec/dummy/config/boot.rb +10 -0
  40. data/spec/dummy/config/database.yml +25 -0
  41. data/spec/dummy/config/environment.rb +5 -0
  42. data/spec/dummy/config/environments/development.rb +37 -0
  43. data/spec/dummy/config/environments/production.rb +67 -0
  44. data/spec/dummy/config/environments/test.rb +37 -0
  45. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  46. data/spec/dummy/config/initializers/inflections.rb +15 -0
  47. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  48. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  49. data/spec/dummy/config/initializers/session_store.rb +8 -0
  50. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  51. data/spec/dummy/config/locales/en.yml +5 -0
  52. data/spec/dummy/config/routes.rb +64 -0
  53. data/spec/dummy/config.ru +4 -0
  54. data/spec/dummy/db/test.sqlite3 +0 -0
  55. data/spec/dummy/public/404.html +26 -0
  56. data/spec/dummy/public/422.html +26 -0
  57. data/spec/dummy/public/500.html +25 -0
  58. data/spec/dummy/public/favicon.ico +0 -0
  59. data/spec/dummy/script/rails +6 -0
  60. data/spec/dummy/test/functional/endpoints_controller_test.rb +14 -0
  61. data/spec/dummy/test/unit/helpers/endpoints_helper_test.rb +4 -0
  62. data/spec/integration/rails_spec.rb +10 -0
  63. data/spec/integration/shared_examples.rb +267 -0
  64. data/spec/integration/sinatra_integration_spec.rb +14 -0
  65. data/spec/lib/batch_api_spec.rb +20 -0
  66. data/spec/lib/batch_error_spec.rb +23 -0
  67. data/spec/lib/configuration_spec.rb +30 -0
  68. data/spec/lib/error_wrapper_spec.rb +68 -0
  69. data/spec/lib/internal_middleware/decode_json_body_spec.rb +37 -0
  70. data/spec/lib/internal_middleware/response_filter_spec.rb +61 -0
  71. data/spec/lib/internal_middleware_spec.rb +91 -0
  72. data/spec/lib/operation/rack_spec.rb +240 -0
  73. data/spec/lib/operation/rails_spec.rb +100 -0
  74. data/spec/lib/processor/executor_spec.rb +22 -0
  75. data/spec/lib/processor/sequential_spec.rb +39 -0
  76. data/spec/lib/processor_spec.rb +134 -0
  77. data/spec/lib/rack_middleware_spec.rb +103 -0
  78. data/spec/lib/response_spec.rb +53 -0
  79. data/spec/spec_helper.rb +28 -0
  80. data/spec/support/sinatra_app.rb +54 -0
  81. metadata +264 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7534a2af10be012a0af6a393d7db504bfed9b153
4
+ data.tar.gz: a13259fc6a2eaf65e1afc01c13862eae54762bf0
5
+ SHA512:
6
+ metadata.gz: 3c6924ef94af17cb2310251ebab090a9098eaa525154348771aef0a67a7edf11c3b5d0361d8919daabe5147ca7a2d6e3fe454fb13a4ebfb141aba2dcb1c11bd6
7
+ data.tar.gz: c0d80c55b7a5560fc3be70cffd2174ef576a09deb9441eddec5ab229fb3f5fbb9a37fba26690964616dfb9fb9be2d5a17b51488983d2e9c75260ae957813a156
data/MIT-LICENSE ADDED
@@ -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.
data/Rakefile ADDED
@@ -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
data/changelog.md ADDED
@@ -0,0 +1,60 @@
1
+ v0.2.2
2
+ * Update documentation to remove old options and add installation section
3
+ * Update gems
4
+
5
+ v0.2.1
6
+ * Default the method to GET if no method is specified
7
+
8
+ v0.2.0
9
+ * Refactor app to use internal middlewares for handling operations
10
+ * Refactor JSON decoding to a middleware
11
+ * Remove timestamp option
12
+
13
+ v0.1.4
14
+ * Refactor errors into ErrorWrapper/BatchError
15
+ * Allow specification of custom status codes raised for errors
16
+
17
+ v0.1.3
18
+ * Refactor config to use a struct
19
+ * Update readme to cover HTTP pipelining
20
+
21
+ v0.1.2
22
+ * Rewrite the readme
23
+ * Add travis icon
24
+
25
+ v0.1.1
26
+ * Fix dumb error
27
+
28
+ v0.1.0
29
+ * Add direct support for Rails for path params
30
+ * Fix spec bugs
31
+
32
+ v0.0.8
33
+ * Return the results wrapped in a hash, rather than a raw array
34
+ * Add process_start timestamp option
35
+
36
+ v0.0.7
37
+ * Return more specific error codes to alert clients to param errors
38
+
39
+ v0.0.6
40
+ * Refactor Rack middleware to be Sinatra-compatible
41
+
42
+ v0.0.5
43
+ * Add setting to decode JSON responses before sending batch results
44
+
45
+ v0.0.4
46
+ * Switch from Rails-based process to a Rack middleware
47
+ * Improve tests
48
+
49
+ v0.0.3
50
+ * Encapsulate processing into a Processor module
51
+ * Prepare for parallel processing in the future
52
+ * Add specific errors
53
+ * Allow controlling the routing target
54
+
55
+ v0.0.2
56
+ * Add config module
57
+ * Add options for operation limit, endpoint, and verb
58
+
59
+ v0.0.1
60
+ * 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, [MultiJson.dump(body)]]
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,24 @@
1
+ module BatchApi
2
+ module InternalMiddleware
3
+ # Public: a middleware that decodes the body of any individual batch
4
+ # operation if the 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
+ result.body = MultiJson.load(result.body) if should_decode?(result)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def should_decode?(result)
20
+ result.headers["Content-Type"] =~ /^application\/json/
21
+ end
22
+ end
23
+ end
24
+ 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,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,74 @@
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 = @url.split("?")
48
+ qs = @params.to_json
49
+
50
+ # Headers
51
+ headrs = (@headers || {}).inject({}) do |heads, (k, v)|
52
+ heads.tap {|h| h["HTTP_" + k.gsub(/\-/, "_").upcase] = v}
53
+ end
54
+ # preserve original headers unless explicitly overridden
55
+ @env.merge!(headrs)
56
+
57
+ # method
58
+ @env["REQUEST_METHOD"] = @method.upcase
59
+
60
+ # path and query string
61
+ @env["REQUEST_URI"] = @env["REQUEST_URI"].gsub(/#{BatchApi.config.endpoint}.*/, @url)
62
+ @env["REQUEST_PATH"] = path
63
+ @env["ORIGINAL_FULLPATH"] = @env["PATH_INFO"] = @url
64
+
65
+ @env["rack.request.query_string"] = qs
66
+ @env["QUERY_STRING"] = qs
67
+
68
+ # parameters
69
+ @env["rack.request.form_hash"] = @params
70
+ @env["rack.request.query_hash"] = @method == "get" ? @params : nil
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,42 @@
1
+ require 'batch_api/operation/rack'
2
+
3
+ module BatchApi
4
+ # Public: an individual batch operation.
5
+ module Operation
6
+ class Rails < Operation::Rack
7
+ # Public: create a new Rails Operation. It does all that Rack does
8
+ # and also some additional Rails-specific processing.
9
+ def initialize(op, base_env, app)
10
+ super
11
+ @params = params_with_path_components
12
+ end
13
+
14
+ # Internal: customize the request environment. This is currently done
15
+ # manually and feels clunky and brittle, but is mostly likely fine, though
16
+ # there are one or two environment parameters not yet adjusted.
17
+ def process_env
18
+ # parameters
19
+ super
20
+ @env["action_dispatch.request.parameters"] = @params
21
+ @env["action_dispatch.request.request_parameters"] = @params
22
+ end
23
+
24
+ private
25
+
26
+ # Internal: process the params the Rails way, merging in the
27
+ # path_parameters. If the route can't be recognized, it will
28
+ # leave the params unchanged.
29
+ #
30
+ # Returns the updated params.
31
+ def params_with_path_components
32
+ begin
33
+ path_params = ::Rails.application.routes.recognize_path(@url, @op)
34
+ @params.merge(path_params)
35
+ rescue
36
+ @params
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,2 @@
1
+ require 'batch_api/operation/rack'
2
+ require 'batch_api/operation/rails' if defined?(Rails)
@@ -0,0 +1,18 @@
1
+ module BatchApi
2
+ class Processor
3
+ # Public: a simple middleware that lives at the end of the internal chain
4
+ # and simply executes each batch operation.
5
+ class Executor
6
+
7
+ # Public: initialize the middleware.
8
+ def initialize(app)
9
+ @app = app
10
+ end
11
+
12
+ # Public: execute the batch operation.
13
+ def call(env)
14
+ env[:op].execute
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,29 @@
1
+ module BatchApi
2
+ class Processor
3
+ class Sequential
4
+ # Public: initialize with the app.
5
+ def initialize(app)
6
+ @app = app
7
+ end
8
+
9
+ # Public: execute all operations sequentially.
10
+ #
11
+ # ops - a set of BatchApi::Operations
12
+ # options - a set of options
13
+ #
14
+ # Returns an array of BatchApi::Response objects.
15
+ def call(env)
16
+ env[:ops].collect do |op|
17
+ # set the current op
18
+ env[:op] = op
19
+
20
+ # execute the individual request inside the operation-specific
21
+ # middeware, then clear out the current op afterward
22
+ middleware = InternalMiddleware.operation_stack
23
+ middleware.call(env).tap {|r| env.delete(:op) }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+