Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
backbone.composite-model
Advanced tools
Supports composite Backbone.Model
objects which represent a "master"
model containing "slave" models or collections maintained automatically
according to the composite model configuration
Let's have a versioned file-system model: folders containing files, files consisting of versions:
A JSON object representing a file would look like this:
{
"id": ..., // unique ID of the file
"name": '...', // display name of the file
"parent": {...}, // object describing the parent folder
"versions": [...] // array of file version description objects
}
Modelling it with a flat Backbone.Model
would look like this:
var FileModel = Backbone.Model.extend({
urlRoot: '/files'
});
Backbone events for attribute changes in models and model additions / removals in collections work only for "flat" models; only for first-level attributes of the main model:
// Declare a model representing a file information
var file = new FileModel({id: 1});
// Inform whenever the file information has been fetched and is ready
file.on('sync', function (file) {
console.log('File information ready for', file.get('name'));
});
// Inform whenever the parent folder of the current file has changed
// THIS DOES NOT WORK: watching 'parent.id'
file.on('change:parent.id', function (parent) {
console.log('Location changed to', parent.get('name'));
});
// Fetch information about the initial file - only the first event above
// will be triggered; the second will be never triggered
file.fetch();
We would not be able to pass the parent or version information alone to some view and let it refreshed as another file would be fetched, for example.
If the parent
and versions
child objects should be exposed like real
Backbone objects, to be able to work with their events in controllers
and views, associated objects can be created and maintained whenever the
"master" model changes, for example:
var FolderModel = Backbone.Model.extend({...}),
VersionModel = Backbone.Model.extend({...}),
VersionCollection = Backbone.Collection.extend({
model: VersionModel,
...
}),
FileModel = Backbone.Model.extend({
initialize: function (attributes, options) {
// Initialize the child models and collections
attributes || (attributes = {});
this.parent = new FolderModel(attributes.parent, options);
this.versions = new VersionCollection(attributes.versions, options);
// Whenever the "master" model is re-fetched, update the child ones
this.on('sync', function (model, response, options) {
this.parent.set(this.attributes.parent, options);
this.versions.reset(this.attributes.versions, options);
}, this);
},
urlRoot: '/files'
});
Accessing the child models or collections is possible using the Backbone interface, including the change events:
// Declare a model representing a file information
var file = new FileModel({id: 1});
// Inform whenever the file information has been fetched and is ready
file.on('sync', function (file) {
console.log('File information ready for', file.get('name'));
});
// Inform whenever the parent folder of the current file has changed
// THIS WORKS NOW: watching 'id' of the child model `file.parent`
file.parent.on('change:id', function (parent) {
console.log('Location changed to', parent.get('name'));
});
// Fetch information about the initial file - both events above will be
// triggered
file.fetch();
// Fetch information about another file - the first event will be always
// triggered, the second one will be triggered only if the new file has
// a different parent folder than the previous one
file.set('id', 2)
.fetch();
Modelling the same scenario with the help of the Backbone.CompositeModel
would look like this:
var FileModel = Backbone.Model.extend({
// Declare what attributes map to what child models or collections
composite: {
parent: FolderModel,
versions: VersionCollection
},
initialize: function (attributes, options) {
// Initialize the child models and collections
this.makeComposite(options);
},
urlRoot: '/files'
});
// Extend the prototype with methods managing the child models and collections
Backbone.mixinCompositeModel(FileModel.prototype);
The FileModel
above will have the child objects parent
and versions
maintained automatically, whenever they change in the "master" model.
The Backbone.CompositeModel
offers a common implementation of the so-called
"master-slave" or "parent-child" model/collection pattern, that propagates
the changes caused by set
, unset
, clear
, fetch
and save
methods on
the "master" model to the "slave" models and/or collections, which are fully
owned by the "master" model, including their creation.
Child models are supposed to be created from object literals:
Child collections are supposed to be created from arrays:
Initialization of a composite model should include the following parts:
composite
configuration object as a property in the
prototype or in the new instance initialization options
makeComposite
method from the constructor
or from the
initialize
methodBackbone.mixinCompositeModel
methodvar MasterModel = Backbone.Model.extend({
// Declare what attributes map to what child models or collections
composite: {
child: SlaveModel,
},
initialize: function (attributes, options) {
// Initialize the child models and collections
this.makeComposite(options);
},
...
});
// Extend the prototype with methods managing the child models and collections
Backbone.mixinCompositeModel(MasterModel.prototype);
The composite
configuration object maps an attribute name to the "slave"
model specification:
'<attribute name>': <"slave" model specification>
The attribute name has to exist in the this.attributes
of the "master"
model and will back up the "slave" model or collection. The property on the
"master" model will be created using the attribute name by default.
The "slave" model specification can be either a Backbone.Model
descendant for "slave" models or a Backbone.Collection
descendant for
"slave" collections. It will be the function object to create the "slave"
model or collection from:
'<attribute name>': Backbone.Model | Backbone.Collection
The default creation and maintenance of the "slave" models and collections can be overridden by passing an object literal as the "slave" model specification:
'<attribute name>': {
type: Backbone.Model | Backbone.Collection,
...
}
The following properties of the "slave" model specification object are supported:
type
: Backbone.Model
descendant for "slave" models or a
Backbone.Collection
descendant for "slave" collections (required)property
: Property name to store the "slave" model or collection on the
"master" model with (optional; the attribute name is the default)options
: Additional options to pass to the constructor of the "slave"
model or collection (optional; undefined by default)method
: Method to call on the "slave" model or collection if updated data
are being set (optional; set
is the default for models, add
for
collections)parse
: Function to call before the value is passed to child model or
collection constructor or to the set
/ add
method to "massage" the
input data (optional; undefined
is the default)Maintain a parent
model based on the this.attributes.parent
object
and a versions
collection based on the this.attributes.versions
array
from the composite model:
composite: {
parent: FolderModel,
versions: VersionCollection
}
Name the property on the composite model "parent", although the backing up property is called "parent_expanded":
composite: {
parent_expanded: {
type: FolderModel,
property: 'parent'
}
}
Ensure that the "slave" model has always a property child_id
with the value
of the id
property of the "master" model:
composite: {
parent: {
type: FolderModel,
parse: function (attributes, options) {
var id = this.get('id');
if (id != null) {
attributes || (attributes = {});
attributes.child_id = id;
}
return attributes;
}
}
}
Use the reset
method to populate the "slave" collection instead of the
add
method, which is used by default:
composite: {
versions: {
type: VersionCollection,
method: 'reset'
}
}
set
method, propagate the change
to the child model or collectionYou can get the up-to-date content of all nested models and collections by
calling toJSON
of the master model.
Instead of updating attributes
of the master model directly, you can
consider calling set/unset/clear/add/remove/reset
methods of nested models
and collections. You will be able to get the up-to-date content of all nested
models and collections by calling toJSON
of the master model.
Make sure that you have NodeJS >= 6 installed. You can use either npm
or bower
to install this package and its dependencies.
With NPM:
npm install backbone.composite-model
With Bower:
bower install backbone.composite-model
Make sure that you have NodeJS >= 6 installed. Clone the Github
repository to a local directory, enter it and install the package
dependencies (including the development dependencies) by npm
:
git clone https://github.com/prantlf/backbone.composite-model.git
cd backbone.composite-model
npm install
Examples and tests will be functional now.
In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality.
Before you start, make sure that you have satisfied native dependencies of the node-canvas module, which are described for every operating system at the documentation wiki of the project.
First fork this repository and clone your fork locally instead of cloning the original. See the "Build" chapter above for more details about how to clone it and install the build dependencies.
Before you commit, update minified files and source maps, re-generate documentation and check if tests succeed:
npm run-script build
npm run-script doc
npm test
Commit your changes to a separtate branch, so that you can create a pull request for it:
git checkout -b <branch name>
git commit -a
git push origin <branch name>
Copyright (c) 2015-2022 Ferdinand Prantl
Licensed under the MIT license.
1.0.4 (2022-01-16)
FAQs
Supports composite Backbone.Model objects
We found that backbone.composite-model demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.