paths.coffee | |
---|---|
Requires | fs = require('fs')
request = null
balUtilPaths = null
balUtilFlow = require("#{__dirname}/flow.coffee") |
Create a counter of all the open files we have As the filesystem will throw a fatal error if we have too many open files | global.numberOfOpenFiles ?= 0
global.maxNumberOfOpenFiles ?= 500 |
===================================== Paths | balUtilPaths = |
===================================== Open and Close Files | |
Allows us to open files safely by tracking the amount of open files we have | |
Open a file Pass your callback to fire when it is safe to open the file | openFile: (next) ->
if global.numberOfOpenFiles >= global.maxNumberOfOpenFiles
setTimeout(
->
balUtilPaths.openFile(next)
,50
)
else
++global.numberOfOpenFiles
next?()
@ |
Close a file Call this once you are done with that file | closeFile: (next) ->
--global.numberOfOpenFiles
next?()
@ |
===================================== Our Extensions | |
Copy a file Or rather overwrite a file, regardless of whether or not it was existing before next(err) | cp: (src,dst,next) ->
balUtilPaths.openFile -> fs.readFile src, 'binary', (err,data) ->
balUtilPaths.closeFile() |
Error | if err
console.log 'balUtilPaths.cp: cp failed on:',src
return next?(err) |
Success | balUtilPaths.openFile -> fs.writeFile dst, data, 'binary', (err) ->
balUtilPaths.closeFile() |
Forward | if err
console.log 'balUtilPaths.cp: writeFile failed on:',dst
return next?(err) |
Chain | @
|
Get the parent path | getParentPathSync: (p) ->
parentPath = p.replace /[\/\\][^\/\\]+$/, ''
return parentPath
|
Ensure path exists next(err) | ensurePath: (p,next) ->
path = require('path')
p = p.replace /[\/\\]$/, ''
path.exists p, (exists) -> |
Error | return next?() if exists |
Success | parentPath = balUtilPaths.getParentPathSync p
balUtilPaths.ensurePath parentPath, (err) -> |
Error | if err
console.log 'balUtilPaths.ensurePath: failed to ensure the path:',parentPath
return next?(err) |
Success | balUtilPaths.openFile -> fs.mkdir p, '700', (err) ->
path.exists p, (exists) -> |
Close | balUtilPaths.closeFile() |
Error | if not exists
console.log 'balUtilPaths.ensurePath: failed to create the directory:',p
return next?(new Error 'Failed to create the directory '+p) |
Success | next?() |
Chain | @
|
Prefix path | prefixPathSync: (path,parentPath) ->
path = path.replace /[\/\\]$/, ''
if /^([a-zA-Z]\:|\/)/.test(path) is false
path = parentPath + '/' + path
return path
|
Is it a directory? next(err,isDirectory) | isDirectory: (fileFullPath,next) -> |
Stat | balUtilPaths.openFile -> fs.stat fileFullPath, (err,fileStat) ->
balUtilPaths.closeFile() |
Error | if err
console.log 'balUtilPaths.isDirectory: stat failed on:',fileFullPath
return next?(err) |
Success | return next?(null, fileStat.isDirectory())
|
Chain | @
|
Generate a slug for a file | generateSlugSync: (fileFullPath) -> |
Slugify | result = fileFullPath.replace(/[^a-zA-Z0-9]/g,'-').replace(/^-/,'').replace(/-+/,'-') |
Return | return result |
Recursively scan a directory, file, or series of files | scan: (files,fileAction,dirAction,next) -> |
Actions | actions =
directory: =>
@scandir( |
Directory | files |
File Action | fileAction |
Dir Action | dirAction |
Complete Action | next
)
files: => |
Queue | tasks = new balUtilFlow.Group next
tasks.total += files.length
|
Array | for file in files
@scan fileFullPath, fileAction, dirAction, tasks.completer()
|
Done | return true
|
String | if typeof files.charAt isnt 'undefined'
balUtilPaths.isDirectory (err,isDirectory) -> |
Error | if err
return next?(err)
|
Directory | else if isDirectory
actions.directory()
|
File | else
files = [file]
actions.files()
|
Array | else if files instanceof Array
actions.files()
|
Unsupported | else
next?(new Error 'balUtilPaths.scandir: unsupported files type:', typeof files, files) |
Chain | @
|
Scan a directory into a tree next(err,tree) | scantree: (dirPath,next) -> |
Handle | balUtilPaths.scandir(
path: dirPath
readFiles: true
ignoreHiddenFiles: true
next: (err,list,tree) ->
next?(err,tree)
) |
Chain | @ |
Recursively scan a directory fileAction(fileFullPath,fileRelativePath,next(err,skip)) or false dirAction(fileFullPath,fileRelativePath,next(err,skip)) or false next(err,list,tree) | scandir: (args...) -> |
Prepare | if args.length is 1
{path,parentPath,fileAction,dirAction,next,relativePath,readFiles,ignoreHiddenFiles,ignorePatterns} = args[0]
else if args.length >= 4
[parentPath,fileAction,dirAction,next] = args
else
err = new Error('balUtilPaths.scandir: unknown arguments')
if next
return next?(err)
else
throw err
|
Prepare defaults | readFiles or= false
ignoreHiddenFiles ?= true
ignorePatterns ?= /(node_modules)$/ |
Check needed | if !parentPath and path
parentPath = path
if !parentPath
err = new Error('balUtilPaths.scandir: parentPath is needed')
if next
return next?(err)
else
throw err |
Return | list = {}
tree = {} |
Group | tasks = new balUtilFlow.Group (err) ->
return next?(err, list, tree)
|
Cycle | balUtilPaths.openFile -> fs.readdir parentPath, (err,files) -> |
Close | balUtilPaths.closeFile() |
Check | if tasks.exited
return |
Error | else if err
console.log 'balUtilPaths.scandir: readdir has failed on:', parentPath
return tasks.exit(err)
|
Empty? | else if !files.length
return tasks.exit()
|
Cycle | else files.forEach (file) -> |
Check | isHiddenFile = ignoreHiddenFiles and /^\./.test(file)
isIgnoredFile = ignorePatterns.test(file)
if isHiddenFile or isIgnoredFile
return |
Prepare | ++tasks.total
fileFullPath = parentPath+'/'+file
fileRelativePath = (if relativePath then relativePath+'/' else '')+file |
IsDirectory | balUtilPaths.isDirectory fileFullPath, (err,isDirectory) -> |
Check | if tasks.exited
return
|
Error | else if err
console.log 'balUtilPaths.scandir: isDirectory has failed on:', fileFullPath
return tasks.exit(err)
|
Directory | else if isDirectory |
Prepare | complete = (err,skip,subtreeCallback) -> |
Error | return tasks.exit(err) if err
|
Exited | return tasks.exit() if tasks.exited |
Handle | if skip isnt true |
Append | list[fileRelativePath] = 'dir'
tree[file] = {} |
Recurse | return balUtilPaths.scandir( |
Path | path: fileFullPath |
File | fileAction: fileAction |
Dir | dirAction: dirAction |
Completed | next: (err,_list,_tree) -> |
Merge in children of the parent directory | tree[file] = _tree
for own filePath, fileType of _list
list[filePath] = fileType |
Exited | if tasks.exited
return tasks.exit() |
Error | else if err
console.log 'balUtilPaths.scandir: has failed on:', fileFullPath
return tasks.exit(err) |
Subtree | else if subtreeCallback
return subtreeCallback tasks.completer() |
Complete | else
return tasks.complete() |
Relative Path | relativePath: fileRelativePath |
Read Files | readFiles: readFiles
)
else |
Done | return tasks.complete()
|
Action | if dirAction
return dirAction fileFullPath, fileRelativePath, complete
else if dirAction is false
return complete(err,true)
else
return complete(err,false)
|
File | else |
Prepare | complete = (err,skip) -> |
Error | return tasks.exit(err) if err
|
Exited | return tasks.exit() if tasks.exited |
Handle | if skip |
Done | return tasks.complete()
else |
Append | list[fileRelativePath] = 'file'
if readFiles |
Read file | balUtilPaths.openFile -> fs.readFile fileFullPath, (err,data) -> |
Close file | balUtilPaths.closeFile() |
Error? | return tasks.exit(err) if err |
Append | tree[file] = data.toString() |
Done | return tasks.complete()
else |
Append | tree[file] = true |
Done | return tasks.complete()
|
Action | if fileAction
return fileAction fileFullPath, fileRelativePath, complete
else if fileAction is false
return complete(err,true)
else
return complete(err,false)
|
Chain | @
|
Copy a directory next(err) | cpdir: (srcPath,outPath,next) -> |
Scan all the files in the diretory and copy them over asynchronously | balUtilPaths.scandir( |
Path | srcPath |
File | (fileSrcPath,fileRelativePath,next) -> |
Prepare | fileOutPath = "#{outPath}/#{fileRelativePath}" |
Ensure the directory that the file is going to exists | balUtilPaths.ensurePath require('path').dirname(fileOutPath), (err) -> |
Error | if err
console.log 'balUtilPaths.cpdir: failed to create the path for the file:',fileSrcPath
return next?(err) |
The directory now does exist So let's now place the file inside it | balUtilPaths.cp fileSrcPath, fileOutPath, (err) -> |
Forward | if err
console.log 'balUtilPaths.cpdir: failed to copy the child file:',fileSrcPath
return next?(err) |
Dir | null |
Completed | next
) |
Chain | @
|
Replace a directory next(err) | rpdir: (srcPath,outPath,next) -> |
Scan all the files in the diretory and copy them over asynchronously | balUtilPaths.scandir( |
Path | srcPath |
File | (fileSrcPath,fileRelativePath,next) -> |
Prepare | fileOutPath = "#{outPath}/#{fileRelativePath}" |
Ensure the directory that the file is going to exists | balUtilPaths.ensurePath require('path').dirname(fileOutPath), (err) -> |
Error | if err
console.log 'balUtilPaths.rpdir: failed to create the path for the file:',fileSrcPath
return next?(err) |
Check if it is worthwhile copying that file | balUtilPaths.isPathOlderThan fileOutPath, fileSrcPath, (err,older) -> |
The src path has been modified since the out path was generated | if older is true or older is null |
The directory now does exist So let's now place the file inside it | balUtilPaths.cp fileSrcPath, fileOutPath, (err) -> |
Forward | if err
console.log 'balUtilPaths.rpdir: failed to copy the child file:',fileSrcPath
return next?(err) |
The out path is new enough | else
return next?() |
Dir | null |
Completed | next
) |
Chain | @
|
Remove a directory next(err) | rmdir: (parentPath,next) ->
path = require('path')
path.exists parentPath, (exists) -> |
Skip | return next?() unless exists |
Remove | balUtilPaths.scandir( |
Path | parentPath
|
File | (fileFullPath,fileRelativePath,next) ->
balUtilPaths.openFile -> fs.unlink fileFullPath, (err) ->
balUtilPaths.closeFile() |
Forward | if err
console.log 'balUtilPaths.rmdir: failed to remove the child file:', fileFullPath
return next?(err)
|
Dir | (fileFullPath,fileRelativePath,next) ->
next? null, false, (next) ->
balUtilPaths.openFile -> fs.rmdir fileFullPath, (err) ->
balUtilPaths.closeFile() |
Forward | if err
console.log 'balUtilPaths.rmdir: failed to remove the child directory:', fileFullPath
return next?(err)
|
Completed | (err,list,tree) -> |
Error | if err
return next?(err, list, tree) |
Success | balUtilPaths.openFile -> fs.rmdir parentPath, (err) ->
balUtilPaths.closeFile() |
Forward | if err
console.log 'balUtilPaths.rmdir: failed to remove the parent directory:', parentPath
return next?(err, list, tree)
)
|
Chain | @ |
Write tree next(err) | writetree: (dstPath,tree,next) -> |
Group | tasks = new balUtilFlow.Group (err) ->
next?(err)
|
Ensure Destination | balUtilPaths.ensurePath dstPath, (err) -> |
Checks | if err
return tasks.exit err
|
Cycle | for own fileRelativePath, value of tree
++tasks.total
fileFullPath = dstPath+'/'+fileRelativePath.replace(/^\/+/,'')
if typeof value is 'object'
balUtilPaths.writetree fileFullPath, value, tasks.completer()
else
balUtilPaths.openFile -> fs.writeFile fileFullPath, value, (err) ->
balUtilPaths.closeFile()
if err
console.log 'balUtilPaths.writetree: writeFile failed on:',fileFullPath
return tasks.complete err
|
Empty? | if tasks.total is 0
tasks.exit() |
Return | return
|
Chain | @ |
Read path Reads a path be it local or remote next(err,data) | readPath: (filePath,next) ->
if /^http/.test(filePath)
request = require('request') unless request
request filePath, (err,response,data) =>
return next?(err) if err
return next?(null,data)
else
balUtilPaths.openFile -> fs.readFile filePath, (err,data) ->
balUtilPaths.closeFile()
return next?(err) if err
return next?(null,data)
|
Chain | @ |
Empty Check if the file does not exist, or is empty next(err,empty) | empty: (filePath,next) -> |
Prepare | path = require('path') |
Check if we exist | path.exists filePath, (exists) -> |
Return empty if we don't exist | return next?(null,true) unless exists |
We do exist, so check if we have content | balUtilPaths.openFile -> fs.stat filePath, (err,stat) ->
balUtilPaths.closeFile() |
Check | return next?(err) if err |
Return whether or not we are actually empty | return next?(null,stat.size is 0) |
Chain | @ |
Is Path Older Than Checks if a path is older than a particular amount of millesconds next(err,older) older will be null if the path does not exist | isPathOlderThan: (aPath,bInput,next) -> |
Prepare | path = require('path') |
Handle mtime | bMtime = null
if typeof bInput is 'number'
mode = 'time'
bMtime = new Date(new Date() - bInput)
else
mode = 'path'
bPath = bInput |
Check if the path exists | balUtilPaths.empty aPath, (err,empty) -> |
If it doesn't then we should return right away | return next?(err,null) if empty or err |
We do exist, so let's check how old we are | balUtilPaths.openFile -> fs.stat aPath, (err,aStat) ->
balUtilPaths.closeFile() |
Check | return next?(err) if err |
Prepare | compare = -> |
Time comparison | if aStat.mtime < bMtime
older = true
else
older = false |
Return result | return next?(null,older) |
Perform the comparison | if mode is 'path' |
Check if the bPath exists | balUtilPaths.empty bPath, (err,empty) -> |
Return result if we are empty | return next?(err,null) if empty or err |
It does exist so lets get the stat | balUtilPaths.openFile -> fs.stat bPath, (err,bStat) ->
balUtilPaths.closeFile() |
Check | return next?(err) if err |
Assign the outer bMtime variable | bMtime = bStat.mtime |
Perform the comparison | return compare()
else |
We already have the bMtime | return compare() |
Chain | @ |
===================================== Export | module.exports = balUtilPaths
|