├── History.md ├── package.json ├── backbone-callbacks-min.js ├── LICENSE ├── README.md ├── grunt.js ├── test └── CallbackTest.js └── backbone-callbacks.js /History.md: -------------------------------------------------------------------------------- 1 | 0.1.3 / 2012-06-25 2 | =================== 3 | 4 | * Automatically calling attach() on the global Backbone class 5 | 6 | 0.1.2 / 2012-06-13 7 | =================== 8 | 9 | * Assuring a null response on error 10 | * Better browser compatibility 11 | 12 | 13 | 0.1.1 / 2012-06-13 14 | =================== 15 | 16 | * Allowed save() with only the callback (no attributes or options) 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backbone-callbacks", 3 | "version": "0.1.3", 4 | "main": "./backbone-callbacks.js", 5 | "description": "Anonymous callback style interface for Backbone.js async methods", 6 | "author": "Loren West ", 7 | "homepage": "http://lorenwest.github.com/backbone-callbacks/", 8 | "dependencies": { 9 | "underscore": "1.3.x", 10 | "backbone": "0.9.x" 11 | }, 12 | "devDependencies": { 13 | "grunt": "~0.3.6" 14 | }, 15 | "engines": {"node": ">0.4.x"}, 16 | "scripts": { 17 | "test" : "grunt test" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /backbone-callbacks-min.js: -------------------------------------------------------------------------------- 1 | /* backbone-callbacks - v0.1.3 - 2012-06-25 */ 2 | (function(a){var b=a._||require("underscore")._,c=a.Backbone||require("backbone"),d=function(a,c){return function(){var d=b.toArray(arguments),e=d[d.length-1];if(typeof e=="function"){d.splice(-1,1),d.length===0&&d.push({}),d.length===1&&a==="save"&&d.push({});var f=d[d.length-1];f.success=function(a,b){e(null,b)},f.error=function(a,b){e(b,null)}}return c.apply(this,d)}};typeof module!="undefined"&&typeof module.exports!="undefined"?module.exports=d:a.BackboneCallbacks=d,d.attach=function(a){b.each(["save","destroy","fetch"],function(b){a.Model.prototype[b]=new d(b,a.Model.prototype[b])}),b.each(["fetch"],function(b){a.Collection.prototype[b]=new d(b,a.Collection.prototype[b])})},d.attach(c)})(this); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | // Copyright 2012, Loren West and other contributors 2 | 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to 5 | // deal in the Software without restriction, including without limitation the 6 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | // sell copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | // IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Anonymous callback interface for Backbone.js async methods 2 | 3 | [Backbone.js](http://documentcloud.github.com/backbone>Backbone.js) exposes a ```success/error``` style callback interface to the asynchronous methods ```fetch```, ```save```, and ```destroy```. Example: 4 | 5 | myModel.fetch({success: function(model, response) { 6 | ... 7 | }, 8 | error: function(model, response) { 9 | ... 10 | } 11 | }); 12 | 13 | If you're using Node.js and use your Backbone models on the client as well as the server, 14 | you may want to interact with your models in more of a Node.js async callback style: 15 | 16 | myModel.fetch(function(error, response) { 17 | ... 18 | }); 19 | 20 | This library adds to the existing interface for ```fetch```, ```save```, and ```destroy```, allowing you to choose the style you prefer. 21 | 22 | If a callback function is provided as the last argument, it uses that style. Otherwise it will use the default ```success/error``` style. 23 | 24 | To use this libary: 25 | 26 | 1) Download from github, or ```npm install backbone-callbacks``` 27 | 28 | 2) Include the library in your application. Load it after Backbone.js on the browser, or require('backbone-callbacks') in Node.js 29 | 30 | -------------------------------------------------------------------------------- /grunt.js: -------------------------------------------------------------------------------- 1 | // grunt.js (c) 2012 Loren West and other contributors 2 | // Node-monitor may be freely distributed under the MIT license. 3 | // For all details and documentation: 4 | // http://lorenwest.github.com/backbone-callbacks 5 | 6 | // Build automation tasks 7 | module.exports = function(grunt) { 8 | 9 | // Project configuration. 10 | grunt.initConfig({ 11 | pkg: '', 12 | meta: { 13 | banner: '/* <%= pkg.name %> - v<%= pkg.version %> - ' + 14 | '<%= grunt.template.today("yyyy-mm-dd") %> */' 15 | }, 16 | lint: { 17 | files: ['grunt.js', 'backbone-callbacks.js', 'test/*.js'] 18 | }, 19 | test: { 20 | files: ['test/*.js'] 21 | }, 22 | watch: { 23 | files: ['grunt.js', 'backbone-callbacks.js', 'test/*.js'], 24 | tasks: 'default' 25 | }, 26 | min: { 27 | all: { 28 | src: ['', './backbone-callbacks.js'], 29 | dest: './backbone-callbacks-min.js' 30 | } 31 | }, 32 | jshint: { 33 | options: { 34 | curly: true, 35 | eqeqeq: true, 36 | immed: true, 37 | latedef: true, 38 | newcap: true, 39 | noarg: true, 40 | sub: true, 41 | undef: true, 42 | boss: true, 43 | eqnull: true, 44 | node: true 45 | }, 46 | globals: { 47 | exports: true 48 | } 49 | } 50 | }); 51 | 52 | // Register grunt tasks 53 | grunt.registerTask('default', 'lint test'); 54 | grunt.registerTask('dist', 'default min:all'); 55 | 56 | }; 57 | -------------------------------------------------------------------------------- /test/CallbackTest.js: -------------------------------------------------------------------------------- 1 | // CallbackTest.js (c) 2012 Loren West and other contributors 2 | // Node-monitor may be freely distributed under the MIT license. 3 | // For further details and documentation: 4 | // http://lorenwest.github.com/node-monitor 5 | (function(root){ 6 | 7 | // Dependencies 8 | var Backbone = root.Backbone || require('backbone'), 9 | _ = root._ || require('underscore'), 10 | BackboneCallbacks = root.BackboneCallbacks || require('../backbone-callbacks'); 11 | 12 | // Build a mock sync for testing. This just returns the 13 | // method name into the result parameter to verify the call. 14 | // For error testing, it returns an error if the method is 'read'. 15 | Backbone.sync = function(method, model, options) { 16 | if (method === 'read') { 17 | return options.error({msg: 'Error test'}); 18 | } 19 | return options.success({method: method}); 20 | }; 21 | 22 | /** 23 | * Unit tests for the BackboneCallbacks functions. 24 | * @class CallbackTest 25 | */ 26 | 27 | /** 28 | * Test group for callback functionality 29 | * 30 | * @method Callback 31 | */ 32 | module.exports['Callback'] = { 33 | 34 | /** 35 | * Test that method injection works 36 | * @method Callback-Injection 37 | */ 38 | Injection: function(test) { 39 | var oldFetch = Backbone.Model.prototype.fetch; 40 | test.ok(Backbone.Model.prototype.fetch === oldFetch, 'Test for function equality passes'); 41 | BackboneCallbacks.attach(Backbone); 42 | test.ok(Backbone.Model.prototype.fetch !== oldFetch, 'Method injection produced a different method'); 43 | test.done(); 44 | }, 45 | 46 | /** 47 | * Test backwards compatibility 48 | * @method Callback-Compatibility 49 | */ 50 | Compatibility: function(test) { 51 | var onSuccess = function(model, response) { 52 | test.ok(model.get('method') === 'create', 'Success returned proper data'); 53 | var onFail = function(model, response) { 54 | test.ok(response.msg === 'Error test', 'Failure returned proper data'); 55 | test.done(); 56 | }; 57 | model.fetch({success: onSuccess, error: onFail}); 58 | }; 59 | var model = new Backbone.Model({}); 60 | model.save(null, {success:onSuccess}); 61 | }, 62 | 63 | /** 64 | * Test the new anonymous callback functionality 65 | * @method Callback-Anonymous 66 | */ 67 | Anonymous: function(test) { 68 | var model = new Backbone.Model({id:'test'}); 69 | model.save({a:'b'}, function(error, response) { 70 | test.ok(!error, 'Save callback successful'); 71 | model.fetch(function(error, response) { 72 | test.ok(error, 'Error callback successful.'); 73 | test.ok(!response, 'Isnt this nice?'); 74 | test.done(); 75 | }); 76 | }); 77 | } 78 | 79 | }; 80 | 81 | }(this)); 82 | -------------------------------------------------------------------------------- /backbone-callbacks.js: -------------------------------------------------------------------------------- 1 | // BackboneCallbacks.js (c) 2012 Loren West and other contributors 2 | // Node-monitor may be freely distributed under the MIT license. 3 | // For all details and documentation: 4 | // http://lorenwest.github.com/backbone-callbacks 5 | (function(root){ 6 | 7 | // Module dependencies 8 | var _ = root._ || require('underscore')._, 9 | Backbone = root.Backbone || require('backbone'); 10 | 11 | /** 12 | * Anonymous callback style interface for Backbone.js async methods. 13 | * 14 | * Load this after Backbone.js to add an anonymous function callback style 15 | * interface for fetch(), save(), and destroy() in addition to the built-in 16 | * success/error style interface. 17 | * 18 | * This adds a shim to the existing interface, allowing either style to be 19 | * used. If a callback function is provided as the last argument, it will 20 | * use that callback style. Otherwise it will use the success/error style. 21 | * 22 | * Example: 23 | * 24 | * customer.save(attrs, options, function(error, response) { 25 | * if (error) { 26 | * return console.log('Error saving customer', error); 27 | * } 28 | * console.log('Customer save successful. Response:', response); 29 | * }); 30 | * 31 | * The callback gets two arguments - an error object and response object. 32 | * One or the other will be set based on an error condition. 33 | * 34 | * The motivation for this callback style is to offer Backbone.js clients a 35 | * common coding style for client-side and server-side applications. 36 | * 37 | * @class BackboneCallbacks 38 | */ 39 | var BackboneCallbacks = function(methodName, method) { 40 | return function() { 41 | 42 | // Connect the success/error methods for callback style requests. 43 | // These style callbacks don't need the model or options arguments 44 | // because they're in the scope of the anonymous callback function. 45 | var args = _.toArray(arguments), callback = args[args.length - 1]; 46 | if (typeof callback === 'function') { 47 | 48 | // Remove the last element (the callback) 49 | args.splice(-1, 1); 50 | 51 | // Place options if none were specified. 52 | if (args.length === 0) { 53 | args.push({}); 54 | } 55 | 56 | // Place attributes if save and only options were specified 57 | if (args.length === 1 && methodName === 'save') { 58 | args.push({}); 59 | } 60 | var options = args[args.length - 1]; 61 | 62 | // Place the success and error methods 63 | options.success = function(model, response) { 64 | callback(null, response); 65 | }; 66 | options.error = function(model, response) { 67 | // Provide the response as the error. 68 | callback(response, null); 69 | }; 70 | } 71 | 72 | // Invoke the original method 73 | return method.apply(this, args); 74 | }; 75 | }; 76 | 77 | // Expose as the module for CommonJS, and globally for the browser. 78 | if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { 79 | module.exports = BackboneCallbacks; 80 | } else { 81 | root.BackboneCallbacks = BackboneCallbacks; 82 | } 83 | 84 | /** 85 | * Attach the shims to a clean Backbone library 86 | * 87 | * Backbone-callbacks works automatically for the global Backbone. If you have 88 | * a clean version of Backbone (via Backbone.noConflict()) you can manually 89 | * attach the backbone-callbacks functionality using this method. 90 | * 91 | * var Backbone = require('backbone').noConflict(); 92 | * require('backbone-callbacks').attach(Backbone); 93 | * 94 | * @static 95 | * @method attach 96 | * @param library {Backbone} Backbone library to attach to 97 | */ 98 | BackboneCallbacks.attach = function(library) { 99 | 100 | // Shim the original methods to allow the alternate calling style 101 | _.each(['save','destroy','fetch'], function(methodName) { 102 | library.Model.prototype[methodName] = new BackboneCallbacks(methodName, library.Model.prototype[methodName]); 103 | }); 104 | _.each(['fetch'], function(methodName) { 105 | library.Collection.prototype[methodName] = new BackboneCallbacks(methodName, library.Collection.prototype[methodName]); 106 | }); 107 | }; 108 | 109 | // Automatically attach the shims to the global Backbone library 110 | BackboneCallbacks.attach(Backbone); 111 | 112 | }(this)); 113 | --------------------------------------------------------------------------------