├── .gitignore ├── Makefile ├── test ├── fixtures │ ├── src │ │ └── index.html │ └── expected │ │ └── index.html └── index.js ├── History.md ├── package.json ├── Readme.md └── lib └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test/fixtures/build/ 3 | .gists 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | test: 2 | ./node_modules/.bin/mocha --reporter spec 3 | 4 | .PHONY: test -------------------------------------------------------------------------------- /test/fixtures/src/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | title: test 3 | gist: expalmer/4bc6203dc2a3f7134e78 sukima/510438:load_config.rb 4 | --- 5 | 6 | Hello World 7 | 8 | gist:expalmer/4bc6203dc2a3f7134e78 9 | 10 | gist:sukima/510438:load_config.rb 11 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 0.3.0 - October 14, 2014 2 | ------------------------ 3 | - Add support to cache gists. 4 | 5 | 0.2.0 - October 11, 2014 6 | ------------------------ 7 | - Designate a specific file in a gist. 8 | 9 | 0.1.0 - August 20, 2014 10 | ------------------------ 11 | - Initial release -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 2 | var equal = require('assert-dir-equal'); 3 | var Metalsmith = require('metalsmith'); 4 | var gist = require('..'); 5 | 6 | describe('metalsmith-gist', function(){ 7 | 8 | it('should get a gist from Github', function( done ){ 9 | 10 | Metalsmith('test/fixtures') 11 | .use(gist()) 12 | .build(function( err, files ){ 13 | if ( err ) { 14 | return done(err); 15 | } 16 | equal('test/fixtures/expected/index.html', 'test/fixtures/build/index.html'); 17 | done(); 18 | }); 19 | 20 | }); 21 | 22 | }); 23 | 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "metalsmith-gist", 3 | "version": "0.3.0", 4 | "description": "A Metalsmith plugin to embed gist", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "make test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/expalmer/metalsmith-gist.git" 12 | }, 13 | "author": "Palmer Oliveira ", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/expalmer/metalsmith-gist/issues" 17 | }, 18 | "homepage": "https://github.com/expalmer/metalsmith-gist", 19 | "devDependencies": { 20 | "assert-dir-equal": "^1.0.1", 21 | "metalsmith": "^0.9.0", 22 | "mocha": "^1.21.4" 23 | }, 24 | "dependencies": { 25 | "q": "^1.0.1", 26 | "request": "^2.40.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # metalsmith-gist 2 | 3 | A [Metalsmith](https://github.com/segmentio/metalsmith) plugin that lets you get gist from [Github Gist](https://gist.github.com) 4 | 5 | ## Installation 6 | 7 | $ npm install metalsmith-gist 8 | 9 | ## Usage 10 | 11 | step 1 12 | 13 | ```js 14 | var gist = require('metalsmith-gist'); 15 | 16 | metalsmith.use(gist({ 17 | debug: true, // optional, it's only to show simple warnings 18 | caching: true, // optional, to caching your gists. default is `true` 19 | cacheDir: '.gists' // optional, cache directory. default is `.gists` 20 | })); 21 | ``` 22 | 23 | step 2 24 | 25 | ``` 26 | --- 27 | title: test 28 | gist: expalmer/4bc6203dc2a3f7134e78 29 | --- 30 | 31 | Hello World 32 | 33 | gist:expalmer/4bc6203dc2a3f7134e78 34 | 35 | ``` 36 | 37 | ## You can get single files too. 38 | 39 | ``` 40 | --- 41 | title: test 42 | gist: expalmer/4bc6203dc2a3f7134e78 sukima/510438:load_config.rb 43 | --- 44 | 45 | Hello World 46 | 47 | gist:expalmer/4bc6203dc2a3f7134e78 // here we will get all files that exist in this gist 48 | 49 | gist:sukima/510438:load_config.rb // here we will get only a single file of this gist 50 | 51 | ``` 52 | 53 | You can put as many files as you want, you only should put on the head all files together separated by spaces, like this: 54 | 55 | ``` 56 | --- 57 | title: test 58 | gist: expalmer/hash1 expalmer/hash2 expalmer/hash3:file1.js expalmer/hash3:file2.js 59 | --- 60 | ... 61 | ``` 62 | 63 | ## CLI Usage 64 | 65 | ```json 66 | { 67 | "plugins": { 68 | "metalsmith-gist": { 69 | "debug": "false" 70 | } 71 | } 72 | } 73 | ``` 74 | 75 | ## Caching 76 | 77 | When the plugin first runs it will download all the gists referenced and store them in the `.gists` folder of the project. This will cache the content so subsequent runs will be significantly faster because they will not make network requests every time. 78 | 79 | This also means that if you make changes to your gists you must remove the cache to receive the update. A simple way to clear the case is to remove the cache directory: (`rm .gists/*`). 80 | 81 | There are a few options you can use to customize the caching. 82 | 83 | * `caching` - true/false - turn on or off caching support. Default: `true` 84 | * `cacheDir` - string - the cache directory to save to. Default: `.gists` 85 | 86 | ## License 87 | 88 | MIT 89 | -------------------------------------------------------------------------------- /test/fixtures/expected/index.html: -------------------------------------------------------------------------------- 1 | 2 | Hello World 3 | 4 |
1 2 3 4 5 6 7 8
var Animal = function ( name ) {
this.name = name;
}
Animal.prototype.getName = function () {
return this.name;
}
var tiger = new Animal('Tiger');
tiger.getName();
view raw gistfile1.js hosted with ❤ by GitHub
5 | 6 |
1 2 3 4 5 6 7
# Uses local config via YAML.
# http://gist.github.com/510438
 
APP_CONFIG = YAML.load_file("#{RAILS_ROOT}/config/config.yml")
APP_CONFIG.merge!(APP_CONFIG['environment'][RAILS_ENV])
APP_CONFIG.delete('environment')
APP_CONFIG.symbolize_keys
view raw load_config.rb hosted with ❤ by GitHub
7 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var request = require('request'); 5 | var Q = require('q'); 6 | var crypto = require('crypto'); 7 | var fs = require('fs'); 8 | var path = require('path'); 9 | 10 | /** 11 | * Gist prototype. 12 | */ 13 | var app = Gist.prototype; 14 | 15 | /** 16 | * Expose `plugin`. 17 | */ 18 | exports = module.exports = plugin; 19 | 20 | /** 21 | * Metalsmith plugin to get gists on Github 22 | * 23 | * @param {Object} options (optional) 24 | * @property {Array} keys 25 | * @return {Function} 26 | */ 27 | 28 | function plugin( options ) { 29 | 30 | var options = options || {}; 31 | 32 | return function ( files, metalsmith, done ) { 33 | 34 | var app = new Gist( options, files ); 35 | app.init( function() { 36 | app.showWarnings(); 37 | done(); 38 | }); 39 | 40 | }; 41 | 42 | }; 43 | 44 | 45 | /** 46 | * Initialize a new `Gist`. 47 | */ 48 | 49 | function Gist( options, files ) { 50 | 51 | this.files = files; 52 | this.posts = []; 53 | this.warnings = []; 54 | this.options = { 55 | debug: options.debug || false, 56 | caching: options.caching !== false, // null, undefined default to true 57 | cacheDir: options.cacheDir || ".gists" 58 | }; 59 | 60 | } 61 | 62 | app.init = function( callback ) { 63 | 64 | var that = this, 65 | total = 0; 66 | 67 | Object.keys( this.files ).forEach( function( file ) { 68 | if ( that.files[file].gist ) { 69 | that.posts.push( file ); 70 | } 71 | }); 72 | 73 | if ( !this.posts.length ) { 74 | this.warnings.push( "you dont have posts with gist" ); 75 | return callback(); 76 | } 77 | 78 | total = this.posts.length; 79 | 80 | var isDone = function() { 81 | total -= 1; 82 | if ( total === 0 ) { 83 | return callback(); 84 | } 85 | }; 86 | 87 | this.posts.forEach( function( file ) { 88 | 89 | that.eachPost( file, function() { 90 | isDone(); 91 | }); 92 | 93 | }); 94 | 95 | }; 96 | 97 | 98 | app.eachPost = function( file, callback ) { 99 | 100 | if ( typeof this.files[file].gist === 'string' ) { 101 | this.files[file].gist = this.helpers.toArray( this.files[file].gist ); 102 | } 103 | 104 | var that = this, 105 | total = this.files[file].gist.length; 106 | 107 | var isDone = function () { 108 | total -= 1; 109 | if ( total === 0 ) { 110 | return callback(); 111 | } 112 | } 113 | 114 | var onSuccess = function ( args ) { 115 | that.replacePost( file, args.gist, args.res, function( res ) { 116 | isDone(); 117 | }); 118 | }; 119 | 120 | var onFail = function ( reason ) { 121 | if ( reason.statusCode ) { 122 | that.warnings.push( reason.gist.name + " not found on Github.com (" + reason.statusCode + ")" ); 123 | } else { 124 | that.warnings.push( "Error: " + reason.gist.name + ": " + ( reason.message || reason ) ); 125 | } 126 | isDone(); 127 | }; 128 | 129 | this.files[file].gist.forEach( function( gist ) { 130 | that.getGist( gist ).then( onSuccess , onFail ); 131 | }); 132 | 133 | }; 134 | 135 | app.getGist = function( gist ) { 136 | 137 | var cachedGist = this.options.caching && this.getCachedData( gist ); 138 | if (cachedGist) { 139 | return Q({ gist: gist, res: cachedGist }); 140 | } 141 | 142 | var that = this; 143 | var deferred = Q.defer(); 144 | var url = this.helpers.githubUrl( gist ); 145 | 146 | request( url, deferred.makeNodeResolver() ); 147 | 148 | return deferred.promise 149 | .spread( function( response, body ) { 150 | if ( response.statusCode !== 200 ) { 151 | throw { statusCode: response.statusCode }; 152 | } 153 | that.writeCachedData( gist, body ); 154 | var res = JSON.parse(body); 155 | return { gist: gist, res: res }; 156 | }) 157 | .fail( function( reason ) { 158 | reason.gist = gist; // make sure we have an associated gist to the error 159 | throw reason; // re-throw to keep the promise rejected 160 | }); 161 | }; 162 | 163 | 164 | app.replacePost = function ( file, gist, ghGist, callback ) { 165 | 166 | var value = this.helpers.prepareGistValue( ghGist ); 167 | contents = this.helpers.replaceGist( this.files[file].contents, gist, value ); 168 | 169 | this.files[file].contents = new Buffer( contents ); 170 | callback('replaced ' + gist ); 171 | 172 | }; 173 | 174 | app.showWarnings = function() { 175 | 176 | if ( this.warnings.length && this.options.debug ) { 177 | this.warnings.forEach( function( w ) { 178 | console.log( "metalsmith-gist: " + w ); 179 | }); 180 | } else if ( this.warnings.length ) { 181 | console.log( "Warnings suppressed. Set debug:true to see warnings." ); 182 | } 183 | 184 | }; 185 | 186 | app.getCachedData = function( gist ) { 187 | var cacheName = path.resolve( path.join( './', this.cacheName( gist ) ) ); 188 | 189 | if ( fs.existsSync( cacheName ) ) { 190 | return require( cacheName ); 191 | } else { 192 | return null; 193 | } 194 | 195 | }; 196 | 197 | app.writeCachedData = function( gist, body ) { 198 | 199 | var cacheName = this.cacheName( gist ); 200 | this.helpers.makeCacheDir( this.options.cacheDir ); 201 | fs.writeFileSync( cacheName, body ); 202 | 203 | }; 204 | 205 | app.cacheName = function( gist ) { 206 | 207 | var md5sum = crypto.createHash( 'md5' ); 208 | md5sum.update( gist.fullName ); 209 | var name = md5sum.digest( 'hex' ); 210 | return path.join( this.options.cacheDir, name + '.json' ); 211 | 212 | }; 213 | 214 | 215 | /** 216 | * Helpers 217 | */ 218 | 219 | app.helpers = { 220 | 221 | replaceGist: function( contents, gist, value ) { 222 | return contents.toString().replace("gist:" + gist.fullName, value); 223 | }, 224 | 225 | toArray: function( gist ) { 226 | return gist.trim() 227 | .replace(/([\s,])/g,'|') 228 | .split('|') 229 | .map(function( a ) { 230 | var parts = a.split(':'); 231 | return {fullName: a, name: parts[0], file: parts[1]}; 232 | }); 233 | }, 234 | 235 | githubUrl: function( gist ) { 236 | return 'https://gist.github.com/' + gist.name + '.json' + (gist.file ? '?file=' + gist.file : ''); 237 | }, 238 | 239 | prepareGistValue: function( ghGist ) { 240 | var s = '', 241 | d = ghGist.div.replace(/(\r\n|\n|\r)/gm,""); 242 | return s + d; 243 | }, 244 | 245 | makeCacheDir: function( directory ) { 246 | if ( !fs.existsSync( directory ) ) { 247 | fs.mkdir( directory ); 248 | } 249 | } 250 | 251 | }; 252 | --------------------------------------------------------------------------------