#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 = []