From ff6ee351b24c81171b2c52af2c4b775aab1973a9 Mon Sep 17 00:00:00 2001 From: Stephen Mizell Date: Thu, 18 Jun 2015 14:30:43 -0500 Subject: [PATCH 1/7] Allow set to take an object --- lib/base.js | 21 ++++++++++++++++++--- test/primitives-test.js | 6 ++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/base.js b/lib/base.js index ba9aae6f..dbbb9244 100644 --- a/lib/base.js +++ b/lib/base.js @@ -781,13 +781,28 @@ var ObjectElement = Collection.extend({ return undefined; }, - set: function(name, value) { - var member = this.getMember(name); + /* + * Set allows either a key/value pair to be given or an object + * If an object is given, each key is set to its respective value + */ + set: function(keyOrObject, value) { + if (_.isObject(keyOrObject)) { + var self = this; + _.each(_.keys(keyOrObject), function(key) { + self.set(key, keyOrObject[key]); + }); + + return this; + } + + // Store as key for clarity + var key = keyOrObject; + var member = this.getMember(key); if (member) { member.value = value; } else { - this.content.push(new MemberElement(name, value)); + this.content.push(new MemberElement(key, value)); } return this; diff --git a/test/primitives-test.js b/test/primitives-test.js index be959e8d..c097c5e3 100644 --- a/test/primitives-test.js +++ b/test/primitives-test.js @@ -1044,6 +1044,12 @@ describe('Minim Primitives', function() { objectElement.set('bar', 'hello world'); expect(objectElement.get('bar').toValue()).to.equal('hello world'); }); + + it('accepts an object', function() { + var obj = new minim.ObjectElement(); + obj.set({ foo: 'bar' }); + expect(obj.get('foo').toValue()).to.equal('bar'); + }); }); describe('#keys', function() { From 0f520b10d782a475d3416c3a57185bb5d2389558 Mon Sep 17 00:00:00 2001 From: Stephen Mizell Date: Thu, 18 Jun 2015 16:12:09 -0500 Subject: [PATCH 2/7] Convert meta to Minim object element --- lib/base.js | 134 +++++++++++----------------------------- test/primitives-test.js | 14 ++--- test/subclass-test.js | 2 +- 3 files changed, 45 insertions(+), 105 deletions(-) diff --git a/lib/base.js b/lib/base.js index dbbb9244..5d33895f 100644 --- a/lib/base.js +++ b/lib/base.js @@ -113,85 +113,6 @@ var ElementRegistry = createClass({ // Initiate a default Minim registry var registry = new ElementRegistry(); -var Meta = createClass({ - constructor: function(meta) { - var self = this; - self.meta = meta || {}; - - _.keys(meta).forEach(function(key) { - self.meta[key] = registry.toElement(meta[key]); - }); - }, - - toObject: function() { - var self = this; - var meta = {}; - - _.forEach(_.keys(self.meta), function(key) { - meta[key] = self.meta[key].toValue(); - }); - - return meta; - }, - - getProperty: function(name, value) { - if (!this.meta[name]) { - this.meta[name] = registry.toElement(value); - } - - return this.meta[name]; - }, - - setProperty: function(name, value) { - this.meta[name] = registry.toElement(value); - } -}, {}, { - id: { - get: function() { - return this.getProperty('id', ''); - }, - set: function(element) { - this.setProperty('id', element); - } - }, - - class: { - get: function() { - return this.getProperty('class', []); - }, - set: function(element) { - this.setProperty('class', element); - } - }, - - name: { - get: function() { - return this.getProperty('name', ''); - }, - set: function(element) { - this.setProperty('name', element); - } - }, - - title: { - get: function() { - return this.getProperty('title', ''); - }, - set: function(element) { - this.setProperty('title', element); - } - }, - - description: { - get: function() { - return this.getProperty('description', ''); - }, - set: function(element) { - this.setProperty('description', element); - } - } -}); - /* * BaseElement is the base element from which all other elements are built. * It has no specific information about how to handle the content, but is @@ -200,7 +121,12 @@ var Meta = createClass({ BaseElement = createClass({ constructor: function(content, meta, attributes) { - this.meta = new Meta(meta || {}); + // Lazy load this.meta because it's a Minim element + // Otherwise, we get into circuluar calls + if (meta) { + this.meta.set(meta); + } + this.attributes = attributes || {}; this.content = content || null; this._attributeElementKeys = []; @@ -218,7 +144,7 @@ BaseElement = createClass({ var attributes = this.convertAttributesToRefract('toRefract'); var initial = { element: this.element, - meta: this.meta.toObject(), + meta: this.meta.toValue(), attributes: attributes, content: this.content }; @@ -227,7 +153,7 @@ BaseElement = createClass({ toCompactRefract: function() { var attributes = this.convertAttributesToRefract('toCompactRefract'); - return [this.element, this.meta.toObject(), attributes, this.content]; + return [this.element, this.meta.toValue(), attributes, this.content]; }, /* @@ -267,7 +193,7 @@ BaseElement = createClass({ }, fromRefract: function(doc) { - this.meta = new Meta(doc.meta); + this.meta = doc.meta; this.attributes = doc.attributes; this.content = doc.content; @@ -283,7 +209,7 @@ BaseElement = createClass({ }, fromCompactRefract: function(tuple) { - this.meta = new Meta(tuple[1]); + this.meta = tuple[1]; this.attributes = tuple[2]; this.content = tuple[3]; @@ -308,15 +234,15 @@ BaseElement = createClass({ }, getMetaProperty: function(name, value) { - if (!this.meta[name]) { - this.meta[name] = registry.toElement(value); + if (!this.meta.hasKey(name)) { + this.meta.set(name, value); } - return this.meta[name]; + return this.meta.get(name); }, setMetaProperty: function(name, value) { - this.meta[name] = registry.toElement(value); + this.meta.set(name, value); } }, {}, { element: { @@ -329,6 +255,19 @@ BaseElement = createClass({ } }, + meta: { + get: function() { + if (!this._meta) { + this._meta = registry.toElement({}); + } + + return this._meta; + }, + set: function(value) { + this.meta.set(value || {}); + } + }, + // TODO: Mixin these mutator functions to share code with Meta class // Subclassing causes circular redundancy id: { @@ -349,6 +288,8 @@ BaseElement = createClass({ } }, + // TODO: Remove, not in Refract spec + // Requires updating subclass test name: { get: function() { return this.getMetaProperty('name', ''); @@ -455,11 +396,11 @@ var Collection = BaseElement.extend({ var compactDoms = this.content.map(function(el) { return el.toCompactRefract(); }); - return [this.element, this.meta.toObject(), attributes, compactDoms]; + return [this.element, this.meta.toValue(), attributes, compactDoms]; }, fromRefract: function(doc) { - this.meta = new Meta(doc.meta); + this.meta = doc.meta; this.attributes = doc.attributes; this.content = (doc.content || []).map(function(content) { return registry.fromRefract(content); @@ -477,7 +418,7 @@ var Collection = BaseElement.extend({ }, fromCompactRefract: function(tuple) { - this.meta = new Meta(tuple[1]); + this.meta = tuple[1]; this.attributes = tuple[2]; this.content = (tuple[3] || []).map(function(content) { return registry.fromCompactRefract(content); @@ -582,7 +523,7 @@ var Collection = BaseElement.extend({ */ getById: function(id) { return this.find(function(item) { - return item.meta.id.toValue() === id; + return item.id.toValue() === id; }).first(); }, @@ -657,7 +598,7 @@ var MemberElement = BaseElement.extend({ return { element: this.element, attributes: this.attributes, - meta: this.meta.toObject(), + meta: this.meta.toValue(), content: { key: this.key.toRefract(), value: this.value.toRefract() @@ -666,14 +607,14 @@ var MemberElement = BaseElement.extend({ }, toCompactRefract: function() { - return [this.element, this.meta.toObject(), this.attributes, { + return [this.element, this.meta.toValue(), this.attributes, { key: this.key.toCompactRefract(), value: this.value.toCompactRefract() }]; }, fromRefract: function(doc) { - this.meta = new Meta(doc.meta); + this.meta = doc.meta; this.attributes = doc.attributes; this.content = { key: registry.fromRefract(doc.content.key), @@ -692,7 +633,7 @@ var MemberElement = BaseElement.extend({ }, fromCompactRefract: function(tuple) { - this.meta = new Meta(tuple[1]); + this.meta = tuple[1]; this.attributes = tuple[2]; this.content = { key: registry.fromCompactRefract(tuple[3].key), @@ -859,7 +800,6 @@ var ObjectElement = Collection.extend({ }); module.exports = { - Meta: Meta, BaseElement: BaseElement, NullElement: NullElement, StringElement: StringElement, diff --git a/test/primitives-test.js b/test/primitives-test.js index c097c5e3..69284c4f 100644 --- a/test/primitives-test.js +++ b/test/primitives-test.js @@ -17,10 +17,10 @@ describe('Minim Primitives', function() { }); it('should initialize the correct meta data', function() { - expect(el.meta.id.toValue()).to.equal('foobar'); - expect(el.meta.class.toValue()).to.deep.equal(['a', 'b']); - expect(el.meta.title.toValue()).to.equal('Title'); - expect(el.meta.description.toValue()).to.equal('Description'); + expect(el.id.toValue()).to.equal('foobar'); + expect(el.class.toValue()).to.deep.equal(['a', 'b']); + expect(el.title.toValue()).to.equal('Title'); + expect(el.description.toValue()).to.equal('Description'); }); }); @@ -63,11 +63,11 @@ describe('Minim Primitives', function() { }); it('returns true when they are equal', function() { - expect(el.meta.id.equals('foobar')).to.be.true; + expect(el.id.equals('foobar')).to.be.true; }); it('returns false when they are not equal', function() { - expect(el.meta.id.equals('not-equal')).to.be.false; + expect(el.id.equals('not-equal')).to.be.false; }); it('does a deep equality check', function() { @@ -105,7 +105,7 @@ describe('Minim Primitives', function() { }); it('stores the correct data in meta for ' + key, function() { - expect(el.meta[key].toValue()).to.deep.equal(meta[key]) + expect(el.meta.get(key).toValue()).to.deep.equal(meta[key]) }); }); }); diff --git a/test/subclass-test.js b/test/subclass-test.js index 1d986bd9..e0588661 100644 --- a/test/subclass-test.js +++ b/test/subclass-test.js @@ -65,7 +65,7 @@ describe('Minim subclasses', function() { describe('serializing attributes', function() { var myElement = new MyElement(); myElement.attributes.headers = new minim.ArrayElement(['application/json']); - myElement.attributes.headers.content[0].meta.name = 'Content-Type'; + myElement.attributes.headers.content[0].meta.set('name', 'Content-Type'); it('should serialize headers element', function() { var refracted = myElement.toCompactRefract(); From 282ed371c21b6fbeb7b60dd1b39cf7bf75a24eba Mon Sep 17 00:00:00 2001 From: Stephen Mizell Date: Thu, 18 Jun 2015 16:41:11 -0500 Subject: [PATCH 3/7] Convert attributes to a Minim object element --- lib/base.js | 36 +++++++++++++++++++++++++----------- test/primitives-test.js | 2 +- test/subclass-test.js | 8 ++++---- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/lib/base.js b/lib/base.js index 5d33895f..a9a39d6e 100644 --- a/lib/base.js +++ b/lib/base.js @@ -121,13 +121,16 @@ var registry = new ElementRegistry(); BaseElement = createClass({ constructor: function(content, meta, attributes) { - // Lazy load this.meta because it's a Minim element + // Lazy load this.meta and this.attributes because it's a Minim element // Otherwise, we get into circuluar calls if (meta) { this.meta.set(meta); } - this.attributes = attributes || {}; + if (attributes) { + this.attributes.set(attributes); + } + this.content = content || null; this._attributeElementKeys = []; }, @@ -163,14 +166,14 @@ BaseElement = createClass({ */ convertAttributesToRefract: function(functionName) { var attributes = {}; - var keys = _.keys(this.attributes); + var keys = this.attributes.keys(); for (var i = 0; i < keys.length; i++) { var key = keys[i]; if (this._attributeElementKeys.indexOf(key) !== -1) { - attributes[key] = this.attributes[key][functionName](); + attributes[key] = this.attributes.get(key)[functionName](); } else { - attributes[key] = this.attributes[key]; + attributes[key] = this.attributes.get(key); } } @@ -186,8 +189,8 @@ BaseElement = createClass({ for (var i = 0; i < this._attributeElementKeys.length; i++) { var key = this._attributeElementKeys[i]; - if (this.attributes[key]) { - this.attributes[key] = conversionFunc(this.attributes[key]); + if (this.attributes.hasKey(key)) { + this.attributes.set(key, conversionFunc(this.attributes.get(key).toValue())); } } }, @@ -268,8 +271,19 @@ BaseElement = createClass({ } }, - // TODO: Mixin these mutator functions to share code with Meta class - // Subclassing causes circular redundancy + attributes: { + get: function() { + if (!this._attributes) { + this._attributes = registry.toElement({}); + } + + return this._attributes; + }, + set: function(value) { + this.attributes.set(value || {}); + } + }, + id: { get: function() { return this.getMetaProperty('id', ''); @@ -597,7 +611,7 @@ var MemberElement = BaseElement.extend({ toRefract: function() { return { element: this.element, - attributes: this.attributes, + attributes: this.attributes.toValue(), meta: this.meta.toValue(), content: { key: this.key.toRefract(), @@ -607,7 +621,7 @@ var MemberElement = BaseElement.extend({ }, toCompactRefract: function() { - return [this.element, this.meta.toValue(), this.attributes, { + return [this.element, this.meta.toValue(), this.attributes.toValue(), { key: this.key.toCompactRefract(), value: this.value.toCompactRefract() }]; diff --git a/test/primitives-test.js b/test/primitives-test.js index 69284c4f..bc0bbc7e 100644 --- a/test/primitives-test.js +++ b/test/primitives-test.js @@ -1196,7 +1196,7 @@ describe('Minim Primitives', function() { }); it('correctly sets the attributes', function() { - expect(member.attributes.foo).to.equal('bar'); + expect(member.attributes.get('foo').toValue()).to.equal('bar'); }); describe('#toRefract', function() { diff --git a/test/subclass-test.js b/test/subclass-test.js index e0588661..7c4ea56e 100644 --- a/test/subclass-test.js +++ b/test/subclass-test.js @@ -54,18 +54,18 @@ describe('Minim subclasses', function() { }); it('should create headers element instance', function() { - expect(myElement.attributes.headers).to.be.instanceof(minim.ArrayElement); + expect(myElement.attributes.get('headers')).to.be.instanceof(minim.ArrayElement); }); it('should leave foo alone', function() { - expect(myElement.attributes.foo).to.be.a('string'); + expect(myElement.attributes.get('foo').toValue()).to.be.a('string'); }); }); describe('serializing attributes', function() { var myElement = new MyElement(); - myElement.attributes.headers = new minim.ArrayElement(['application/json']); - myElement.attributes.headers.content[0].meta.set('name', 'Content-Type'); + myElement.attributes.set('headers', new minim.ArrayElement(['application/json'])); + myElement.attributes.get('headers').content[0].meta.set('name', 'Content-Type'); it('should serialize headers element', function() { var refracted = myElement.toCompactRefract(); From 69e88eb83b9aa98d9d0ef6fec7c551998e52ea90 Mon Sep 17 00:00:00 2001 From: Stephen Mizell Date: Thu, 18 Jun 2015 16:49:29 -0500 Subject: [PATCH 4/7] Add getValue method --- README.md | 22 ++++++++++++++++++++-- lib/base.js | 14 ++++++++++++++ test/primitives-test.js | 28 ++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bfb823f8..cf6c1878 100644 --- a/README.md +++ b/README.md @@ -225,7 +225,16 @@ The `get` method returns the item of the `ArrayElement` instance at the given in ```javascript var arrayElement = new minim.ArrayElement(['a', 'b', 'c']); -var value = arrayElement.get(0) // get(0) returns 'a' +var value = arrayElement.get(0) // get(0) returns item for 'a' +``` + +##### getValue + +The `getValue` method returns the value of the item of the `ArrayElement` instance at the given index. + +```javascript +var arrayElement = new minim.ArrayElement(['a', 'b', 'c']); +var value = arrayElement.getValue(0) // get(0) returns 'a' ``` ##### set @@ -363,7 +372,7 @@ This is an element for representing objects. Objects store their items as an ord ##### get -The `get` method returns the value of the `ObjectElement` instance at the given name. +The `get` method returns the `ObjectElement` instance at the given name. See `getKey` and `getMember` for ways to get more instances around a key-value pair. ```javascript @@ -371,6 +380,15 @@ var objectElement = new minim.ObjectElement({ foo: 'bar' }); var value = objectElement.get('foo') // returns string instance for 'bar' ``` +##### getValue + +The `getValue` method returns the value of the `ObjectElement` instance at the given name. + +```javascript +var objectElement = new minim.ObjectElement({ foo: 'bar' }); +var value = objectElement.getValue('foo') // returns 'bar' +``` + ##### getKey The `getKey` method returns the key element of a key-value pair. diff --git a/lib/base.js b/lib/base.js index a9a39d6e..7f962fb3 100644 --- a/lib/base.js +++ b/lib/base.js @@ -453,6 +453,20 @@ var Collection = BaseElement.extend({ return this.content[index]; }, + /* + * Helper for returning the value of an item + * This works for both ArrayElement and ObjectElement instances + */ + getValue: function(indexOrKey) { + var item = this.get(indexOrKey); + + if (item) { + return item.toValue(); + } + + return; + }, + set: function(index, value) { this.content[index] = registry.toElement(value); return this; diff --git a/test/primitives-test.js b/test/primitives-test.js index bc0bbc7e..ea3badce 100644 --- a/test/primitives-test.js +++ b/test/primitives-test.js @@ -785,6 +785,20 @@ describe('Minim Primitives', function() { }); }); + describe('#getValue', function() { + context('when an index is given', function() { + it('returns the item from the array', function() { + expect(arrayElement.getValue(0)).to.equal('a'); + }); + }); + + context('when no index is given', function() { + it('is undefined', function() { + expect(arrayElement.getValue()).to.be.undefined; + }); + }); + }); + describe('#set', function() { it('sets the value of the array', function() { arrayElement.set(0, 'hello world'); @@ -999,6 +1013,20 @@ describe('Minim Primitives', function() { }); }); + describe('#getValue', function() { + context('when a property name is given', function() { + it('returns the value of the name given', function() { + expect(objectElement.getValue('foo')).to.equal('bar'); + }); + }); + + context('when a property name is not given', function() { + it('is undefined', function() { + expect(objectElement.getValue()).to.be.undefined; + }); + }); + }); + describe('#getMember', function() { context('when a property name is given', function() { it('returns the correct member object', function() { From 68c1726a4f6e29296a22f2d5a62b5ca9727442ce Mon Sep 17 00:00:00 2001 From: Stephen Mizell Date: Thu, 18 Jun 2015 17:11:32 -0500 Subject: [PATCH 5/7] Fix linking issues --- lib/base.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/base.js b/lib/base.js index 7f962fb3..87a82b01 100644 --- a/lib/base.js +++ b/lib/base.js @@ -464,7 +464,7 @@ var Collection = BaseElement.extend({ return item.toValue(); } - return; + return undefined; }, set: function(index, value) { @@ -757,8 +757,8 @@ var ObjectElement = Collection.extend({ set: function(keyOrObject, value) { if (_.isObject(keyOrObject)) { var self = this; - _.each(_.keys(keyOrObject), function(key) { - self.set(key, keyOrObject[key]); + _.each(_.keys(keyOrObject), function(objectKey) { + self.set(objectKey, keyOrObject[objectKey]); }); return this; From e5dfefbae3ed24e02df8dbae44fc81a6d7ccc186 Mon Sep 17 00:00:00 2001 From: Stephen Mizell Date: Thu, 18 Jun 2015 20:10:01 -0500 Subject: [PATCH 6/7] Address comments --- lib/base.js | 2 +- test/primitives-test.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/base.js b/lib/base.js index 87a82b01..041518aa 100644 --- a/lib/base.js +++ b/lib/base.js @@ -625,8 +625,8 @@ var MemberElement = BaseElement.extend({ toRefract: function() { return { element: this.element, - attributes: this.attributes.toValue(), meta: this.meta.toValue(), + attributes: this.attributes.toValue(), content: { key: this.key.toRefract(), value: this.value.toRefract() diff --git a/test/primitives-test.js b/test/primitives-test.js index ea3badce..57831714 100644 --- a/test/primitives-test.js +++ b/test/primitives-test.js @@ -17,10 +17,10 @@ describe('Minim Primitives', function() { }); it('should initialize the correct meta data', function() { - expect(el.id.toValue()).to.equal('foobar'); - expect(el.class.toValue()).to.deep.equal(['a', 'b']); - expect(el.title.toValue()).to.equal('Title'); - expect(el.description.toValue()).to.equal('Description'); + expect(el.meta.get('id').toValue()).to.equal('foobar'); + expect(el.meta.get('class').toValue()).to.deep.equal(['a', 'b']); + expect(el.meta.get('title').toValue()).to.equal('Title'); + expect(el.meta.get('description').toValue()).to.equal('Description'); }); }); From 384d435db6f5bf4d39b8552ee83a0b9208365dd0 Mon Sep 17 00:00:00 2001 From: Stephen Mizell Date: Fri, 19 Jun 2015 09:11:04 -0500 Subject: [PATCH 7/7] Add CHANGELOG.md to communicate changes --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..2bf5cb60 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Unreleased + +- Allow `#set` to take an object for Object Elements +- Convert `meta` to be Minim Object Elements +- Convert `attributes` to be Minim Object Elements +- Sync class and method names with Refract 0.2.0 spec +- Add convenience methods for `meta` attributes, such as `id` or `class` +- Add finder functions, such as `findByElement` and `findByClass`