Q:

ActionScript: Package-level introspection? Or some other pluggable loading scheme?

I'm coding up some delicious business logic and I've come to a situation like this:
There are a bunch of rules (ruleA, ruleB, etc...) which all follow a standard pattern. I've created a Rule interface, and implemented it in RuleA, RuleB, and so on.
So now I just need a way to load them.
Right now I'm using a map:
var rules:Object = {
    ruleA: new RuleA(),
    ruleB: new RuleB(),
   ...
};

Then calling it like this:
function doStuff(whichRule:String, someData:Object):* {
    return rules[whichRule].applyTo(someData);
}

Now, the rules follow a standard naming scheme, and they are all part of one package (foo.rules)... Is there any way to load the rules without keeping them some sort of list?
Thanks!
Edit: To clarify, would like to discover the possible rules at runtime so I don't need to maintain a list of rules. For example:
function loadRule(name:String):Rule {
    import foo.rules;
    var rule:Class = foo.rules[name];
    return new rule();
}

Except, of course, I can't index into the foo.rules namespace... But that's what I'd like to be able to do.

A:

i am sorry to say so, but what you want to do, is simply impossible ... it is not so much about package-level introspection ... if there were a way, it still would not be possible ...
the actual problem is:
all ActionScript compilers always only compile the main class and dependancies ... thus, if your Rule implementations never are mentioned anywhere, they will not be compiled into the resulting swf ... in consequence, they will not be available at runtime at all ...
the closest thing to what you want to do, would probably be to write a build script, that traverses your packages, and - following some rule - registers the classes using some automatically generated code ... the least you need, is an Array literal, containing all the classes
[Rule1, Rule2,...RuleN]

as simple as that ... relying on the fact that all rules are in the same package (which i find an awful restriction), you could then use getDefinitionByName to get the right class ...
IMHO however, apart from not being easy to accomplish, the approach is also bad style ... it's quite hackish, and imposes way too much ...
the map approach is pretty much the best you can get ... by using the map, the classes appear in you code and are thus compiled, and it's a clean way for a lookup ... also, you have more influence ...
you could create rules like this:
class NameIsRule implements IRule {
    private var _name:String;
    public function NameIsRule(name:String) {
        this._name = name;
    }
    public function applyTo(target:Object):* {
        return target.hasOwnProperty("name") && (target.name == this._name)
    }
}

and then have
rules = {
    isCalledJim : new NameIsRule("Jim"),
    isCalledJohn : new NameIsRule("John")
}

(stupid example, but i hope it illustrates the point)
and you might wanna wrap that into a class, to be cleaner ...
package {
    public class RuleMap {
        private var _rules:Object = {}
        public function RuleMap(init:Object = null) {
            for (var name:String in init) 
                this.addRule(name, init[name]);
        }
        public function addRule(id:String, rule:IRule):IRule {
            if (this.ruleExists(id))
                throw new Error("duplicate rule for id " + id);
            if (rule == nul)
                throw new ArgumentError("invalid value null for parameter rule");
            this._rules[id] = rule;
            return rule;
        }
        public function applyRule(id:String, target:Object):* {
            return this.getRule(id).applyTo(target);
        }
        public function getRule(id:String):IRule {
            if (this.ruleExists(id))
                return this._rules[id];
            else 
                throw new Error("no rule for id " + id);
        }
        public function ruleExists(id:String):Boolean {
            return this._rules.hasOwnProperty(id);
        }
    }
}

this is a little more verbous (well, you can always pass in an object to the constructor), but it is much safer, throws more reasonable values, and is faster, since AVM2 is faster when executing strictly typed code (in contrast to dynamic code, you actually make use of the interface ... it is quite pointless to declare an interface, if none of your code relies on it) ...
also, you should take the time, to create constants for the rules, instead of having tons of string literals all over you code ... this will help against typos etc. ...
when working with sealed classes in AS3, as you do, untyped coding does not offer any benefits, because it makes only real sense with ducktyped languages ... and AS3 is not ... in strict languages, keep strict, and the compiler will do a lot of work for you, and save you a lot of debugging time ...
i like the idea behind the Rules (nice, clean and flexible), but IMHO, you should rather be more exact than lazy at that Point ... i'd guess, this is something rather at the heart of your App ... so let it be rock solid ... ;)
greetz
back2dos

A:

You can get a reference to a class from a string with the flash.utils.getDefinitionByName(className);
For example
def getRule(someValue: String) : Class{
    return getDefinitionByName("mybusiness.rules.Rule" + someValue);
}

In the example above you are returning a class, but you can also instantiate it and return the object.
Note however that this will only work if the class, identified by the class name is compiled into your swf. If no where else in your code you refer to that class, or else it won't be compiled. This is usually achieved creating references in some other piece of code or your build system can pass that to the compiler.
Do note, that functions are first class objects in AS3, so, depending on your use case, you can define each rule as a function to a Rule class and pass that:
def getRule(someValue: String) : Function{
    return new Rule()["theRuleName"];
}

That depends on how much logic you'd like to put into those, and how much common code there is between those rules, of course.

