Navigation Menu

Skip to content

Commit

Permalink
Adds v0.2 Implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
goatslacker committed May 4, 2012
1 parent fb924e3 commit d2b0a0d
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 143 deletions.
300 changes: 158 additions & 142 deletions gister.js
@@ -1,179 +1,205 @@
/*jshint asi: true */
/*jshint asi: true, expr: true, curly: false */
var request = require('request')
var EventEmitter = require('events').EventEmitter

var url = 'https://api.github.com'

// Rate limiting
var time = null
var rate = 1

// ## Gist
//
// Constructs a new Gist object.
// Instance of EventEmitter.
//
// **o** is an Object which contains
//
// * __username__ GitHub username
// * __token__ Your secret API token, can be found in [Account Settings](https://github.com/account/admin)
// * __gist_id__ (optional) The Gist ID
function Gist(o) {
EventEmitter.call(this)
function api(uri, method) {
var opts = {
uri: 'https://api.github.com/' + uri,
method: method,
json: {}
}

o = o || {}
return opts
}

this.username = o.username
this.password = o.password
this.token = o.token
this.gist_id = o.gist_id
api.all = function (id) {
var opts
if (id) {
opts = api('users', 'GET')
opts.uri += '/' + id + '/gists'
} else {
opts = api('gists', 'GET')
}
return { req: opts, statusCode: 200 }
}

Gist.prototype = Object.create(EventEmitter.prototype)
api.get = function (id) {
var opts = api('gists', 'GET')
opts.uri += '/' + id
return { req: opts, statusCode: 200 }
}

function response(self, statuses) {
return function (err, response, body) {
if (err) {
return self.emit('err', err)
}
api.create = function (data) {
var opts = api('gists', 'POST')
opts.json = add_data(data)
return { req: opts, statusCode: 201 }
}

var limit = response.headers['x-ratelimit-remaining']
if (limit) {
rate = limit
}
api.patch = function (data, id) {
var opts = api('gists', 'PATCH')
opts.uri += '/' + id
opts.json = add_data(data)
return { req: opts, statusCode: 200 }
}

var cb = statuses[response.statusCode]
if (cb) {
return cb(body, response)
}
api.auth = function (appName) {
var opts = api('authorizations', 'POST')
opts.json = { scopes: ['gist'], note: appName }
return { req: opts, statusCode: 201 }
}

switch (response.statusCode) {
case 201:
self.emit('created', body, response)
break
case 404:
self.emit('error:notfound', body, response)
break
default:
self.emit('err', body, response)
}
function response(statusCode, cb) {
var gist = this

return function (err, res, body) {
// return if there's an error
if (err) return gist.emit('error', err)

// update our rate limit counter
var limit = res.headers['x-ratelimit-remaining']
limit && (rate = limit)

// if we have a callback attached, call it
if (res.statusCode === statusCode) return cb(body, res)

// otherwise it's an error
return gist.emit('error', new Error(body.message), body, res)
}
}

function xhr(opts, data, cb, name) {
// if we're over the limit
if (rate <= 0) {
// if it's been over an hour, reset the rate
// and reset the timer
if (Date.now() > time + 360000) {
time = null
rate = 1
} else {
return this.emit('error:ratelimit', 'Over rate limit: http://developer.github.com/v3/#rate-limiting')
}
function check_gist_id(gist_id) {
gist_id = gist_id || this.gist_id
if (!gist_id) throw new ReferenceError('No gist id provided')

return gist_id
}

function check_data(data) {
if (!data || typeof data !== 'object')
throw new TypeError('Expected data to be an Object')
}

function add_data(data) {
if (data.files) {
return data
}

var json = {
description: '',
files: {},
public: true
}

if (data) {
Object.keys(data).forEach(function (name) {
json.files[name] = { content: data[name] }
})

return json
}

function authenticate(opts) {
// authentication
if (Object.keys(opts.json).length > 0) {
if (this.token) {
opts.headers = { 'Authorization': 'token ' + this.token }
} else if (this.username && this.password) {
opts.headers = { 'Authorization': 'Basic ' + new Buffer(this.username + ':' + this.password).toString('base64') }
} else {
return this.emit('error:credentials', 'No OAuth token or username and password provided')
}

opts.json = {
description: '',
files: {},
public: true
throw new ReferenceError('No OAuth token or username and password provided')
}

opts.json.files[name] = { content: data }

// Object.keys(data).forEach(function (name) {
// opts.json.files[name] = { content: data[name] }
// })
}

return this.request(opts, cb)
return opts
}

// Uses request to talk to GitHub API.
// Provided in the prototype so request can be mocked for tests.
Gist.prototype.request = function (opts, cb) {
function xhr(def, cb) {
// set the time if it isn't set
time = time || Date.now()
return request(opts, cb)
}

Gist.prototype.auth = function (appName) {
if (!appName) {
throw new ReferenceError('Application Name is missing')
}

this.token = null

if (!this.username || !this.password) {
return this.emit('error:credentials', 'No OAuth token or username and password provided')
}
var opts = def.req
var statusCode = def.statusCode

var opts = { method: 'POST', uri: 'https://api.github.com/authorizations' }
opts.headers = { 'Authorization': 'Basic ' + new Buffer(this.username + ':' + this.password).toString('base64') }
opts.json = { scopes: ['gist'], note: appName }
opts = authenticate.call(this, opts)

var res = response(this, {
201: function (body) {
this.token = body.token
this.emit('token', this.token)
}.bind(this)
})

return xhr.bind(this)(opts, null, res)
return this.request(opts, response.call(this, statusCode, cb))
}

// Retrieves a gist from gist.github.com

// ## Gist
//
// Uses `gist_id` to determine which gist it'll retrieve
// compatible with GitHub API v3
// Constructs a new Gist object.
// Instance of EventEmitter.
//
// If no `gist_id` is provided, event **error:gist_id** is emitted.
// **o** is an Object which contains
//
// On success, event **get** is emitted with `body` passed.
// `body` is the response from GitHub.
Gist.prototype.get = function (name) {
var gist = this
// * __username__ GitHub username
// * __token__ Your secret API token, can be found in [Account Settings](https://github.com/account/admin)
// * __gist_id__ (optional) The Gist ID
function Gist(o) {
EventEmitter.call(this)

if (!this.gist_id) {
return this.emit('error:gist_id', 'No gist id provided')
}
o = o || {}

var opts = {
uri: 'https://api.github.com/gists/' + this.gist_id
}
this.username = o.username
this.password = o.password
this.token = o.token
this.gist_id = o.gist_id
}

Gist.prototype = Object.create(EventEmitter.prototype)

var res = response(this, {
200: function (body) {
if (name) {
var data = JSON.parse(body)
return gist.emit('get', data.files[name])
}
// Uses request to talk to GitHub API.
Gist.prototype.request = function (opts, cb) {
return request(opts, cb)
}

return gist.emit('get', body)
Gist.prototype.get = function (gist_id, name) {
gist_id = check_gist_id.call(this, gist_id)

return xhr.call(this, api.get(gist_id), function (body) {
if (name) {
var data = JSON.parse(body)
return this.emit('gist', data.files[name])
}
})

return xhr.bind(this)(opts, null, res, name)
return this.emit('gist', body)
}.bind(this))
}

Gist.prototype.getAll = function (user_id) {
return xhr.call(this, api.all(user_id), function (body) {
this.emit('gists', body)
}.bind(this))
}

Gist.prototype.auth = function (appName) {
if (!appName) throw new ReferenceError('Application Name is missing')

this.token = null

return xhr.call(this, api.auth(appName), function (body) {
this.token = body.token
this.emit('token', this.token)
}.bind(this))
}

// Convenience method which will create a new gist
// if `gist_id` is not provided. If it is provided,
// the gist will be updated.
//
// Parameter __data__ is the data to create/edit to gist
Gist.prototype.sync = function (data, name) {
if (!this.gist_id) {
return this.create(data, name)
Gist.prototype.sync = function (data, gist_id) {
if (!gist_id && !this.gist_id) {
return this.create(data)
}

return this.edit(data, name)
return this.edit(data, gist_id)
}

// Edits a gist
Expand All @@ -184,20 +210,13 @@ Gist.prototype.sync = function (data, name) {
//
// On success, event **updated** is emitted with
// `body`, the response from GitHub.
Gist.prototype.edit = function (data, name) {
if (!this.gist_id) {
return this.emit('error:gist_id', 'No gist id provided')
}
Gist.prototype.edit = function (data, gist_id) {
gist_id = check_gist_id.call(this, gist_id)
check_data(data)

var opts = {
uri: 'https://api.github.com/gists/' + this.gist_id,
method: 'PATCH'
}
var req = xhr.bind(this)

req(opts, data, response(this, { 200: function (body) {
this.emit('updated', body)
}.bind(this) }), name)
return xhr.call(this, api.patch(data, gist_id), function (body) {
this.emit('edited', body)
}.bind(this))
}

// Creates a new gist
Expand All @@ -206,14 +225,10 @@ Gist.prototype.edit = function (data, name) {
//
// On success, event **created** is emitted with
// `body` as well as the new `gist_id`.
Gist.prototype.create = function (data, name) {
var opts = {
uri: 'https://api.github.com/gists',
method: 'POST'
}
var req = xhr.bind(this)
Gist.prototype.create = function (data) {
check_data(data)

req(opts, data, response(this, { 201: function (body, res) {
return xhr.call(this, api.create(data), function (body, res) {
var gist = /(\d+)/
var location = res.headers.location
var gist_id = null
Expand All @@ -223,7 +238,8 @@ Gist.prototype.create = function (data, name) {
}

this.emit('created', body, gist_id)
}.bind(this) }), name)
}.bind(this))
}


module.exports = Gist
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "gister",
"version": "0.1.0",
"version": "0.2.0",
"author": {
"name": "Josh Perez",
"email": "josh@goatslacker.com",
Expand Down

0 comments on commit d2b0a0d

Please sign in to comment.