{"id":21065076,"url":"https://github.com/prolificinteractive/glenlivet","last_synced_at":"2025-03-14T01:43:08.928Z","repository":{"id":24356319,"uuid":"27754839","full_name":"prolificinteractive/glenlivet","owner":"prolificinteractive","description":"Create flexible, reusable processing pipelines powered by plugins.","archived":false,"fork":false,"pushed_at":"2020-06-17T06:41:25.000Z","size":151,"stargazers_count":1,"open_issues_count":1,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-01-20T20:53:42.587Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prolificinteractive.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-09T07:28:45.000Z","updated_at":"2015-03-04T21:56:58.000Z","dependencies_parsed_at":"2022-07-25T09:32:09.751Z","dependency_job_id":null,"html_url":"https://github.com/prolificinteractive/glenlivet","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prolificinteractive%2Fglenlivet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prolificinteractive%2Fglenlivet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prolificinteractive%2Fglenlivet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prolificinteractive%2Fglenlivet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prolificinteractive","download_url":"https://codeload.github.com/prolificinteractive/glenlivet/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243508986,"owners_count":20302106,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-19T17:53:24.052Z","updated_at":"2025-03-14T01:43:08.907Z","avatar_url":"https://github.com/prolificinteractive.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Glenlivet\n\nCreate flexible, reusable processing pipelines powered by plugins.\n\n## Installation\n\n`npm install glenlivet`\n\n## Usage\n\n### Bottles\n\nBottles encapsulate a hierarchical system of named hooks. When a bottle is \"served\", it traverses the hooks and runs any middleware attached to each.\n\n#### Plugins\n\nPlugins modify bottles by extending the hook hierarchy, attaching middleware, or adding methods. You add them to bottles with the .plugins() method:\n\n```javascript\nvar htmlToJsonPlugin = require('glenlivet-htmltojson');\nvar requestPlugin = require('glenlivet-request');\n\nvar getStructure = glenlivet.createBottle({\n  request: {\n    uri: function (data) {\n      return data.uri;\n    }\n  },\n  htmlToJson: ['\u003e *', {\n    'id': function ($el) {\n      return $el.attr('id') || null;\n    },\n    'tagName': function ($el) {\n      return $el[0].tagName;\n    },\n    'className': function ($el) {\n      return $el.attr('class') || null;\n    },\n    'children': function () {\n      return this.recurse();\n    }\n  }]\n}).plugins([\n  htmlToJsonPlugin,\n  requestPlugin\n]);\n```\n\nPlugins are loaded if their namespace exists as a key within the bottle configuration.\n\n### Barrels\n\nBarrels group together bottles, and provide functionality like configuration defaults, plugin autoloading, and global middleware for member bottles.\n\n```javascript\nvar glenlivet = require('glenlivet');\n\nvar prolific = glenlivet.createBarrel({\n  plugins: [\n    require('glenlivet-request'),\n    require('glenlivet-htmltojson')\n  ],\n  pluginDefaults: {\n    request: {\n      host: 'www.prolificinteractive.com'\n    }\n  }\n});\n\nvar getLinks = prolific.bottle('getLinks', {\n  request: {\n    pathname: function (data) {\n      return data.page || '/';\n    }\n  },\n  htmlToJson: ['a[href]', {\n    'href': function ($a) {\n      return $a.attr('href');\n    },\n    'text': function ($a) {\n      return $a.text();\n    }\n  }]\n}).method('json');\n```\n\nAny `plugins` will be loaded with each bottle, as long as they have the config key corresponding to the plugin's namespace. Any `pluginDefaults` will be mixed into the bottle configuration for the corresponding plugins.\n\n### Writing Plugins\n\n#### Middleware\n\nBottles come with a set of default middleware hooks:\n- `preempt`: Things that might make the rest of the middleware unnecessary, for example caching.\n- `setup`: Getting options prepared for `process` middleware. The main purpose is to allow other plugins to modify those options.\n- `process`: For data-generating procedures, for example making an HTTP request.\n- `filter`: Transforms data created by `process` middleware.\n- `persist`: Meant for things like caching to save the results at the end of the pipeline.\n\n#### glenlivet.createPlugin()\n\nPlugins are created using the glenlivet.createPlugin() method. This method takes two arguments, the first being the configuration namespace it will use, and a callback that is called every time the plugin is used by a bottle. The first argument of that function is the configuration for that plugin, while `this` corresponds to the bottle.\n\nYou'll mostly be using the bottle's middleware() method to read and write new values on the data object, like so:\n\n```javascript\nvar htmlToJson = require('htmlToJson');\nvar glenlivet = reuqire('glenlivet');\n\nmodule.exports = glenlivet.createPlugin('htmlToJson', function (filter) {\n  this.middleware('filter:htmlToJson', function (data, next, done) {\n    htmlToJson\n      .parse(data.html, filter)\n      .done(function (json) {\n        data.json = json;\n        next();\n      }, done);\n  });\n});\n```\n\nNotice the name of the middleware `filter:htmlToJson`. It will automatically create that hook within the existing `filter`. However, the hook will not be added if the immediate parent does not exist. For example, `filter:a:b:c` will not work until all ancestors are added to the hook hierarchy. This way we can create sub-plugins that depend on the existence of another plugin to work.\n\n## Examples\n\n[A scraped API with htmlToJson, request, and Express](examples/prolific.js)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprolificinteractive%2Fglenlivet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprolificinteractive%2Fglenlivet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprolificinteractive%2Fglenlivet/lists"}