Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Create InternalMiddleware system #12

Merged
merged 21 commits into from

1 participant

@arsduo
Owner

This pull request refactors the BatchApi gem to use a middleware system to process requests
using mitchellh's middleware gem.

The InternalMiddleware module provides a configurable stack that handles batch requests.
This happens in four parts (described in more detail in internal_middleware.rb):

1) Batch-wide middleware, which can act on the entire set of requests.
2) The processor kicks off the individual operations (currently sequential only).
3) Operation-specific middleware, which act on individual batch ops.
4) The executor, which actually calls onward in the Rack stack, executing the RESTful request.

This system allow users a lot more flexibility in how requests are processed. For instance:

  • Decoding the body of JSON responses, making it easier for clients to process them (already included)
  • Format the entire response in whatever format you want (will be factored out)
  • Add or remove additional components to the operation results (for instance, to surpress output from calls you don't care about, as described in the readme)

No doubt other apps will have other specific needs that this system will address.

NOTE: I'd like to refactor the middleware into two stacks (batch and ops) rather than one; this will
be cleaner and more straightforward to work with. (I of course realized this as I wrote this PR.) That will
happen as a separate request.

@arsduo Merge branch 'refs/heads/ostruct' into middleware
Conflicts:
	changelog.md
	lib/batch_api/version.rb
914ca74
@arsduo arsduo merged commit cdacb18 into master

1 check passed

Details default The Travis build passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 19, 2012
  1. One more file for renaming

    authored
  2. Add InternalMiddleware system

    authored
Commits on Sep 20, 2012
  1. Add middleware gem

    authored
  2. Decoding is now its own module

    authored
  3. Fix response spec

    authored
  4. Cleanup

    authored
  5. Remove test for old option

    authored
  6. Cleanup

    authored
  7. Updated documentation

    authored
Commits on Sep 21, 2012
  1. Update readme and version

    authored
  2. Fix bug in tests

    authored
Commits on Oct 4, 2012
  1. Merge branch 'refs/heads/ostruct' into middleware

    authored
    Conflicts:
    	changelog.md
    	lib/batch_api/version.rb
This page is out of date. Refresh to see the latest.
Showing with 413 additions and 137 deletions.
  1. +0 −2  Gemfile
  2. +3 −5 Gemfile.lock
  3. +8 −6 batch_api.gemspec
  4. +5 −0 changelog.md
  5. +2 −1  lib/batch_api.rb
  6. +12 −2 lib/batch_api/configuration.rb
  7. +1 −1  lib/batch_api/errors/base.rb
  8. +67 −0 lib/batch_api/internal_middleware.rb
  9. +24 −0 lib/batch_api/internal_middleware/decode_json_body.rb
  10. +13 −5 lib/batch_api/processor.rb
  11. +18 −0 lib/batch_api/processor/executor.rb
  12. +28 −0 lib/batch_api/processor/sequential.rb
  13. +0 −18 lib/batch_api/processor/strategies/sequential.rb
  14. +1 −1  lib/batch_api/{middleware.rb → rack_middleware.rb}
  15. +1 −6 lib/batch_api/response.rb
  16. +1 −1  lib/batch_api/version.rb
  17. +1 −1  spec/dummy/config/application.rb
  18. +17 −24 spec/integration/shared_examples.rb
  19. +21 −18 spec/lib/configuration_spec.rb
  20. +1 −1  spec/lib/errors/base_spec.rb
  21. +37 −0 spec/lib/internal_middleware/decode_json_body_spec.rb
  22. +70 −0 spec/lib/internal_middleware_spec.rb
  23. +22 −0 spec/lib/processor/executor_spec.rb
  24. +34 −0 spec/lib/processor/sequential_spec.rb
  25. +18 −6 spec/lib/processor_spec.rb
  26. +3 −3 spec/lib/{middleware_spec.rb → rack_middleware_spec.rb}
  27. +4 −30 spec/lib/response_spec.rb
  28. +0 −5 spec/lib/sequential_spec.rb
  29. +1 −1  spec/support/sinatra_app.rb
View
2  Gemfile
@@ -5,8 +5,6 @@ source "http://rubygems.org"
# development dependencies will be added by default to the :development group.
gemspec
-# jquery-rails is used by the dummy application
-gem "jquery-rails"
group :development, :test do
# Testing infrastructure
View
8 Gemfile.lock
@@ -1,7 +1,8 @@
PATH
remote: .
specs:
- batch_api (0.1.1)
+ batch_api (0.2.0)
+ middleware
GEM
remote: http://rubygems.org/
@@ -48,9 +49,6 @@ GEM
hike (1.2.1)
i18n (0.6.0)
journey (1.0.4)
- jquery-rails (2.1.0)
- railties (>= 3.1.0, < 5.0)
- thor (~> 0.14)
json (1.7.4)
listen (0.4.7)
rb-fchange (~> 0.0.5)
@@ -60,6 +58,7 @@ GEM
i18n (>= 0.4.0)
mime-types (~> 1.16)
treetop (~> 1.4.8)
+ middleware (0.1.0)
mime-types (1.19)
multi_json (1.3.6)
polyglot (0.3.3)
@@ -136,7 +135,6 @@ DEPENDENCIES
faker
guard
guard-rspec
- jquery-rails
rack-contrib
rails (~> 3.2)
rb-fsevent
View
14 batch_api.gemspec
@@ -17,10 +17,12 @@ Gem::Specification.new do |s|
s.files = Dir["{app,config,db,lib}/**/*"] + ["MIT-LICENSE", "Rakefile", "changelog.md", "readme.md"]
s.test_files = Dir["test/**/*"]
- s.add_development_dependency "rails", "~> 3.2"
- s.add_development_dependency "sinatra"
- s.add_development_dependency "rspec"
- s.add_development_dependency "rspec-rails"
- s.add_development_dependency "sqlite3"
- s.add_development_dependency "rack-contrib"
+ s.add_runtime_dependency("middleware")
+
+ s.add_development_dependency("rails", "~> 3.2")
+ s.add_development_dependency("sinatra")
+ s.add_development_dependency("rspec")
+ s.add_development_dependency("rspec-rails")
+ s.add_development_dependency("sqlite3")
+ s.add_development_dependency("rack-contrib")
end
View
5 changelog.md
@@ -1,3 +1,8 @@
+v0.2.0
+* Refactor app to use internal middlewares for handling operations
+* Refactor JSON decoding to a middleware
+* Remove timestamp option
+
v0.1.3
* Refactor config to use a struct
* Update readme to cover HTTP pipelining
View
3  lib/batch_api.rb
@@ -2,7 +2,8 @@
require 'batch_api/version'
require 'batch_api/utils'
require 'batch_api/processor'
-require 'batch_api/middleware'
+require 'batch_api/internal_middleware'
+require 'batch_api/rack_middleware'
module BatchApi
View
14 lib/batch_api/configuration.rb
@@ -1,3 +1,5 @@
+require 'batch_api/internal_middleware'
+
module BatchApi
# Public: configuration options.
# Currently, you can set:
@@ -9,16 +11,24 @@ module BatchApi
# - decode_json_responses: automatically decode JSON response bodies,
# so they don't get double-decoded (e.g. when you decode the batch
# response, the bodies are already objects).
+ #
+ # There are also two middleware-related options -- check out middleware.rb
+ # for more information.
+ # - global_middleware: any middlewares to use round the entire batch request
+ # (such as authentication, etc.)
+ # - per_op_middleware: any middlewares to run around each individual request
+ # (adding headers, decoding JSON, etc.)
CONFIGURATION_OPTIONS = {
verb: :post,
endpoint: "/batch",
limit: 50,
- decode_json_responses: true
+ batch_middleware: InternalMiddleware::DEFAULT_BATCH_MIDDLEWARE,
+ operation_middleware: InternalMiddleware::DEFAULT_OPERATION_MIDDLEWARE
}
# Batch API Configuration
class Configuration < Struct.new(*CONFIGURATION_OPTIONS.keys)
- # Public; initialize a new configuration option and apply the defaults.
+ # Public: initialize a new configuration option and apply the defaults.
def initialize
super
CONFIGURATION_OPTIONS.each {|k, v| self[k] = v}
View
2  lib/batch_api/errors/base.rb
@@ -27,7 +27,7 @@ def body
#
# Returns: an Array with the error body represented as JSON.
def render
- [status_code, Middleware.content_type, [MultiJson.dump(body)]]
+ [status_code, RackMiddleware.content_type, [MultiJson.dump(body)]]
end
# Public: the status code to return for the given error.
View
67 lib/batch_api/internal_middleware.rb
@@ -0,0 +1,67 @@
+require 'middleware'
+require 'batch_api/processor/sequential'
+require 'batch_api/processor/executor'
+require 'batch_api/internal_middleware/decode_json_body'
+
+module BatchApi
+ # Public: the internal middleware system used to process batch requests.
+ # (Not to be confused with RackMiddleware, which handles incoming Rack
+ # request.) Based on Mitchel Hashimoto's middleware gem.
+ #
+ # The middleware stack is defined in a block, and has four phases:
+ # 1) Batch - these middlewares will be run around the entire sequence of
+ # batch requests. Useful for global processing on a batch request; for
+ # instance, the timestamp middleware adds a timestamp based on when the
+ # original request was started. This should return an array of
+ # BatchApi::Response objects.
+ #
+ # 2) Processor - this automatically-provided middleware will execute all
+ # batch requests either sequentially (currently the only option) or in
+ # parallel (in the future). This will return an array of
+ # BatchApi::Response objects.
+ #
+ # 3) Operation - these middlewares will run once per each operation, giving
+ # you a chance to alter the results or details of an op -- for instance,
+ # decoding the body if it's JSON. This should return an individual
+ # BatchApi::Response object.
+ #
+ # 4) Executor - this automatically-provided middleware actually executes
+ # the individual Rack request, and returns a BatchApi::Response object.
+ #
+ # All middlewares#call will receive the following as an env hash:
+ # {
+ # ops: [], # the total set of operations
+ # op: obj, # the specific operation being executed, if appropriate
+ # rack_env: {}, # the Rack environment
+ # rack_app: app # the Rack application
+ # }
+ #
+ # All middlewares should return the result of their individual operation or
+ # the array of operation results, depending on where they are in the chain.
+ # (See above.)
+ module InternalMiddleware
+ # Public: the default internal middlewares to be run around the entire
+ # operation.
+ DEFAULT_BATCH_MIDDLEWARE = Proc.new {}
+
+ # Public: the default internal middlewares to be run around each batch
+ # operation.
+ DEFAULT_OPERATION_MIDDLEWARE = Proc.new do
+ # Decode JSON response bodies, so they're not double-encoded.
+ use InternalMiddleware::DecodeJsonBody
+ end
+
+ # Public: the middleware stack to use for requests.
+ def self.stack(processor)
+ Middleware::Builder.new do
+ # evaluate these in the context of the middleware object
+ self.instance_eval &BatchApi.config.batch_middleware
+ # for now, everything's sequential, but that will change
+ use processor.strategy
+ self.instance_eval &BatchApi.config.operation_middleware
+ # and end with actually executing the batch request
+ use Processor::Executor
+ end
+ end
+ end
+end
View
24 lib/batch_api/internal_middleware/decode_json_body.rb
@@ -0,0 +1,24 @@
+module BatchApi
+ module InternalMiddleware
+ # Public: a middleware that decodes the body of any individual batch
+ # operation if the it's JSON.
+ class DecodeJsonBody
+ # Public: initialize the middleware.
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ @app.call(env).tap do |result|
+ result.body = MultiJson.load(result.body) if should_decode?(result)
+ end
+ end
+
+ private
+
+ def should_decode?(result)
+ result.headers["Content-Type"] =~ /^application\/json/
+ end
+ end
+ end
+end
View
18 lib/batch_api/processor.rb
@@ -1,4 +1,4 @@
-require 'batch_api/processor/strategies/sequential'
+require 'batch_api/processor/sequential'
require 'batch_api/operation'
module BatchApi
@@ -30,26 +30,34 @@ def initialize(request, app)
@env = request.env
@ops = self.process_ops
@options = self.process_options
-
- @start_time = Time.now.to_i
end
# Public: the processing strategy to use, based on the options
# provided in BatchApi setup and the request.
# Currently only Sequential is supported.
def strategy
- BatchApi::Processor::Strategies::Sequential
+ BatchApi::Processor::Sequential
end
# Public: run the batch operations according to the appropriate strategy.
#
# Returns a set of BatchResponses
def execute!
- format_response(strategy.execute!(@ops, @options))
+ stack = InternalMiddleware.stack(self)
+ format_response(stack.call(middleware_env))
end
protected
+ def middleware_env
+ {
+ ops: @ops,
+ rack_env: @env,
+ rack_app: @app,
+ options: @options
+ }
+ end
+
# Internal: format the result of the operations, and include
# any other appropriate information (such as timestamp).
#
View
18 lib/batch_api/processor/executor.rb
@@ -0,0 +1,18 @@
+module BatchApi
+ class Processor
+ # Public: a simple middleware that lives at the end of the internal chain
+ # and simply executes each batch operation.
+ class Executor
+
+ # Public: initialize the middleware.
+ def initialize(app)
+ @app = app
+ end
+
+ # Public: execute the batch operation.
+ def call(env)
+ env[:op].execute
+ end
+ end
+ end
+end
View
28 lib/batch_api/processor/sequential.rb
@@ -0,0 +1,28 @@
+module BatchApi
+ class Processor
+ class Sequential
+
+ # Public: initialize with the app.
+ def initialize(app)
+ @app = app
+ end
+
+ # Public: execute all operations sequentially.
+ #
+ # ops - a set of BatchApi::Operations
+ # options - a set of options
+ #
+ # Returns an array of BatchApi::Response objects.
+ def call(env)
+ env[:ops].collect do |op|
+ # set the current op
+ env[:op] = op
+ # execute the individual request
+ # then clear out the current op afterward
+ @app.call(env).tap {|r| env.delete(:op) }
+ end
+ end
+ end
+ end
+end
+
View
18 lib/batch_api/processor/strategies/sequential.rb
@@ -1,18 +0,0 @@
-module BatchApi
- class Processor
- module Strategies
- module Sequential
- # Public: execute all operations sequentially.
- #
- # ops - a set of BatchApi::Operations
- # options - a set of options
- #
- # Returns an array of BatchApi::Response objects.
- def self.execute!(ops, options = {})
- ops.map(&:execute)
- end
- end
- end
- end
-end
-
View
2  lib/batch_api/middleware.rb → lib/batch_api/rack_middleware.rb
@@ -1,5 +1,5 @@
module BatchApi
- class Middleware
+ class RackMiddleware
def initialize(app, &block)
@app = app
yield BatchApi.config if block
View
7 lib/batch_api/response.rb
@@ -24,12 +24,7 @@ def process_body(body_pieces)
# so turn it into a string
base_body = ""
body_pieces.each {|str| base_body << str}
- should_decode? ? MultiJson.load(base_body) : base_body
- end
-
- def should_decode?
- @headers["Content-Type"] =~ /^application\/json/ &&
- BatchApi.config.decode_json_responses
+ base_body
end
end
end
View
2  lib/batch_api/version.rb
@@ -1,3 +1,3 @@
module BatchApi
- VERSION = "0.1.3"
+ VERSION = "0.2.0"
end
View
2  spec/dummy/config/application.rb
@@ -55,7 +55,7 @@ class Application < Rails::Application
# Version of your assets, change this if you want to expire all your assets
config.assets.version = '1.0'
- config.middleware.use BatchApi::Middleware do |batch|
+ config.middleware.use BatchApi::RackMiddleware do |batch|
batch.limit = 25
end
end
View
41 spec/integration/shared_examples.rb
@@ -39,21 +39,6 @@ def headerize(hash)
headers: { "GET" => "hello" }
} }
- let(:parameter) {
- (rand * 10000).to_i
- }
-
- let(:parameter_request) { {
- url: "/endpoint/capture/#{parameter}",
- method: "get"
- } }
-
- let(:parameter_result) { {
- body: {
- "result" => parameter.to_s
- }
- } }
-
# these are defined in the dummy app's endpoints controller
let(:post_headers) { {"foo" => "bar"} }
let(:post_params) { {"other" => "value"} }
@@ -99,6 +84,21 @@ def headerize(hash)
body: {}
} }
+ let(:parameter) {
+ (rand * 10000).to_i
+ }
+
+ let(:parameter_request) { {
+ url: "/endpoint/capture/#{parameter}",
+ method: "get"
+ } }
+
+ let(:parameter_result) { {
+ body: {
+ "result" => parameter.to_s
+ }
+ } }
+
before :each do
@t = Time.now
xhr :post, "/batch", {
@@ -127,15 +127,7 @@ def headerize(hash)
@result = JSON.parse(response.body)["results"][0]
end
- it "returns the body raw if decode_json_responses = false" do
- BatchApi.config.stub(:decode_json_responses).and_return(false)
- xhr :post, "/batch", {ops: [get_request], sequential: true}.to_json,
- "CONTENT_TYPE" => "application/json"
- @result = JSON.parse(response.body)["results"][0]
- @result["body"].should == get_result[:body].to_json
- end
-
- it "returns the body as objects if decode_json_responses = true" do
+ it "returns the body as objects" do
@result = JSON.parse(response.body)["results"][0]
@result["body"].should == get_result[:body]
end
@@ -149,6 +141,7 @@ def headerize(hash)
end
it "verifies that the right headers were received" do
+ puts @result.inspect
@result["headers"]["REQUEST_HEADERS"].should include(
headerize(get_headers)
)
View
39 spec/lib/configuration_spec.rb
@@ -1,25 +1,28 @@
require 'spec_helper'
-describe BatchApi::Configuration do
- let(:config) { BatchApi::Configuration.new }
+module BatchApi
+ describe Configuration do
+ let(:config) { Configuration.new }
- describe "options" do
- {
- verb: :post,
- endpoint: "/batch",
- limit: 50,
- decode_json_responses: true
- }.each_pair do |option, default|
- opt, defa = option, default
- describe "##{opt}" do
- it "has an accessor for #{opt}" do
- stubby = stub
- config.send("#{opt}=", stubby)
- config.send(opt).should == stubby
- end
+ describe "options" do
+ {
+ verb: :post,
+ endpoint: "/batch",
+ limit: 50,
+ batch_middleware: InternalMiddleware::DEFAULT_BATCH_MIDDLEWARE,
+ operation_middleware: InternalMiddleware::DEFAULT_OPERATION_MIDDLEWARE
+ }.each_pair do |option, default|
+ opt, defa = option, default
+ describe "##{opt}" do
+ it "has an accessor for #{opt}" do
+ stubby = stub
+ config.send("#{opt}=", stubby)
+ config.send(opt).should == stubby
+ end
- it "defaults #{opt} to #{defa.inspect}" do
- config.send(opt).should == defa
+ it "defaults #{opt} to #{defa.inspect}" do
+ config.send(opt).should == defa
+ end
end
end
end
View
2  spec/lib/errors/base_spec.rb
@@ -35,7 +35,7 @@
it "returns appropriate content type" do
ctype = stub
- BatchApi::Middleware.stub(:content_type).and_return(ctype)
+ BatchApi::RackMiddleware.stub(:content_type).and_return(ctype)
error.render[1].should == ctype
end
View
37 spec/lib/internal_middleware/decode_json_body_spec.rb
@@ -0,0 +1,37 @@
+require 'spec_helper'
+
+describe BatchApi::InternalMiddleware::DecodeJsonBody do
+ let(:app) { stub("app", call: result) }
+ let(:decoder) { BatchApi::InternalMiddleware::DecodeJsonBody.new(app) }
+ let(:env) { stub("env") }
+ let(:json) { {"data" => "is_json", "more" => {"hi" => "there"} } }
+ let(:result) {
+ BatchApi::Response.new([
+ 200,
+ {"Content-Type" => "application/json"},
+ [MultiJson.dump(json)]
+ ])
+ }
+
+ describe "#call" do
+ context "for json results" do
+ it "decodes JSON results for application/json responses" do
+ result = decoder.call(env)
+ result.body.should == json
+ end
+
+ it "doesn't change anything else" do
+ result = decoder.call(env)
+ result.status.should == 200
+ result.headers.should == {"Content-Type" => "application/json"}
+ end
+ end
+
+ context "for non-JSON responses" do
+ it "doesn't decode" do
+ result.headers = {"Content-Type" => "text/html"}
+ decoder.call(env).body.should == MultiJson.dump(json)
+ end
+ end
+ end
+end
View
70 spec/lib/internal_middleware_spec.rb
@@ -0,0 +1,70 @@
+require 'spec_helper'
+
+describe BatchApi::InternalMiddleware do
+
+ class FakeBuilder
+ attr_accessor :middlewares
+
+ def initialize(&block)
+ @middlewares = []
+ instance_eval(&block) if block_given?
+ end
+
+ def use(middleware, *args)
+ @middlewares << [middleware, args]
+ end
+ end
+
+ let(:builder) { FakeBuilder.new }
+
+ it "builds an empty default global middleware" do
+ builder.instance_eval(
+ &BatchApi::InternalMiddleware::DEFAULT_BATCH_MIDDLEWARE
+ )
+ builder.middlewares.should be_empty
+ end
+
+ it "builds a per-op middleware with the JSON decoder" do
+ builder.instance_eval(
+ &BatchApi::InternalMiddleware::DEFAULT_OPERATION_MIDDLEWARE
+ )
+ builder.middlewares.length.should == 1
+ builder.middlewares.first.should ==
+ [BatchApi::InternalMiddleware::DecodeJsonBody, []]
+ end
+
+ describe ".stack" do
+ # we can't use stubs inside the procs since they're instance_eval'd
+ let(:global_config) { Proc.new { use "Global" } }
+ let(:op_config) { Proc.new { use "Op" } }
+ let(:strategy) { stub("strategy") }
+ let(:processor) { stub("processor", strategy: strategy) }
+ let(:stack) { BatchApi::InternalMiddleware.stack(processor) }
+
+ before :each do
+ BatchApi.config.stub(:batch_middleware).and_return(global_config)
+ BatchApi.config.stub(:operation_middleware).and_return(op_config)
+ stub_const("Middleware::Builder", FakeBuilder)
+ end
+
+ it "builds the stack with the right number of wares" do
+ stack.middlewares.length.should == 4
+ end
+
+ it "builds a middleware stack starting with the configured global wares" do
+ stack.middlewares[0].first.should == "Global"
+ end
+
+ it "inserts the appropriate strategy from the processor" do
+ stack.middlewares[1].first.should == strategy
+ end
+
+ it "builds a middleware stack including the configured per-op wares" do
+ stack.middlewares[2].first.should == "Op"
+ end
+
+ it "builds a middleware stack ending with the executor" do
+ stack.middlewares[3].first.should == BatchApi::Processor::Executor
+ end
+ end
+end
View
22 spec/lib/processor/executor_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+require 'batch_api/processor/executor'
+
+describe BatchApi::Processor::Executor do
+
+ let(:app) { stub("app", call: stub) }
+ let(:executor) { BatchApi::Processor::Executor.new(app) }
+ let(:result) { stub("result") }
+ let(:op) { stub("operation", execute: result) }
+ let(:env) { {op: op} }
+
+ describe "#call" do
+ it "executes the operation" do
+ op.should_receive(:execute)
+ executor.call(env)
+ end
+
+ it "returns the result" do
+ executor.call(env).should == result
+ end
+ end
+end
View
34 spec/lib/processor/sequential_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+
+describe BatchApi::Processor::Sequential do
+
+ let(:app) { stub("app", call: stub) }
+ let(:sequential) { BatchApi::Processor::Sequential.new(app) }
+
+ describe "#call" do
+ let(:call_results) { 3.times.collect {|i| stub("called #{i}") } }
+ let(:env) { {
+ ops: 3.times.collect {|i| stub("op #{i}") }
+ } }
+
+ before :each do
+ app.stub(:call).and_return(*call_results)
+ end
+
+ it "calls the app onward, setting the env appropriately" do
+ env[:ops].each {|op|
+ app.should_receive(:call).with(hash_including(op: op)).ordered
+ }
+ sequential.call(env)
+ end
+
+ it "includes the rest of the env in the calls" do
+ app.should_receive(:call).with(hash_including(env)).exactly(3).times
+ sequential.call(env)
+ end
+
+ it "returns the results of the calls" do
+ sequential.call(env).should == call_results
+ end
+ end
+end
View
24 spec/lib/processor_spec.rb
@@ -86,20 +86,32 @@
end
describe "#strategy" do
- it "returns BatchApi::Processor::Strategies::Sequential" do
- processor.strategy.should == BatchApi::Processor::Strategies::Sequential
+ it "returns BatchApi::Processor::Sequential" do
+ processor.strategy.should == BatchApi::Processor::Sequential
end
end
describe "#execute!" do
- it "executes on the provided strategy" do
- processor.strategy.should_receive(:execute!).with(processor.ops, processor.options)
+ let(:result) { stub("result") }
+ let(:stack) { stub("stack", call: result) }
+ let(:middleware_env) { {
+ ops: processor.ops, # the processed Operation objects
+ rack_env: env,
+ rack_app: app,
+ options: options
+ } }
+
+ before :each do
+ BatchApi::InternalMiddleware.stub(:stack).and_return(stack)
+ end
+
+ it "calls an internal middleware stacks with the appropriate data" do
+ stack.should_receive(:call).with(middleware_env)
processor.execute!
end
it "returns the formatted result of the strategy" do
- stubby = stub
- processor.strategy.stub(:execute!).and_return(stubby)
+ stack.stub(:call).and_return(stubby = stub)
processor.execute!["results"].should == stubby
end
end
View
6 spec/lib/middleware_spec.rb → spec/lib/rack_middleware_spec.rb
@@ -1,10 +1,10 @@
require 'spec_helper'
-describe BatchApi::Middleware do
+describe BatchApi::RackMiddleware do
describe "#initialize" do
it "allows access to the BatchApi configuration" do
limit = rand * 100
- middleware = BatchApi::Middleware.new(stub("app")) do |conf|
+ middleware = BatchApi::RackMiddleware.new(stub("app")) do |conf|
conf.limit = limit
end
BatchApi.config.limit.should == limit
@@ -17,7 +17,7 @@
let(:app) { stub("app") }
let(:middleware) {
- BatchApi::Middleware.new(app) do |conf|
+ BatchApi::RackMiddleware.new(app) do |conf|
conf.endpoint = endpoint
conf.verb = verb
end
View
34 spec/lib/response_spec.rb
@@ -17,37 +17,11 @@
response.status.should == raw_response.first
end
- it "sets headers to the HTTP headers" do
- response.headers.should == raw_response[1]
+ it "sets body to the HTTP body turned into a string" do
+ response.body.should == raw_response[2].join
end
- describe "the body" do
- context "for non-JSON requests" do
- before :each do
- raw_response[1]["Content-Type"] = "text/html"
- end
-
- it "sets body to the string representation of the response body" do
- response.body.should == raw_response[2].join
- end
- end
-
- context "for JSON responses" do
- let(:json) { {"a" => 2, "b" => {"c" => 3}} }
-
- before :each do
- raw_response[1]["Content-Type"] = "application/json"
- raw_response[2] = [json.to_json]
- end
-
- it "decodes the body if the decode_json_responses is set" do
- response.body.should == json
- end
-
- it "doesn't decode the body if decode_json_responses is false" do
- BatchApi.config.stub(:decode_json_responses).and_return(false)
- response.body.should == json.to_json
- end
- end
+ it "sets headers to the HTTP headers" do
+ response.headers.should == raw_response[1]
end
end
View
5 spec/lib/sequential_spec.rb
@@ -1,5 +0,0 @@
-require 'spec_helper'
-
-describe BatchApi::Processor::Strategies::Sequential do
- pending "needs a test"
-end
View
2  spec/support/sinatra_app.rb
@@ -3,7 +3,7 @@
class SinatraApp < Sinatra::Base
use Rack::PostBodyContentTypeParser
- use BatchApi::Middleware
+ use BatchApi::RackMiddleware
get "/endpoint" do
headers["GET"] = "hello"
Something went wrong with that request. Please try again.