From 553a073b5f835aee35389ba9a3f530dc02c32e97 Mon Sep 17 00:00:00 2001 From: Travis Hoover Date: Thu, 3 Sep 2015 13:08:12 -0700 Subject: [PATCH 1/3] New stateFor CP --- addon/state-for.js | 77 ++++++++++++++++++++++++ tests/dummy/app/components/edit-email.js | 6 +- 2 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 addon/state-for.js diff --git a/addon/state-for.js b/addon/state-for.js new file mode 100644 index 0000000..24dddda --- /dev/null +++ b/addon/state-for.js @@ -0,0 +1,77 @@ +import Ember from 'ember'; + +var { + computed, + assert +} = Ember; + +var stateHashMap = {}; + +/* +* +*/ +function stateFactoryLookup(container, stateName) { + let stateContainerName = `state:${stateName}`; + let StateFactory = container.lookupFactory(stateContainerName); + + if (StateFactory === undefined) { + throw new TypeError('Unknown StateFactory: `' + stateContainerName + '`'); + } + + return StateFactory; +} + +/* +* +*/ +function stateFromCache(stateName, keyValue) { + let stateCache = stateHashMap[stateName]; + + if (stateCache && stateCache[keyValue]) { + return stateCache[keyValue]; + } +} + +/* +* +*/ +function addStateToCache(stateName, keyValue, state) { + + if (stateHashMap[stateName] && stateHashMap[stateName][keyValue]) { + throw new Error(`${keyValue} is no unqiue and has collided with another state.`); + } + + if (stateHashMap[stateName]) { + stateHashMap[stateName][keyValue] = state; + return state; + } + + stateHashMap[stateName] = { + [keyValue]: state + }; + + return state; +} + + +export default function(stateName, options) { + assert('key property must be passed in via the second parameter', options.key); + + return computed(options.key, function() { + debugger; + + let stateCache = stateFromCache(stateName, this.get(options.key)); + + if (stateCache) { + return stateCache; + } + + let stateFactory = stateFactoryLookup(this.container, stateName); + let state = stateFactory.create(); + + addStateToCache(stateName, this.get(options.key), state); + + return state; + }); + +} diff --git a/tests/dummy/app/components/edit-email.js b/tests/dummy/app/components/edit-email.js index 371378e..0d00250 100644 --- a/tests/dummy/app/components/edit-email.js +++ b/tests/dummy/app/components/edit-email.js @@ -1,11 +1,9 @@ import Ember from 'ember'; +import stateFor from 'ember-state-services/state-for'; export default Ember.Component.extend({ tagName: 'form', - editEmailService: Ember.inject.service('edit-email'), - data: Ember.computed('email', function() { - return this.get('editEmailService').stateFor(this.get('email')); - }).readOnly(), + data: stateFor('edit-email', { key: 'email.id' }), actions: { save: function() { From 44a01edf34011fa9d1f1cebef5ab2d43ef796adf Mon Sep 17 00:00:00 2001 From: Travis Hoover Date: Thu, 3 Sep 2015 13:47:33 -0700 Subject: [PATCH 2/3] Making sure that default state is correct --- addon/state-for.js | 67 ++++++------------------ tests/dummy/app/components/edit-email.js | 2 +- 2 files changed, 17 insertions(+), 52 deletions(-) diff --git a/addon/state-for.js b/addon/state-for.js index 24dddda..5eb73ed 100644 --- a/addon/state-for.js +++ b/addon/state-for.js @@ -5,73 +5,38 @@ var { assert } = Ember; -var stateHashMap = {}; +var states = {}; -/* -* -*/ -function stateFactoryLookup(container, stateName) { - let stateContainerName = `state:${stateName}`; - let StateFactory = container.lookupFactory(stateContainerName); +function createState(container, stateName) { + const stateContainerName = `state:${stateName}`; + const StateFactory = container.lookupFactory(stateContainerName); if (StateFactory === undefined) { throw new TypeError('Unknown StateFactory: `' + stateContainerName + '`'); } - return StateFactory; -} - -/* -* -*/ -function stateFromCache(stateName, keyValue) { - let stateCache = stateHashMap[stateName]; + states[stateName] = new Ember.MapWithDefault({ + defaultValue: key => setupState(StateFactory, key) + }); - if (stateCache && stateCache[keyValue]) { - return stateCache[keyValue]; - } + return states; } -/* -* -*/ -function addStateToCache(stateName, keyValue, state) { - - if (stateHashMap[stateName] && stateHashMap[stateName][keyValue]) { - throw new Error(`${keyValue} is no unqiue and has collided with another state.`); - } - - if (stateHashMap[stateName]) { - stateHashMap[stateName][keyValue] = state; - return state; - } - - stateHashMap[stateName] = { - [keyValue]: state - }; - - return state; +function setupState(Factory, model) { + return Factory.create({ + content: model + }); } - export default function(stateName, options) { assert('key property must be passed in via the second parameter', options.key); return computed(options.key, function() { - debugger; - - let stateCache = stateFromCache(stateName, this.get(options.key)); - - if (stateCache) { - return stateCache; + if (states[stateName]) { + return states[stateName].get(this.get(options.key)); } - let stateFactory = stateFactoryLookup(this.container, stateName); - let state = stateFactory.create(); - - addStateToCache(stateName, this.get(options.key), state); - - return state; + createState(this.container, stateName); + return states[stateName].get(this.get(options.key)); }); - } diff --git a/tests/dummy/app/components/edit-email.js b/tests/dummy/app/components/edit-email.js index 0d00250..546a81b 100644 --- a/tests/dummy/app/components/edit-email.js +++ b/tests/dummy/app/components/edit-email.js @@ -3,7 +3,7 @@ import stateFor from 'ember-state-services/state-for'; export default Ember.Component.extend({ tagName: 'form', - data: stateFor('edit-email', { key: 'email.id' }), + data: stateFor('edit-email', { key: 'email' }), actions: { save: function() { From 4f0bc68e134da70f30404ee99798f825280a5005 Mon Sep 17 00:00:00 2001 From: Travis Hoover Date: Thu, 3 Sep 2015 16:25:26 -0700 Subject: [PATCH 3/3] Adding builder method for initialState --- addon/state-for.js | 74 +++++++++++++++++++++------- tests/dummy/app/states/edit-email.js | 6 +++ 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/addon/state-for.js b/addon/state-for.js index 5eb73ed..ff68e85 100644 --- a/addon/state-for.js +++ b/addon/state-for.js @@ -2,41 +2,79 @@ import Ember from 'ember'; var { computed, - assert + assert, + typeOf } = Ember; var states = {}; -function createState(container, stateName) { - const stateContainerName = `state:${stateName}`; - const StateFactory = container.lookupFactory(stateContainerName); +/* +* Create the states object for a given stateName. +* +* @param container {object} container to lookup the state factory on +* @param stateName {string} state name which is also the name of the factory +*/ +function createStates(container, stateName) { + let stateContainerName = `state:${stateName}`; + let StateFactory = container.lookupFactory(stateContainerName); - if (StateFactory === undefined) { - throw new TypeError('Unknown StateFactory: `' + stateContainerName + '`'); + if (!StateFactory) { + throw new TypeError(`Unknown StateFactory: \`${stateContainerName}\``); } states[stateName] = new Ember.MapWithDefault({ - defaultValue: key => setupState(StateFactory, key) + defaultValue: () => buildDefaultState.call(this, StateFactory) }); - return states; + return states[stateName]; } -function setupState(Factory, model) { - return Factory.create({ - content: model - }); +/* +* When creating the state instance we use `initialState` method +* on the state class to build its initial state. If it not +* specified then {} is used as the default state. +* +* @param Factory {StateFactoryClass} state factory from the container +* @return {stateInstance} state instance object from the factory +*/ +function buildDefaultState(Factory) { + let defaultState = {}; + + if (typeOf(Factory.initialState) === 'function') { + defaultState = Factory.initialState.call(this); + } + + return Factory.create(defaultState); } -export default function(stateName, options) { - assert('key property must be passed in via the second parameter', options.key); +/* +* Returns a computed property that returns state based off of a dynamic key. +* +* @param stateName {string} name of the state factory which is located in /states/.js +* @param options {object} +* @param {string} options.key - required - the dynamic state key +* @param {object} options.container - optional - container reference +* @return {computed property} +*/ +export default function stateFor(stateName, options) { + var { key, container } = options; + + assert(` + Missing \`key\` property within the second argument. You passed: ${JSON.stringify(options)} + `, key); + + return computed(key, function() { + assert(` + Could not find the container on \`this\` or passed in via: + stateFor('${stateName}', { key: ${key}, container: }) + `, this.container || container); - return computed(options.key, function() { if (states[stateName]) { - return states[stateName].get(this.get(options.key)); + return states[stateName].get(this.get(key)); } - createState(this.container, stateName); - return states[stateName].get(this.get(options.key)); + return createStates + .apply(this, [container || this.container, stateName]) + .get(this.get(key)); }); } diff --git a/tests/dummy/app/states/edit-email.js b/tests/dummy/app/states/edit-email.js index 3deb697..6c10325 100644 --- a/tests/dummy/app/states/edit-email.js +++ b/tests/dummy/app/states/edit-email.js @@ -1,3 +1,9 @@ import BufferedProxy from 'ember-buffered-proxy/proxy'; +BufferedProxy.reopenClass({ + initialState() { + return { content: this.get('email') }; + } +}); + export default BufferedProxy.extend();