├── .versions ├── README.md ├── lib └── controller.js ├── package.js └── tests ├── controller-tests.js └── test-template.html /.versions: -------------------------------------------------------------------------------- 1 | base64@1.0.3 2 | binary-heap@1.0.3 3 | blaze@2.1.2 4 | blaze-tools@1.0.3 5 | callback-hook@1.0.3 6 | check@1.0.5 7 | ddp@1.1.0 8 | deps@1.0.7 9 | ejson@1.0.6 10 | geojson-utils@1.0.3 11 | html-tools@1.0.4 12 | htmljs@1.0.4 13 | id-map@1.0.3 14 | jquery@1.11.3_2 15 | json@1.0.3 16 | local-test:themeteorchef:controller@1.0.3 17 | logging@1.0.7 18 | meteor@1.1.6 19 | minifiers@1.1.5 20 | minimongo@1.0.8 21 | mongo@1.1.0 22 | observe-sequence@1.0.6 23 | ordered-dict@1.0.3 24 | random@1.0.3 25 | reactive-var@1.0.5 26 | retry@1.0.3 27 | spacebars-compiler@1.0.6 28 | templating@1.1.1 29 | themeteorchef:controller@1.0.3 30 | tinytest@1.0.5 31 | tracker@1.0.7 32 | underscore@1.0.3 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Controller 2 | An API for organizing your template logic. 3 | 4 | #### Features 5 | - Simplified syntax for defining template logic. 6 | 7 | #### Contents 8 | 9 | 1. Basic Usage 10 | 2. API 11 | 3. Tests 12 | 4. Contributing 13 | 1. Contributors 14 | 5. License 15 | 16 | #### Basic Usage 17 | Controller is nothing more than an abstraction layer. Syntax sugar. Its only purpose is to help you tidy up your template logic. Okay...why? 18 | 19 | Cleanliness. A few saved keystrokes on bigger chunks of logic. A little DRYer. Because I said so, _okay_? 20 | 21 | **Before Using Controller (356 Characters)** 22 | 23 | --- 24 | ```js 25 | Template.exampleTemplate.onRendered(function(){ 26 | $(".widget").activate(); 27 | }); 28 | 29 | Template.exampleTemplate.onDestroyed(function(){ 30 | $(".widget").deactivate(); 31 | }); 32 | 33 | Template.exampleTemplate.helpers({ 34 | widgetHelper: function() { 35 | return "some string of text"; 36 | } 37 | }); 38 | 39 | Template.exampleTemplate.events({ 40 | 'click .widget': function() { 41 | magic(); 42 | } 43 | }); 44 | ``` 45 | 46 | **Using Controller (334 Characters)** 47 | 48 | --- 49 | ```js 50 | Controller('exampleTemplate', { 51 | rendered: function() { 52 | $(".widget").activate(); 53 | }, 54 | destroyed: function() { 55 | $(".widget").deactivate(); 56 | }, 57 | helpers: { 58 | widgetHelper: function() { 59 | return "some string of text"; 60 | } 61 | }, 62 | events: { 63 | 'click .widget': function() { 64 | magic(); 65 | } 66 | } 67 | }); 68 | ``` 69 | 70 | **Possible reactions to this...** 71 | 72 | - "This is stupid!" 73 | - "You'll never work in this town again!" 74 | - "I went to MIT and _I_ think this is useless! _spits_" 75 | 76 | :cry: Buh. [Here](http://www.snickers.com/Resources/images/nutrition/products/large/1_Snickers.jpg). Better? Thought that'd do the trick. 77 | 78 | #### API 79 | Controller comes with support for all of Meteor's standard template methods: 80 | 81 | - `Controller` 82 | - `{string} templateName` 83 | - `{object} actions` 84 | - `{function} created` 85 | - `{function} rendered` 86 | - `{function} destroyed` 87 | - `{object} helpers` 88 | - `{object} events` 89 | 90 | Full API usage: 91 | 92 | ```js 93 | Controller('myTemplate', { 94 | created: function() { 95 | // Stuff to do on created. 96 | }, 97 | rendered: function() { 98 | // Stuff to do on rendered. 99 | }, 100 | destroyed: function() { 101 | // Stuff to do on destroyed. 102 | }, 103 | helpers: { 104 | myHelper: function() { 105 | // Put something on the template. 106 | } 107 | }, 108 | events: { 109 | 'click .something': function() { 110 | // Do something on click. 111 | } 112 | } 113 | }); 114 | ``` 115 | Easy peasy. 116 | 117 | #### Tests 118 | Controller comes with [a small suite of TinyTest-based tests](https://github.com/themeteorchef/controller/tree/master/tests) to ensure that all of your logic makes it to the dark side safely. To run the tests: 119 | 120 | 1. Install the TinyTest package `meteor add tinytest`. 121 | 2. Run Meteor with tests `meteor test-packages`. 122 | 3. Pop open your browser `http://localhost:3000`. 123 | 4. Verify tests are passing. 124 | 125 | ![http://cl.ly/am9W/Image%202015-04-26%20at%206.20.29%20PM.png](http://cl.ly/am9W/Image%202015-04-26%20at%206.20.29%20PM.png) 126 | 127 | **Note:** if your app is already running on `http://localhost:3000`, you can run tests separately by running `meteor --port 3001 test-packages`. 128 | 129 | #### Contributing 130 | Contributing, forking, and dorking is fully encouraged with Controller! If you'd like to help out with the package, take a look at the [contribution guide](https://github.com/themeteorchef/controller/wiki/Contribution-Guide) and start hacking :) 131 | 132 | ##### Contributors 133 | A special thanks to people who have contributed to Controller. 134 | - [Francois Lecroart (peernohell)](https://github.com/peernohell) 135 | 136 | #### License 137 | The code for this package is licensed under the [MIT License](http://opensource.org/licenses/MIT). 138 | -------------------------------------------------------------------------------- /lib/controller.js: -------------------------------------------------------------------------------- 1 | /** 2 | * themeteorchef:controller 3 | * An API for organizing your template logic. 4 | * 5 | * @see {@link https://github.com/themeteorchef/controller|Controller on GitHub} 6 | * @license MIT 7 | */ 8 | 9 | /** 10 | * @public 11 | * @module 12 | * 13 | * Controler 14 | * Takes a template name and actions object to delegate to each of Meteor's 15 | * template methods. Acessible on the client as Controller('templateName', {}); 16 | * 17 | * @example 18 | * Controller('templateName', { 19 | * created: function() { 20 | * // Logic to run when your template is created. 21 | * }, 22 | * rendered: function() { 23 | * // Logic to run when your template is rendered. 24 | * }, 25 | * destroyed: function() { 26 | * // Logic to run when your template is destroyed. 27 | * }, 28 | * helpers: { 29 | * exampleHelper: function() { 30 | * // Example helper logic. 31 | * } 32 | * }, 33 | * events: { 34 | * 'click .thing': function( element, template ) { 35 | * // Logic to run when .thing is clicked. 36 | * } 37 | * } 38 | * }); 39 | * 40 | * @param {string} template - The name of the template to match with this controller. 41 | * @param {object} actions - The various template methods to call on this template. 42 | */ 43 | Controller = function( template, actions ) { 44 | for ( var action in actions ) { 45 | _delegateMethod( template, action, actions[action] ); 46 | } 47 | }; 48 | 49 | /** 50 | * @private 51 | * @function 52 | * 53 | * Maps the passed template, actionName, and actionContent to the corresponding 54 | * Meteor template method. 55 | */ 56 | var _delegateMethod = function( template, actionName, actionContent ) { 57 | var templateMethod = _convertActionToTemplateMethod[actionName]; 58 | if ( templateMethod ) { 59 | Template[template][templateMethod]( actionContent ); 60 | } else { 61 | _throwError( 'Sorry, "' + actionName + '" is not a supported method.' ); 62 | } 63 | }; 64 | 65 | /** 66 | * @private 67 | * @object 68 | * 69 | * Contains association between controller's action name and template's method name. 70 | */ 71 | var _convertActionToTemplateMethod = { 72 | created: 'onCreated', 73 | rendered: 'onRendered', 74 | destroyed: 'onDestroyed', 75 | helpers: 'helpers', 76 | events: 'events' 77 | }; 78 | 79 | /** 80 | * @private 81 | * @function 82 | * 83 | * Throws an error in the browser console. 84 | */ 85 | var _throwError = console.error.bind( console, '[themeteorchef:controller]:' ); 86 | -------------------------------------------------------------------------------- /package.js: -------------------------------------------------------------------------------- 1 | Package.describe({ 2 | name: 'themeteorchef:controller', 3 | version: '1.2.0', 4 | summary: 'An API for organizing your template logic.', 5 | git: 'https://github.com/themeteorchef/controller', 6 | documentation: 'README.md' 7 | }); 8 | 9 | Package.onUse(function(api) { 10 | api.versionsFrom('1.1.0.2'); 11 | 12 | api.use(['templating'], 'client'); 13 | 14 | api.addFiles([ 15 | 'lib/controller.js' 16 | ], ['client']); 17 | 18 | api.export('Controller', ['client', 'server']); 19 | }); 20 | 21 | Package.onTest(function(api) { 22 | api.use([ 23 | 'tinytest', 24 | 'templating', 25 | 'themeteorchef:controller' 26 | ], ['client']); 27 | 28 | api.addFiles([ 29 | 'tests/test-template.html', 30 | 'tests/controller-tests.js' 31 | ], ['client']); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/controller-tests.js: -------------------------------------------------------------------------------- 1 | Tinytest.add('Is the createUser template available on the client?', function (test) { 2 | // createUser is a tester template, defined in /tests/test-template.html. 3 | test.equal(typeof Template.createUser, "object"); 4 | }); 5 | 6 | Tinytest.add('Is Controller available on the client?', function (test) { 7 | test.equal(typeof Controller, "function"); 8 | }); 9 | 10 | Tinytest.add('Does Controller properly assign the onCreated method?', function (test) { 11 | // Get our createUser template's created callback. 12 | var createdCallback = Template.createUser._callbacks.created; 13 | 14 | // Make sure that our template DOES NOT have an onCreated callback defined. 15 | test.equal(createdCallback.length, 0); 16 | 17 | // Define an onCreated method for our template using Controller. 18 | Controller('createUser', { 19 | created: function() { 20 | console.log("Do this onCreated."); 21 | } 22 | }); 23 | 24 | // Make sure that our template DOES have an onCreated callback defined. 25 | test.equal(createdCallback.length, 1); 26 | }); 27 | 28 | Tinytest.add('Does Controller properly assign the onRendered method?', function (test) { 29 | // Get our createUser template's rendered callback. 30 | var renderedCallback = Template.createUser._callbacks.rendered; 31 | 32 | // Make sure that our template DOES NOT have an onCreated callback defined. 33 | test.equal(renderedCallback.length, 0); 34 | 35 | // Define an onRendered method for our template using Controller. 36 | Controller('createUser', { 37 | rendered: function() { 38 | console.log("Do this onRendered."); 39 | } 40 | }); 41 | 42 | // Make sure that our template DOES have an onRendered callback defined. 43 | test.equal(renderedCallback.length, 1); 44 | }); 45 | 46 | Tinytest.add('Does Controller properly assign the onDestroyed method?', function (test) { 47 | // Get our createUser template's destroyed callback. 48 | var destroyedCallback = Template.createUser._callbacks.destroyed; 49 | 50 | // Make sure that our template DOES NOT have an onDestroyed callback defined. 51 | test.equal(destroyedCallback.length, 0); 52 | 53 | // Define an onRendered method for our template using Controller. 54 | Controller('createUser', { 55 | destroyed: function() { 56 | console.log("Do this onDestroyed."); 57 | } 58 | }); 59 | 60 | // Make sure that our template DOES have an onRendered callback defined. 61 | test.equal(destroyedCallback.length, 1); 62 | }); 63 | 64 | Tinytest.add('Does Controller properly assign the helpers method?', function (test) { 65 | // Get our createUser template's HelperMap. 66 | var helperMap = Template.createUser.__helpers; 67 | 68 | // Make sure that our template DOES NOT have an exampleHelper defined. 69 | // Note: the space before exampleHelper here is something that Meteor introduces 70 | // and is not a bug introduced by Controller. 71 | test.equal(typeof helperMap[" exampleHelper"], "undefined"); 72 | 73 | // Define a helper for our template using Controller. 74 | Controller('createUser', { 75 | helpers: { 76 | exampleHelper: function() { 77 | return "just an example"; 78 | }, 79 | anotherHelper: function() { 80 | return "testing multiple helpers"; 81 | } 82 | } 83 | }); 84 | 85 | // Make sure that our template DOES have the exampleHelper helper defined. 86 | test.equal(typeof helperMap[" exampleHelper"], "function"); 87 | 88 | // Make sure that our template DOES have the anotherHelper helper defined. 89 | test.equal(typeof helperMap[" anotherHelper"], "function"); 90 | 91 | // Make sure that our template DOES NOT have the nonExistentHelper defined. 92 | test.equal(typeof helperMap[" nonExistentHelper"], "undefined"); 93 | }); 94 | 95 | Tinytest.add('Does Controller properly assign the events method?', function (test) { 96 | 97 | // Get our createUser template's event map. 98 | var eventMaps = Template.createUser.__eventMaps; 99 | 100 | // Make sure that our template DOES NOT have any event maps defined. 101 | test.equal(eventMaps.length, 0); 102 | 103 | // Define an event for our template using Controller. 104 | Controller('createUser', { 105 | events: { 106 | 'click .button': function() { 107 | console.log("This is just a test"); 108 | }, 109 | 'click .another-button': function() { 110 | console.log("Testing multiple events."); 111 | } 112 | } 113 | }); 114 | 115 | // Make sure that our template DOES have an event map defined. 116 | test.equal(eventMaps.length, 1); 117 | 118 | // Make sure that our template DOES have a 'click .button' event. 119 | test.equal(typeof eventMaps[0]['click .button'], "function"); 120 | 121 | // Make sure that our template DOES have a 'click .another-button' event. 122 | test.equal(typeof eventMaps[0]['click .another-button'], "function"); 123 | 124 | // Make sure that our template DOES NOT have a 'click .does-not-exist' event. 125 | test.equal(typeof eventMaps[0]['click .does-not-exist'], "undefined"); 126 | }); 127 | -------------------------------------------------------------------------------- /tests/test-template.html: -------------------------------------------------------------------------------- 1 | 13 | --------------------------------------------------------------------------------