Skip to content

emmerge/graviton

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

graviton CircleCI

Graviton

Relations and models for Meteor collections.

Allows you to:

  • Transform your Mongo docs into models with attributes and methods.
  • Define relationships between collections.
  • Traverse relationships and retrieve related models.
  • Use other packages to handle validation (meteor-simple-schema) hooks (meteor-collection-hooks) and relational pub/sub (cottz:publish-relations).

Collections defined with Graviton automatically convert retrieved objects into models. You specify the type(s) when define the collection. Passing {transform: null} to find() etc. will bypass model transformation. The raw document is stored in model.attributes. Use built-in transformation methods like set, push, pop to make changes to your model locally. Call model.save() to persist changes to the database. All methods work on both server and client.

Installation

In a Meteor app directory run:

$ meteor add emmerge:graviton

API Docs

Mongo.Collection.prototype

The following are added to all your meteor collections:

all() - an alias for find().fetch().

build() - returns a new local Gravition.Model based on your collection definition. Does not save to db. The instance can be saved using Model.save().

create() - calls build() to generate a Model instance AND inserts it into the db.

Graviton

###Graviton.define(collectionName, options) Use to define your collections. Returns a Mongo.Collection instantiated with a transform function based on the options passed.

Param Type Description
collectionName String The collection name to define.
[options] Object Options.
[options.persist] boolean Passing false will define a local collection. defaults to true
[options.modelCls] Gravtion.Model constructor or Object A model constructor generated by calling Graviton.Model.extend. Used to transform for objects in the collection. For polymorphism, pass an object that maps _type to model constructor like {key1: ModelClassA, key2: ModelClassB}. collection.build({_type: 'key2'}) would return an instance of ModelClassB.
[options.defaultType] String Use when supplying a object for modelCls. Specify the type to use when object being transformed does not contain _type.
[options.typeAttribute] String Optional key to the type attribute if you want to use something other than the default _type.
[options.timestamps] boolean If true and you have the collection-hooks package installed, createdAt and updatedAt timestamps will be added to your models.
[options.<relationships>] Object DEPRECATED (define relations on the modelCl) - Use to define relationships with other collections. See Relations section below.

###Graviton.getProperty(key) Use to access deeply-nested attributes without worry of throwing errors. Use period-delimited strings as keys. For example:

Gravition.getPropery({}, 'some.deep.nested.prop')

would simply return undefined instead of throwing an error because obj.some is undefined. This method works best with keys that are meant to be stored in mongo since periods are not allowed in them.

###Graviton.setProperty(thing, [value]) Set deeply-nested attributes. Example:

var obj = {};
Graviton.setProperty(obj, 'some.deeply.nested.prop', 'hello');
>> {some: {deeply: {nested: {prop: 'hello'}}}}

You can pass an object instead of [key, value] to set several at once.

###Graviton.isModel(obj) Use to check if an object is a model.

Defining Relations

Singular relation types:

  • belongsTo
  • hasOne
  • embeds

Multiple relation types:

  • belongsToMany
  • hasMany
  • embedsMany
  • hasAndBelongsToMany

Each of these is defined as a configuration object with the following keys/value pairs:

Key Type Description
[collection] Mongo.Collection collection this relation refereneces
[collectionName] or [klass] or [relationName] String the name of another collection defined in Graviton
[field] String the key name on this document
[foreignKey] String used to specify the key name on the foreign document (if not _id)

Pass these as keys in the options Object to Model.extend or Graviton.define to declare relationships with other collections. Example:

CarModel = Graviton.Model.extend({
  belongsTo: {
    owner: {
      klass: 'people',
      foreignKey: 'ownerId'
    }
  },
  belongsToMany: {
    drivers: {
      klass: 'drivers',
      field: 'driverIds'
    }
  },
  hasOne: {
    // note that manufacturer should really be a belongsTo relationship since manufacturer shouldn't have a single carId
    manufacturer: {
      klass: 'manufacturers',
      foreignKey: 'carId'
    }
  },
  hasMany: {
    wheels: {
      klass: 'wheels',
      foreignKey: 'carId'
    }
  },
  embeds: {
    plate: {
      klass: 'plates'
    }
  },
  embedsMany: {
    windows: {
      klass: 'windows'
    }
  }
},{});

Car = Graviton.define("cars", {
  modelCls: CarModel
});

Would make the following possible:

var car = Car.findOne();
car.owner(); // returns a model
car.owner(person); // sets the ownerId of person

car.wheels.find(); // returns a cursor. same as Wheel.find({carId: car._id})
car.wheels.add({}); // insert a document into the wheels collection with carId = car._id

car.drivers.find(); // returns a cursor. same as Driver.find({_id: {$in: car.get('driverIds')}})

car.manufacturer();
car.plate();

// embedded models don't have the same finder capability since they aren't kept in minimongo
car.windows.all(); // returns all models
car.windows.at(2); // only builds one model

Relations to Meteor.users collection

You can have relations to the Meteor.users collection (added by Meteor built in package accounts-base). You can choose to make relations directly to the collection like this:

hasMany: {
  users: {
    collection: Meteor.users,
    foreignKey: 'foreignId'
  }
}

You can also define or register 'users' in Graviton. Graviton.define() treats 'users' as a special collection and backs it with Meteor.users.

Graviton.define('users', options);

or

Graviton.registerCollection(Meteor.users);

Then the following configuration is valid:

hasMany: {
  users: {
    collectionName: 'users',
    foreignKey: 'foreignId'
  }
}

Note: Unlike other Graviton defined collections the special 'user' collection defined in Graviton will not tranform results into models by default. This is also true for user objects returned from relations. This is done for maximum compatability with Meteor itself and other external pacakges. The following are examples of how to get a user Model.

GravitonUsers = Graviton.define('users', options);
GravitonUsers.build(Meteor.user());

// or from a relation
GravitonUsers.build(Chats.findOne().user());

Graviton.Model.extend

Graviton transforms collection objects into Models for you. This allows them to carry useful metadata and functions with the data. The vanilla Graviton.Model allows for basic functionality. Defining an extension of the Graviton.Model allows you to specify details of the collection's relationships or other custom functionality.

###Graviton.Model.extend(options, extensionPrototype) Use to create a model constructor.

Param Type Description
[options] Object options
[options.defaults] Object an object containing default key:value pairs for the collection. These key:values will be added to all model instances where there is not already a stored value with the same key. Functions should not be placed here as stored records cannot have functions as values.
[options.initialize] Function a function which will be run on initialization.
[options.<relationships>] Object define how this model relates to other collections. belongsTo, belongsToMany, hasOne, hasMany, etc.
[extensionPrototype] Object This object contains the extension prototype. This is the place to add functions to the model. Values could also be placed here if they relate to this specific model. These do not behave as attributes - any values placed here will not be stored.

##Model manipulation

Model.get(key)

Model.set(thing, value)

Model.inc(thing, increment)

Model.push(thing, value)

Model.pop(thing, value)

Model.shift(thing, value)

Model.addToSet(thing, value)

##Model database interactions

Model.persist(callback)

Model.update(modifier, callback)

Model.save(callback)

Model.remove(callback)

Examples

full app example

Graviscope - a graviton version of the Microscope app built in the book Discover Meteor

Working with relations

Model.hasMany.add() example (simplified from graviscope)

PostModel = Graviton.Model.extend({
  hasMany: {
    comments: {
      collection: 'comments',
      foreignKey: 'postId'
    }
  }
},{};

Posts = Graviton.define('posts', {
  modelCls: PostModel
});
post = Posts.findOne();
comment = Comments.build();
comment.set({author:'Steve', body:'Check out the Posts.hasMany.comments relationship.'})
post.comments.add(comment);
console.log('Newly created comment id', comment._id);

belongsTo examples (simplified from graviscope)

Posts = Graviton.define('posts', {
  hasMany: {
    comments: {
      collection: 'comments',
      foreignKey: 'postId'
    }
  }
});

Comments = Graviton.define('comments', {
  belongsTo: {
    post: {
      collection: 'posts',
      field: 'postId'
    }
  }
})
post = Posts.findOne();
comment = Comments.build();
comment.set({author:'Steve', body:'Check out the Posts.hasMany.comments relationship.'})
post.comments.add(comment);
comment.post()
// return  Posts object.
console.log('It will return same _id :',post._id,comment.post()._id)