├── .gitignore ├── README.textile ├── property-list.js ├── dsl_methods_args.js ├── closure_and_with.js └── with_dsl_new_function.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw? 2 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | These examples relate to a blog post on "alexyoung.org":http://alexyoung.org. 2 | 3 | The final technique can be seen in use in "riotjs":http://github.com/alexyoung/riotjs. 4 | -------------------------------------------------------------------------------- /property-list.js: -------------------------------------------------------------------------------- 1 | if (typeof print === 'undefined') { 2 | print = alert; 3 | } 4 | 5 | var DSLRunner = { 6 | run: function(methods) { 7 | this.ingredients = []; 8 | this.methods = methods; 9 | 10 | this.executeAndRemove('first'); 11 | 12 | for (var key in this.methods) { 13 | if (key !== 'last' && key.match(/^bake/)) { 14 | this.executeAndRemove(key); 15 | } 16 | } 17 | 18 | this.executeAndRemove('last'); 19 | }, 20 | 21 | addIngredient: function(ingredient) { 22 | this.ingredients.push(ingredient); 23 | }, 24 | 25 | executeAndRemove: function(methodName) { 26 | var output = this.methods[methodName](); 27 | delete(this.methods[methodName]); 28 | return output; 29 | } 30 | }; 31 | 32 | DSLRunner.run({ 33 | first: function() { 34 | print("I happen first"); 35 | }, 36 | 37 | bakeCake: function() { 38 | print("Commencing cake baking"); 39 | }, 40 | 41 | bakeBread: function() { 42 | print("Baking bread"); 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /dsl_methods_args.js: -------------------------------------------------------------------------------- 1 | if (typeof print === 'undefined') { 2 | print = alert; 3 | } 4 | 5 | var DSLRunner = { 6 | methodQueue: [], 7 | 8 | run: function(definition) { 9 | definition.call(this, this.bake, this.first, this.last); 10 | 11 | if (typeof this.firstMethod !== 'undefined') { 12 | this.firstMethod(); 13 | } 14 | 15 | for (var i = 0; i < this.methodQueue.length; i++) { 16 | this.methodQueue[i](); 17 | } 18 | 19 | if (typeof this.lastMethod !== 'undefined') { 20 | this.lastMethod(); 21 | } 22 | }, 23 | 24 | bake: function(callback) { 25 | DSLRunner.methodQueue.push(callback); 26 | }, 27 | 28 | first: function(callback) { 29 | DSLRunner.firstMethod = callback; 30 | }, 31 | 32 | last: function(callback) { 33 | DSLRunner.lastMethod = callback; 34 | } 35 | }; 36 | 37 | DSLRunner.run(function(bake, first, last) { 38 | first(function() { 39 | print("I happen first"); 40 | }); 41 | 42 | bake(function() { 43 | print("Baking bread"); 44 | }); 45 | 46 | bake(function() { 47 | print("Baking a cake"); 48 | }); 49 | }); 50 | 51 | -------------------------------------------------------------------------------- /closure_and_with.js: -------------------------------------------------------------------------------- 1 | if (typeof print === 'undefined') { 2 | print = alert; 3 | } 4 | 5 | var DSLRunner = { 6 | ingredients: [], 7 | 8 | prepareFunctionBody: function(fn) { 9 | return '(' + fn.toString().replace(/\s+$/, '') + ')()'; 10 | }, 11 | 12 | withThis: function(callback) { 13 | var body = this.prepareFunctionBody(callback), 14 | that = this; 15 | return function() { return eval('with(that) { ' + body + ' } '); }; 16 | }, 17 | 18 | run: function(definition) { 19 | this.withThis(definition)(); 20 | print("Your specified ingredients included: " + this.ingredients.join(', ')); 21 | }, 22 | 23 | bake: function(callback) { 24 | callback.call(this); 25 | }, 26 | 27 | addIngredient: function(ingredient) { 28 | this.ingredients.push(ingredient); 29 | }, 30 | 31 | last: function(callback) { 32 | callback.call(this); 33 | } 34 | }; 35 | 36 | DSLRunner.run(function() { 37 | bake(function() { 38 | addIngredient('flour'); 39 | addIngredient('yeast'); 40 | addIngredient('water'); 41 | 42 | print("Baking bread"); 43 | }); 44 | 45 | bake(function() { 46 | print("Baking a cake"); 47 | }); 48 | }); 49 | 50 | 51 | -------------------------------------------------------------------------------- /with_dsl_new_function.js: -------------------------------------------------------------------------------- 1 | if (typeof print === 'undefined') { 2 | print = alert; 3 | } 4 | 5 | var DSLRunner = { 6 | ingredients: [], 7 | 8 | prepareFunctionBody: function(fn) { 9 | return '(' + fn.toString().replace(/\s+$/, '') + ')()'; 10 | }, 11 | 12 | withDSL: function(callback) { 13 | var body = this.prepareFunctionBody(callback), 14 | f = new Function('bake', 'addIngredient', body), 15 | args = [this.bake, this.addIngredient]; 16 | return function() { f.apply(this, args); }; 17 | }, 18 | 19 | run: function(definition) { 20 | this.withDSL(definition)(); 21 | print("Your specified ingredients included: " + this.ingredients.join(', ')); 22 | }, 23 | 24 | bake: function(callback) { 25 | callback.call(this); 26 | }, 27 | 28 | addIngredient: function(ingredient) { 29 | DSLRunner.ingredients.push(ingredient); 30 | }, 31 | 32 | last: function(callback) { 33 | callback.call(this); 34 | } 35 | }; 36 | 37 | DSLRunner.run(function() { 38 | bake(function() { 39 | addIngredient('flour'); 40 | addIngredient('yeast'); 41 | addIngredient('water'); 42 | 43 | print("Baking bread"); 44 | }); 45 | 46 | bake(function() { 47 | print("Baking a cake"); 48 | }); 49 | }); 50 | 51 | 52 | --------------------------------------------------------------------------------