-
-
Save cakekindel/113f01915aeadcb1dfea11654082dd2d to your computer and use it in GitHub Desktop.
Why are some people so excited about Functional Programming?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const getFileArg = () => process.argv[2]; | |
const File = { | |
doRead: path => ..., | |
doWrite: (path, buf) => ..., | |
doExists: path => ..., | |
doHash: path => ..., | |
}; | |
const Dir = { | |
doRead: dir => ..., | |
doCreate: dir => ..., | |
doReadOrCreate: dir => ..., | |
}; | |
const Path = { | |
of: str => ..., | |
join: paths => ..., | |
dir: path => ..., | |
toString: path => ..., | |
name: path => ..., | |
}; | |
const Backup = { | |
backupDirFor: path => ..., | |
doNewBackupFilename: path => ..., | |
doBackup: path => ..., | |
doGetLatestBackup: path => ..., | |
doShouldBackup: path => ..., | |
doTryBackup: path => doShouldBackup(path) | |
? doBackup(path) | |
: {}, | |
}; | |
const main = () => doTryBackup(Path.of(getFileArg())); | |
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Backuper { | |
constructor() { | |
this.path = new Path(process.argv[2]); | |
this.file = new File(this.path); | |
const backupDirName = `${this.path.name()}_backups`; | |
this.backupDir = new Dir(this.path.dir().join(backupDirName)); | |
this.backupDir.createIfNotExist(); | |
} | |
backup() { ... } | |
newestBackup() { ... } | |
go() { | |
const backups = this.backupDir.read(); | |
if (backups.length === 0) { | |
this.backup(); | |
} else { | |
const newestBackup = this.newestBackup(); | |
if (new File(newestBackup).hash() !== this.file.hash()) { | |
this.backup(); | |
} | |
} | |
} | |
} | |
class Path { | |
constructor(path) { ... } | |
dir() { ... } | |
name() { ... } | |
toString() { ... } | |
join(other) { ... } | |
} | |
class Dir { | |
constructor(path) { ... } | |
createIfNotExist() { ... } | |
read() { ... } | |
} | |
class File { | |
constructor(path) { ... } | |
writeBuf(buf) { ... } | |
readBuf() { ... } | |
hash() { ... } | |
} | |
File.exists = function(path) { ... } | |
new Backuper().go(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function main() { | |
const path = getPathToBackup(); | |
const backups = readBackups(path); | |
if (backups.length === 0) { | |
makeBackup(path); | |
} else { | |
const newestBackup = getNewestBackup(backups); | |
if (areDifferent(newestBackup, path)) { | |
makeBackup(path); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs'); | |
const nodePath = require('path'); | |
const crypto = require('crypto'); | |
const {pipe} = require('fp-ts/lib/function'); | |
// NOTE: main moved to the bottom | |
const getFileArg = () => process.argv[2]; | |
const File = { | |
doRead: path => fs.readFileSync(Path.toString(path)), | |
doWrite: (path, buf) => fs.writeFileSync(Path.toString(path), buf), | |
doExists: path => fs.existsSync(Path.toString(path)), | |
}; | |
File.doHash = path => { | |
if (path === undefined || !File.doExists(path)) { | |
return undefined; | |
} | |
const hash = crypto.createHash('md5'); | |
hash.update(File.doRead(path)); | |
return hash.digest('utf8'); | |
}; | |
const Dir = { | |
doRead: dir => pipe(dir, Path.toString, fs.readdirSync).map(f => Path.join([dir, Path.of(f)])), | |
doCreate: dir => {pipe(dir, Path.toString, fs.mkdirSync); return [];}, | |
}; | |
Dir.doReadOrCreate = dir => File.doExists(dir) ? Dir.doRead(dir) : Dir.doCreate(dir); | |
const Path = { | |
dir: path => nodePath.parse(path.dir), | |
of: str => nodePath.parse(str), | |
toString: path => nodePath.format(path), | |
name: path => path.name, | |
}; | |
Path.join = paths => pipe(paths.map(Path.toString), ps => nodePath.resolve(...ps), Path.of); | |
const backupDirFor = path => Path.join([Path.dir(path), Path.of(`${path.name}_backups`)]); | |
const doNewBackupFilename = path => Path.join([backupDirFor(path), Path.of(new Date().toISOString())]); | |
const doBackup = path => File.doWrite(doNewBackupFilename(path), File.doRead(path)); | |
const doGetLatestBackup = path => pipe(path, backupDirFor, Dir.doReadOrCreate) | |
.sort((a, b) => new Date(Path.name(b)) - new Date(Path.name(a))) // descending | |
[0]; | |
const doShouldBackup = path => File.doHash(path) !== pipe(path, doGetLatestBackup, File.doHash); | |
const doTryBackup = path => doShouldBackup(path) | |
? doBackup(path) | |
: {}; | |
const main = () => pipe(getFileArg(), Path.of, doTryBackup); | |
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs'); | |
const nodePath = require('path'); | |
const crypto = require('crypto'); | |
// NOTE: main moved to the bottom | |
const getFileArg = () => process.argv[2]; | |
const File = { | |
doRead: path => fs.readFileSync(Path.toString(path)), | |
doWrite: (path, buf) => fs.writeFileSync(Path.toString(path), buf), | |
doExists: path => fs.existsSync(Path.toString(path)), | |
}; | |
File.doHash = path => { | |
if (path === undefined || !File.doExists(path)) { | |
return undefined; | |
} | |
const hash = crypto.createHash('md5'); | |
hash.update(File.doRead(path)); | |
return hash.digest('utf8'); | |
}; | |
const Dir = { | |
doRead: dir => fs.readdirSync(Path.toString(dir)).map(f => Path.join([dir, Path.of(f)])), | |
doCreate: dir => {fs.mkdirSync(Path.toString(dir)); return [];}, | |
}; | |
Dir.doReadOrCreate = dir => File.doExists(dir) ? Dir.doRead(dir) : Dir.doCreate(dir); | |
const Path = { | |
join: paths => nodePath.parse(nodePath.resolve(...paths.map(path => nodePath.format(path)))), | |
dir: path => nodePath.parse(path.dir), | |
of: str => nodePath.parse(str), | |
toString: path => nodePath.format(path), | |
name: path => path.name, | |
}; | |
const backupDirFor = path => Path.join([Path.dir(path), Path.of(`${path.name}_backups`)]); | |
const doNewBackupFilename = path => Path.join([backupDirFor(path), Path.of(new Date().toISOString())]); | |
const doBackup = path => File.doWrite(doNewBackupFilename(path), File.doRead(path)); | |
const doGetLatestBackup = path => Dir.doReadOrCreate(backupDirFor(path)) | |
.sort((a, b) => new Date(Path.name(b)) - new Date(Path.name(a))) // descending | |
[0]; | |
const doShouldBackup = path => File.doHash(path) !== File.doHash(doGetLatestBackup(path)); | |
const doTryBackup = path => doShouldBackup(path) | |
? doBackup(path) | |
: {}; | |
const main = () => doTryBackup(Path.of(getFileArg())); | |
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs'); | |
const nodePath = require('path'); | |
const crypto = require('crypto'); | |
class Backuper { | |
constructor() { | |
this.path = new Path(process.argv[2]); | |
this.file = new File(this.path); | |
const backupDirName = `${this.path.name()}_backups`; | |
this.backupDir = new Dir(this.path.dir().join(backupDirName)); | |
this.backupDir.createIfNotExist(); | |
} | |
backup() { | |
const now = new Date().toISOString(); | |
const dest = this.backupDir.path.join(now); | |
const destFile = new File(dest); | |
destFile.writeBuf(this.file.readBuf()); | |
} | |
newestBackup() { | |
const backups = this.backupDir.read(); | |
if (backups.length === 0) { | |
return undefined; | |
} | |
let mostRecentDate; | |
let mostRecentBackup; | |
for (let i = 0; i < backups.length; i++) { | |
const date = new Date(backups[i].name()); | |
if (mostRecentDate === undefined || date < mostRecentDate) { | |
mostRecentDate = date; | |
mostRecentBackup = backups[i]; | |
} | |
} | |
return mostRecentBackup; | |
} | |
go() { | |
const backups = this.backupDir.read(); | |
if (backups.length === 0) { | |
this.backup(); | |
} else { | |
const newestBackup = this.newestBackup(); | |
if (new File(newestBackup).hash() !== this.file.hash()) { | |
this.backup(); | |
} | |
} | |
} | |
} | |
class Path { | |
constructor(path) { | |
if (typeof path === 'string') { | |
this.nodePath = nodePath.parse(nodePath.resolve(__dirname, path)); | |
} else { | |
this.nodePath = path; | |
} | |
} | |
dir() { | |
return new Path(this.nodePath.dir); | |
} | |
name() { | |
return this.nodePath.name; | |
} | |
toString() { | |
return nodePath.format(this.nodePath); | |
} | |
join(other) { | |
return new Path(nodePath.resolve(this.toString(), other)); | |
} | |
} | |
class Dir { | |
constructor(path) { | |
this.path = path; | |
} | |
read() { | |
let files = fs.readdirSync(this.path.toString()); | |
for (let i = 0; i < files.length; i++) { | |
files[i] = this.path.join(files[i]); | |
} | |
return files; | |
} | |
createIfNotExist() { | |
if (!File.exists(this.path)) { | |
fs.mkdirSync(this.path.toString()); | |
} | |
} | |
} | |
class File { | |
constructor(path) { | |
this.path = path; | |
} | |
writeBuf(buf) { | |
fs.writeFileSync(this.path.toString(), buf); | |
} | |
readBuf() { | |
return fs.readFileSync(this.path.toString()); | |
} | |
hash() { | |
const hash = crypto.createHash('md5'); | |
hash.update(this.readBuf()); | |
return hash.digest('utf8'); | |
} | |
} | |
File.exists = function(path) { | |
return fs.existsSync(path.toString()); | |
} | |
new Backuper().go(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs'); | |
const nodePath = require('path'); | |
const crypto = require('crypto'); | |
main(); | |
function main() { | |
const path = getPathToBackup(); | |
const backups = readBackups(path); | |
if (backups.length === 0) { | |
makeBackup(path); | |
} else { | |
const newestBackup = getNewestBackup(backups); | |
if (areDifferent(newestBackup, path)) { | |
makeBackup(path); | |
} | |
} | |
} | |
function getPathToBackup() { | |
let path = process.argv[2]; | |
if (path === undefined) { | |
throw new Error('you didn\'t give me a path!'); | |
} | |
path = nodePath.resolve(__dirname, path) | |
path = nodePath.parse(path); | |
const pathStr = nodePath.format(path); | |
if (!fs.existsSync(pathStr)) { | |
throw new Error(`path ${pathStr} does not exist.`) | |
} | |
return path; | |
} | |
function readFile(path) { | |
path = nodePath.format(path); | |
return fs.readFileSync(path); | |
} | |
function backupDir(path) { | |
return nodePath.resolve(path.dir, `${path.name}_backups`); | |
} | |
function readBackups(path) { | |
const backupDir_ = backupDir(path); | |
if (!fs.existsSync(backupDir_)) { | |
fs.mkdirSync(backupDir_); | |
return []; | |
} | |
let backups = fs.readdirSync(backupDir_); | |
for (let i = 0; i < backups.length; i++) { | |
backups[i] = nodePath.resolve(backupDir_, backups[i]); | |
backups[i] = nodePath.parse(backups[i]); | |
} | |
return backups; | |
} | |
function getNewestBackup(backups) { | |
if (backups.length === 0) { | |
return undefined; | |
} | |
let mostRecent; | |
for (let i = 0; i < backups.length; i++) { | |
const date = new Date(backups[i]); | |
if (date < mostRecent) { | |
mostRecent = date; | |
} | |
} | |
return mostRecent.toISOString(); | |
} | |
function makeBackup(path) { | |
const backupDir_ = backupDir(path); | |
const now = new Date().toISOString(); | |
const dest = nodePath.resolve(backupDir_, now); | |
path = nodePath.format(path); | |
const buf = fs.readFileSync(path); | |
fs.writeFileSync(dest, buf); | |
} | |
function areDifferent(aPath, bPath) { | |
aPath = nodePath.format(aPath); | |
bPath = nodePath.format(bPath); | |
const a = fs.readFileSync(aPath); | |
const b = fs.readFileSync(bPath); | |
return hashBuf(a) !== hashBuf(b); | |
} | |
function hashBuf(buf) { | |
const hash = crypto.createHash('md5'); | |
hash.update(buf); | |
return hash.digest('utf8'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment