Browse files

comments

  • Loading branch information...
1 parent 3e17932 commit 959ef16a0c171d5a4241d217e61979f3ed30df26 @seanhess committed Oct 19, 2009
View
BIN .README.mdown.swp
Binary file not shown.
View
37 README.mdown
@@ -1,3 +1,38 @@
# Zero
-Secret Dependency Injection Framework for Actionscript
+Dependency Injection Framework for Actionscript
+
+## Goals
+
+1. To allow classes to "implement" interfaces in pieces
+2. To make overriding by decoration simple
+3. Make it easy to create short-term objects (Injecting factories)
+4. Testing and Local Mocking should be easy
+5. To make it configurable at runtime, including the ability to disconnect
+6. To allow different implementations based on context
+7. Beautiful syntax in both MXML and AS
+8. Easy to override functionality of the core
+
+## Use Cases
+
+#### When testing, pass in mock implementations to a class
+
+Switch to a mock server connection at runtime (a checkbox)
+
+4 different views of a list of users. Invisible ones are inactive and don't receive updates
+
+When a module is unloaded it is deactivated and GC'd
+
+Give a particular view a unique implementation
+
+Give a particular class a unique implementation
+
+Give a set of views/classes a unique implementation
+
+Proxy all service requests, to convert xml data to strongly typed data before returning
+
+Log all requests in the system without requiring the implementations to know about it
+
+
+
+
View
190 ideas.mdown
@@ -0,0 +1,190 @@
+## Contexts without requirements
+
+I have already seen that passing a context from one node to another is pretty easy. Not bad. The problem is waiting for an actual context object to get there.
+
+So, if I continue to do it the way I am now, but then maintain a map of real context objects in the core, based on the original object.
+
+contexts[owner] = new Context();
+
+So, when a class is created by the framework, we create a context for it, mapped to its parent context (which is defined by the class the mapping is defined in.
+
+This is awesome!
+
+<Connect> or new Connect(this);
+
+So, we don't have to grab the context from the current parent. Just have everything keep track of its owner (context) and we can recreate a tree of connectedness that way. Sweetness... The only thing is we need a way to enable communication between them.
+
+Disconnecting everything in a particular context. VIEW - create a context mapping for each item in the view? Meh...
+
+Well, but I can do something here... to create that tree I wanted :) The dictionary maintains a reference to.... umm..... well, whoever the parent was???
+
+How would I use this?
+
+Making everything in a particular view inactive -
+
+1) I could go back to proxying EVERY request through the context chain
+
+2) I could send a disconnect to everyone within that context
+
+DependencyInterface doesn't have to change either way. You can just put the logic in Implementation instead. If it needs a context, give it one :)
+
+Which is faster... proxy chains, or event chains? I'm guessing proxies?
+
+A) send through the chain - this option makes it easier to change things at runtime. It makes it harder to set up binding (not if we use a proxy chain instead).
+
+Proxy chains aren't that hard to set up either. ..
+
+B) Just sent seek/update/disconnect commands through the chain
+
+C) Connect directly, but have logic that detects the chain. For example, implementations are always given by context. When you seek, it tries to locate the context where that is stored.
+
+
+Implementation
+ - created by the system.
+ - associated with a given context
+
+Context
+ - implementations in it, or associated with it, or something
+ - parent context
+ - hasImplementationFor?
+
+Interface - seek (context, type)
+ - Calls Register.seek(context, type)
+ - context = contexts[context]; (which happens to be a particular implementation)
+ - we know the parent
+
+
+MOCK SITUATION - Library
+
+ We have a library database. The views are as follows
+
+ MainView
+ SearchView
+ ResultsView - (Book, Library)
+ BookView - (Book, Library)
+ EditView - (Book, Library)
+ AddView - (Book, Library)
+
+ DependencyInterfaces
+
+ LibraryService (Book)
+ - search(term:String):IList
+ - addBook(book:Book):void
+ - updateBook(book:Book):void
+
+ Book
+ - title:String
+ - details:String;
+
+ Library (Book, LibraryService)
+ - results:IList;
+ - currentBook:Book;
+ - currentPage:String;
+
+ - search(term:String)
+ - goToSearch()
+ - goToEdit(book:Book)
+ - goToAdd()
+ - goToShow(book:Book)
+
+
+I define all implementations in Application. - each RULE has a context? Or what? I guess so. Yeah, sure.
+
+ Library: contexts[library_implementation] = the_application;
+
+SearchView (Library)
+ new Library(this); - it has a context!
+ connect() - goes through the context, or just keeps it in mind?
+ - generate -
+ - context(SearchView).parent = context(MainView) - how do we know? We know it is a view because it extends DisplayObject? Because it has a "parent" property. so, we look for a context of its parent property, and create one if it doesn't exist. And so on, until we run into.... something.
+
+
+-> System Creates a new Library Implementation - it doesn't exist yet, so we can't run into it's context. But we can put a context on a RULE. the context of a rule could be an object - in which case you only use it if you run into it, or a regexp, in which case your context name has to match it. Assume the FullImplementation Rule has a context of an object, the main application.
+
+In Generate, when we're testing to see if it matches, we call
+ context.hasAncenstor(rule.context); -
+
+ - i have a view object
+ - the rule context is a parent view
+
+ 1) check to see if the context object has a parent. No (context.parent)
+ 2) check to see if the source has a parent - YES (context.source.parent) - MainView
+ 3) if no, return false
+ 4) If YES, see if we have a context for the parent - NO
+ 5) If NO, see if the source has a parent - YES - Application
+ 6) See if we have a context for the parent - YES
+ 7) See if it matches - YES - return true
+
+ ! Create a new Library, and save its context as a new context, referencing it, and with the Application as a parent.
+
+
+
+ So, I'm holding a reference to every object in the system that contains a reference to an interface. Yikes!
+
+Now, let's have Libary get a reference to LibraryService
+
+ context.matches(rules.context);
+
+ contexts[library_instance] is already set to the_applications_context
+
+ 1) the LibraryService Interface has a context of the LibraryImplementation
+
+ does LibraryImplementation have a context in the system? (Yes, w/ parent = Application)
+
+ 1) does it match? NO
+ 2) does the context have a parent? yes, Application
+ 3) does it match? YES
+
+
+
+
+OK >>> Now, let's create a new implementation of Library, that just traces things, and give it a context of MainView (kind of useless, but let's pretend)
+
+ When BookView goes to get an instance of Library
+
+ 1) does it match? NO
+ 2) does it have a parent in its context? NO
+ 3) does it have a parent itself? YES
+ --
+ 1) does it match? YES
+
+ When the mock Library goes to get a reference to LibraryService
+
+ MockLibrary has a context of MainView
+ The LibraryService interface has a context of MockLibrary
+
+ 1) does it match? NO
+ 2) have a parent? YES - MainView
+ 3) match? NO
+ 4) parent? YES - Application
+ 5) match? YES
+
+ return true!
+
+ If I had implementated LibraryService as well, it would return that..
+
+ So... why not put rules into the context object?
+
+ Application - Rule for all implementations
+ MainView - Rule for Library (MockLibrary)
+
+ then, you can just get the rules for it, and see if any exist?
+
+ You could still have them in one big list, and just filter them that way, I suppose. Well, the idea is that you DON'T return the others if you found them hiding out in that context.
+
+ Sheesh this is a tough problem! Do I even gain anything by it?
+
+ I should start by designing a how-to page, similar to the one for Sinatra and God, and solicit feedback based on that. I think I have a good idea of how it should work.
+
+
+
+
+
+
+
+
+
+
+
+
+
View
3 source/src/net/seanhess/zero/dependency/Connect.as
@@ -2,6 +2,9 @@ package net.seanhess.zero.dependency
{
import net.seanhess.zero.util.Invalidator;
+ /**
+ * An MXML tag to facilitate mapping implementations
+ */
[DefaultProperty("rules")]
public class Connect
{
View
3 source/src/net/seanhess/zero/dependency/DependencyError.as
@@ -1,5 +1,8 @@
package net.seanhess.zero.dependency
{
+ /**
+ * Needs work.
+ */
public class DependencyError extends Error
{
public function DependencyError(message:String)
View
9 source/src/net/seanhess/zero/dependency/DependencyInterface.as
@@ -3,7 +3,14 @@ package net.seanhess.zero.dependency
import mx.core.IMXMLObject;
/**
- * The implementation will be
+ * You can extend DependencyInterface to have the system pass you
+ * an implementation. This class proxies the implementation.
+ *
+ * You MUST send in a reference to the context when extending this if
+ * you want it to grab the implementation from the system.
+ *
+ * You can pass in null as the context, and set implementation to your
+ * own class for mocking.
*/
public class DependencyInterface implements IMXMLObject
{
View
1 source/src/net/seanhess/zero/dependency/Implement.as
@@ -2,7 +2,6 @@ package net.seanhess.zero.dependency
{
/**
* Allows you to specify a full impelemntation, which matches up everything that matches
- * Can match based on regular expression
*/
public class Implement
{
View
6 source/src/net/seanhess/zero/dependency/Implementation.as
@@ -3,6 +3,12 @@ package net.seanhess.zero.dependency
import flash.utils.Proxy;
import flash.utils.flash_proxy;
+ /**
+ * Proxy to allow us to manage multiple overlapping implementations
+ *
+ * Also lets DependencyInterfaces just send in "arguments" instead of the
+ * parameters.
+ */
public class Implementation extends Proxy
{
public var onlyImplementation:*;
View
2 source/src/net/seanhess/zero/dependency/Intercept.as
@@ -1,7 +1,7 @@
package net.seanhess.zero.dependency
{
/**
- * Allows you to wrap calls. Should allow you to do regular expressions in the match.
+ * Allows you to wrap calls. Should allow you to use wildcards in the match
*/
public class Intercept
{
View
8 source/src/net/seanhess/zero/dependency/Register.as
@@ -7,6 +7,10 @@ package net.seanhess.zero.dependency
import mx.collections.ArrayCollection;
import mx.collections.ListCollectionView;
+ /**
+ * Global register of creation rules and caches of singleton
+ * implementations (the default)
+ */
public class Register
{
/**
@@ -50,7 +54,9 @@ package net.seanhess.zero.dependency
{
throw new DependencyError("Could not find implementation for " + type + " in context "+ context);
}
-
+
+
+ // doesn't currently support multiple rules per dependency
var rule:Rule = rules.getItemAt(rules.length-1) as Rule;
var instance:*;
View
46 source/src/net/seanhess/zero/dependency/Rule.as
@@ -3,55 +3,15 @@ package net.seanhess.zero.dependency
import mx.core.ClassFactory;
import mx.core.IFactory;
+ /**
+ * Instructions to create an implementation for a DependencyInterface
+ */
public class Rule
{
public static const IMPLEMENT:String = "implement"; // means it creates one instance
public static const FACTORY:String = "factory"; // means it creates a new one every time
public static const PROXY:String = "proxy"; // means it pumps everything through it. Needs to use the cache rule of the parent object?
- // Remember that BasicProxy nonsense?
- // That way you can avoid having a billion proxies
- // You ALWAYS have one (extra) proxy
- // Or, should I make the interface itself be that kind of a proxy?
- // It could get a reference to all the several implementations, and a list of the rules
- // then it could know where to go for each thing?
-
- // if two classes are listen as implementations for the same interface
- // the latter takes priority
- // if nothing overlaps, hopefully we can just do both, right?
- // if things DO overlap, we use the latter...
- // how would you even DO that? a try/catch block?... sure... you can specifically catch TypeErrors
-
- // Proxies - can my little proxy handle other proxies? He gets asked to call "send" for which he has a proxy and an
- // implementation. The proxy is second (thank goodness?).
-
- // Definitely easiest to send EVERYTHING through the proxy. So do that!
- // Does a proxy have to actually BE a proxy? If so, how do you set the item?
- // We'd need to be able to change the item it is proxying? NO - so they can extend ObjectProxy
-
-
-
- // They either need to extend ObjectProxy, and send everything through them, or they need
- // some other way to know the item they'll be proxying, right?
-
- // Since I try to make this class-based... as in, the class makes the decisions, let's go with
- // the other option.
-
- // Overlapping implementations... Hmm, just
-
- // Overlapping implementations - just use the later one... Or rather, try the later one first?
- // Or, should we create a NEW proxy that uses all our stuff for us.
- // How would it call a proxy?
- //
-
- // callProperty
- // try calling the first proxy...
- // no, proxies need access!
- // k, do it the normal way.
- // so, when it returns an implementation, though, it needs to be
-
- // just pass in the real implementation for now?
-
public var kind:String;
public var interfaces:RegExp = /.*/;
View
3 source/src/net/seanhess/zero/dependency/Utils.as
@@ -2,6 +2,9 @@ package net.seanhess.zero.dependency
{
import flash.utils.getQualifiedClassName;
+ /**
+ * Just returns the full name of the class, without the stupid double-colon
+ */
public class Utils
{
public function getName(source:*):String
View
3 source/src/net/seanhess/zero/util/Invalidator.as
@@ -5,6 +5,9 @@ package net.seanhess.zero.util
import flash.events.TimerEvent;
import flash.utils.Timer;
+ /**
+ * delays stuff
+ */
[Event(name="commit", type="flash.events.Event")]
public class Invalidator extends EventDispatcher
{

0 comments on commit 959ef16

Please sign in to comment.