8
0
Эх сурвалжийг харах

Introduced NamedCollection.

Piotrek Koszuliński 10 жил өмнө
parent
commit
bc7c6afb92

+ 2 - 0
packages/ckeditor5-utils/src/collection.js

@@ -8,6 +8,8 @@
 /**
  * Collections are ordered sets of models.
  *
+ * See also {@link core/NamedCollection}.
+ *
  * @class Collection
  * @mixins EventEmitter
  */

+ 155 - 0
packages/ckeditor5-utils/src/namedcollection.js

@@ -0,0 +1,155 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+/**
+ * Named collections are key => model maps.
+ *
+ * See also {@link core/Collection}.
+ *
+ * @class NamedCollection
+ * @mixins EventEmitter
+ */
+
+CKEDITOR.define( [ 'emittermixin', 'ckeditorerror', 'utils' ], function( EmitterMixin, CKEditorError, utils ) {
+	class NamedCollection {
+		/**
+		 * Creates a new NamedCollection instance.
+		 *
+		 * @constructor
+		 */
+		constructor() {
+			/**
+			 * The internal map of models in the collection.
+			 *
+			 * @property _models
+			 * @private
+			 */
+			this._models = Object.create( null );
+		}
+
+		/**
+		 * The number of items available in the collection.
+		 *
+		 * @property length
+		 */
+		get length() {
+			return Object.keys( this._models ).length;
+		}
+
+		/**
+		 * Adds an item into the collection.
+		 *
+		 * Throws exception if an item with this name already exists or if the item does not have a name.
+		 *
+		 * @param {Model} model The item to be added.
+		 */
+		add( model ) {
+			var name = model.name;
+
+			if ( !name || ( name in this._models ) ) {
+				/**
+				 * Model isn't named or such model already exists in this collection
+				 *
+				 * Thrown when:
+				 *
+				 * * Model without a name was given.
+				 * * Model with this name already exists in the collection.
+				 *
+				 * @error namedcollection-add
+				 * @param {String} name Name of the model.
+				 */
+				throw new CKEditorError(
+					'namedcollection-add: Model isn\'t named or such model already exists in this collection',
+					{ name: name }
+				);
+			}
+
+			this._models[ name ] = model;
+
+			this.fire( 'add', model );
+		}
+
+		/**
+		 * Gets one item from the collection.
+		 *
+		 * @param {String} name The name of the item to take.
+		 * @returns {Model} The requested item.
+		 */
+		get( name ) {
+			var model = this._models[ name ];
+
+			if ( !model ) {
+				/**
+				 * Model not found.
+				 *
+				 * @error namedcollection-get
+				 * @param {String} name Name of the model.
+				 */
+				throw new CKEditorError( 'namedcollection-get: Model not found.', { name: name } );
+			}
+
+			return model;
+		}
+
+		/**
+		 * Removes an item from the collection.
+		 *
+		 * @param {Model|String} modelOrName Either the item itself (it must have a `name` property)
+		 * or its name inside the collection.
+		 * @returns {Model} The removed item.
+		 */
+		remove( modelOrName ) {
+			var nameGiven = typeof modelOrName == 'string';
+			var name = nameGiven ? modelOrName : modelOrName.name;
+			var model = this._models[ name ];
+
+			if ( nameGiven ? !model : ( model !== modelOrName ) ) {
+				/**
+				 * Model not found or other model exists under its name.
+				 *
+				 * Thrown when:
+				 *
+				 * * a model without a name was given,
+				 * * no model found when a name was given,
+				 * * a model was given and it does not exist in the collection or some other model was found under its name.
+				 *
+				 * @error namedcollection-remove
+				 * @param {String} name Name of the model to remove.
+				 * @param {Model} model The model which was found under the given name.
+				 */
+				throw new CKEditorError(
+					'namedcollection-remove: Model not found or other model exists under its name.',
+					{ name: name, model: model }
+				);
+			}
+
+			delete this._models[ name ];
+
+			this.fire( 'remove', model );
+
+			return model;
+		}
+	}
+
+	utils.extend( NamedCollection.prototype, EmitterMixin );
+
+	return NamedCollection;
+} );
+
+/**
+ * Fired when an item is added to the collection.
+ *
+ * @event add
+ * @param {Model} model The added item.
+ */
+
+/**
+ * Fired when an item is removed from the collection.
+ *
+ * @event remove
+ * @param {Model} model The removed item.
+ */

+ 187 - 0
packages/ckeditor5-utils/tests/mvc/collection/namedcollection.js

@@ -0,0 +1,187 @@
+/**
+ * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+'use strict';
+
+var modules = bender.amd.require( 'namedcollection', 'model', 'ckeditorerror' );
+
+describe( 'add', function() {
+	it( 'changes the length and enables get', function() {
+		var box = getCollection();
+
+		expect( box ).to.have.length( 0 );
+
+		var item = getItem( 'foo' );
+		box.add( item );
+
+		expect( box ).to.have.length( 1 );
+
+		expect( box.get( 'foo' ) ).to.equal( item );
+	} );
+
+	it( 'fires the "add" event', function() {
+		var spy = sinon.spy();
+
+		var box = getCollection();
+		box.on( 'add', spy );
+
+		var item = getItem( 'foo' );
+		box.add( item );
+
+		sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', box ), item );
+	} );
+
+	it( 'throws an error if model is not named', function() {
+		var Model = modules.model;
+		var CKEditorError = modules.ckeditorerror;
+		var box = getCollection();
+
+		expect( box ).to.have.length( 0 );
+
+		var item = new Model();
+
+		expect( function() {
+			box.add( item );
+		} ).to.throw( CKEditorError, /^namedcollection-add/ );
+	} );
+
+	it( 'throws an error if some model already exists under the same name', function() {
+		var CKEditorError = modules.ckeditorerror;
+		var box = getCollection();
+
+		expect( box ).to.have.length( 0 );
+
+		box.add( getItem( 'foo' ) );
+
+		expect( function() {
+			box.add( getItem( 'foo' ) );
+		} ).to.throw( CKEditorError, /^namedcollection-add/ );
+	} );
+} );
+
+describe( 'get', function() {
+	it( 'should throw an error on invalid name', function() {
+		var CKEditorError = modules.ckeditorerror;
+		var box = getCollection();
+
+		box.add( getItem( 'foo' ) );
+
+		expect( function() {
+			box.get( 'bar' );
+		} ).to.throw( CKEditorError, /^namedcollection-get/ );
+	} );
+} );
+
+describe( 'remove', function() {
+	it( 'should remove the model by name', function() {
+		var box = getCollection();
+		var item = getItem( 'foo' );
+
+		box.add( item );
+
+		expect( box ).to.have.length( 1 );
+
+		box.remove( 'foo' );
+
+		expect( box ).to.have.length( 0 );
+	} );
+
+	it( 'should remove the model by model', function() {
+		var box = getCollection();
+		var item = getItem( 'foo' );
+
+		box.add( item );
+
+		expect( box ).to.have.length( 1 );
+
+		box.remove( item );
+
+		expect( box ).to.have.length( 0 );
+	} );
+
+	it( 'should fire the "remove" event', function() {
+		var box = getCollection();
+		var item1 = getItem( 'foo' );
+		var item2 = getItem( 'bar' );
+
+		box.add( item1 );
+		box.add( item2 );
+
+		var spy = sinon.spy();
+
+		box.on( 'remove', spy );
+
+		box.remove( 'foo' ); // by name
+		box.remove( item2 ); // by model
+
+		sinon.assert.calledTwice( spy );
+		sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', box ), item1 );
+		sinon.assert.calledWithExactly( spy, sinon.match.has( 'source', box ), item2 );
+	} );
+
+	it( 'should throw an error if model is not named', function() {
+		var CKEditorError = modules.ckeditorerror;
+		var Model = modules.model;
+		var box = getCollection();
+
+		expect( function() {
+			box.remove( new Model() );
+		} ).to.throw( CKEditorError, /^namedcollection-remove/ );
+	} );
+
+	it( 'should throw an error if model does not exist (by name)', function() {
+		var CKEditorError = modules.ckeditorerror;
+		var box = getCollection();
+
+		expect( function() {
+			box.remove( 'foo' );
+		} ).to.throw( CKEditorError, /^namedcollection-remove/ );
+	} );
+
+	it( 'should throw an error if model does not exist (by model)', function() {
+		var CKEditorError = modules.ckeditorerror;
+		var box = getCollection();
+
+		expect( function() {
+			box.remove( getItem( 'foo' ) );
+		} ).to.throw( CKEditorError, /^namedcollection-remove/ );
+	} );
+
+	it( 'should throw an error if model does not exist (by model)', function() {
+		var CKEditorError = modules.ckeditorerror;
+		var box = getCollection();
+
+		expect( function() {
+			box.remove( getItem( 'foo' ) );
+		} ).to.throw( CKEditorError, /^namedcollection-remove/ );
+	} );
+
+	it( 'should throw an error if a different model exists under the same name', function() {
+		var CKEditorError = modules.ckeditorerror;
+		var box = getCollection();
+		var item = getItem( 'foo' );
+
+		box.add( item );
+
+		expect( function() {
+			box.remove( getItem( 'foo' ) );
+		} ).to.throw( CKEditorError, /^namedcollection-remove/ );
+	} );
+} );
+
+function getCollection() {
+	var NamedCollection = modules.namedcollection;
+
+	return new NamedCollection();
+}
+
+function getItem( name ) {
+	var Model = modules.model;
+
+	var model = new Model();
+	model.name = name;
+
+	return model;
+}