ember and containers
DESCRIPTION
Please refer to this talk: http://www.slideshare.net/mixonic/containers-di Much more up to date, and discussing detailed use-cases.TRANSCRIPT
Let’s talk about containers.
Matthew Beale -- @mixonic -- madhatted.com
I build Ember apps for spiffy clients in NYC
Friday, July 12, 13
Friday, July 12, 13
Y’all got issues.
Friday, July 12, 13
Convention over configuration.Global namespace! So easy!
new App[controllerName+‘Controller’]()
Friday, July 12, 13
• Namespaces are !ood. Less !lobals, less conflicts.
• Files map to modules. Could be useful!
• Mana!e dependencies in JS. Better/simpler build pipeline and re-usability.
Oh, maybe we should use modules….
Modules for JS
Friday, July 12, 13
THEY’RE COMING
ES6 MODULESFriday, July 12, 13
Memory management.Who cares!
Friday, July 12, 13
Friday, July 12, 13
• When do you declare an object un-used?
• What about nested collections of objects?
• How do you reset sin!letons durin! tests?
Oh, maybe we should use an Inversion of Control Container….
Herding objects is hard.
Friday, July 12, 13
Dependencies between objectsGlobal namespace! So easy!
App.fooController = Ember.Controller.create({
Friday, July 12, 13
• Namespaces are !ood. Less !lobals, less conflicts. App.everythin!IsNotASolution
• Often, it will be useful to attach a dependency based on type.
• Knowin! and possibly stubbin! dependencies in tests would be nice.
Oh, maybe we should use Dependency Injection….
Dependencies between objects
Friday, July 12, 13
Friday, July 12, 13
352 PAGESFriday, July 12, 13
• Factories receive instance variables.
• Resolvers find factories.
• Containers mana!e injections.
Three Components
Friday, July 12, 13
1 var Factory = Ember.Object.extend(); 2 3 // Receives instance variables as a new instance. 4 Factory.create(injections); 5 6 // Can receive injections for future instances. 7 Factory.extend(injections); 8 9 10 // Today in Ember, injections are only sent to instances.11 // Don't worry yourself about this too much, it may change.
Ember Factories
Friday, July 12, 13
Ember Resolvers
1 Ember.DefaultResolver = Ember.Object.extend({ 2 namespace: null, 3 4 resolve: function(fullName) { 5 var parsedName = this.parseName(fullName); 6 7 // Some magic for specific types, but usually getting to: 8 return this.resolveOther(parsedName); 9 },10 11 resolveOther: function(parsedName) {12 var className = classify(parsedName.name) + classify(parsedName.type),13 factory = get(parsedName.root, className);14 if (factory) { return factory; }15 }16 })
Resolves fullNames like controller:application
Must provide `resolve`
Friday, July 12, 13
Ember Containers
1 var container = new Ember.Container(); 2 3 container.register('worker:uploader', MyUploader); 4 container.injection('controller', 'uploader', 'worker:uploader'); 5 6 container.resolve('worker:uploader'); //=> MyUploader 7 container.lookup('worker:uploader'); //=> instance of MyUploader 8 9 container.lookup('controller:application').get('uploader');10 //=> same instance of MyUploader11 12 container.reset();
Friday, July 12, 13
Friday, July 12, 13
In your own app
Thus, `worker` is available on FilePickerController instances
1 var App = Ember.Application.create();2 3 App.register('worker:uploader', MyUploader);4 App.inject('controller:filePicker', 'worker', 'worker:uploader');5 6 // Ah, so simple.
Friday, July 12, 13
In Ember Data
1 Ember.onLoad('Ember.Application', function(Application) { 2 Application.initializer({ 3 name: "store", 4 5 initialize: function(container, application) { 6 application.register('store:main', application.Store); 7 8 // Eagerly generate the store so defaultStore is populated. 9 // TODO: Do this in a finisher hook10 container.lookup('store:main');11 }12 });13 14 Application.initializer({15 name: "injectStore",16 17 initialize: function(container, application) {18 application.inject('controller', 'store', 'store:main');19 application.inject('route', 'store', 'store:main');20 }21 });22 });
Thus, `store` is available on controllers and routes
Friday, July 12, 13
In your tests
1 Ember.Container.prototype.stub = function(fullName, instance) { 2 instance.destroy = instance.destroy || function() {}; 3 this.cache.dict[fullName] = instance; 4 }; 5 6 var container; 7 8 module('UserController saves', { 9 setup: function(){ container = App.__container__ }10 });11 12 test('is saves', function(){13 expect(1);14 container.stub('main:store', function(){15 this.save = function(){ ok(true) };16 });17 controller = container.lookup('controller:user');18 controller.send('submit');19 });
Friday, July 12, 13
THEY’RE COMING
ES6 MODULESFriday, July 12, 13
THEY’RE HERE
ES6 MODULES
EMBER APP KIT
Friday, July 12, 13
In your own app
Thus, `worker` is available on FilePickerController instances
1 var App = Ember.Application.create();2 3 App.register('worker:uploader', MyUploader);4 App.inject('controller:filePicker', 'worker', 'worker:uploader');5 6 // Ah, so simple.
GLOBAL
Flashback to…
Friday, July 12, 13
• Namespaces are !ood. Less !lobals, less conflicts.
• Files map to modules. Could be useful!
• Mana!e dependencies in JS. Better/simpler build pipeline and re-usability.
Oh, maybe we should use modules….
Modules for JS
Flashback to…
Friday, July 12, 13
https://github.com/stefanpenner/ember-app-kit
Grunt pipeline, es6-module-transpiler
Friday, July 12, 13
1 module "appkit/app" { 2 var App = Ember.Application.create(); 3 export default App; 4 // <script type="text/javascript">import App from “appkit/app”;</script> 5 } 6 7 module "appkit/templates/application" { 8 var template = Ember.Handlebars.compile("Howdy Washington!"); 9 export default template;10 }
Real scopes. No globals.
Files become ES6 modules
Friday, July 12, 13
Friday, July 12, 13
Ember Resolvers
1 Ember.DefaultResolver = Ember.Object.extend({ 2 namespace: null, 3 4 resolve: function(fullName) { 5 var parsedName = this.parseName(fullName); 6 7 // Some magic for specific types, but usually getting to: 8 return this.resolveOther(parsedName); 9 },10 11 resolveOther: function(parsedName) {12 var className = classify(parsedName.name) + classify(parsedName.type),13 factory = get(parsedName.root, className);14 if (factory) { return factory; }15 }16 })
Resolves fullNames like controller:application
Must provide `resolve`
Flashback to...
IMPLIES APP.SOMETHING
Friday, July 12, 13
1 module "appkit/app" { 2 var App = Ember.Application.create(); 3 4 import applicationTemplate from "appkit/templates/application"; 5 Em.TEMPLATES['application'] = applicationTemplate; 6 7 export default App; 8 // <script type="text/javascript">import App from “appkit/app”;</script> 9 }10 11 module "appkit/templates/application" {12 var template = Ember.Handlebars.compile("Howdy Washington!");13 export default template;14 }
Quick fix...
But do that for everything? No way.
Friday, July 12, 13
• Factories receive instance variables.
• Resolvers find factories.
• Containers mana!e injections.
Three Components
Flashback to…
OH HAI
Friday, July 12, 13
80 function resolveOther(parsedName) { 81 var prefix = this.namespace.modulePrefix; 82 Ember.assert('module prefix must be defined', prefix); 83 84 var pluralizedType = typeMap[parsedName.type] || parsedName.type; 85 var name = parsedName.fullNameWithoutType; 86 87 var moduleName = prefix + '/' + pluralizedType + '/' + underscore(name); 88 var module; 89 90 if (define.registry[moduleName]) { 91 module = requireModule(moduleName); 92 93 if (typeof module.create !== 'function') { 94 module = classFactory(module); 95 } 96 97 if (Ember.ENV.LOG_MODULE_RESOLVER){ 98 Ember.logger.info('hit', moduleName); 99 }100 101 return module;102 } else {103 if (Ember.ENV.LOG_MODULE_RESOLVER){104 Ember.logger.info('miss', moduleName);105 }106 107 return this._super(parsedName);108 }109 }
/vendor/loader.js
Friday, July 12, 13
1 module "appkit/app" { 2 var App = Ember.Application.create(); 3 export default App; 4 // <script type="text/javascript">import App from “appkit/app”;</script> 5 } 6 7 module "appkit/templates/application" { 8 var template = Ember.Handlebars.compile("Howdy Washington!"); 9 export default template;10 }
Ember & ES6 modules, no hacks
Friday, July 12, 13
• Views
• Controllers
• Templates
• Routes
Today, works with...
Yay, good-guy classes are fetched via containers
Friday, July 12, 13
• Some Views
• Models
• Helpers
Today, busted with...
Boo, Scumbag classes are not referenced via the container
Friday, July 12, 13
• Subcontainers. Flush only part of the app.
• Sin!leton controllers live forever. Problem? Feature?
• Ember.Container could become a micro-lib. Uses no Ember internally.
The future
Friday, July 12, 13
Questions? Ideas?
Matthew Beale - @mixonic - madhatted.com
Friday, July 12, 13