adavidev_batch_api 0.2.1.pre.2

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 (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
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe BatchApi::InternalMiddleware::DecodeJsonBody do
4
+ let(:app) { stub("app", call: result) }
5
+ let(:decoder) { BatchApi::InternalMiddleware::DecodeJsonBody.new(app) }
6
+ let(:env) { stub("env") }
7
+ let(:json) { {"data" => "is_json", "more" => {"hi" => "there"} } }
8
+ let(:result) {
9
+ BatchApi::Response.new([
10
+ 200,
11
+ {"Content-Type" => "application/json"},
12
+ [MultiJson.dump(json)]
13
+ ])
14
+ }
15
+
16
+ describe "#call" do
17
+ context "for json results" do
18
+ it "decodes JSON results for application/json responses" do
19
+ result = decoder.call(env)
20
+ result.body.should == json
21
+ end
22
+
23
+ it "doesn't change anything else" do
24
+ result = decoder.call(env)
25
+ result.status.should == 200
26
+ result.headers.should == {"Content-Type" => "application/json"}
27
+ end
28
+ end
29
+
30
+ context "for non-JSON responses" do
31
+ it "doesn't decode" do
32
+ result.headers = {"Content-Type" => "text/html"}
33
+ decoder.call(env).body.should == MultiJson.dump(json)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe BatchApi::InternalMiddleware::ResponseFilter do
4
+ let(:app) { stub("app", call: result) }
5
+ let(:surpressor) { BatchApi::InternalMiddleware::ResponseFilter.new(app) }
6
+ let(:env) { {
7
+ op: stub("operation", options: {"silent" => true})
8
+ } }
9
+
10
+ let(:result) {
11
+ BatchApi::Response.new([
12
+ 200,
13
+ {"Content-Type" => "application/json"},
14
+ ["{}"]
15
+ ])
16
+ }
17
+
18
+ describe "#call" do
19
+ context "for results with silent" do
20
+ context "for successful (200-299) results" do
21
+ it "empties the response so its as_json is empty" do
22
+ surpressor.call(env)
23
+ result.as_json.should == {}
24
+ end
25
+ end
26
+
27
+ context "for non-successful responses" do
28
+ it "doesn't change anything else" do
29
+ result.status = 301
30
+ expect {
31
+ surpressor.call(env)
32
+ }.not_to change(result, :to_s)
33
+ end
34
+ end
35
+ end
36
+
37
+ context "for results without silent" do
38
+ before :each do
39
+ env[:op].options[:silent] = nil
40
+ end
41
+
42
+ context "for successful (200-299) results" do
43
+ it "does nothing" do
44
+ expect {
45
+ surpressor.call(env)
46
+ }.not_to change(result, :to_s)
47
+ end
48
+ end
49
+
50
+ context "for non-successful responses" do
51
+ it "doesn't change anything else" do
52
+ result.status = 301
53
+ expect {
54
+ surpressor.call(env)
55
+ }.not_to change(result, :to_s)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe BatchApi::InternalMiddleware do
4
+ class FakeBuilder
5
+ attr_accessor :middlewares
6
+
7
+ def initialize(&block)
8
+ @middlewares = []
9
+ instance_eval(&block) if block_given?
10
+ end
11
+
12
+ def use(middleware, *args)
13
+ @middlewares << [middleware, args]
14
+ end
15
+ end
16
+
17
+ let(:builder) { FakeBuilder.new }
18
+
19
+ it "builds an empty default global middleware" do
20
+ builder.instance_eval(
21
+ &BatchApi::InternalMiddleware::DEFAULT_BATCH_MIDDLEWARE
22
+ )
23
+ builder.middlewares.should be_empty
24
+ end
25
+
26
+ describe "internal middleware defaults" do
27
+ before :each do
28
+ builder.instance_eval(
29
+ &BatchApi::InternalMiddleware::DEFAULT_OPERATION_MIDDLEWARE
30
+ )
31
+ end
32
+
33
+ it "builds a per-op middleware with the response silencer" do
34
+ builder.middlewares[0].should ==
35
+ [BatchApi::InternalMiddleware::ResponseFilter, []]
36
+ end
37
+
38
+ it "builds a per-op middleware with the JSON decoder" do
39
+ builder.middlewares[1].should ==
40
+ [BatchApi::InternalMiddleware::DecodeJsonBody, []]
41
+ end
42
+ end
43
+
44
+ describe ".batch_stack" do
45
+ # we can't use stubs inside the procs since they're instance_eval'd
46
+ let(:global_config) { Proc.new { use "Global" } }
47
+ let(:strategy) { stub("strategy") }
48
+ let(:processor) { stub("processor", strategy: strategy) }
49
+ let(:stack) { BatchApi::InternalMiddleware.batch_stack(processor) }
50
+
51
+ before :each do
52
+ BatchApi.config.stub(:batch_middleware).and_return(global_config)
53
+ stub_const("Middleware::Builder", FakeBuilder)
54
+ end
55
+
56
+ it "builds the stack with the right number of wares" do
57
+ stack.middlewares.length.should == 2
58
+ end
59
+
60
+ it "builds a middleware stack starting with the configured global wares" do
61
+ stack.middlewares[0].first.should == "Global"
62
+ end
63
+
64
+ it "inserts the appropriate strategy from the processor" do
65
+ stack.middlewares[1].first.should == strategy
66
+ end
67
+ end
68
+
69
+ describe ".operation_stack" do
70
+ # we can't use stubs inside the procs since they're instance_eval'd
71
+ let(:op_config) { Proc.new { use "Op" } }
72
+ let(:stack) { BatchApi::InternalMiddleware.operation_stack }
73
+
74
+ before :each do
75
+ BatchApi.config.stub(:operation_middleware).and_return(op_config)
76
+ stub_const("Middleware::Builder", FakeBuilder)
77
+ end
78
+
79
+ it "builds the stack with the right number of wares" do
80
+ stack.middlewares.length.should == 2
81
+ end
82
+
83
+ it "builds a middleware stack including the configured per-op wares" do
84
+ stack.middlewares[0].first.should == "Op"
85
+ end
86
+
87
+ it "builds a middleware stack ending with the executor" do
88
+ stack.middlewares[1].first.should == BatchApi::Processor::Executor
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,240 @@
1
+ require 'spec_helper'
2
+ require 'batch_api/operation'
3
+
4
+ describe BatchApi::Operation::Rack do
5
+ let(:op_params) { {
6
+ "method" => "POST",
7
+ # this matches a route in our dummy application
8
+ "url" => "/endpoint?foo=baz",
9
+ "params" => {a: 2},
10
+ "headers" => {"foo" => "bar"}
11
+ } }
12
+
13
+ # for env, see bottom of file - it's long
14
+ let(:operation) { BatchApi::Operation::Rack.new(op_params, env, app) }
15
+ let(:app) { stub("application", call: [200, {}, ["foo"]]) }
16
+
17
+ describe "accessors" do
18
+ [
19
+ :method, :url, :params, :headers,
20
+ :env, :app, :result, :options
21
+ ].each do |a|
22
+ attr = a
23
+ it "has an accessor for #{attr}" do
24
+ value = stub
25
+ operation.send("#{attr}=", value)
26
+ operation.send(attr).should == value
27
+ end
28
+ end
29
+ end
30
+
31
+ describe "#initialize" do
32
+ ["method", "url", "params", "headers"].each do |a|
33
+ attr = a
34
+ it "extracts the #{attr} information from the operation params" do
35
+ operation.send(attr).should == op_params[attr]
36
+ end
37
+ end
38
+
39
+ it "sets options to the op" do
40
+ operation.options.should == op_params
41
+ end
42
+
43
+ it "defaults method to get if not provided" do
44
+ op = BatchApi::Operation::Rack.new(op_params.except("method"), env, app)
45
+ op.method.should == "get"
46
+ end
47
+
48
+ it "defaults params to {} if not provided" do
49
+ op = BatchApi::Operation::Rack.new(op_params.except("params"), env, app)
50
+ op.params.should == {}
51
+ end
52
+
53
+ it "defaults headers to {} if not provided" do
54
+ op = BatchApi::Operation::Rack.new(op_params.except("headers"), env, app)
55
+ op.headers.should == {}
56
+ end
57
+
58
+ it "does a deep dup of the env" do
59
+ operation.env.should == env
60
+
61
+ flat_env = env.to_a.flatten
62
+ operation.env.to_a.flatten.each_with_index do |obj, index|
63
+ # this is a rough test for deep dup -- make sure the objects
64
+ # that aren't symbols aren't actually the same objects in memory
65
+ if obj.is_a?(Hash) || obj.is_a?(Array)
66
+ obj.object_id.should_not == flat_env[index].object_id
67
+ end
68
+ end
69
+ end
70
+
71
+ it "raises a MalformedOperationError if URL is missing" do
72
+ no_url = op_params.dup.tap {|o| o.delete("url") }
73
+ expect {
74
+ BatchApi::Operation::Rack.new(no_url, env, app)
75
+ }.to raise_exception(BatchApi::Errors::MalformedOperationError)
76
+ end
77
+ end
78
+
79
+ describe "#process_env" do
80
+ let(:processed_env) { operation.tap {|o| o.process_env}.env }
81
+
82
+ before { BatchApi.config.stub(endpoint: '/api/batch') }
83
+
84
+ it "merges any headers in in the right format" do
85
+ key = "HTTP_FOO" # as defined above in op_params
86
+
87
+ processed_env[key].should_not == env[key]
88
+ # in this case, it's a batch controller
89
+ processed_env[key].should == op_params["headers"]["foo"]
90
+ end
91
+
92
+ it "preserves existing headers" do
93
+ processed_env["HTTP_PREVIOUS_HEADERS"].should == env["HTTP_PREVIOUS_HEADERS"]
94
+ end
95
+
96
+ it "updates the method" do
97
+ key = "REQUEST_METHOD"
98
+ processed_env[key].should_not == env[key]
99
+ processed_env[key].should == "POST"
100
+ end
101
+
102
+ it "updates the REQUEST_URI" do
103
+ key = "REQUEST_URI"
104
+ processed_env[key].should_not == env[key]
105
+ processed_env[key].should == "http://localhost:3000#{op_params["url"]}"
106
+ end
107
+
108
+ it "updates the REQUEST_PATH with the path component (w/o params)" do
109
+ key = "REQUEST_PATH"
110
+ processed_env[key].should_not == env[key]
111
+ processed_env[key].should == op_params["url"].split("?").first
112
+ end
113
+
114
+ it "updates the original fullpath" do
115
+ key = "ORIGINAL_FULLPATH"
116
+ processed_env[key].should_not == env[key]
117
+ processed_env[key].should == op_params["url"]
118
+ end
119
+
120
+ it "updates the PATH_INFO" do
121
+ key = "PATH_INFO"
122
+ processed_env[key].should_not == env[key]
123
+ processed_env[key].should == op_params["url"]
124
+ end
125
+
126
+ it "updates the rack query string" do
127
+ key = "rack.request.query_string"
128
+ processed_env[key].should_not == env[key]
129
+ processed_env[key].should == op_params["url"].split("?").last
130
+ end
131
+
132
+ it "updates the QUERY_STRING" do
133
+ key = "QUERY_STRING"
134
+ processed_env[key].should_not == env[key]
135
+ processed_env[key].should == op_params["url"].split("?").last
136
+ end
137
+
138
+ it "updates the form hash" do
139
+ key = "rack.request.form_hash"
140
+ processed_env[key].should_not == env[key]
141
+ processed_env[key].should == op_params["params"]
142
+ end
143
+
144
+ context "query_hash" do
145
+ it "sets it to params for a GET" do
146
+ operation.method = "get"
147
+ processed_env = operation.tap {|o| o.process_env}.env
148
+ key = "rack.request.query_hash"
149
+ processed_env[key].should_not == env[key]
150
+ processed_env[key].should == op_params["params"]
151
+ end
152
+
153
+ it "sets it to nil for a POST" do
154
+ key = "rack.request.query_hash"
155
+ processed_env[key].should_not == env[key]
156
+ processed_env[key].should be_nil
157
+ end
158
+ end
159
+ end
160
+
161
+ describe "#execute" do
162
+ context "when it works" do
163
+ let(:result) { [
164
+ 200,
165
+ {header: "footer"},
166
+ stub(body: "{\"data\":2}", cookies: nil)
167
+ ] }
168
+ let(:processed_env) { stub }
169
+
170
+ before :each do
171
+ operation.stub(:process_env) { operation.env = processed_env }
172
+ end
173
+
174
+ it "executes the call with the application" do
175
+ app.should_receive(:call).with(processed_env)
176
+ operation.execute
177
+ end
178
+
179
+ it "returns a BatchAPI::Response made from the result" do
180
+ response = stub
181
+ app.stub(:call).and_return(result)
182
+ BatchApi::Response.should_receive(:new).with(result).and_return(response)
183
+ operation.execute.should == response
184
+ end
185
+
186
+ it "returns a BatchApi::Response from an ErrorWrapper for errors" do
187
+ err = StandardError.new
188
+ result, rendered, response = stub, stub, stub
189
+ b_err = stub("batch error", render: rendered)
190
+
191
+ # simulate the error
192
+ app.stub(:call).and_raise(err)
193
+ # we'll create the BatchError
194
+ BatchApi::ErrorWrapper.should_receive(:new).with(err).and_return(b_err)
195
+ # render that as the response
196
+ BatchApi::Response.should_receive(:new).with(rendered).and_return(response)
197
+ # and return the response overall
198
+ operation.execute.should == response
199
+ end
200
+ end
201
+ end
202
+
203
+ let(:env) {
204
+ {
205
+ "CONTENT_LENGTH"=>"10",
206
+ "CONTENT_TYPE"=>"application/x-www-form-urlencoded",
207
+ "GATEWAY_INTERFACE"=>"CGI/1.1",
208
+ "PATH_INFO"=>"/foo",
209
+ "QUERY_STRING"=>"",
210
+ "REMOTE_ADDR"=>"127.0.0.1",
211
+ "REMOTE_HOST"=>"1035.spotilocal.com",
212
+ "REQUEST_METHOD"=>"REPORT",
213
+ "REQUEST_URI"=>"http://localhost:3000/api/batch",
214
+ "SCRIPT_NAME"=>"",
215
+ "SERVER_NAME"=>"localhost",
216
+ "SERVER_PORT"=>"3000",
217
+ "SERVER_PROTOCOL"=>"HTTP/1.1",
218
+ "SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/1.9.3/2012-02-16)",
219
+ "HTTP_USER_AGENT"=>"curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5",
220
+ "HTTP_HOST"=>"localhost:3000",
221
+ "HTTP_ACCEPT"=>"*/*",
222
+ "HTTP_PREVIOUS_HEADERS" => "value",
223
+ "rack.version"=>[1,1],
224
+ "rack.input"=>StringIO.new("{\"ops\":{}}"),
225
+ "rack.errors"=>$stderr,
226
+ "rack.multithread"=>false,
227
+ "rack.multiprocess"=>false,
228
+ "rack.run_once"=>false,
229
+ "rack.url_scheme"=>"http",
230
+ "HTTP_VERSION"=>"HTTP/1.1",
231
+ "REQUEST_PATH"=>"/api/batch",
232
+ "ORIGINAL_FULLPATH"=>"/api/batch",
233
+ "rack.request.form_input"=>StringIO.new("{\"ops\":{}}"),
234
+ "rack.request.form_hash"=>{"{\"ops\":{}}"=>nil},
235
+ "rack.request.form_vars"=>"{\"ops\":{}}",
236
+ "rack.request.query_string"=>"",
237
+ "rack.request.query_hash"=>{}
238
+ }
239
+ }
240
+ end
@@ -0,0 +1,100 @@
1
+ require 'spec_helper'
2
+ require 'batch_api/operation'
3
+
4
+ describe BatchApi::Operation::Rails do
5
+ let(:op_params) { {
6
+ "method" => "POST",
7
+ # this matches a route in our dummy application
8
+ "url" => "/endpoint?foo=baz",
9
+ "params" => {a: 2},
10
+ "headers" => {"foo" => "bar"}
11
+ } }
12
+
13
+ # for env, see bottom of file - it's long
14
+ let(:operation) { BatchApi::Operation::Rails.new(op_params, env, app) }
15
+ let(:app) { stub("application", call: [200, {}, ["foo"]]) }
16
+ let(:path_params) { {controller: "batch_api/batch", action: "batch"} }
17
+ let(:mixed_params) { op_params["params"].merge(path_params) }
18
+
19
+ before :each do
20
+ ::Rails.application.routes.stub(:recognize_path).and_return(path_params)
21
+ end
22
+
23
+ describe "#initialize" do
24
+ it "merges in the Rails path params" do
25
+ ::Rails.application.routes.should_receive(:recognize_path).with(
26
+ op_params["url"],
27
+ op_params
28
+ ).and_return(path_params)
29
+
30
+ operation.params.should include(path_params)
31
+ end
32
+
33
+ it "doesn't change the params if the path isn't recognized" do
34
+ ::Rails.application.routes.stub(:recognize_path).and_raise(StandardError)
35
+ operation.params.should == op_params["params"]
36
+ end
37
+ end
38
+
39
+ describe "#process_env" do
40
+ let(:processed_env) { operation.tap {|o| o.process_env}.env }
41
+
42
+ it "updates the ActionDispatch params" do
43
+ key = "action_dispatch.request.parameters"
44
+ processed_env[key].should_not == env[key]
45
+ processed_env[key].should == mixed_params
46
+ end
47
+
48
+ it "updates the ActionDispatch request params" do
49
+ key = "action_dispatch.request.request_parameters"
50
+ processed_env[key].should_not == env[key]
51
+ processed_env[key].should == mixed_params
52
+ end
53
+ end
54
+
55
+ let(:env) {
56
+ {
57
+ "CONTENT_LENGTH"=>"10",
58
+ "CONTENT_TYPE"=>"application/x-www-form-urlencoded",
59
+ "GATEWAY_INTERFACE"=>"CGI/1.1",
60
+ "PATH_INFO"=>"/foo",
61
+ "QUERY_STRING"=>"",
62
+ "REMOTE_ADDR"=>"127.0.0.1",
63
+ "REMOTE_HOST"=>"1035.spotilocal.com",
64
+ "REQUEST_METHOD"=>"REPORT",
65
+ "REQUEST_URI"=>"http://localhost:3000/batch",
66
+ "SCRIPT_NAME"=>"",
67
+ "SERVER_NAME"=>"localhost",
68
+ "SERVER_PORT"=>"3000",
69
+ "SERVER_PROTOCOL"=>"HTTP/1.1",
70
+ "SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/1.9.3/2012-02-16)",
71
+ "HTTP_USER_AGENT"=>"curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8r zlib/1.2.5",
72
+ "HTTP_HOST"=>"localhost:3000",
73
+ "HTTP_ACCEPT"=>"*/*",
74
+ "HTTP_PREVIOUS_HEADERS" => "value",
75
+ "rack.version"=>[1,1],
76
+ "rack.input"=>StringIO.new("{\"ops\":{}}"),
77
+ "rack.errors"=>$stderr,
78
+ "rack.multithread"=>false,
79
+ "rack.multiprocess"=>false,
80
+ "rack.run_once"=>false,
81
+ "rack.url_scheme"=>"http",
82
+ "HTTP_VERSION"=>"HTTP/1.1",
83
+ "REQUEST_PATH"=>"/batch",
84
+ "ORIGINAL_FULLPATH"=>"/batch",
85
+ "action_dispatch.routes"=>Rails.application.routes,
86
+ "action_dispatch.parameter_filter"=>[:password],
87
+ "action_dispatch.secret_token"=>"fc6fbc81b3204410da8389",
88
+ "action_dispatch.show_exceptions"=>true,
89
+ "action_dispatch.show_detailed_exceptions"=>true,
90
+ "action_dispatch.logger"=>Rails.logger,
91
+ "action_dispatch.backtrace_cleaner"=>nil,
92
+ "action_dispatch.request_id"=>"2e7c988bea73e13dca4fac059a1bb187",
93
+ "action_dispatch.remote_ip"=>"127.0.0.1",
94
+ "action_dispatch.request.content_type"=>"application/x-www-form-urlencoded",
95
+ "action_dispatch.request.path_parameters"=> {},
96
+ "rack.request.query_string"=>"",
97
+ "rack.request.query_hash"=>{}
98
+ }
99
+ }
100
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+ require 'batch_api/processor/executor'
3
+
4
+ describe BatchApi::Processor::Executor do
5
+
6
+ let(:app) { stub("app", call: stub) }
7
+ let(:executor) { BatchApi::Processor::Executor.new(app) }
8
+ let(:result) { stub("result") }
9
+ let(:op) { stub("operation", execute: result) }
10
+ let(:env) { {op: op} }
11
+
12
+ describe "#call" do
13
+ it "executes the operation" do
14
+ op.should_receive(:execute)
15
+ executor.call(env)
16
+ end
17
+
18
+ it "returns the result" do
19
+ executor.call(env).should == result
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe BatchApi::Processor::Sequential do
4
+
5
+ let(:app) { stub("app", call: stub) }
6
+ let(:sequential) { BatchApi::Processor::Sequential.new(app) }
7
+
8
+ describe "#call" do
9
+ let(:call_results) { 3.times.collect {|i| stub("called #{i}") } }
10
+ let(:env) { {
11
+ ops: 3.times.collect {|i| stub("op #{i}") }
12
+ } }
13
+ let(:op_middleware) { stub("middleware", call: {}) }
14
+
15
+ before :each do
16
+ BatchApi::InternalMiddleware.
17
+ stub(:operation_stack).and_return(op_middleware)
18
+ op_middleware.stub(:call).and_return(*call_results)
19
+ end
20
+
21
+ it "creates an operation middleware stack and calls it for each op" do
22
+ env[:ops].each {|op|
23
+ op_middleware.should_receive(:call).
24
+ with(hash_including(op: op)).ordered
25
+ }
26
+ sequential.call(env)
27
+ end
28
+
29
+ it "includes the rest of the env in the calls" do
30
+ op_middleware.should_receive(:call).
31
+ with(hash_including(env)).exactly(3).times
32
+ sequential.call(env)
33
+ end
34
+
35
+ it "returns the results of the calls" do
36
+ sequential.call(env).should == call_results
37
+ end
38
+ end
39
+ end