Skip to content

Commit

Permalink
Big rewrite of the JS bridge stuff. Removed that silly prototype wrap…
Browse files Browse the repository at this point in the history
…ping, instead I'm totally encapsulating the bindings and native objects and exposing pure JS constructors. Wrote an argument marshalling/validating lib to do strict checking on args coming into JS bridge.
  • Loading branch information
Sam Day committed May 30, 2012
1 parent 375fdb3 commit f5b0efd
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 64 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,3 +1,4 @@
build/
node_modules/
lib/gitteh.js
lib/args.js
65 changes: 65 additions & 0 deletions coffee/args.coffee
@@ -0,0 +1,65 @@
module.exports = fn = (params) ->
paramList = for own name, param of params
param.name = name
param
ret = []

numRequired = 0
leftRequired = for param in paramList
break if param.hasOwnProperty("default")
numRequired++
param
paramList.splice 0, leftRequired.length
rightRequired = for param in paramList.slice(0).reverse()
break if param.hasOwnProperty("default")
numRequired++
param
rightRequired = rightRequired.reverse()
paramList.splice -rightRequired.length

args = Array.prototype.slice.call fn.caller.arguments
throw new Error("Not enough arguments.") if args.length < numRequired

left = args.splice 0, leftRequired.length
right = args.splice -rightRequired.length

argn = 0
for arg, i in left
argn++
param = leftRequired[i]
if not fn.validators[param.type] arg
throw new TypeError "#{param.name} (#{argn}) is not a valid #{param.type}"
ret.push arg

for param, i in paramList
argn++
if args.length > i
arg = args[i]
if not fn.validators[param.type] arg
throw new TypeError "#{param.name} (#{argn}) is not a valid #{param.type}"
ret.push arg
else ret.push param.default
for arg, i in right
argn++
param = rightRequired[i]
if not fn.validators[param.type] arg
throw new TypeError "#{param.name} (#{argn}) is not a valid #{param.type}"
ret.push arg
return ret
fn.validators =
string: (val) ->
return typeof val is "string"
function: (val) -> return typeof val is "function"
bool: (val) ->
return typeof val is "boolean"

###
myfn = ->
[path, bare, cb] = fn
path: type: "string"
bare: type: "bool", default: false
cb: type: "function"
console.log path, bare, cb
myfn "awesome", "zzz"
###
131 changes: 74 additions & 57 deletions coffee/gitteh.coffee
@@ -1,5 +1,8 @@
module.exports = Gitteh = require "../build/Debug/gitteh"
{Repository} = Gitteh
bindings = require "../build/Debug/gitteh"
{minOidLength, types, NativeRepository} = bindings
args = require "./args"

module.exports = Gitteh = {}

immutable = (obj, src) ->
return o = {
Expand Down Expand Up @@ -73,66 +76,80 @@ Gitteh.Tag = Tag = (@repository, obj) ->
@target = (cb) =>
@repository.object @targetId, @type, cb
return @

oidRegex = /^[a-zA-Z0-9]{0,40}$/
args.validators.oid = (val) ->
return false if typeof val isnt "string"
return false if not oidRegex.test val
return false if val.length < minOidLength
return true

objectTypes = ["any", "blob", "commit", "tag", "tree"]
args.validators.objectType = (val) ->
return objectTypes.indexOf val > -1

checkOid = (str, allowLookup = true) ->
throw new TypeError "OID should be a string" if typeof str isnt "string"
throw new TypeError "Invalid OID" if not oidRegex.test str
throw new Error "OID is too short" if str.length < Gitteh.minOidLength
throw new TypeError "Invalid OID" if not allowLookup and str.length isnt 40

wrap = (clazz, fn, prototype, newFn) ->
override = if prototype then clazz.prototype else clazz
orig = override[fn]

override[fn] = ->
shadowed = if prototype then orig.bind @ else orig
newFn.apply @, [shadowed].concat Array.prototype.slice.call arguments

wrap Gitteh, "openRepository", false, (shadowed, path, cb) ->
shadowed path, cb

wrap Gitteh, "initRepository", false, (shadowed, path, bare, cb) ->
if typeof bare is "function"
cb = bare
bare = false

shadowed path, bare, cb

wrap Repository, "exists", true, (shadowed, oid, cb) ->
checkOid oid, false
shadowed oid, cb

wrap Repository, "object", true, (shadowed, args..., cb) ->
[oid, type] = args
type = "any" if not type
checkOid oid
shadowed oid, type, (err, object) =>
return cb err if err?
clazz = switch object._type
when Gitteh.types.commit then Commit
when Gitteh.types.tree then Tree
when Gitteh.types.blob then Blob
when Gitteh.types.tag then Tag
else undefined
return cb new TypeError("Unexpected object type") if clazz is undefined
return cb null, new clazz @, object

Repository.prototype.commit = (oid, cb) ->
@object oid, "commit", cb

Repository.prototype.tree = (oid, cb) ->
@object oid, "tree", cb

Repository.prototype.blob = (oid, cb) ->
@object oid, "blob", cb

Repository.prototype.tag = (oid, cb) ->
@object oid, "tag", cb

wrap Repository, "reference", true, (shadowed, name, resolve, cb) ->
if typeof resolve is "function"
cb = resolve
resolve = false
shadowed name, resolve, cb
wrapCallback = (orig, cb) ->
return (err) ->
return orig err if err?
cb.apply null, Array.prototype.slice.call arguments, 1

module.exports.Repository = Repository = (nativeRepo) ->
if nativeRepo not instanceof NativeRepository
throw new Error "Don't construct me, see gitteh.(open|init)Repository"

immutable(@, nativeRepo)
.set("bare")
.set("path")
.set("workDir", "workingDirectory")
@exists = =>
[oid, cb] = args
oid: type: "oid"
cb: type: "function"
nativeRepo.exists oid, cb
@object = =>
[oid, type, cb] = args
oid: type: "oid"
type: type: "objectType", default: "any"
cb: type: "function"
nativeRepo.object oid, type, wrapCallback cb, (object) =>
clazz = switch object._type
when types.commit then Commit
when types.tree then Tree
when types.blob then Blob
when types.tag then Tag
else undefined
return cb new TypeError("Unexpected object type") if clazz is undefined
return cb null, new clazz @, object
@blob = (oid, cb) => @object oid, "blob", cb
@commit = (oid, cb) => @object oid, "commit", cb
@tag = (oid, cb) => @object oid, "tag", cb
@tree = (oid, cb) => @object oid, "tree", cb
@reference = =>
[name, resolve, cb] = args
name: type: "string"
resolve: type: "bool"
cb: type: "function"
nativeRepo.reference name, resolve, cb
@ref = @reference
return @

Repository.prototype.ref = Repository.prototype.reference
Gitteh.openRepository = ->
[path, cb] = args
path: type: "string"
cb: type: "function"
bindings.openRepository path, wrapCallback cb, (repo) ->
cb null, new Repository repo

Gitteh.initRepository = () ->
[path, bare, cb] = args
path: type: "string"
bare: type: "bool", default: false
cb: type: "function"
bindings.initRepository path, bare, wrapCallback cb, (repo) ->
cb null, new Repository repo
4 changes: 1 addition & 3 deletions src/repository.cc
Expand Up @@ -33,7 +33,6 @@ static Persistent<String> repo_class_symbol;
static Persistent<String> path_symbol;
static Persistent<String> bare_symbol;

static Persistent<String> git_dir_symbol;
static Persistent<String> object_dir_symbol;
static Persistent<String> index_file_symbol;
static Persistent<String> work_dir_symbol;
Expand Down Expand Up @@ -134,10 +133,9 @@ Repository::~Repository() {
void Repository::Init(Handle<Object> target) {
HandleScope scope;

repo_class_symbol = NODE_PSYMBOL("Repository");
repo_class_symbol = NODE_PSYMBOL("NativeRepository");
path_symbol = NODE_PSYMBOL("path");
bare_symbol = NODE_PSYMBOL("bare");
git_dir_symbol = NODE_PSYMBOL("gitDirectory");
object_dir_symbol = NODE_PSYMBOL("objectDirectory");
index_file_symbol = NODE_PSYMBOL("indexFile");
work_dir_symbol = NODE_PSYMBOL("workDir");
Expand Down
8 changes: 4 additions & 4 deletions test/test.repository.coffee
Expand Up @@ -27,14 +27,14 @@ describe "Repository", ->
repo.path.should.equal fixtures.projectRepo.gitPath
delete repo.path
repo.path.should.equal fixtures.projectRepo.gitPath
describe "#workDir", ->
describe "#workingDirectory", ->
it "should point to #{fixtures.projectRepo.path}", ->
repo.workDir.should.equal fixtures.projectRepo.path
repo.workingDirectory.should.equal fixtures.projectRepo.path
it "should be immutable", ->
repo.path = "foo"
repo.workDir.should.equal fixtures.projectRepo.path
repo.workingDirectory.should.equal fixtures.projectRepo.path
delete repo.path
repo.workDir.should.equal fixtures.projectRepo.path
repo.workingDirectory.should.equal fixtures.projectRepo.path
describe "#exists()", ->
it "should return true for first commit in repo :)", (done) ->
repo.exists "1f4425ce2a14f21b96b9c8dde5bcfd3733467b14", (err, exists) ->
Expand Down

0 comments on commit f5b0efd

Please sign in to comment.