Skip to content

Instantly share code, notes, and snippets.

@cakekindel
Last active January 22, 2022 23:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cakekindel/113f01915aeadcb1dfea11654082dd2d to your computer and use it in GitHub Desktop.
Save cakekindel/113f01915aeadcb1dfea11654082dd2d to your computer and use it in GitHub Desktop.
Why are some people so excited about Functional Programming?
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();
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();
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);
}
}
}
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();
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();
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();
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