From 3e74c4a921c45316c7b9d676647f3b8cf6494a01 Mon Sep 17 00:00:00 2001 From: Till Schneidereit Date: Fri, 28 Oct 2011 22:25:51 +0200 Subject: [PATCH] Replaced README by a stub for version 2. --- README.md | 51 ++++++++ README.textile | 337 ------------------------------------------------- 2 files changed, 51 insertions(+), 337 deletions(-) create mode 100644 README.md delete mode 100644 README.textile diff --git a/README.md b/README.md new file mode 100644 index 0000000..96cafdb --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +# SwiftSuspenders + +Attention: This README is just a stub to prevent you from reading the heavily outdated [one for Swiftsuspenders 1.x](https://github.com/tschneidereit/SwiftSuspenders/blob/the-past/README.textile). + +That being said, here's a very quick outline of the new version's capabilities and API: + +## Features + +- injection requests configurable using standardized metadata +- can inject into vars, setters, methods and constructors +- injection requests can be optional +- mapping dependencies by class and, optionally, name +- satisfying dependencies using value, class instance, singleton or custom providers +- chaining multiple injectors to create modular dependency-mapping graphs much like inheritance works in OOP +- defining local and shared scope for mappings, akin to public and private members of a class +- defining soft mappings that only get used if no injector higher up in the inheritance chain can satisfy a dependency +- support object live-cycle management (note: The latter part of that management, i.e. the destruction, is not yet implemented, but will be for 2.0 final) + +## API + +### Requests + +Requests, aka injection points, can be defined using the metadata tag `[Inject]` atop the var, setter or method to inject into. For constructor injection, no metadata is required. + +Named injections can be defined using the syntax `[Inject(name="injection name")]`. In this case, constructors have to have their metadata placed atop the class itself, not the constructor. This is a limitation of the Flex compiler. + +Optional injection requests can be defined using the syntax `[Inject(optional=true)]` + +### Mappings + +The API is expressed as a DSL to make very fine-grained configuration of each mapping easy and readable: + + const injector : Injector = new Injector; + + //create a basic mapping: + injector.map(Sprite); //will instantiate a new Sprite for each request for Sprite + + //map to another class: + injector.map(Sprite).toType(BetterSprite); //will instantiate a new BetterSprite for each request for Sprite + + //map as a singleton: + injector.map(EventDispatcher).asSingleton(); //will lazily create an instance and return it for each consecutive request + + //map an interface to a singleton: + injector.map(IEventDispatcher).toSingleton(EventDispatcher); + +### Beta + +So, that's it for now as far as documentation is concerned. The implementation, on the other hand, is very stable and shouldn't regress anything that used to work in 1.x, as long as the API changes are dealt with, of course. + +More documentation will soon come here, in the github wiki and as asdocs. \ No newline at end of file diff --git a/README.textile b/README.textile deleted file mode 100644 index b8cb647..0000000 --- a/README.textile +++ /dev/null @@ -1,337 +0,0 @@ -h1. SwiftSuspenders - -SwiftSuspenders is a basic metadata driven IOC (Inversion Of Control) solution for AS3. In its basic approach, it is similar to the "SmartyPants IOC framework":http://code.google.com/p/smartypants-ioc/ by "Josh McDonald":http://flex.joshmcdonald.info/, but it differs from SmartyPants in two respects: It is inferior in capabilities and it is quite somewhat faster. - -Its main Raison d'ĂȘtre is supporting the very nice "Robotlegs":http://www.robotlegs.org/ AS3 MCVS framework by "Shaun Smith":http://shaun.boyblack.co.za/blog/ - hence the name. - -To enable using SwiftSuspenders in the Flash Professional IDE, which doesn't support keeping custom metadata, it's also possible to configure injection points using a simple XML configuration format. - -h2. Features - -SwiftSuspenders supports the following features as described in more details later in the documentation: - -* metadata based annotation of injection points -* XML based annotation of injection points for working with the Flash Professional IDE (which doesn't support custom metadata annotations as of version CS4) -* injecting into - - * properties (i.e. setters) - * variables - * methods (with support for optional arguments) - * constructors (with support for optional arguments) -* named injections, allowing for more specific binding of injections than just by their type. (See "Defining injection points") -* [PostConstruct] annotations for invoking methods after all injections have been applied -* mapping - - * values - * classes (of which new instances are created for each injection) - * singletons (which are created lazily on first injection and then reused for each additional injection of the same rule) - * rules (which allows sharing singletons between multiple mapping rules) -* creating child injectors which share their parents' injection mappings but can define additional mappings complementing or replacing the parents' ones -* querying for the existence of injection rules using Injector#hasMapping -* direct application of injection rules using Injector#getInstance - -h2. Installation - -The easiest way to use SwiftSuspenders is by adding the provided SWC file to your project. If you want to use the source, you have to add the following parameters to your MXMLC settings: - -@-keep-as3-metadata+=Inject@ - -@-keep-as3-metadata+=PostConstruct@ - -h2. Usage - -h3. Defining dependencies - -SwiftSuspenders supports three types of dependency definitions: -* *value bindings*, which simply map an injection request to be satisfied by injecting the given object -* *class bindings*, which map an injection request to be satisfied by injecting a new instance of the given class -* *singleton bindings*, which map all injection requests for the given class by injecting the same shared instance, which itself gets created on first request - -Additionally, it's possible to re-use dependency mappings with @mapRule@. - -For all definition types, it's possible to specify names, which allows using multiple injection bindings to the same class. - -h3. Defining injection points - -Dependency bindings can be injected into an object using constructor, setter, property or method injection (or a combination of these). -Setter, property and method injection require metadata for all injections to be added to the injectee class, whereas you only need to add metadata for named dependencies when using constructor injection: - -@[Inject]@ - -and for injecting named dependencies - -@[Inject(name="NamedDependency")]@ - -When using named dependencies for constructor injection, the metadata has to be placed above the @class@ definition, not above the constructor. This is an unfortunate restriction the Flash Player imposes. - -For methods and constructors accepting multiple parameters, it's possible to define mixes of named and unnamed dependency bindings. In this case, trailing unnamed dependencies can simply be omitted in the metadata, whereas unnamed dependencies followed by named ones have to be declared as the empty string: - -@[Inject(name='', name="NamedDependency")]@ - -For methods and constructors, only the mandatory arguments have to have injection mappings. Optional arguments are added in order as long as mapping are available for them. - -Injection points apply to inheriting classes just as they do to the class they are defined for. Thus, it's possible to define injection points for a base class and use them with all derived classes (which in turn might specify additional injection points). - -h4. Problems with constructor injection - -A word of warning about constructor injection: Due to "a bug":http://bugs.adobe.com/jira/browse/FP-183 in the Flash Player, full type information for constructor arguments is only available after the affected class has been instantiated at least once. To work around this bug, SwiftSuspenders checks if type information for the arguments is available when performing constructor injection. If not, SwiftSuspenders will create one throw-away instance of the class. Because of this behavior, it is important not to start any complex processes in constructors of classes that are used with constructor injection. - -h3. PostConstruct: Automatically invoking methods on injection completion - -Instances of classes that depend on automatic DI are only ready to be used after the DI has completed. Annotating methods in the injectee class with the @[PostConstruct]@ metadata causes them to be invoked directly after all injections have completed and it is safe to use the instance. Multiple methods can be invoked in a defined order by using the @order@ parameter: @[PostConstruct(order=1)]@. - -h3. Querying for injection mapping existence - -As of version 1.5, SwiftSuspenders supports querying for the existence of mapping rules for any request using @Injector#hasMapping@. -@hasMapping@ expects a class or an interface and optionally a name for the mapping and returns @true@ if a request for this class/ name combination can be satisfied. Otherwise, it returns @false@. - -h3. Directly applying injection mappings - -As of version 1.5, SwiftSuspenders supports directly applying injection mappings using @Injector#getInstance@. -@getInstance@ expects a class or an interface and optionally a name for the mapping and returns the mapping's result if one is defined. Otherwise, an exception is thrown. - -The returned value depends on the mapping defined for the relevant request. E.g., if a singleton mapping has been defined for the request, the shared singleton instance will be returned instead of creating a new instance of the class. - -h3. Error handling - -If a mapping for a requested injection is not found, an exception containing the target class and the requested property type. - -h3. Child Injectors - -As of version 1.5, SwiftSuspenders supports creating child injectors. These are dependent on their parent injector and automatically inherit all rule mappings the parent has. Additionally, they can have their own rule mappings, complementing or overriding the parent mappings. - -The main use-case for this feature is as a solution to the so-called "robot legs problem". When using Dependency Injection, one often wants to create very similar object trees that have only slight differences. A good illustration is a simplified robot, that can be built using identical parts for its legs but needs different parts for the left and the right foot. Using normal Dependency Injection, one would have to subclass the RobotLeg class for each leg only to enable specifying different injections for each foot. The subclasses would then have to implement boilerplate code to apply the injection to the variable the parent expects the foot in: -
-public class Robot
-{
-	[Inject] public var leftLeg : LeftRobotLeg;
-	[Inject] public var rightLeg : RightRobotLeg;
-}
-public class RobotLeg
-{
-	protected var foot : RobotFoot;
-}
-
-public class LeftRobotLeg extends RobotLeg
-{
-	[Inject] public function set foot (foot : LeftRobotFoot) : void
-	{
-		this.foot = foot;
-	}
-}
-
-public class RightRobotLeg extends RobotLeg
-{
-	[Inject] public function set foot (foot : RightRobotFoot) : void
-	{
-		this.foot = foot;
-	}
-}
-
-public class RobotConstruction
-{
-	var injector : Injector = new Injector();
-	injector.mapClass(LeftRobotLeg, LeftRobotLeg);
-	injector.mapClass(RightRobotLeg, RightRobotLeg);
-	injector.mapClass(LeftRobotFoot, LeftRobotFoot);
-	injector.mapClass(RightRobotFoot, RightRobotFoot);
-	var robot : Robot = injector.instantiate(Robot);
-}
-
- -Using child injectors, the robot can be built with just the RobotLeg class, while still supplying different feet for each leg: -
-public class Robot
-{
-	[Inject(name='leftLeg')] public var leftLeg : RobotLeg;
-	[Inject(name='rightLeg')] public var rightLeg : RobotLeg;
-}
-public class RobotLeg
-{
-	protected var foot : RobotFoot;
-}
-
-public class RobotConstruction
-{
-	var injector : Injector = new Injector();
-	var leftLegRule : InjectionConfig = injector.mapClass(RobotLeg, RobotLeg, 'leftLeg'); //store a reference to the rule
-	var leftLegInjector : Injector = injector.createChildInjector(); //create a child injector
-	leftLegInjector.mapClass(RobotFoot, LeftRobotFoot); //create a mapping for the correct foot in the child injector
-	leftLegRule.setInjector(leftLegInjector); //instruct SwiftSuspenders to use the child injector for all dependency injections in the left leg object tree
-	//and the same for the right leg:
-	var rightLegInjector : Injector = injector.createChildInjector();
-	rightLegInjector.mapClass(RobotFoot, RightRobotFoot);
-	rightLegRule.setInjector(rightLegInjector);
-	//finally, create the object tree by instantiating the Robot class:
-	var robot : Robot = injector.instantiate(Robot);
-}
-
- -The child injectors forward all injection requests they don't have a mapping for to their parent injector. This enables sharing additional rules between the injectors. For example, the robot feet might have toes that work the same for both feet, so a mapping for these could be added to the main injector instead of adding the mappings to both child injectors. - -If a mapping from a parent (or other ancestor) injector is used, that doesn't mean that the child injector isn't used for subsequent injections anymore. I.e., you can have "holes" in your child injector's mappings that get filled by an ancestor injector and still define other mappings in your child injector that you want to have applied later on in the object tree that is constructed through DI. - -Injectors can be nested freely to create configuration trees of arbitrary complexity. - -h3. XML configuration of injection points - -*Important note: It's possible to use metadata in Flash CS3/4/5, so the main reason for XML-based injection points configuration is moot. In order to keep all metadata in your application intact, check the "Export SWC" option in the "Publish Settings" dialog. See "this blog post":http://www.patrickmowrer.com/2010/03/03/compiling-custom-as3-metadata-flash-professional for details.* - -The @Injector@ takes an optional parameter specifying an XML configuration which, if provided, is used to configure all injection points. - -The configuration XML is a simple list of @@ nodes with their respective injections as child nodes. The target class is specified by the @name@ property. Setter and property injections are specified by @@ nodes, methods by @@ nodes and constructors by @@ nodes. For all injection types, the target property is given by the @name@ property and named injections are specified by adding the @injectionname@ property. For method and constructor injection, multiple named arguments can be defined by adding @@ child-nodes with the same format as the other injection nodes. - -Injection types can still be acquired using runtime reflection, so it's not necessary to supply those. - -In addition to injection points, @PostConstruct@ method annotations can be specified as well, by adding @@ nodes. - -The following example code contains all possible configuration options: - -
-
-	
-		
-		
-		
-		
-	
-	
-		
-		
-		
-			
-			
-		
-	
-	
-		
-			
-			
-		
-	
-
-
- -Note that, to ensure functional equivalence between compiling with the Flash IDE and MXMLC (i.e. Flex), SwiftSuspenders ignores all metadata that might be present in the injectee classes if XML configuration is used. - -h3. Examples - -h4. Field and Setter Injection - -Suppose you have a class into which you want to inject dependencies that looks like this (Note that I've left out import statements for brevity): - -
-package
-{
-	public class MyDependentClass
-	{
-		[Inject]
-		public var firstDepency : MovieClip;
-		
-		[Inject(name="currentTime")]
-		public var secondDependency : Date;
-		
-		[Inject]
-		public function set thirdDependency(value : Sprite) : void
-		{
-			m_thirdDependency = value;
-		}
-		private var m_thirdDependency : Sprite;
-	}
-}
-
- -p. To inject dependencies into an instance of this class, you would first define dependency mappings and then invoke @SwiftSuspendersInjector#injectInto@: - -
-var injector : SwiftSuspendersInjector = new SwiftSuspendersInjector();
-injector.mapValue(MovieClip, new MovieClip());
-var currentTime : Date = new Date();
-injector.mapValue(Date, currentTime, 'currentTime');
-injector.mapSingleton(Sprite); //obviously, you wouldn't _really_ use Sprite singletons
-var injectee : MyDependentClass = new MyDependentClass();
-injector.injectInto(injectee);
-
- -h4. Method Injection - -Suppose you have a class into which you want to inject dependencies that looks like this (Note that I've left out import statements for brevity): - -
-package
-{
-	public class MyDependentClass
-	{
-		private var myMovieClip : MovieClip;
-		private var currentTime : Date;
-		
-		[Inject]
-		public function setFirstDependency(injection : MovieClip) : void
-		{
-			myMovieClip = injection;
-		}
-		
-		[Inject(name="currentTime")]
-		public function setSecondDependency(injection : Date) : void
-		{
-			currentTime = injection;
-		}
-		
-		[Inject(name='', name="currentTime")]
-		public function setMultipleDependencies(movieClip : MovieClip, date : Date) : void
-		{
-			myMovieClip = movieClip;
-			currentTime = date;
-		}
-	}
-}
-
- -p. To inject dependencies into an instance of this class, you would first define dependency mappings and then invoke @SwiftSuspendersInjector#injectInto@: - -
-var injector : SwiftSuspendersInjector = new SwiftSuspendersInjector();
-injector.mapValue(MovieClip, new MovieClip());
-var currentTime : Date = new Date();
-injector.mapValue(Date, currentTime, 'currentTime');
-var injectee : MyDependentClass = new MyDependentClass();
-injector.injectInto(injectee);
-
- -In this case, the defined dependencies are partly redundant, which is waste- but otherwise not harmful. - -h4. Constructor Injection - -Suppose you have a class into which you want to inject dependencies that looks like this (Note that I've left out import statements for brevity): - -
-package
-{
-	[Inject(name='', name="currentTime")]
-	public class MyDependentClass
-	{
-		private var myMovieClip : MovieClip;
-		private var currentTime : Date;
-		
-		public function MyDependentClass(movieClip : MovieClip, date : Date)
-		{
-			myMovieClip = movieClip;
-			currentTime = date;
-		}
-	}
-}
-
- -p. To inject dependencies into an instance of this class, you would first define dependency mappings and then invoke @SwiftSuspendersInjector#instantiate@: - -
-var injector : SwiftSuspendersInjector = new SwiftSuspendersInjector();
-injector.mapValue(MovieClip, new MovieClip());
-var currentTime : Date = new Date();
-injector.mapValue(Date, currentTime, 'currentTime');
-var injectee : MyDependentClass = injector.instantiate(MyDependentClass);
-
- -h3. More Information - -p. As these are a rather contrived and useless examples, I urge you to check out "RobotLegs":http://www.robotlegs.org/ and its "examples":http://github.com/robotlegs/robotlegs-demos-Bundle, which contain much better examples for using IOC in AS3. \ No newline at end of file