FreeAllMedia/dovima

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# Dovima.js [![npm version](https://img.shields.io/npm/v/dovima.svg)](https://www.npmjs.com/package/dovima) [![license type](https://img.shields.io/npm/l/dovima.svg)](https://github.com/FreeAllMedia/dovima.git/blob/master/LICENSE) [![npm downloads](https://img.shields.io/npm/dm/dovima.svg)](https://www.npmjs.com/package/dovima) ![ECMAScript 6 & 5](https://img.shields.io/badge/ECMAScript-6%20/%205-red.svg)

[![Build Status](https://travis-ci.org/FreeAllMedia/dovima.png?branch=master)](https://travis-ci.org/FreeAllMedia/dovima) [![Coverage Status](https://coveralls.io/repos/FreeAllMedia/dovima/badge.svg)](https://coveralls.io/r/FreeAllMedia/dovima) [![Code Climate](https://codeclimate.com/github/FreeAllMedia/dovima/badges/gpa.svg)](https://codeclimate.com/github/FreeAllMedia/dovima) [![bitHound Score](https://www.bithound.io/github/FreeAllMedia/dovima/badges/score.svg)](https://www.bithound.io/github/FreeAllMedia/dovima)
[![Dependency Status](https://david-dm.org/FreeAllMedia/dovima.png?theme=shields.io)](https://david-dm.org/FreeAllMedia/dovima?theme=shields.io) [![Dev Dependency Status](https://david-dm.org/FreeAllMedia/dovima/dev-status.svg)](https://david-dm.org/FreeAllMedia/dovima?theme=shields.io#info=devDependencies)
![node 0.12.x](https://img.shields.io/badge/node-0.12.x-brightgreen.svg) ![node 0.11.x](https://img.shields.io/badge/node-0.11.x-brightgreen.svg) ![node 0.10.x](https://img.shields.io/badge/node-0.10.x-brightgreen.svg) ![iojs 2.x.x](https://img.shields.io/badge/iojs-2.x.x-brightgreen.svg) ![iojs 1.x.x](https://img.shields.io/badge/iojs-1.x.x-brightgreen.svg)

<!-- [![Sauce Test Status](https://saucelabs.com/browser-matrix/dovima.svg)](https://saucelabs.com/u/dovima) -->

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

``` javascript
// models/user.js

import Model from "dovima";

class User extends Model {}
```

``` javascript
// 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

``` javascript
// models/user.js

import Model, {isNotEmpty} from "dovima";

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

``` javascript
// 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

``` javascript
// 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);
  }
}
```

``` javascript
// 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

``` javascript
// models/user.js

import Model from "dovima";

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

``` javascript
// 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.

``` javascript
// models/user.js

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

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

``` javascript
// models/article.js

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

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

``` javascript
// 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:

``` javascript

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

``` javascript
// models/user.js

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

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

``` javascript
// models/article.js

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

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

``` javascript
// 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

``` javascript
// models/user.js

import Model from "dovima";

class User extends Model {}
```

``` javascript
// 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

``` javascript
// models/user.js

import Model from "dovima";

class User extends Model {}
```

``` javascript
// 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");
});
```