YAML database is a document database which stores documents as YAML files. The documents in the database can be maintained by simply editing the yaml files.
This database was designed to be used for systems like CMS systems, where an easy way to edit data is necessary and the number of data objects is not very high. It can be also used to store settings and configurations.
Storing the database as separate files lets you use version control systems like git on the database, which is again ideal for storing settings, configurations, blog posts and CMS content.
fs = require 'fs'
YAML = require "yamljs"
_ = require 'underscore'Find files in a directory
findFiles = (dir, callback) ->
fileList = []
err = []
callbackCount = 0
done = ->
callbackCount--
if callbackCount is 0
err = null if err.length is 0
callback err, fileList
recurse = (path) ->
callbackCount++
fs.readdir path, (e1, files) ->
if e1?
err.push e1
done()
return
for file in files
continue if file[0] is '.'
do (file) ->
f = "#{path}/#{file}"
callbackCount++
fs.stat f, (e2, stats) ->
if e2?
err.push e2
done()
return
if stats.isDirectory()
recurse f
else if stats.isFile()
fileList.push f
done()
done()
recurse dirSetup the database with a set of models and a directory. The models will reside in subdirectories with the same name.
Each model should be a subclass of Model class.
class Database
constructor: (path, models) ->
@models = models
@path = path save: (model, data, file, callback) ->
data = YAML.stringify data, 1000, 1
fs.writeFile file, data, encoding: 'utf8', (err) ->
callback errThis will load all the files of type model recursing over the subdirectories.
loadFiles: (model, callback) ->
path = "#{@path}/#{model}"
objs = []
files = []
err = []
n = 0
loadFile = =>
if n >= files.length
err = null if err.length is 0
callback err, objs
return
@loadFile model, files[n], (e, obj) ->
if e?
err.push e
else
objs.push obj
n++
loadFile()
findFiles path, (e, f) ->
err = e
err ?= []
files = f
loadFile()Loads a single file of type model
loadFile: (model, file, callback) ->
fs.readFile file, encoding: 'utf8', (e1, data) =>
if e1?
callback "Error reading file: #{file}, #{e1}", null
return
try
data = YAML.parse data
catch e2
callback "Error parsing file: #{file}, #{e2}", null
return
callback null, new @models[model] data, file: file, db: thisIntroduces class level function initialize and include. This class is the base class of all other data models. It has get and set methods to change values. The structure of the object is defined by defaults.
class Model
constructor: ->
@_init.apply @, arguments
_initFuncs: []All initializer funcitons in subclasses will be called with the constructor arguments.
@initialize: (func) ->
@::_initFuncs = _.clone @::_initFuncs
@::_initFuncs.push func
_init: ->
for init in @_initFuncs
init.apply @, argumentsYou can include objects by registering them with @include. This solves the problem of single inheritence.
@include: (obj) ->
for k, v of obj when not @::[k]?
@::[k] = v
model: 'Model'
_defaults: {}Subclasses can add to default key-values of parent classes
@defaults: (defaults) ->
@::_defaults = _.clone @::_defaults
for k, v of defaults
@::_defaults[k] = vBuild a model with the structure of defaults. options.db is a reference to the Database object, which will be used when updating the object. options.file is the path of the file, which will be null if this is a new object.
@initialize (values, options) ->
@file = options.file if options.file?
@db = options.db
@values = {}
values ?= {}
for k, v of @_defaults
if values[k]?
@values[k] = values[k]
else
@values[k] = v toJSON: -> _.clone @values get: (key) -> @values[key] set: (obj) ->
for k, v of obj
@values[k] = v if k of @_defaults save: (callback) ->
return unless @file?
@db.save @model, @toJSON(), @file, callbackexports.Database = Database
exports.Model = Model