#414 Batch API Requests pro
Here I demonstrate how to perform bulk API operations though a single request using Rack middleware. This is great if you need to trigger multiple actions at once such as if the user goes offline.
- Download:
- source codeProject Files in Zip (57.8 KB)
- mp4Full Size H.264 Video (45.9 MB)
- m4vSmaller H.264 Video (22.1 MB)
- webmFull Size VP8 Video (24.7 MB)
- ogvFull Size Theora Video (55.7 MB)
Resources
terminal
rake middleware
curl -d 'requests=[{"method":"GET","url":"/tasks/1.json"},{"method":"GET","url":"/tasks/2.json"}]' localhost:3000/batch
config/application.rb
config.middleware.insert_before 0, "BatchRequests"
app/middleware/batch_requests.rb
class BatchRequests def initialize(app) @app = app end def call(env) if env["PATH_INFO"] == "/batch" request = Rack::Request.new(env.deep_dup) responses = JSON.parse(request[:requests]).map do |override| process_request(env.deep_dup, override) end [200, {"Content-Type" => "application/json"}, [{responses: responses}.to_json]] else @app.call(env) end end def process_request(env, override) path, query = override["url"].split("?") env["REQUEST_METHOD"] = override["method"] env["PATH_INFO"] = path env["QUERY_STRING"] = query env["rack.input"] = StringIO.new(override["body"].to_s) status, headers, body = @app.call(env) body.close if body.respond_to? :close {status: status, headers: headers, body: body.join} end end
app/assets/javascripts/tasks.js.coffee
jQuery -> new ToDoList
class ToDoList
constructor: ->
@requests = []
@tasks = $('#tasks').data('tasks')
for task in @tasks
@render(task)
$('#tasks').on('submit', '#new_task', @add)
$('#tasks').on('change', 'input[type=checkbox]', @update)
$('#tasks').on('click', '#complete_all', @completeAll)
$('#tasks').on('click', '#toggle_offline', @toggleOffline)
render: (task) ->
content = Mustache.render($('#task_template').html(), task)
if task.complete
$('#complete_tasks').append(content)
else
$('#incomplete_tasks').append(content)
find: (id) ->
for task in @tasks
return task if task.id == id
toggleOffline: (event) =>
event.preventDefault()
@offline = !@offline
$('#toggle_offline').text(if @offline then "Go Online" else "Go Offline")
@sync()
add: (event) =>
event.preventDefault()
task = {name: $('#task_name').val(), complete: false}
@render(task)
$('#task_name').val("")
@tasks.push(task)
$.post("/tasks.json", task: task)
update: (event) =>
checkbox = $(event.target)
task = @find(checkbox.data('id'))
task.complete = checkbox.prop('checked')
checkbox.parent().remove()
@render(task)
@save(task)
completeAll: (event) =>
event.preventDefault()
@offline = true
$('#incomplete_tasks').find('input[type=checkbox]').click()
@offline = false
@sync()
save: (task) ->
@requests.push
method: "PUT"
url: "/tasks/#{task.id}.json"
body: $.param(task: {complete: task.complete})
@sync()
sync: ->
unless @offline
$.post("/batch", requests: JSON.stringify(@requests))
@requests = []

