Comparing version 0.0.2 to 0.1.0
286
lib/csvDb.js
@@ -1,196 +0,164 @@ | ||
var Q = require('q'); | ||
var fs = require('fs'); | ||
var util = require('util'); | ||
var CsvDB = function (file, fields) { | ||
this.file = file; | ||
this.fields = fields; | ||
}; | ||
class CsvDB { | ||
constructor (file, fields) { | ||
this.file = file; | ||
this.fields = fields; | ||
} | ||
CsvDB.prototype.getFileContent = function () { | ||
var deferred = Q.defer(); | ||
getFileContent() { | ||
return new Promise((resolve, reject) => { | ||
fs.readFile(this.file, 'utf-8', (err, data) => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve(data); | ||
}); | ||
}); | ||
} | ||
fs.readFile(this.file, 'utf-8', function (err, data) { | ||
if (err) { | ||
deferred.reject(err); | ||
return; | ||
transform(input) { | ||
if (input === '') { | ||
return []; | ||
} | ||
deferred.resolve(data); | ||
}); | ||
return deferred.promise; | ||
}; | ||
var lines = input.split('\n'); | ||
var result = []; | ||
if (Array.isArray(lines) && lines.length > 0) { | ||
for (var i = 0; i < lines.length; i++) { | ||
result[i] = {}; | ||
var cols = lines[i].split(';'); | ||
CsvDB.prototype.transform = function (input) { | ||
if (input === '') { | ||
return []; | ||
} | ||
var lines = input.split('\n'); | ||
var result = []; | ||
if (util.isArray(lines) && lines.length > 0) { | ||
for (var i = 0; i < lines.length; i++) { | ||
result[i] = {}; | ||
var cols = lines[i].split(';'); | ||
for (var j = 0; j < this.fields.length; j++) { | ||
result[i][this.fields[j]] = cols[j]; | ||
for (var j = 0; j < this.fields.length; j++) { | ||
result[i][this.fields[j]] = cols[j]; | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
return result; | ||
}; | ||
CsvDB.prototype.get = function (id) { | ||
var deferred = Q.defer(); | ||
get(id) { | ||
return new Promise((resolve, reject) => { | ||
this.getFileContent().then((data) => { | ||
var fileContents = this.getFileContent(); | ||
var result = this.transform(data); | ||
fileContents.then(function (data) { | ||
if (id) { | ||
result = result.filter((row) => { | ||
return row['id'] == id; | ||
}).pop(); | ||
} | ||
var result = this.transform(data); | ||
resolve(result); | ||
}, err => reject(err)); | ||
}); | ||
} | ||
if (id) { | ||
result = this.fetchRow(result, id); | ||
} | ||
getNextId() { | ||
return new Promise((resolve, reject) => { | ||
this.get().then(data => { | ||
if (Array.isArray(data) && data.length === 0) { | ||
resolve(1); | ||
return; | ||
} | ||
deferred.resolve(result); | ||
}.bind(this), function (err) { | ||
deferred.reject(err); | ||
}); | ||
const lastIndex = data.reduce((prev, curr) => { | ||
const itemId = parseInt(curr['id'], 10); | ||
return (prev < itemId) ? | ||
itemId : | ||
prev; | ||
}, 0); | ||
return deferred.promise; | ||
}; | ||
CsvDB.prototype.fetchRow = function (input, id) { | ||
for (var i = 0; i < input.length; i++) { | ||
if (input[i]['id'] == id) { | ||
return input[i]; | ||
} | ||
resolve(lastIndex + 1); | ||
}, err => deferred.reject(err)); | ||
}); | ||
} | ||
}; | ||
CsvDB.prototype.getNextId = function () { | ||
var deferred = Q.defer(); | ||
insert(newData) { | ||
return new Promise((resolve, reject) => { | ||
this.get().then(data => { | ||
this.getNextId().then((id) => { | ||
newData.id = id; | ||
data.push(newData); | ||
var promise = this.get(); | ||
data = this.flatten(data); | ||
promise.then(function (data) { | ||
if (util.isArray(data) && data.length === 0) { | ||
deferred.resolve(1); | ||
return; | ||
} | ||
fs.writeFile(this.file, data, err => { | ||
if (err) { | ||
reject(err); | ||
return; | ||
} | ||
resolve(id); | ||
}); | ||
}, () => reject()); | ||
}, () => reject()); | ||
}); | ||
} | ||
var lastIndex = 0; | ||
flatten(input) { | ||
var result = []; | ||
var row = []; | ||
for (var i = 0; i < data.length; i++) { | ||
if (parseInt(lastIndex) < parseInt(data[i]['id'])) { | ||
lastIndex = data[i]['id']; | ||
if (Array.isArray(input) && input.length > 0) { | ||
for (var i = 0; i < input.length; i++) { | ||
row = []; | ||
for (var j = 0; j < this.fields.length; j++) { | ||
row.push(input[i][this.fields[j]]); | ||
} | ||
result.push(row.join(';') + ';'); | ||
} | ||
} | ||
return result.join('\n'); | ||
} | ||
deferred.resolve(parseInt(lastIndex) + 1); | ||
}.bind(this), function (err) { | ||
deferred.reject(err); | ||
}); | ||
update(data) { | ||
return new Promise((resolve, reject) => { | ||
this.get().then((existingContent) => { | ||
if (!Array.isArray(data)) { | ||
data = [data]; | ||
} | ||
for (let i = 0; i < data.length; i++) { | ||
existingContent = existingContent.map(item => { | ||
if (item.id === data[i].id) { | ||
return Object.assign(item, data[i]); | ||
} | ||
return item; | ||
}); | ||
} | ||
return deferred.promise; | ||
}; | ||
return this.write(this.flatten(existingContent)); | ||
}, () => reject()).then(() => resolve(), err => reject(err)); | ||
}); | ||
} | ||
CsvDB.prototype.insert = function (newData) { | ||
var deferred = Q.defer(); | ||
this.get().then(function (data) { | ||
var nextIdPromise = this.getNextId(); | ||
nextIdPromise.then(function (id) { | ||
newData.id = id; | ||
data.push(newData); | ||
data = this.flatten(data); | ||
fs.writeFile(this.file, data, function (err) { | ||
write(data) { | ||
return new Promise((resolve, reject) => { | ||
fs.writeFile(this.file, data, err => { | ||
if (err) { | ||
deferred.reject(err); | ||
reject(err); | ||
return; | ||
} | ||
deferred.resolve(id); | ||
resolve(); | ||
}); | ||
}.bind(this)); | ||
}.bind(this)); | ||
return deferred.promise; | ||
}; | ||
}); | ||
} | ||
CsvDB.prototype.flatten = function (input) { | ||
var result = []; | ||
var row = []; | ||
if (util.isArray(input) && input.length > 0) { | ||
for (var i = 0; i < input.length; i++) { | ||
row = []; | ||
for (var j = 0; j < this.fields.length; j++) { | ||
row.push(input[i][this.fields[j]]); | ||
} | ||
result.push(row.join(';') + ';'); | ||
} | ||
delete(id) { | ||
return new Promise((resolve, reject) => { | ||
this.get().then(data => { | ||
for (var i = 0; i < data.length; i++) { | ||
if (data[i].id == id) { | ||
data.splice(i, 1) | ||
break; | ||
} | ||
} | ||
return this.write(this.flatten(data)); | ||
}, () => reject()).then(() => resolve(), err => reject(err)); | ||
}); | ||
} | ||
return result.join('\n'); | ||
}; | ||
} | ||
CsvDB.prototype.update = function (data, id) { | ||
var deferred = Q.defer(); | ||
this.get().then(function (existingContent) { | ||
for (var i = 0; i < existingContent.length; i++) { | ||
if (existingContent[i].id == id) { | ||
data.id = id; | ||
existingContent[i] = data; | ||
} | ||
} | ||
return this.write(this.flatten(existingContent)); | ||
}.bind(this)).then(function () { | ||
deferred.resolve(); | ||
}, function (err) { | ||
deferred.reject(err); | ||
}); | ||
return deferred.promise; | ||
}; | ||
CsvDB.prototype.write = function (data) { | ||
var deferred = Q.defer(); | ||
fs.writeFile(this.file, data, function (err) { | ||
if (err) { | ||
deferred.reject(err); | ||
return; | ||
} | ||
deferred.resolve(); | ||
}); | ||
return deferred.promise; | ||
}; | ||
CsvDB.prototype.delete = function (id) { | ||
var deferred = Q.defer(); | ||
this.get().then(function (data) { | ||
for (var i = 0; i < data.length; i++) { | ||
if (data[i].id == id) { | ||
data.splice(i, 1) | ||
break; | ||
} | ||
} | ||
return this.write(this.flatten(data)); | ||
}.bind(this)).then(function () { | ||
deferred.resolve(); | ||
}, function (err) { | ||
deferred.reject(err); | ||
}) | ||
return deferred.promise; | ||
}; | ||
module.exports = CsvDB; | ||
module.exports = CsvDB; |
{ | ||
"name": "csv-db", | ||
"version": "0.0.2", | ||
"version": "0.1.0", | ||
"description": "Simple filebased database", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "node_modules/.bin/mocha" | ||
}, | ||
@@ -19,3 +19,3 @@ "repository": { | ||
"name": "Sebastian Springer", | ||
"email": "sebastian.springer@mayflower.de" | ||
"email": "info@sebastian-springer.com" | ||
}, | ||
@@ -27,13 +27,6 @@ "license": "BSD-2-Clause", | ||
"homepage": "https://github.com/sspringer82/nodeCsvDb", | ||
"dependencies": { | ||
"q": "~1.0.0", | ||
"expect.js": "~0.2.0" | ||
}, | ||
"readme": "# CSV based Database\n\nI was looking for a lightweight replacement for a database in one of my node.js workshops.\nThis is the result: the data is stored in files. The rows are separated by new line characters\nand the fields are separated by semicolons.\nThe first version of the database implementation was completely synchronous whereas the current version\nheavily relies on promises.\n\n# API\n## get(id)\nRead either all data of a certain file or just one data set. The output is an array containing\none or more objects, depending on what was fetched.\n## insert(newData)\nInsert a new row to the database. Data is an object with column names as keys and the values to be inserted as - the values.\n## update(data, id)\nUpdate an existing row. Data is an object containing ALL the values excluding the id. id is the identifier of the row to be updated.\n## delete(id)\nDelete an existing row. id is the identifier of the row to be deleted.", | ||
"readmeFilename": "README.md", | ||
"_id": "csv-db@0.0.2", | ||
"dist": { | ||
"shasum": "9706dd725e035db2bb9ce50725b47497fa431c2e" | ||
}, | ||
"_from": "/Users/sspringer/srv/nodeCsvDb/" | ||
"devDependencies": { | ||
"expect.js": "~0.2.0", | ||
"mocha": "^3.2.0" | ||
} | ||
} |
@@ -9,3 +9,28 @@ # CSV based Database | ||
# Installation | ||
`npm install csv-db` | ||
# Usage | ||
Given a file named input.csv of the following format (columns ID, username and password): | ||
``` | ||
1;admin;secret; | ||
``` | ||
##Initialization: | ||
``` | ||
const csvDb = new CsvDb('input.csv', ['id', 'username', 'password']); | ||
``` | ||
##Usage: | ||
``` | ||
csvDb.get().then((data) => { | ||
console.log(data); | ||
}, (err) => { | ||
console.log(err); | ||
}); | ||
``` | ||
# API | ||
## CsvDb(filename, columns) | ||
Create a file handle to access the database | ||
## get(id) | ||
@@ -19,2 +44,2 @@ Read either all data of a certain file or just one data set. The output is an array containing | ||
## delete(id) | ||
Delete an existing row. id is the identifier of the row to be deleted. | ||
Delete an existing row. id is the identifier of the row to be deleted. |
@@ -16,8 +16,8 @@ var CsvDb = require('../lib/csvDb'); | ||
var copyFile = function (src, dest) { | ||
function copyFile (src, dest) { | ||
deleteFile(dest); | ||
fs.createReadStream(src).pipe(fs.createWriteStream(dest)); | ||
}; | ||
var deleteFile = function (fileName) { | ||
function deleteFile (fileName) { | ||
if (fs.exists(fileName)) { | ||
@@ -31,3 +31,3 @@ fs.unlinkSync(fileName); | ||
describe("instanciate", function () { | ||
describe('instanciate', function () { | ||
@@ -38,7 +38,7 @@ beforeEach(function () { | ||
it ("should instanciate with a filename as argument", function () { | ||
it('should instanciate with a filename as argument', function () { | ||
expect(csvDb.file).to.be(file.source); | ||
}); | ||
it ("should instanciate with a filename and field names as arguments", function () { | ||
it('should instanciate with a filename and field names as arguments', function () { | ||
var csvDb = new CsvDb(file.source, ['column']); | ||
@@ -50,3 +50,3 @@ expect(csvDb.file).to.be(file.source); | ||
describe ("read", function () { | ||
describe('read', function () { | ||
@@ -57,3 +57,3 @@ beforeEach(function () { | ||
it ("should read the contents of the file", function (done) { | ||
it('should read the contents of the file', function (done) { | ||
var promise = csvDb.getFileContent(); | ||
@@ -73,3 +73,3 @@ | ||
it ("should make an object out of a text block", function () { | ||
it('should make an object out of a text block', function () { | ||
var input = '1;lala;lulu;\n2;mumu;meme;'; | ||
@@ -89,3 +89,3 @@ var expected = [{ | ||
it ("should transform an empty input", function () { | ||
it('should transform an empty input', function () { | ||
var input = ''; | ||
@@ -97,3 +97,3 @@ var expected = []; | ||
it ("should get all contents of the specified file and return an object structure", function (done) { | ||
it('should get all contents of the specified file and return an object structure', function (done) { | ||
var expected = [{ | ||
@@ -123,5 +123,5 @@ id: 1, | ||
it ("should get a specified set of data", function (done) { | ||
it('should get a specified set of data', function (done) { | ||
var expected = { | ||
id: 2, | ||
id: '2', | ||
name: 'mumu', | ||
@@ -144,27 +144,5 @@ password: 'meme' | ||
}); | ||
it ("should fetch a single row out of a rowset", function () { | ||
var input = [{ | ||
id: 1, | ||
name: 'lala', | ||
password: 'lulu' | ||
},{ | ||
id: 2, | ||
name: 'mumu', | ||
password: 'meme' | ||
}]; | ||
var expected = { | ||
id: 2, | ||
name: 'mumu', | ||
password: 'meme' | ||
}; | ||
var output = csvDb.fetchRow(input, 2); | ||
expect(output).to.eql(expected); | ||
}); | ||
}); | ||
describe("create", function () { | ||
describe('create', function () { | ||
@@ -177,3 +155,3 @@ beforeEach(function () { | ||
it ("should get the next id of an empty file", function (done) { | ||
it('should get the next id of an empty file', function (done) { | ||
fs.writeFileSync(file.create, ''); | ||
@@ -195,3 +173,3 @@ | ||
it ("should get the next id of a file with content", function (done) { | ||
it('should get the next id of a file with content', function (done) { | ||
copyFile(file.source, file.create); | ||
@@ -213,3 +191,3 @@ | ||
it ("should transform an array-object structure to a valid string", function () { | ||
it('should transform an array-object structure to a valid string', function () { | ||
var input = [{ | ||
@@ -231,3 +209,3 @@ id: 1, | ||
it ("should insert a new data set in an empty file", function (done) { | ||
it('should insert a new data set in an empty file', function (done) { | ||
fs.writeFileSync(file.create, ''); | ||
@@ -258,3 +236,3 @@ var data = { | ||
it ("should append a new data set to an existing file", function (done) { | ||
it('should append a new data set to an existing file', function (done) { | ||
copyFile(file.source, file.create); | ||
@@ -287,3 +265,3 @@ | ||
describe("write", function () { | ||
describe('write', function () { | ||
@@ -296,3 +274,3 @@ beforeEach(function () { | ||
it ("should write the content of a new file", function (done) { | ||
it('should write the content of a new file', function (done) { | ||
var expected = '1;lala;lulu;\n2;foo;meme;'; | ||
@@ -316,3 +294,3 @@ | ||
describe("update", function () { | ||
describe('update', function () { | ||
@@ -325,13 +303,12 @@ beforeEach(function () { | ||
it ("should update a certain data set in an existing file", function (done) { | ||
it('should update a certain data set in an existing file', function (done) { | ||
var expectedFileContent = '1;lala;lulu;\n2;foo;meme;'; | ||
var data = { | ||
id: '2', | ||
name: 'foo', | ||
password: 'meme' | ||
}; | ||
var id = 2; | ||
var promise = csvDb.update(data, id); | ||
var promise = csvDb.update(data); | ||
promise.then(function () { | ||
@@ -351,5 +328,26 @@ try { | ||
}); | ||
it('should update multiple datasets at once', function (done) { | ||
var expectedFileContent = '1;abc;abc;\n2;def;def;'; | ||
const data = [ | ||
{id: '1', name: 'abc', password: 'abc'}, | ||
{id: '2', name: 'def', password: 'def'} | ||
]; | ||
csvDb.update(data).then(() => { | ||
try { | ||
var fileContent = fs.readFileSync(file.update, 'utf-8'); | ||
expect(fileContent).to.eql(expectedFileContent); | ||
done(); | ||
} catch (e) { | ||
done(e); | ||
} | ||
}, () => { | ||
done(); | ||
}); | ||
}); | ||
}); | ||
describe("delete", function () { | ||
describe('delete', function () { | ||
@@ -362,3 +360,3 @@ beforeEach(function () { | ||
it ("should delete a certain dataset", function (done) { | ||
it('should delete a certain dataset', function (done) { | ||
var expected = '1;lala;lulu;'; | ||
@@ -365,0 +363,0 @@ |
11
todo.txt
@@ -0,4 +1,13 @@ | ||
tests | ||
columns in first line | ||
joins | ||
id column in a variable | ||
file names in config | ||
if there is no file - create it | ||
if there is no file - create it | ||
support multipe inserts |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
0
1
44
17342
2
444
- Removedexpect.js@~0.2.0
- Removedq@~1.0.0
- Removedexpect.js@0.2.0(transitive)
- Removedq@1.0.1(transitive)