forked from deftjs/DeftJS
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit of README, LICENSE, CONTRIBUTORS and CoffeeScript sour…
…ce code.
- Loading branch information
John Yanarella
committed
Feb 22, 2012
1 parent
52b6545
commit 968304e
Showing
7 changed files
with
456 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Contributors | ||
|
||
* [John Yanarella](http://codecatalyst.com/) (Creator) | ||
* [David Tucker](http://www.davidtucker.net/) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Copyright (c) 2012 DeftJS Framework Contributors | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
# DeftJS | ||
|
||
DeftJS is a micro-architecture that enables nimble development of enterprise class web applications with Ext JS and Sencha Touch. | ||
|
||
# Goals | ||
|
||
DeftJS enhances Ext JS and Sencha Touch's APIs with patterns and best practices discovered by top RIA developers at some of the best consulting firms in the industry. | ||
|
||
DeftJS provides: | ||
|
||
* **Simplicity** | ||
* Eliminates boilerplate code in favor of the simplest expression of developer intent. | ||
* **Approachability** | ||
* Builds on familiar Ext JS API syntax conventions for 'pay-as-you-go' complexity. | ||
* **Flexibility** | ||
* Coordinates dynamic assembly of object dependencies based on a configurable IoC container. | ||
* **Reusability** | ||
* Enables business layer code reuse between Ext JS and Sencha Touch applications. | ||
* **Testability** | ||
* Promotes loose coupling through class annotation driven dependency injection. | ||
|
||
DeftJS provides to building blocks to scale up to meet the needs of large enterprise class applications and development teams. | ||
|
||
# Features | ||
|
||
* **IoC Container** | ||
* Provides class annotation driven dependency injection. | ||
* Maps dependencies by user-defined identifiers. | ||
* Resolves dependencies by class instance, factory function or value. | ||
* Supports singleton and prototype resolution of class instance and factory function dependencies. | ||
* Offers eager and lazy instantiation of dependencies. | ||
* Capable of injecting into Ext JS class configs and properties. | ||
* Injects dependencies before the class constructor is executed. | ||
|
||
# API | ||
|
||
## Deft.ioc.Injector | ||
|
||
A lightweight IoC container for dependency injection. | ||
|
||
### Configuration | ||
|
||
**Classes** | ||
|
||
In the simplest scenario, the Injector can be configured to map identifiers by class names: | ||
|
||
Deft.Injector.configure([ | ||
contactStore: 'MyApp.store.ContactStore', | ||
contactManager: 'MyApp.manager.ContactManager', | ||
... | ||
]); | ||
|
||
When the Injector attempts to resolve dependencies for these identifiers, a singleton instance of the specified class will be created and returned. | ||
|
||
Where necessary, you can also specify constructor parameters: | ||
|
||
Deft.Injector.configure({ | ||
contactStore: { | ||
className: 'MyApp.store.ContactStore', | ||
parameters: [{ | ||
proxy: { | ||
type: 'ajax', | ||
url: '/contacts.json', | ||
reader: { | ||
type: 'json', | ||
root: 'contacts' | ||
} | ||
} | ||
}] | ||
}, | ||
contactManager: 'MyApp.manager.ContactManager', | ||
... | ||
}); | ||
|
||
You can also specify whether a class is instantiated as a singleton (the default) or a prototype: | ||
|
||
Deft.Injector.configure({ | ||
contactController: { | ||
className: 'MyApp.controller.ContactViewController', | ||
singleton: false | ||
}, | ||
... | ||
}); | ||
|
||
Additionally, you can configure dependency providers to be eagerly or lazily (the default) instantiated: | ||
|
||
Deft.Injector.configure({ | ||
preferences: { | ||
preferences: 'MyApp.preferences.Preferences', | ||
eager: true | ||
}, | ||
... | ||
}); | ||
|
||
When a dependency provider is configured for eager instantiation, it will be immediately created and cached in the Injector after all the identifiers in that `configure()` call have been processed. | ||
|
||
*NOTE:* Only singletons can be eagerly instantiated. | ||
|
||
**Factory Functions** | ||
|
||
The Injector can also be configured to map identifiers to factory functions: | ||
|
||
Deft.Injector.configure({ | ||
contactStore: { | ||
fn: function() { | ||
if ( useMocks ) { | ||
return Ext.create( 'MyApp.mock.store.ContactStore' ); | ||
} | ||
else { | ||
return Ext.create( 'MyApp.store.ContactStore' ); | ||
} | ||
}, | ||
eager: true | ||
}, | ||
contactManager: { | ||
fn: function( instance ) { | ||
if ( instance.session.getIsAdmin() ) { | ||
return Ext.create( 'MyApp.manager.admin.ContactManager' ); | ||
} | ||
else { | ||
return Ext.create( 'MyApp.manager.user.ContactManager' ); | ||
} | ||
}, | ||
singleton: false | ||
}, | ||
... | ||
}); | ||
|
||
When the Injector attempts to resolve dependencies for these identifiers, the factory function is called and the dependency is resolved with the return value. | ||
|
||
As shown above, a lazily instantiated factory function can optionally accept an instance parameter, corresponding to the instance for which the Injector is currently injecting dependencies. | ||
|
||
Factory function dependency providers can be configured as singletons or prototypes and can be eagerly or lazily instantiated. | ||
|
||
*NOTE:* Only singleton factory functions can be eagerly instantiated. | ||
|
||
**Values** | ||
|
||
The Injector can also be configured to map identifiers to values: | ||
|
||
Deft.Injector.configure({ | ||
brandedApplicationName: { | ||
value: "Contact Manager" | ||
}, | ||
versionNumber: { | ||
value: 1.0 | ||
}, | ||
modules: { | ||
value: [ 'contacts', 'customers', 'orders' ] | ||
}, | ||
... | ||
}); | ||
|
||
A value can be any native JavaScript type, including Strings, Arrays, Numbers, Objects or class instances… even Functions! | ||
|
||
*NOTE:* Values can only be configured as singletons and cannot be eagerly instantiated. | ||
|
||
## Deft.mixin.Injectable | ||
|
||
A class is marked as participating in dependency injection by including the Injectable mixin: | ||
|
||
Ext.define( 'MyApp.manager.ContactManager', { | ||
extend: 'MyApp.manager.AbstractManager', | ||
mixins: [ 'Deft.mixin.Injectable' ] | ||
... | ||
}); | ||
|
||
Its dependencies are expressed using the `inject` annotation: | ||
|
||
Ext.define( 'MyApp.manager.ContactManager', { | ||
extend: 'MyApp.manager.AbstractManager', | ||
mixins: [ 'Deft.mixin.Injectable' ] | ||
inject: [ 'contactManager' ], | ||
... | ||
}); | ||
|
||
Any class that includes the Injectable mixin will have the dependencies described in its `inject` annotation resolved and injected by the Injector prior to the class constructor being called. | ||
|
||
By default, the dependency will be injected into the config or property of the same name. | ||
|
||
You also can specify the specific property to inject into, by using slightly more verbose syntax: | ||
|
||
Ext.define( 'MyApp.manager.ContactManager', | ||
extend: 'MyApp.manager.AbstractManager', | ||
mixins: [ 'Deft.mixin.Injectable' ], | ||
inject: { | ||
manager: 'contactManager' | ||
}, | ||
... | ||
}); | ||
|
||
In this case, the `contactManager` dependency will be resolved into a new `manager` property. | ||
|
||
A class does not need to explicitly define a config or property for the property to be injected. However, if that property is defined as an existing config (even in a superclass), the Injector will correctly populate the config value. | ||
|
||
Ext.define( 'MyApp.manager.ContactManager', | ||
extend: 'MyApp.manager.AbstractManager', | ||
mixins: [ 'Deft.mixin.Injectable' ], | ||
inject: { | ||
manager: 'contactManager' | ||
}, | ||
config: { | ||
manager: null | ||
}, | ||
constructor: function( config ) { | ||
this.initConfig( config ); | ||
// this.getManager() will return the injected value. | ||
return this.callParent( arguments ) | ||
} | ||
... | ||
}); | ||
|
||
|
||
# Version History | ||
|
||
* 0.1.0 - Preview release, introducing an IoC container for dependency injection. | ||
|
||
# Roadmap | ||
|
||
* Logo (*in progress*) | ||
* Website (*in progress*) | ||
* FAQ | ||
* Mailing list | ||
* Twitter account (*in progress*) | ||
* Full suite of Jasmine tests (*in progress*) | ||
* Example Ext JS and Sencha Touch applications | ||
* Alternative MVC implementation (Model View ViewController) | ||
* Hierarchical ViewController-aware Routing | ||
* Deferreds / Promises | ||
* AOP with an Ext JS-style API (i.e. JSON style configuration) | ||
* Occasionally-Connected Store (simplifing online / offline capabilities) | ||
|
||
# Development Team | ||
|
||
* [John Yanarella](http://twitter.com/johnyanarella) (Creator) | ||
|
||
# Acknowledgements | ||
|
||
* Inspiration drawn from other IoC frameworks: | ||
* Spring | ||
* Swiz | ||
* Swift Suspenders | ||
* AngularJS | ||
* Special thanks to: | ||
* [David Tucker](http://www.davidtucker.net/) for reviewing several iterations of the proposed syntax. | ||
* [Claude Gauthier](http://www.sencha.com/training) for leading the 5-day 'Fast Track to Ext JS' training where this idea was born. | ||
* [Tim Marshall](http://twitter.com/timothymarshall) for parting with the twitter account and project name, which he'd previously used for a personal project. | ||
|
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
###* | ||
@private | ||
Used by {@link Deft.ioc.Injector}. | ||
### | ||
Ext.define( 'Deft.ioc.DependencyProvider', | ||
|
||
config: | ||
identifier: null | ||
###* | ||
Class to be instantiated, by either full name, alias or alternate name, to resolve this dependency. | ||
### | ||
className: null | ||
|
||
###* | ||
Optional arguments to pass to the class' constructor when instantiating a class to resolve this dependency. | ||
### | ||
parameters: null | ||
|
||
###* | ||
Factory function to be executed to obtain the corresponding object instance or value to resolve this dependency. | ||
NOTE: For lazily instantiated dependencies, this function will be passed the object instance for which the dependency is being resolved. | ||
### | ||
fn: null | ||
|
||
###* | ||
Value to use to resolve this dependency. | ||
### | ||
value: null | ||
|
||
###* | ||
Indicates whether this dependency should be resolved as a singleton, or as a transient value for each resolution request. | ||
### | ||
singleton: true | ||
|
||
###* | ||
Indicates whether this dependency should be 'eagerly' instantiated when this provider is defined, rather than 'lazily' instantiated when later requested. | ||
NOTE: Only valid when either a factory function or class is specified as a singleton. | ||
### | ||
eager: false | ||
|
||
constructor: ( config ) -> | ||
@initConfig( config ) | ||
|
||
if @getEager() | ||
if @getValue()? | ||
Ext.Error.raise( "Error while configuring '#{ @getIndentifier() }': a 'value' cannot be created eagerly." ) | ||
if not @getSingleton() | ||
Ext.Error.raise( "Error while configuring '#{ @getIndentifier() }': only singletons can be created eagerly." ) | ||
|
||
if not @getSingleton() | ||
if @getValue()? | ||
Ext.Error.raise( "Error while configuring '#{ @getIndentifier() }': a 'value' can only be configured as a singleton." ) | ||
|
||
return @ | ||
|
||
###* | ||
Resolve a target instance's dependency with an object instance or value generated by this dependency provider. | ||
### | ||
resolve: ( targetInstance ) -> | ||
Ext.log( "Resolving '#{ @getIdentifier() }'." ) | ||
if @getValue()? | ||
return @getValue() | ||
|
||
instance = null | ||
if @getFn()? | ||
Ext.log( "Executing factory function." ) | ||
instance = @fn( targetInstance ) | ||
else if @getClassName()? | ||
Ext.log( "Creating instance of '#{ @getClassName() }'." ) | ||
parameters = if @getParameters()? then [ @getClassName() ].concat( @getParameters() ) else [ @getClassName() ] | ||
instance = Ext.create.apply( this, parameters ) | ||
else | ||
Ext.Error.raise( "Error while configuring rule for '#{ @getIndentifier() }': no 'value', 'fn', or 'className' was specified." ) | ||
|
||
if @getSingleton() | ||
@setValue( instance ) | ||
|
||
return instance | ||
) |
Oops, something went wrong.