dovima

A gorgeous multi-platform standalone model.

Usage no npm install needed!

<script type="module">
  import dovima from 'https://cdn.skypack.dev/dovima';
</script>

README

Dovima.js npm version license type npm downloads ECMAScript 6 & 5

Build Status Coverage Status Code Climate bitHound Score Dependency Status Dev Dependency Status node 0.12.x node 0.11.x node 0.10.x iojs 2.x.x iojs 1.x.x

Dovima is a high-quality, stand-alone ORM written in ES6. We call it a "supermodel" because it gracefully does everything you expect an ORM to do, plus a whole lot of time-saving, code-cleaning extra features that make it an absolute pleasure to work with.

1. Installation

$ npm install dovima --save

You may wish to run the automated test suite to ensure that Dovima is fully compatible in your environment:

$ cd node_modules/dovima
$ npm test

2. Import / Require

// ES6
import Model from "dovima";
// ES5
var Model = require("dovima");

3. Getting Started

Dovima doesn't require centralized schema files for associations, validations, or anything else. You simply define a model, add the features you want to it, and use it.

3.1 Define a Model

// models/user.js

import Model from "dovima";

class User extends Model {}
// test.js

import User from "./models/user.js";

const userAttributes = {
  name: "Bob Builder"
};

const user = new User(userAttributes);

user.name.should.eql(userAttributes.name);

3.2 Define Attribute Validations

// models/user.js

import Model, {isNotEmpty} from "dovima";

class User extends Model {
  validations() {
    this.validate("name", isNotEmpty);
  }
}
// test.js

import User from "./models/user.js";

const user = new User();

// Check if the model is valid or not
user.isValid((isValid) => {
  isValid.should.be.false;
});

// Get a list of all invalid attributes
// and the reasons they are invalid
user.invalidAttributes((invalidAttributes) => {
  invalidAttributes.should.eql({
    "name": ["cannot be empty"]
  });
});

3.3 Define Simple Associations

// models/user.js

import Model from "dovima";
import Article from "./models/article.js";
import Comment from "./models/comment.js";

class User extends Model {
  associations() {
    this.hasMany("articles", Article);

    this.hasMany("comments", Comment);

    this.hasMany("articleComments", Comment)
        .through("articles");
  }
}
// models/article.js

import Model from "dovima";
import User from "./models/user.js";
import Comment from "./models/comment.js";

class Article extends Model {
  associations() {
    this.belongsTo("user", User);
    this.hasMany("comments", Comment);
  }
}
// models/comment.js

import Model from "dovima";
import User from "./models/user.js";
import Article from "./models/article.js";

class Comment extends Model {
  associations() {
    this.belongsTo("user", User);
    this.belongsTo("article", Article);
  }
}
// test.js

import User from "./models/user.js";
import Article from "./models/article.js";
import Comment from "./models/comment.js";

const user = new User({
  name: "Bob Builder"
});

const article = new Article({
  title: "How to Build Things!",
  user: user
});

const commentAttributes = {
  user: user,
  article: article,
  text: "I really like this article."
};

const comment = new Comment(commentAttributes);

user.articles.push(article);
article.comments.push(comment);

const articleComment = user.articleComments[0];

articleComment.text.should.eql(commentAttributes.text);

3.4 Define Association Validations

// models/user.js

import Model, {isPresent} from "dovima";
import ProfilePicture from "./models/profilePicture.js";

class User extends Model {
  associations() {
    this.hasOne("profilePicture", ProfilePicture);
  }
  validations() {
    this.validate("profilePicture", isPresent);
  }
}
// test.js

import User from "./models/user.js";

const user = new User();

// Check if the model is valid or not
user.isValid((isValid) => {
  isValid.should.be.false;
});

// Get a list of all invalid attributes
// and the reasons they are invalid
user.invalidAttributes((invalidAttributes) => {
  invalidAttributes.should.eql({
    "profilePicture": ["must be present"]
  });
});

3.5 Define Custom Constructor

// models/user.js

import Model from "dovima";

class User extends Model {
  instantiate(attributes = {}, options = {}) {
    if (options.convertToDogYears) {
      this.age = attributes.age * 7;
    }
  }
}
// test.js

import User from "./models/user.js";

const user = new User({
  age: 8
}, {
  convertToDogYears: true
});

user.age.should.eql(56);

3.6 Global Data Persistence

Note: Dovima utilizes a database adapter called almaden to make calls to the database.

// models/user.js

import Model from "dovima";
import Article from "./models/article.js";

class User extends Model {
  associations() {
    this.hasMany("articles", Article);
  }
}
// models/article.js

import Model from "dovima";
import User from "./models/user.js";

class Article extends Model {
  associations() {
    this.belongsTo("user", User);
  }
}
// test.js

import Database from "almaden";

import Model from "dovima";

import User from "./models/user.js";
import Article from "./models/article.js";

const databaseCredentials = {
  client: "mysql",
  connection: {
    host     : "127.0.0.1",
    user     : "myUser",
    password : "123456789",
    database : "test"
  }
};

const database = new Database(databaseCredentials);

/**
 * Setting Model.database will cause all models to use it by default.
 */
Model.database = database;

/**
 * Set up the models
 */
const user = new User({
  name: "Norman"
});

const article = new Article({
  title: "Things Happening Around Town"
});

user.articles.push(article);

/**
 * Now, all models can .save(callback)
 */
user.save((error) => {
  if (error) { throw error; }
  // user was saved, and now has an id
  (user.id === undefined).should.be.false;
  // article was saved as well and has an id due to .save
  // propagating to all hasOne and hasMany associations
  (article.id === undefined).should.be.false;
});

3.6.1 K'Nex Compatibility

almaden is built on knex, so if you have an existing knex connection you'd like to use instead of creating a new one, just use the following instead of passing credentials:


import knex from "knex";
import Database from "almaden";
import Model from "dovima";

const databaseCredentials = {
  client: "mysql",
  connection: {
    host     : "127.0.0.1",
    user     : "myUser",
    password : "123456789",
    database : "test"
  }
};

const database = knex(databaseCredentials);

/**
 * Use existing knex connection
 */
Model.database = new Database({
  knex: database
});

3.7 Finding Models in Database

// models/user.js

import Model from "dovima";
import Article from "./models/article.js";

class User extends Model {
  associations() {
    this.hasMany("articles", Article);
  }
}
// models/article.js

import Model from "dovima";
import User from "./models/user.js";

class Article extends Model {
  associations() {
    this.belongsTo("user", User);
  }
}
// test.js

import Database from "almaden";

import Model from "dovima";

import User from "./models/user.js";
import Article from "./models/article.js";

const databaseCredentials = {
  client: "mysql",
  connection: {
    host     : "127.0.0.1",
    user     : "myUser",
    password : "123456789",
    database : "test"
  }
};

Model.database = new Database(databaseCredentials);

User
  .find.one
  .where("id", 1)
  .include("articles")
  .results((error, user) => {
    user.name.should.eql("Norman");
    user.articles[0].title.should.eql("Things Happening Around Town");
  });

3.8 Mocking Model Find Chain

// models/user.js

import Model from "dovima";

class User extends Model {}
// test.js

import User from "./models/user.js";

User.mock.find.one.where("id", 1).results({
  id: 1,
  name: "Bob"
});

User.find.one.where("id", 1).results((error, user) => {
  user.name.should.eql("Bob");
});

3.9 Mocking Model Instance

// models/user.js

import Model from "dovima";

class User extends Model {}
// test.js

import User from "./models/user.js";

const user = new User({id: 1});

user.mock.instance({
  id: 1,
  name: "Bob"
});

user.fetch(() => {
  user.id.should.eql(1);
  user.name.should.eql("Bob");
});