Skip to content

Commit

Permalink
Merge pull request #44 from bem-sdk/refactor-type
Browse files Browse the repository at this point in the history
refactor(type): not use `naming.typeOf()`
  • Loading branch information
blond committed Aug 4, 2016
2 parents 637ef11 + cde22b9 commit bc59aae
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 82 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.nyc_output
node_modules
coverage
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
.nyc_output
node_modules
coverage
.nyc_output
103 changes: 60 additions & 43 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,46 @@

const util = require('util');

const naming = require('bem-naming');

const stringifyEntity = naming.stringify;
const typeOfEntity = naming.typeOf;
const stringifyEntity = require('bem-naming').stringify;

/**
* Enum for types of BEM entities.
*
* @readonly
* @enum {String}
*/
const TYPES = {
BLOCK: 'block',
BLOCK_MOD: 'blockMod',
ELEM: 'elem',
ELEM_MOD: 'elemMod'
};

module.exports = class BemEntityName {
/**
* @param {[object]} obj — representation of entity name.
* @param {[string]} obj.block — the block name of entity.
* @param {[string]} [obj.elem] — the element name of entity.
* @param {[object]} [obj.mod] — the modifier of entity.
* @param {[string]} [obj.mod.name] — the modifier name of entity.
* @param {[string]} [obj.mod.val] — the modifier value of entity.
* @param {object} obj — representation of entity name.
* @param {string} obj.block — the block name of entity.
* @param {string} [obj.elem] — the element name of entity.
* @param {object} [obj.mod] — the modifier of entity.
* @param {string} [obj.mod.name] — the modifier name of entity.
* @param {string} [obj.mod.val] — the modifier value of entity.
*/
constructor(obj) {
if (!obj.block) {
throw new Error('This is not valid BEM entity: the field `block` is undefined.');
}

this._obj = { block: obj.block };
obj.elem && (this._obj.elem = obj.elem);

const data = this._data = { block: obj.block };
const modName = (typeof obj.mod === 'string' ? obj.mod : obj.mod && obj.mod.name) || obj.modName;

obj.elem && (data.elem = obj.elem);

if (modName) {
const modVal = obj.hasOwnProperty('modVal') || obj.mod && obj.mod.hasOwnProperty('val')
? obj.mod && obj.mod.val || obj.modVal
: true;

this._obj.mod = {
data.mod = {
name: modName,
val: modVal
};
Expand All @@ -40,40 +50,43 @@ module.exports = class BemEntityName {
/**
* Returns the name of block to which this entity belongs.
*
* @returns {string} name of entity block.
* @example
* const BemEntityName = require('bem-entity-name');
* const name = new BemEntityName({ block: 'button' });
*
* console.log(name.block); // button
*
* @returns {string} name of entity block.
*/
get block() { return this._obj.block; }
get block() { return this._data.block; }
/**
* Returns the element name of this entity.
*
* If entity is not element or modifier of element then returns empty string.
*
* @returns {string} name of entity element.
* @example
* const BemEntityName = require('bem-entity-name');
* const name = new BemEntityName({ block: 'button', elem: 'text' });
*
* console.log(name.elem); // text
*
* @returns {string} name of entity element.
*/
get elem() { return this._obj.elem; }
get elem() { return this._data.elem; }
/**
* Returns the modifier of this entity.
*
* If entity is not modifier then returns empty object.
*
* @returns {object} entity modifier.
* @example
* const BemEntityName = require('bem-entity-name');
* const name = new BemEntityName({ block: 'button', mod: 'disabled' });
*
* console.log(name.mod); // { name: 'disabled', val: true }
*
* @returns {{mod: string, val: *}} entity modifier.
*/
get mod() { return this._obj.mod || {}; }
get mod() { return this._data.mod || {}; }
/**
* Returns the modifier name of this entity.
*
Expand All @@ -100,17 +113,18 @@ module.exports = class BemEntityName {
* If you want to get string representation in accordance with the provisions naming convention
* you should use `bem-naming` package.
*
* @returns {string} id of entity.
* @example
* const BemEntityName = require('bem-entity-name');
* const name = new BemEntityName({ block: 'button', mod: 'disabled' });
*
* console.log(name.id); // button_disabled
*
* @returns {string} id of entity.
*/
get id() {
if (this._id) { return this._id; }

const entity = { block: this._obj.block };
const entity = { block: this._data.block };

this.elem && (entity.elem = this.elem);
this.mod.name && (entity.modName = this.mod.name);
Expand All @@ -123,7 +137,6 @@ module.exports = class BemEntityName {
/**
* Returns type for this entity.
*
* @returns {string} type of entity.
* @example <caption>type of element</caption>
* const BemEntityName = require('bem-entity-name');
* const name = new BemEntityName({ block: 'button', elem: 'text' });
Expand All @@ -134,17 +147,18 @@ module.exports = class BemEntityName {
* const name = new BemEntityName({ block: 'menu', elem: 'item', mod: 'current' });
*
* console.log(name.type); // elemMod
*
* @returns {string} type of entity.
*/
get type() {
if (this._type) { return this._type; }

const entity = { block: this._obj.block };

this.elem && (entity.elem = this.elem);
this.mod.name && (entity.modName = this.mod.name);
this.mod.val && (entity.modVal = this.mod.val);
const data = this._data;
const isMod = data.mod;

this._type = typeOfEntity(entity);
this._type = data.elem
? isMod ? TYPES.ELEM_MOD : TYPES.ELEM
: isMod ? TYPES.BLOCK_MOD : TYPES.BLOCK;

return this._type;
}
Expand All @@ -154,12 +168,13 @@ module.exports = class BemEntityName {
* Important: If you want to get string representation in accordance with the provisions naming convention
* you should use `bem-naming` package.
*
* @returns {string}
* @example
* const BemEntityName = require('bem-entity-name');
* const name = new BemEntityName({ block: 'button' });
*
* console.log(`name: ${name}`); // button
* console.log(`name: ${name}`); // name: button
*
* @returns {string}
*/
toString() { return this.id; }
/**
Expand All @@ -171,14 +186,15 @@ module.exports = class BemEntityName {
* The representation object contains only `block`, `elem` and `mod` fields
* without private and deprecated fields (`modName` and `modVal`).
*
* @returns {object}
* @example
* const BemEntityName = require('bem-entity-name');
* const name = new BemEntityName({ block: 'button' });
*
* console.log(name); // { block: 'button' }
*
* @returns {{block: string, elem: ?string, mod: ?{name: ?string, val: *}}}
*/
valueOf() { return this._obj; }
valueOf() { return this._data; }
/**
* Returns object representing the entity name. Is needed for debug in Node.js.
*
Expand All @@ -188,26 +204,27 @@ module.exports = class BemEntityName {
* The representation object contains only `block`, `elem` and `mod` fields
* without private and deprecated fields (`modName` and `modVal`).
*
* @param {integer} depth — tells inspect how many times to recurse while formatting the object.
* @param {object} options — An optional `options` object may be passed
* that alters certain aspects of the formatted string.
*
* @returns {object}
* @example
* const BemEntityName = require('bem-entity-name');
* const name = new BemEntityName({ block: 'button' });
*
* console.log(name); // { block: 'button' }
* console.log(name); // BemEntityName { block: 'button' }
*
* @param {integer} depth — tells inspect how many times to recurse while formatting the object.
* @param {object} options — An optional `options` object may be passed
* that alters certain aspects of the formatted string.
*
* @returns {string}
*/
inspect(depth, options) {
const stringRepresentation = util.inspect(this._obj, options);
const stringRepresentation = util.inspect(this._data, options);

return `BemEntityName ${stringRepresentation}`;
}
/**
* Determines whether specified entity is the deepEqual entity.
*
* @param {object} entity - the entity to compare.
* @param {BemEntityName} entityName - the entity to compare.
*
* @returns {boolean} A Boolean indicating whether or not specified entity is the deepEqual entity.
* @example
Expand All @@ -219,7 +236,7 @@ module.exports = class BemEntityName {
* console.log(inputName.isEqual(buttonName)); // false
* console.log(buttonName.isEqual(buttonName)); // true
*/
isEqual(entity) {
return entity && (this.id === entity.id);
isEqual(entityName) {
return entityName && (this.id === entityName.id);
}
};
61 changes: 23 additions & 38 deletions test/type.test.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,43 @@
const test = require('ava');
const sinon = require('sinon');
const proxyquire = require('proxyquire');

test.beforeEach('setup', t => {
t.context.stub = sinon.stub().returns('type');
t.context.BemEntityName = proxyquire('../index', {
'bem-naming': {
typeOf: t.context.stub
}
});
});

test('should use `naming.typeOf()` for block', t => {
const entity = new t.context.BemEntityName({ block: 'block' });
const BemEntityName = require('../index');

/*eslint no-unused-expressions: "off"*/
entity.type;
test('should determine block', t => {
const entityName = new BemEntityName({ block: 'block' });

t.truthy(t.context.stub.calledWith({ block: 'block' }));
t.is(entityName.type, 'block');
});

test('should use `naming.typeOf()` for elem', t => {
const entity = new t.context.BemEntityName({ block: 'block', elem: 'elem' });

/*eslint no-unused-expressions: "off"*/
entity.type;
test('should determine modifier of block', t => {
const entityName = new BemEntityName({ block: 'block', mod: 'mod' });

t.truthy(t.context.stub.calledWith({ block: 'block', elem: 'elem' }));
t.is(entityName.type, 'blockMod');
});

test('should use `naming.typeOf()` for block modifier', t => {
const entity = new t.context.BemEntityName({ block: 'block', modName: 'mod', modVal: 'val' });
test('should determine elem', t => {
const entityName = new BemEntityName({ block: 'block', elem: 'elem' });

t.is(entityName.type, 'elem');
});

/*eslint no-unused-expressions: "off"*/
entity.type;
test('should determine modifier of element', t => {
const entityName = new BemEntityName({ block: 'block', elem: 'elem', mod: 'mod' });

t.truthy(t.context.stub.calledWith({ block: 'block', modName: 'mod', modVal: 'val' }));
t.is(entityName.type, 'elemMod');
});

test('should use naming.typeOf() for element modifier', t => {
const entity = new t.context.BemEntityName({ block: 'block', elem: 'elem', modName: 'mod', modVal: 'val' });
test('should cache type value', t => {
const entity = new BemEntityName({ block: 'block' });

/*eslint no-unused-expressions: "off"*/
entity.type;
entity.type; // eslint-disable-line no-unused-expressions

t.truthy(t.context.stub.calledWith({ block: 'block', elem: 'elem', modName: 'mod', modVal: 'val' }));
t.is(entity._type, 'block');
});

test('should cache type value', t => {
const entity = new t.context.BemEntityName({ block: 'block' });
test('should get type from cache', t => {
const entity = new BemEntityName({ block: 'block' });

/*eslint no-unused-expressions: "off"*/
entity.type;
entity.type;
entity._type = 'fake';

t.is(t.context.stub.callCount, 1);
t.is(entity.type, 'fake');
});

0 comments on commit bc59aae

Please sign in to comment.