[Arp] Arp 3 Plans
gilles Bertrand
gilles at b-u.be
Sat Jan 7 02:09:57 PST 2006
This looks great aral, ill read into it later but great thinking..
Gilles
Le 07-janv.-06 à 11:00, Aral Balkan a écrit :
> Hi all,
>
> I mentioned that I had begun to think about Arp 3 in the OSFlash
> list. I
> also fleshed out ideas and code for the next version of the framework
> and wanted to share this with you for feedback, comments, etc. I've
> been
> talking with Christophe about his work on extensions as well as
> Muse and
> you will find elements of both in the plan below.
>
> The most important goal, as always, is for Arp3 to simplify things
> even
> further.
>
> The plans below detail a system that will be as easy, if not easier to
> use that Ruby on Rails for Flash/Flex/RIA development. I'm personally
> really excited about its implications and look forward to your
> thoughts.
>
> PS. Please note: None of the code below has been tested, it's a
> complete
> brain-dump that I wrote into SePY.
>
> /*
>
> What is Arp?
>
> Arp is a simple yet powerful structural framework for creating Rich
> Internet Applications (RIAs) on the Flash Platform. Simplicity and DRY
> (Don't Repeat Yourself) are Arp's core tenets. Arp aims to boost
> developer productivity with code generation and introspection and
> integration with major application servers and databases. Whenever
> possible, Arp uses established Software Design Patterns. These include
> core patterns such as Controller, Business Delegate and Value Object.
> Arp favors convention over configuration -- this means that it uses
> intelligent defaults and naming conventions whenever possible. The
> ultimate aim is to bring the start-up time of a new RIA project
> close to
> zero and encourage best practices, test-first development for RIAs on
> the Flash Platform.
>
> * * *
>
> What's New in Arp 3?
>
>
> Full-stack framework:
>
> Creating and working with RIAs on the Flash Platform is now a piece of
> cake with the integration of Ruby On Rails-type functionality.
>
> Arp 3 supports cross-platform code generation developed in PHP [Using
> Cake? CakeAMFPHP?], ActiveRecord-type database introspection, etc. for
> quickly getting up and running with your RIAs. Server-side support for
> the following languages is provided based on Flash Remoting:
>
> * PHP (AMFPHP)
> * J2EE (OpenAMF)
> * Ruby (on Rails)
>
> ActiveRecord-style introspection is supported for the following
> databases:
>
> * MySQL
> * PostgreSQL
>
>
> DRY:
>
> One of the core tenets of Arp is simplicity. Arp is even simpler in
> its
> third incarnation and has gone DRY (Don't Repeat Yourself). This means
> that your ARP3 applications will have less code and thus lower risk.
>
>
> Convention over configuration:
>
> As part of DRY, Arp 3 favors convention over configuration. This was
> also a cornerstone of Arp 2, but we take it further here with the
> introduction of Requests.
>
>
> Requests:
>
> When a form in the view needs to make a request that will require
> business logic to be executed, it creates a new Request instance. A
> request is a special event. It bundles the necessary data for the
> request to be carried along with the request itself and specifies
> optional success and failure handlers to be notified.
>
> The success and failure handlers *only* carry out view logic.
> Unlike Arp
> 2, no data handling, storages, update, etc. is done there.
>
>
> Custom Controllers are now optional:
>
> For most applications you will not need to create custom
> controllers for
> your application or for external forms. The new, DRY Arp's base
> controller class, along with the introduction of Requests will handle
> all the hard work for you.
>
>
> Explicit Commands are now optional:
>
> The new base Controller class and Requests make the explicit
> declaration
> of Commands redundant for most cases. Of course, if you would rather
> implement explicit Commands (eg. to use as part of a Memento
> pattern to
> implement Undo/Redo), you can.
>
>
> Business Delegates are now a central part of the framework:
>
> At the very basic implementation, a Business Delegate includes a
> reference to a remote service to which Requests will be proxied.
>
> If you would rather carry out business logic on the client side in
> response to a request, just implement the necessary method in the
> Business Delegate and it will get called in place of the remote
> service
> (if any).
>
> In the most advanced, you can use a map to specify multiple remote
> services for methods and mix in local method implementations too.
>
>
> Data Binding and the Model:
>
> The model is updated from the Business Delegate and data bindings
> on the
> View [based on Christophe's code] (along with custom formatter
> functions, etc.) carry out view updates automatically.
>
>
> External Forms:
>
> Easy workflow for using external forms and registering them and their
> controllers with the main application.
>
>
> Shared Libraries:
>
> Supports transparent use of shared libraries.
>
>
> Integrated unit testing:
>
> Arp 3 includes the open source ASUnit unit testing framework and
> advocates test-first development.
>
>
> Integrated logging:
>
> Arp 3 includes the open source LuminicBox logger for logging.
>
>
> IDE support:
>
> Support and documentation for creating Arp 3 projects is provided for
> the following IDEs:
>
> * Eclipse
> * FlashDevelop <-- Do we need anything special? (No?)
> * SePY <-- Do we need anything special? (No?)
> * Others????
>
>
> Compatibility:
>
> Works with Flash, Flex 1.5 and Flex 2, ActionScript 2 (AS2) *and*
> ActionScript 3 (AS3). Use the same workflow regardless of which
> technology you're using. Applications based on Arp 3 are very easy to
> port between these technologies. (Note: Since Flex 2 and AS3 are
> currently in Alpha, these features may change in the future in line
> with
> Adobe's code changes in future alphas, betas and releases.)
>
> */
>
>
> //////////////////////////////////////////////////////////////////////
> //
> //
> // Arp 3 Base Application Class
> //
> //
> //////////////////////////////////////////////////////////////////////
>
> class org.osflash.arp3.Application
> {
> //////////////////////////////////////////////////////////////////
> ////
> //
> // Method: registerScreen()
> //
> // Registers an external screen with its Controller so that the
> // Controller can listen for System Events on the screen and
> // map them to Commands.
> //
> // Also calls a handler on the Application form so that it can
> // register to listen for View Events.
> //
> //////////////////////////////////////////////////////////////////
> ////
> public function registerScreen ( screenName:String,
> screenRef:Object )
> {
> Log.info ("Registering screen: " + screenName);
>
> //
> // Register the screen with its Controller
> //
> var ControllerClass = findControllerClass( screenName );
>
> // Remove the controller if it has already been created
> // since if we go back to a screen, the screen refuses
> // to display correctly unless this is done.
> delete ControllerClass["inst"];
>
> var screenController:Object = ControllerClass.getInstance();
> screenController.registerScreen ( screenRef );
>
> //
> // Call handler on Application form. If you want to listen for
> // View Events on the external screen, that's the place to do
> // it. The handler is named onRegisterScreenameScreen
> // (eg. onRegisterLoginScreen.)
> //
> var localHandler:Function = this [ "onRegister" + screenName +
> "Screen" ];
> localHandler.apply ( this, [ { target: screenRef } ] );
> }
>
> //////////////////////////////////////////////////////////////////
> ////
> //
> // Method: unRegisterScreen()
> //
> // Removes the controller for the screen so that the screen can be
> // re-created successfully in the future.
> //
> //////////////////////////////////////////////////////////////////
> ////
> public function unRegisterScreen ( screenName:String,
> screenRef:Object )
> {
> Log.info ("Un-registering screen: " + screenName);
>
> var ControllerClass:Object = findControllerClass
> ( screenName );
> var screenController:Object = ControllerClass.getInstance();
>
> // Remove the controller since the screen is being removed -
> // this allows us to re-open popup windows without issue.
> //screenController.unRegisterScreen ( screenRef );
> delete ControllerClass["inst"];
>
> var localHandler:Function = this [ "onUnRegister" +
> screenName +
> "Screen" ];
> localHandler.apply ( this, [ { target: screenRef } ] );
> }
>
> //////////////////////////////////////////////////////////////////
> ////
> //
> // Group: Private methods
> //
> //////////////////////////////////////////////////////////////////
> ////
>
> private function findControllerClass( screenName:String ):Object
> {
> //
> // The package string must be declared in the projectPackage
> property.
> // It can contain any number of packages. However, the
> Controller class
> // *must* be in a package called "control"
> //
> var packageExploded:Array = mProjectPackage.split(".");
> var numPackages:Number = packageExploded.length;
> var packageRef:Object = _global;
> for ( var i:Number = 0; i < numPackages; i++ )
> {
> packageRef = packageRef [ packageExploded [ i ] ];
> }
> return packageRef["control"][ screenName + "Controller" ];
> }
>
> }
>
>
> //////////////////////////////////////////////////////////////////////
> //
> //
> // Arp 3 Base Controller Class
> //
> //
> //////////////////////////////////////////////////////////////////////
>
> class org.osflash.arp3.Controller
> {
>
> private var inst:Controller;
>
> //////////////////////////////////////////////////////////////////
> ////
> //
> // Private constructor - Singleton - Implement getInstance()
> // abstract method in subclass.
> //
> //////////////////////////////////////////////////////////////////
> ////
> private function Controller ()
> {
> // DO NOT CALL WITH new(). Singleton.
> }
>
>
> //////////////////////////////////////////////////////////////////
> ////
> //
> // Registers the view with the controller
> //
> //////////////////////////////////////////////////////////////////
> ////
>
> public function registerView ( view:Object )
> {
> mView = view;
>
> registerRequests();
> }
>
>
> //////////////////////////////////////////////////////////////////
> ////
> //
> // Registers to listen for requests on the view
> //
> //////////////////////////////////////////////////////////////////
> ////
>
> private function registerRequests()
> {
> for ( var i:String in mView )
> {
> if (i.substring(0, 7) == "request" )
> {
> mView.addEventListener ( i, this );
> }
> }
> }
>
>
>
>
> //////////////////////////////////////////////////////////////////////
> //////
> //
> // Handle event instances
> //
>
> //////////////////////////////////////////////////////////////////////
> //////
> function handleEvent ( eventObj )
> {
> //
> // handleEvent() is a generic event handler that gets called
> whenever
> // an event is heard.
> //
>
> // Get the name of the system request
> var requestMethodName:String = eventObj.type;
>
> // Strip the request name while staying compatible with Arp 2
> (in which the
> // system event would not have a "request" prefix.
> requestMethodName = substring(requestMethodName, 0, 7) ==
> "request" ? substring(requestMethodName, 7) : requestMethodName;
>
> // Reference to the view for this request
> var view:Object = eventObj.target;
>
> // Reference to the business delegate
> var requestBusinessDelegate:Function =
> eventObj.businessDelegate;
>
> // Reference to the data
> var data:Object = eventObj.data;
>
> //
> // Does an explicit Command exist to handle this request?
> //
> // Note: This behavior is different from Arp 2: You *will not*
> get an error
> // if you forget to add a command manually to your Controller.
> //
> var commandNameToCheck:String = requestMethodName + "Command";
> if ( commands [ commandNameToCheck ] != undefined )
> {
> //
> // Yes: Use the explicit command
> //
>
> // Create a new command
> var theCommand = new commands [ commandNameToCheck ] ();
>
> // Execute the command
> theCommand.execute( view );
> }
> else
> {
> //
> // No: Use automatic mapping to call the correct business
> // method on the Business Delegate
> //
> requestBusinessDelegate [ requestMethodName ].apply (
> requestBusinessDelegate, [ view, data ] );
> }
> }
>
>
> //////////////////////////////////////////////////////////////////////
> //////
> //
> // Singleton accessor method: If you override the base Controller
> class,
> // make sure your overwrite this static function with one that
> generates
> // an instance of your subclass.
> //
>
> //////////////////////////////////////////////////////////////////////
> //////
> public static function getInstance ()
> {
> if ( undefined == inst ) inst = new Controller();
> return inst;
> }
>
> }
>
>
>
> //////////////////////////////////////////////////////////////////////
> //
> //
> // Arp 3 Base Business Delegate
> //
> //
> //////////////////////////////////////////////////////////////////////
> class org.osflash.arp3.BusinessDelegate
> {
>
>
> //////////////////////////////////////////////////////////////////////
> //////
> //
> // Use the __resolve function to get notification of methods
> that don't
> // exist. We try to handle these requests with automatic
> proxying to
> // a server-side service if one is provided.
> //
> // For business methods that have client-side implementations,
> __resolve
> // will *not* get called and thus they have highest precedence.
> //
>
> //////////////////////////////////////////////////////////////////////
> //////
> function __resolve ( functionName )
> {
> // Does a specific service mapping exist for this method?
> // TODO
>
> // Yes: Proxy the method call to the service and register
> // the default success and failure handlers on the
> view
> // TODO
>
> // Does a global service declaration exist for this Business
> Delegate?
> // TODO
>
> // Yes: Proxy the method call to the service and register
> // the default success and failure handlers on the
> view
> // TODO
>
> // No: Raise an error as no explicit business method
> exists and
> // neither does a remote service proxy.
> }
> }
>
> /
> *********************************************************************/
>
> //////////////////////////////////////////////////////////////////////
> //
> //
> // Sample Application Class
> //
> //
> //////////////////////////////////////////////////////////////////////
> class com.somedomain.my.Application extends org.osflash.arp3.Form
> implements org.osflash.arp3.IView
> {
> //
> // Instance variables (members)
> //
> var mController:Controller;
>
> //
> // Declare requests.
> //
> // Controller subclasses will use these to automatically register
> themselves to listen for requests.
> // The naming convention states that request declarations must
> begin
> with "request". The base Controller
> // class uses for...in (AS2) and introspection (AS3) to map these.
> //
> var requestA;
> var requestB;
> var requestOtherOne;
>
>
> //////////////////////////////////////////////////////////////////
> ////
> //
> // Constructor.
> //
> // Do *not* place code here that relies on child components or
> forms.
> //
> //////////////////////////////////////////////////////////////////
> ////
> function Application()
> {
> // Nothing yet.
> }
>
>
> //////////////////////////////////////////////////////////////////
> ////
> //
> // viewInit() - A common initialization method that will be called
> // at onLoad() in Flash and onChildrenCreated() in
> Flex.
> //
> //////////////////////////////////////////////////////////////////
> ////
> function viewInit()
> {
> mController = ApplicationController.getInstance();
> mController.registerView ( this );
> }
>
>
>
> //////////////////////////////////////////////////////////////////////
> //
> // Request Example
> //
> //////////////////////////////////////////////////////////////////
> ////
>
> // A View method
> function someMethod()
> {
> // Get the value object
> var valueObject = getValueObject();
>
> // Make a system request: A request is really just an event by
> any other name but uses a naming convention, has a specific use,
> bundles
> necessary data and and has expectations associated with it.
> var myRequest:Request = new Request
> ( "requestACommandOnModel",
> valueObject );
> }
>
>
>
> //////////////////////////////////////////////////////////////////////
> //
> //
> // Event Handlers
> //
> //
>
> //////////////////////////////////////////////////////////////////////
>
>
> //////////////////////////////////////////////////////////////////////
> //
> // Requests
> //
>
> //////////////////////////////////////////////////////////////////////
>
> // Success
> function requestASuccess ()
> {
> // Will be called if request A completes successfully
> }
>
> // Failure
> function requestAFailure ()
> {
> // Will be called if request A fails
> }
>
>
>
> //////////////////////////////////////////////////////////////////////
> //
> // External Form Registrations
> //
> // To listen for events on external forms, create methods here
> using
> the following naming convention:
> //
> // onRegister[Screename]Screen
> // (eg. onRegisterLoginScreen is the place to add event listeners
> for the Login screen. The method
> // will be called when the external form is loaded, after its
> controller has been registered.)
> //
> //////////////////////////////////////////////////////////////////
> ////
>
> //
> // None
> //
>
> }
>
>
>
> //
> // THIS IS ACTUALLY NOT NECESSARY ==>
> //
>
> //////////////////////////////////////////////////////////////////////
> //
> //
> // My Application's Controller Class
> //
> //
> //////////////////////////////////////////////////////////////////////
> class ApplicationController extends org.osflash.arp3.Controller
> {
> }
>
> //
> // <== THIS IS ACTUALLY NOT NECESSARY
> //
>
>
>
> //////////////////////////////////////////////////////////////////////
> //
> //
> // A Sample Business Delegate Class on My Application
> //
> //
> //////////////////////////////////////////////////////////////////////
> class SomeBusinessDelegate
> {
> import org.osflash.arp3.services.IRemoteService;
>
> import org.osflash.arp3.services.RemotingService;
> import org.osflash.arp3.services.WebService;
> import org.osflash.arp3.services.XmlService;
>
> ` //
> // (Optional) If the remoteService variable is defined, it acts
> as the
> // global remote service for this business delegate. If no
> remoteService
> // is defined, the base Business Delegate class will check if a
> // remoteServiceMap is defined and use the method to service
> mappings in
> // that strucuture. If that doesn't exist either, you must
> implement
> // all the business methods locally on the client.
> //
> // The precedence rules are as follows:
> //
> // Local business method -> Method defined in remoteServiceMap ->
> global remote service
> //
> // Thus if a method is defined locally, it will override any
> custom
> // mapping or global remote service mapping (ie., the local
> method will
> // be executed.)
> //
> var remoteService:IRemoteService = RemotingService.getInstance();
>
> //
> // (Optional) If multiple methods have to use different remote
> services,
> // you need to specify a remoteServiceMap. It is kept in this
> // instance variable. The base Business Delegate class will call
> // the createRemoteServiceMap() method to initialize this object.
> //
> var remoteServiceMap:Object;
>
> //
> // (Optional) Create Remote Service Map, to proxy different
> business
> // methods to different services (this should be very useful when
> // migrating from, say, a legacy LoadVars or XML-based system to
> // Remoting, allowing incremental switchover on a per-method
> basis.)
> //
> var remotingService:RemotingService =
> RemotingService.getInstance();
> var webService:WebService = WebService.getInstance();
> var xmlService:XmlService = XmlService.getInstance();
>
> function createRemoteServiceMap ()
> {
> methodTable =
> {
> remotingService:
> [
> "someMethod"
> ],
>
> webService:
> [
> "aMethod", "anotherMethod", "yetAnotherMethod"
> ],
>
> xmlService:
> [
> "anXmlServiceMethod"
> ]
> };
> }
>
> //
> // Local methods
> //
> function aLocalBusinessMethod ( parameters )
> {
> // Business logic
> }
> }
>
>
>
>
> _______________________________________________
> Arp mailing list
> Arp at ariaware.com
> http://ariaware.com/mailman/listinfo/arp_ariaware.com
More information about the Arp
mailing list