Ember and containers

Post on 12-May-2015

4.301 views 2 download

Tags:

description

Please refer to this talk: http://www.slideshare.net/mixonic/containers-di Much more up to date, and discussing detailed use-cases.

Transcript of Ember and containers

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