1. Damon Oehlman
  2. recipe

Commits

Damon Oehlman  committed 0633057

Initial commit

  • Participants
  • Branches master

Comments (0)

Files changed (13)

File .gitignore

View file
+.DS_Store
+node_modules

File .travis.yml

View file
+language: node_js
+node_js:
+  - 0.6
+  - 0.8
+
+notifications:
+  email:
+    - travis@sidelab.com
+  irc: "irc.freenode.org#sidelab"

File Makefile

View file
+SHELL := /bin/bash
+
+build:
+	@interleave src --package
+
+test:
+	@mocha --reporter spec
+
+.PHONY: test

File README.md

View file
+# cookbook
+
+This library provides a set of Javascript helpers for interpreting textual recipes which define the requirements of a web component.

File package.json

View file
+{
+  "name": "cookbook",
+  "description": "Javascript recipes",
+  "author": "Damon Oehlman <damon.oehlman@sidelab.com>",
+  "tags": [
+    "query",
+    "match",
+    "filter"
+  ],
+  "version": "0.0.0",
+  "engines": {
+    "node": ">= 0.6.x < 0.9.0"
+  },
+  "dependencies": {
+    "formatter": "0.1.x"
+  },
+  "devDependencies": {
+    "expect.js": "0.1.x",
+    "mocha": "1.2.x"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/DamonOehlman/cookbook.git"
+  },
+  "bugs": {
+    "url": "http://github.com/DamonOehlman/cookbook/issues"
+  },
+  "scripts": {
+    "test": "node_modules/mocha/bin/mocha --reporter spec"
+  },
+  "contributors": [],
+  "optionalDependencies": {}
+}

File pkg/amd/cookbook.js

View file
+define('cookbook', ['formatter'], function(formatter) {
+  
+  var reDownload = /^([\w\.\-]*?)\s*<\=\s*(.*)$/,
+      reField = /^\s*\#\s*(\w+)\:\s*(.*)$/,
+      reSection = /\s*\[(\w+)\]/;
+  
+  function Recipe(opts) {
+      // if opts is a string, then map this to opts.text
+      if (typeof opts == 'string' || (opts instanceof String)) {
+          opts = {
+              text: opts
+          };
+      }
+  
+      // ensure the opts have been initialised
+      opts = opts || {};
+  
+      // define the sections
+      this.sections = opts.sections || {};
+      
+      // initialise requirements
+      this.requirements = [].concat(opts.requirements || []);
+      
+      // if we have text, then parse the text
+      if (opts.text) {
+          this.parse(opts.text, opts);
+      }
+  }
+  
+  Recipe.prototype = {
+      parse: function(text, opts) {
+          var match, downloadMatch,
+              activeSection,
+              lines = (text || '').split(/\n/),
+              line,
+              inSections = false,
+              ii, count;
+              
+          // iterate through the lines until we hit the first section
+          for (ii = 0; ii < lines.length; ii++) {
+              // get a reference to the line
+              line = lines[ii];
+              
+              // check to see if we are in the sections
+              inSections = inSections || reSection.test(line);
+              
+              // if we are not in the sections, then check for field matches
+              if (! inSections) {
+                  match = reField.exec(line);
+                  if (match) {
+                      this[match[1]] = match[2];
+                  }
+              }
+              else {
+                  match = reSection.exec(line);
+                  if (match) {
+                      // create the active section
+                      activeSection = this.sections[match[1]] = {};
+                  }
+                  else {
+                      // check for a download match
+                      downloadMatch = reDownload.exec(line);
+  
+                      // if we have a download match, then add it to the active section
+                      if (downloadMatch) {
+                          var fileType = downloadMatch[1] || 'js';
+  
+                          // if the active section is currently undefined, then create the core section
+                          if (! activeSection) {
+                              activeSection = this.sections.core = {};
+                          }
+  
+                          // if the file type is not in the active section then add it
+                          if (! activeSection[fileType]) {
+                              activeSection[fileType] = [];
+                          }
+  
+                          // add the download to the file types in the active section
+                          activeSection[fileType].push(formatter(downloadMatch[2])(this));
+                      }
+                  }
+              }
+          }
+      }
+  };
+  
+  // define a `req` property to deal with parsing the string requirements into an array
+  Object.defineProperty(Recipe.prototype, 'req', {
+      set: function(value) {
+          var recipe = this;
+          
+          // update the requirements
+          this.requirements = value.split(reCommaDelim);
+          
+          // iterate through the requirements and update to absolute paths if it they are local
+          this.requirements.forEach(function(req, index) {
+              if (reLocalFile.test(req)) {
+                  recipe.requirements[index] = path.resolve(recipe.basePath, req);
+              }
+          });
+      }
+  });
+  
+  // define a `req` property to deal with parsing the string requirements into an array
+  Object.defineProperty(Recipe.prototype, 'dsc', {
+      set: function(value) {
+          this.desc = value;
+      }
+  });
+  
+  // define a dep property as an alias to req
+  Object.defineProperty(Recipe.prototype, 'dep', {
+      set: function(value) {
+          this.requirements = value.split(reCommaDelim);
+      }
+  });
+  
+  function cookbook() {
+  }
+  
+  // patch the recipe class into the cookbook
+  cookbook.Recipe = Recipe;
+
+  if (typeof cookbook != 'undefined') { return cookbook; }
+});

File pkg/cjs/cookbook.js

View file
+var formatter = require('formatter');
+
+
+var reDownload = /^([\w\.\-]*?)\s*<\=\s*(.*)$/,
+    reField = /^\s*\#\s*(\w+)\:\s*(.*)$/,
+    reSection = /\s*\[(\w+)\]/;
+
+function Recipe(opts) {
+    // if opts is a string, then map this to opts.text
+    if (typeof opts == 'string' || (opts instanceof String)) {
+        opts = {
+            text: opts
+        };
+    }
+
+    // ensure the opts have been initialised
+    opts = opts || {};
+
+    // define the sections
+    this.sections = opts.sections || {};
+    
+    // initialise requirements
+    this.requirements = [].concat(opts.requirements || []);
+    
+    // if we have text, then parse the text
+    if (opts.text) {
+        this.parse(opts.text, opts);
+    }
+}
+
+Recipe.prototype = {
+    parse: function(text, opts) {
+        var match, downloadMatch,
+            activeSection,
+            lines = (text || '').split(/\n/),
+            line,
+            inSections = false,
+            ii, count;
+            
+        // iterate through the lines until we hit the first section
+        for (ii = 0; ii < lines.length; ii++) {
+            // get a reference to the line
+            line = lines[ii];
+            
+            // check to see if we are in the sections
+            inSections = inSections || reSection.test(line);
+            
+            // if we are not in the sections, then check for field matches
+            if (! inSections) {
+                match = reField.exec(line);
+                if (match) {
+                    this[match[1]] = match[2];
+                }
+            }
+            else {
+                match = reSection.exec(line);
+                if (match) {
+                    // create the active section
+                    activeSection = this.sections[match[1]] = {};
+                }
+                else {
+                    // check for a download match
+                    downloadMatch = reDownload.exec(line);
+
+                    // if we have a download match, then add it to the active section
+                    if (downloadMatch) {
+                        var fileType = downloadMatch[1] || 'js';
+
+                        // if the active section is currently undefined, then create the core section
+                        if (! activeSection) {
+                            activeSection = this.sections.core = {};
+                        }
+
+                        // if the file type is not in the active section then add it
+                        if (! activeSection[fileType]) {
+                            activeSection[fileType] = [];
+                        }
+
+                        // add the download to the file types in the active section
+                        activeSection[fileType].push(formatter(downloadMatch[2])(this));
+                    }
+                }
+            }
+        }
+    }
+};
+
+// define a `req` property to deal with parsing the string requirements into an array
+Object.defineProperty(Recipe.prototype, 'req', {
+    set: function(value) {
+        var recipe = this;
+        
+        // update the requirements
+        this.requirements = value.split(reCommaDelim);
+        
+        // iterate through the requirements and update to absolute paths if it they are local
+        this.requirements.forEach(function(req, index) {
+            if (reLocalFile.test(req)) {
+                recipe.requirements[index] = path.resolve(recipe.basePath, req);
+            }
+        });
+    }
+});
+
+// define a `req` property to deal with parsing the string requirements into an array
+Object.defineProperty(Recipe.prototype, 'dsc', {
+    set: function(value) {
+        this.desc = value;
+    }
+});
+
+// define a dep property as an alias to req
+Object.defineProperty(Recipe.prototype, 'dep', {
+    set: function(value) {
+        this.requirements = value.split(reCommaDelim);
+    }
+});
+
+function cookbook() {
+}
+
+// patch the recipe class into the cookbook
+cookbook.Recipe = Recipe;
+
+if (typeof cookbook != 'undefined') { module.exports = cookbook; }

File pkg/iife/cookbook.js

View file
+// dep: formatter
+
+(function () {
+  
+  var reDownload = /^([\w\.\-]*?)\s*<\=\s*(.*)$/,
+      reField = /^\s*\#\s*(\w+)\:\s*(.*)$/,
+      reSection = /\s*\[(\w+)\]/;
+  
+  function Recipe(opts) {
+      // if opts is a string, then map this to opts.text
+      if (typeof opts == 'string' || (opts instanceof String)) {
+          opts = {
+              text: opts
+          };
+      }
+  
+      // ensure the opts have been initialised
+      opts = opts || {};
+  
+      // define the sections
+      this.sections = opts.sections || {};
+      
+      // initialise requirements
+      this.requirements = [].concat(opts.requirements || []);
+      
+      // if we have text, then parse the text
+      if (opts.text) {
+          this.parse(opts.text, opts);
+      }
+  }
+  
+  Recipe.prototype = {
+      parse: function(text, opts) {
+          var match, downloadMatch,
+              activeSection,
+              lines = (text || '').split(/\n/),
+              line,
+              inSections = false,
+              ii, count;
+              
+          // iterate through the lines until we hit the first section
+          for (ii = 0; ii < lines.length; ii++) {
+              // get a reference to the line
+              line = lines[ii];
+              
+              // check to see if we are in the sections
+              inSections = inSections || reSection.test(line);
+              
+              // if we are not in the sections, then check for field matches
+              if (! inSections) {
+                  match = reField.exec(line);
+                  if (match) {
+                      this[match[1]] = match[2];
+                  }
+              }
+              else {
+                  match = reSection.exec(line);
+                  if (match) {
+                      // create the active section
+                      activeSection = this.sections[match[1]] = {};
+                  }
+                  else {
+                      // check for a download match
+                      downloadMatch = reDownload.exec(line);
+  
+                      // if we have a download match, then add it to the active section
+                      if (downloadMatch) {
+                          var fileType = downloadMatch[1] || 'js';
+  
+                          // if the active section is currently undefined, then create the core section
+                          if (! activeSection) {
+                              activeSection = this.sections.core = {};
+                          }
+  
+                          // if the file type is not in the active section then add it
+                          if (! activeSection[fileType]) {
+                              activeSection[fileType] = [];
+                          }
+  
+                          // add the download to the file types in the active section
+                          activeSection[fileType].push(formatter(downloadMatch[2])(this));
+                      }
+                  }
+              }
+          }
+      }
+  };
+  
+  // define a `req` property to deal with parsing the string requirements into an array
+  Object.defineProperty(Recipe.prototype, 'req', {
+      set: function(value) {
+          var recipe = this;
+          
+          // update the requirements
+          this.requirements = value.split(reCommaDelim);
+          
+          // iterate through the requirements and update to absolute paths if it they are local
+          this.requirements.forEach(function(req, index) {
+              if (reLocalFile.test(req)) {
+                  recipe.requirements[index] = path.resolve(recipe.basePath, req);
+              }
+          });
+      }
+  });
+  
+  // define a `req` property to deal with parsing the string requirements into an array
+  Object.defineProperty(Recipe.prototype, 'dsc', {
+      set: function(value) {
+          this.desc = value;
+      }
+  });
+  
+  // define a dep property as an alias to req
+  Object.defineProperty(Recipe.prototype, 'dep', {
+      set: function(value) {
+          this.requirements = value.split(reCommaDelim);
+      }
+  });
+  
+  function cookbook() {
+  }
+  
+  // patch the recipe class into the cookbook
+  cookbook.Recipe = Recipe;
+}());

File pkg/oldschool/cookbook.js

View file
+// dep: formatter
+
+(function (glob) {
+  
+  var reDownload = /^([\w\.\-]*?)\s*<\=\s*(.*)$/,
+      reField = /^\s*\#\s*(\w+)\:\s*(.*)$/,
+      reSection = /\s*\[(\w+)\]/;
+  
+  function Recipe(opts) {
+      // if opts is a string, then map this to opts.text
+      if (typeof opts == 'string' || (opts instanceof String)) {
+          opts = {
+              text: opts
+          };
+      }
+  
+      // ensure the opts have been initialised
+      opts = opts || {};
+  
+      // define the sections
+      this.sections = opts.sections || {};
+      
+      // initialise requirements
+      this.requirements = [].concat(opts.requirements || []);
+      
+      // if we have text, then parse the text
+      if (opts.text) {
+          this.parse(opts.text, opts);
+      }
+  }
+  
+  Recipe.prototype = {
+      parse: function(text, opts) {
+          var match, downloadMatch,
+              activeSection,
+              lines = (text || '').split(/\n/),
+              line,
+              inSections = false,
+              ii, count;
+              
+          // iterate through the lines until we hit the first section
+          for (ii = 0; ii < lines.length; ii++) {
+              // get a reference to the line
+              line = lines[ii];
+              
+              // check to see if we are in the sections
+              inSections = inSections || reSection.test(line);
+              
+              // if we are not in the sections, then check for field matches
+              if (! inSections) {
+                  match = reField.exec(line);
+                  if (match) {
+                      this[match[1]] = match[2];
+                  }
+              }
+              else {
+                  match = reSection.exec(line);
+                  if (match) {
+                      // create the active section
+                      activeSection = this.sections[match[1]] = {};
+                  }
+                  else {
+                      // check for a download match
+                      downloadMatch = reDownload.exec(line);
+  
+                      // if we have a download match, then add it to the active section
+                      if (downloadMatch) {
+                          var fileType = downloadMatch[1] || 'js';
+  
+                          // if the active section is currently undefined, then create the core section
+                          if (! activeSection) {
+                              activeSection = this.sections.core = {};
+                          }
+  
+                          // if the file type is not in the active section then add it
+                          if (! activeSection[fileType]) {
+                              activeSection[fileType] = [];
+                          }
+  
+                          // add the download to the file types in the active section
+                          activeSection[fileType].push(formatter(downloadMatch[2])(this));
+                      }
+                  }
+              }
+          }
+      }
+  };
+  
+  // define a `req` property to deal with parsing the string requirements into an array
+  Object.defineProperty(Recipe.prototype, 'req', {
+      set: function(value) {
+          var recipe = this;
+          
+          // update the requirements
+          this.requirements = value.split(reCommaDelim);
+          
+          // iterate through the requirements and update to absolute paths if it they are local
+          this.requirements.forEach(function(req, index) {
+              if (reLocalFile.test(req)) {
+                  recipe.requirements[index] = path.resolve(recipe.basePath, req);
+              }
+          });
+      }
+  });
+  
+  // define a `req` property to deal with parsing the string requirements into an array
+  Object.defineProperty(Recipe.prototype, 'dsc', {
+      set: function(value) {
+          this.desc = value;
+      }
+  });
+  
+  // define a dep property as an alias to req
+  Object.defineProperty(Recipe.prototype, 'dep', {
+      set: function(value) {
+          this.requirements = value.split(reCommaDelim);
+      }
+  });
+  
+  function cookbook() {
+  }
+  
+  // patch the recipe class into the cookbook
+  cookbook.Recipe = Recipe;
+  
+  glob.cookbook = cookbook;
+  
+})(this);

File pkg/raw/cookbook.js

View file
+// req: formatter
+
+var reDownload = /^([\w\.\-]*?)\s*<\=\s*(.*)$/,
+    reField = /^\s*\#\s*(\w+)\:\s*(.*)$/,
+    reSection = /\s*\[(\w+)\]/;
+
+function Recipe(opts) {
+    // if opts is a string, then map this to opts.text
+    if (typeof opts == 'string' || (opts instanceof String)) {
+        opts = {
+            text: opts
+        };
+    }
+
+    // ensure the opts have been initialised
+    opts = opts || {};
+
+    // define the sections
+    this.sections = opts.sections || {};
+    
+    // initialise requirements
+    this.requirements = [].concat(opts.requirements || []);
+    
+    // if we have text, then parse the text
+    if (opts.text) {
+        this.parse(opts.text, opts);
+    }
+}
+
+Recipe.prototype = {
+    parse: function(text, opts) {
+        var match, downloadMatch,
+            activeSection,
+            lines = (text || '').split(/\n/),
+            line,
+            inSections = false,
+            ii, count;
+            
+        // iterate through the lines until we hit the first section
+        for (ii = 0; ii < lines.length; ii++) {
+            // get a reference to the line
+            line = lines[ii];
+            
+            // check to see if we are in the sections
+            inSections = inSections || reSection.test(line);
+            
+            // if we are not in the sections, then check for field matches
+            if (! inSections) {
+                match = reField.exec(line);
+                if (match) {
+                    this[match[1]] = match[2];
+                }
+            }
+            else {
+                match = reSection.exec(line);
+                if (match) {
+                    // create the active section
+                    activeSection = this.sections[match[1]] = {};
+                }
+                else {
+                    // check for a download match
+                    downloadMatch = reDownload.exec(line);
+
+                    // if we have a download match, then add it to the active section
+                    if (downloadMatch) {
+                        var fileType = downloadMatch[1] || 'js';
+
+                        // if the active section is currently undefined, then create the core section
+                        if (! activeSection) {
+                            activeSection = this.sections.core = {};
+                        }
+
+                        // if the file type is not in the active section then add it
+                        if (! activeSection[fileType]) {
+                            activeSection[fileType] = [];
+                        }
+
+                        // add the download to the file types in the active section
+                        activeSection[fileType].push(formatter(downloadMatch[2])(this));
+                    }
+                }
+            }
+        }
+    }
+};
+
+// define a `req` property to deal with parsing the string requirements into an array
+Object.defineProperty(Recipe.prototype, 'req', {
+    set: function(value) {
+        var recipe = this;
+        
+        // update the requirements
+        this.requirements = value.split(reCommaDelim);
+        
+        // iterate through the requirements and update to absolute paths if it they are local
+        this.requirements.forEach(function(req, index) {
+            if (reLocalFile.test(req)) {
+                recipe.requirements[index] = path.resolve(recipe.basePath, req);
+            }
+        });
+    }
+});
+
+// define a `req` property to deal with parsing the string requirements into an array
+Object.defineProperty(Recipe.prototype, 'dsc', {
+    set: function(value) {
+        this.desc = value;
+    }
+});
+
+// define a dep property as an alias to req
+Object.defineProperty(Recipe.prototype, 'dep', {
+    set: function(value) {
+        this.requirements = value.split(reCommaDelim);
+    }
+});
+
+function cookbook() {
+}
+
+// patch the recipe class into the cookbook
+cookbook.Recipe = Recipe;

File src/cookbook.js

View file
+// req: formatter
+
+//= core/recipe
+
+function cookbook() {
+}
+
+// patch the recipe class into the cookbook
+cookbook.Recipe = Recipe;

File src/core/recipe.js

View file
+var reDownload = /^([\w\.\-]*?)\s*<\=\s*(.*)$/,
+    reField = /^\s*\#\s*(\w+)\:\s*(.*)$/,
+    reSection = /\s*\[(\w+)\]/;
+
+function Recipe(opts) {
+    // if opts is a string, then map this to opts.text
+    if (typeof opts == 'string' || (opts instanceof String)) {
+        opts = {
+            text: opts
+        };
+    }
+
+    // ensure the opts have been initialised
+    opts = opts || {};
+
+    // define the sections
+    this.sections = opts.sections || {};
+    
+    // initialise requirements
+    this.requirements = [].concat(opts.requirements || []);
+    
+    // if we have text, then parse the text
+    if (opts.text) {
+        this.parse(opts.text, opts);
+    }
+}
+
+Recipe.prototype = {
+    parse: function(text, opts) {
+        var match, downloadMatch,
+            activeSection,
+            lines = (text || '').split(/\n/),
+            line,
+            inSections = false,
+            ii, count;
+            
+        // iterate through the lines until we hit the first section
+        for (ii = 0; ii < lines.length; ii++) {
+            // get a reference to the line
+            line = lines[ii];
+            
+            // check to see if we are in the sections
+            inSections = inSections || reSection.test(line);
+            
+            // if we are not in the sections, then check for field matches
+            if (! inSections) {
+                match = reField.exec(line);
+                if (match) {
+                    this[match[1]] = match[2];
+                }
+            }
+            else {
+                match = reSection.exec(line);
+                if (match) {
+                    // create the active section
+                    activeSection = this.sections[match[1]] = {};
+                }
+                else {
+                    // check for a download match
+                    downloadMatch = reDownload.exec(line);
+
+                    // if we have a download match, then add it to the active section
+                    if (downloadMatch) {
+                        var fileType = downloadMatch[1] || 'js';
+
+                        // if the active section is currently undefined, then create the core section
+                        if (! activeSection) {
+                            activeSection = this.sections.core = {};
+                        }
+
+                        // if the file type is not in the active section then add it
+                        if (! activeSection[fileType]) {
+                            activeSection[fileType] = [];
+                        }
+
+                        // add the download to the file types in the active section
+                        activeSection[fileType].push(formatter(downloadMatch[2])(this));
+                    }
+                }
+            }
+        }
+    }
+};
+
+// define a `req` property to deal with parsing the string requirements into an array
+Object.defineProperty(Recipe.prototype, 'req', {
+    set: function(value) {
+        var recipe = this;
+        
+        // update the requirements
+        this.requirements = value.split(reCommaDelim);
+        
+        // iterate through the requirements and update to absolute paths if it they are local
+        this.requirements.forEach(function(req, index) {
+            if (reLocalFile.test(req)) {
+                recipe.requirements[index] = path.resolve(recipe.basePath, req);
+            }
+        });
+    }
+});
+
+// define a `req` property to deal with parsing the string requirements into an array
+Object.defineProperty(Recipe.prototype, 'dsc', {
+    set: function(value) {
+        this.desc = value;
+    }
+});
+
+// define a dep property as an alias to req
+Object.defineProperty(Recipe.prototype, 'dep', {
+    set: function(value) {
+        this.requirements = value.split(reCommaDelim);
+    }
+});

File test/metadata.js

View file
+var cookbook = require('../pkg/cjs/cookbook'),
+    expect = require('expect.js'),
+    samples = {
+        simple: '# dsc: small JS library for DOM class manipulation\n' + 
+                '# url: https://github.com/DamonOehlman/classtweak\n' + 
+                '# bug: https://github.com/DamonOehlman/classtweak/issues\n\n' + 
+                '[core]\n' + 
+                'js <= github://DamonOehlman/classtweak/classtweak.js'
+    };
+
+describe('recipe metadata tests', function() {
+    it('should be able to read metdata from a simple recipe definition', function() {
+        var recipe = new cookbook.Recipe(samples.simple);
+        
+        expect(recipe.desc).to.equal('small JS library for DOM class manipulation');
+        expect(recipe.url).to.equal('https://github.com/DamonOehlman/classtweak');
+    });
+    
+    it('should be able to find sections within a simple recipe', function() {
+        var recipe = new cookbook.Recipe(samples.simple);
+        
+        expect(recipe.sections.core).to.be.ok();
+    });
+    
+    it('should have contents for the section specified in the simple recipe', function() {
+        var recipe = new cookbook.Recipe(samples.simple),
+            core = recipe.sections.core;
+        
+        expect(core.js).to.be.ok();
+        expect(core.js).to.contain('github://DamonOehlman/classtweak/classtweak.js');
+    });
+});