SkillAgentSearch skills...

RRM

Javascript ORM

Install / Use

/learn @Burgov/RRM
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

RRM

Build Status

This library will help you manage relations between entities in JavaScript.

To use, simply include rrm.js in your project and follow these steps:

Instantiate the ObjectManager

The very first thing you need to do is create a new ObjectManager instance:

var om = new ObjectManager();

Define your entities

For every entity you're going to use in your project, you need to define it's schema. Then you need to combine the class and the schema. The ObjectManager class provides a helper method for that. Here's an example of two schema's with relations to each other:

var ProjectSchema = function() {
    this.id = new RRM.Property.Int('id', { writable: false, persistable: false });
    this.start = new RRM.Property.Date('start', { writable: false, persistable: false });
    this.name = new RRM.Property.String('name');
    this.products = new RRM.Relation.OneToMany('products', { entityClass: 'product', backReference: 'project' });
}
var Project = function() {
}
ObjectManager.prepareEntity('project', Project, ProjectSchema);
var ProductSchema = function() {
    this.id = new RRM.Property.Int('id', { writable: false, persistable: false });
    this.name = new RRM.Property.String('name');
}
var Product = function() {
}
ObjectManager.prepareEntity('product', Product, ProductSchema);

Load the data

You're now ready to use the RRM. In order to load a Project into the RRM, you need to call:

var data = {
  id: 3,
  name: 'My project',
  start: '2014-01-01T10:00:00+00:00'
  products: [
    {
      id: 1,
      name: 'Product 1'
    },
    {
      id: 2,
      name: 'Product 2'
    }
  ]
}

var project = om.create('project', data);

Usage details

Defining properties

As you can see above, we're defining various properties on the Project and Product entities. There are quite some predefined property definitions which all have their own functionality. Also, some properties have additional options defined. These will be explained below the property list.

Properties

| Property | Function | | ---------------------- | ------------------------------------------------------------------------------- | | RRM.Property.Int | Will make sure the value passed to it is cast to an integer | | RRM.Property.String | Will make sure the value passed to it is cast to a string | | RRM.Property.Boolean | Will make sure the value passed to it is cast to a Boolean | | RRM.Property.Array | Supports the loading of simple array data without the need to define a relation | | RRM.Property.Object | Same as array, but for simple objects | | RRM.Property.Date | Will cast the value into a Date object using the Date constructor |

Options

| Option | Default | Function | | ------------------ | ------- | ------------------------------------------------------------- | | readable | true | The value can be read | | writable | true | The value can be updated | | loadable | true | The value should be loaded from the data passed to create() | | persistable | true | The value should be returned when calling toArray() |

Defining your own property type

Defining your own property type is done by extending one of the existing property types. Let's take the Int property as an example:

// Int
RRM.Property.Int = function() {
    RRM.Property.Base.apply(this, arguments);
};
RRM.Property.Int.prototype = Object.create(RRM.Property.Base.prototype);
RRM.Property.Int.prototype.constructor = RRM.Property.Int;

Don't pay too much attention to these first 5 lines. Just copy them as is and rename them as you wish. If you want to know more about what is happening here, read Introduction to Object-Oriented JavaScript

Now you have a basic property defined, but it doesn't do much interesting yet. It will just pass around the value you put into it and fetch from it as-is. If you want to add some magic, define the transform and/or reverseTransform properties:

Object.defineProperty(RRM.Property.Int.prototype, 'transform', {
    value: function(value) {
        return parseInt(RRM.Property.Base.prototype.transform.call(this, value), 10);
    }
});

(while it is not really necessary to call the base transform method now, it's good practice to do it still to be ready for a BC change in that part.)

Now our Int property will make sure any value passed into it is cast into an integer when the entity data is set. The reverseTransform property functions mainly the same.

You can always call the transform or reverseTransform method of another property definition by calling for example:

Object.defineProperty(RRM.Property.Int.prototype, 'transform', {
    value: function(value) {
        var stringValue = RRM.Property.String.prototype.transform.call(this, value);
        return parseInt(string, 10);
    }
});

Keep in mind: transform is called when setting raw data to the entity (both when loading initially and when setting the property later on). reverseTransform is called for example when you use the ObjectManager.toArray method to convert the Entity back into a simple object.

Defining what property to use as "identifier"

By default, RRM will assume there is a property called id, and will use it as identifier. You can override this by defining an id property on the schema constructor:

Object.defineProperty(ProjectSchema, 'id', { value: 'name' });

Now the name property will be used to identify the projects.

Relations

These are pretty self explanatory, so here's a simple list of supported relations:

RRM.Relation.OneToMany, RRM.Relation.ManyToOne, RRM.Relation.OneToOne, RRM.Relation.ManyToMany

These all support the same options as the simple properties.

The only relation to have a special option is RRM.Relation.OneToMany. It supports a backReference which you can use to tell RRM to populate that specific property of the other side of the relation with the instance itself.

Basically it makes the following example possible, given the mapping and data examples from the top of this documentation:

var project = om.create('project', data);
console.log(project === project.products[0].project); // true

Storing data into the ObjectManager

As shown above you can store data into the ObjectManager using the create() method. If the entity already exists in the ObjectManager, it will update that instance with the new data:

var product = om.create('project', { id: 7, name: 'test' });
console.log(project.name); // "test"

om.create('project', { id: 7, name: 'other test' });
console.log(project.name); // "other test"

Loading your data from the ObjectManager

You can fetch your data from the ObjectManager using either the get() method or the getReference() method. The first one will fail if the object is not loaded yet, the second will create a Proxy object for you (see below).

var project = om.get('project', 1); // Project entity
var project2 = om.get('project', 2); // throws an error

var project = om.getReference('project', 1); // The same Project entity
var project2 = om.getReference('project', 2); // A Proxy object for Project with ID 2

Using the getAll() method you can fetch all the loaded entities of a specific class;

var projects = om.getAll('project');

Internal properties

Every entity is automatically populated with some internal properties.

  • $raw: this contains the raw, unprocessed data that was fed to RRM.
  • $values: all the processed values accessible without touching getters and setters.
  • $dirty: whenever a property is updated by other code than RRM, this is set to true. It is reset to false when toArray is called on the entity.
  • $writeCounter: whenever a property is updated, be it by RRM or by other code, this counter is raised by 1.

Hooks

You can define hooks on your entity object. These hooks will be called during the lifetime of an entity. Just define them as functions on your object prototype or in it's constructor:

  • $postCreate: after the entity is created and loaded with data using om.create(), this function is called
  • $postUpdate: after the entity is updated using om.create(), this function is called

Working with proxies

In the example above we are loading the whole schema (Project and its Products) at once, but this might not always be necessary or possible due to performance issues. Imagine a Project with 1000 Products, which all have to be downloaded through an API. Instead, you can just load a list of ID's. These will then be converted to Proxy objects:

var data = {
  id: 3,
  name: 'My project',
  start: '2014-01-01T10:00:00+00:00'
  products: [ 1, 2, 3, 4, 5, 6 ]
}

var project = om.create('project', data);

Now, when you try to access a property of one of the Products, you will get an error. However, you do have access to the IDS.

console.log(project.products[0].id) // 1
console.log(project.products[0].name) // throws an error

This allows you to lazy load the data of

View on GitHub
GitHub Stars42
CategoryDevelopment
Updated5y ago
Forks3

Languages

JavaScript

Security Score

55/100

Audited on Jan 20, 2021

No findings