Tuesday, September 15, 2009

Flex Compiler Integration with SHP

This is a continuation of Building FLEX Integration with SHP - please refer to it for refresher.

The next step of the integration is to do FLEX development directly within SHP. If you are used to develop FLEX apps in IDE such as Flex Builder, you might not necessary need this capability.  But consider the following:
  • potential for modularizing the flex scripts
  • potential for graceful degradations into ajax or pure html 
  • possibility for automating the generation of simple flex-based solutions 
 All of which are powerful building blocks for web development, so we'll give it a shot.  Let's get started.

Goal

We want to be able to write in MXML and ActionScripts in SHP, and have the script being compiled in real-time into the flash movie and served to browser transparently.

Example:

Here's a mxml shp script for hello world:

(mx:app (mx:script "private function clickHandler(evt:Event):void {
    messageDisplay.text = \"I am Glad, it does.\";
}")
     (mx:label "Flex without Flex Builder")
     (mx:button #:label "Yes, It Works!" #:click "clickHandler(event)")
     (mx:label #:id "messageDisplay"))
Which should be compiled into the following mxml:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private function clickHanlder(evt:Event):void {
    messageDisplay.text = "I am Glad, it does.";
}
 ]]>
</mx:Script>
<mx:Label text="Flex without Flex Builder" />
<mx:Button label="Yes, It Works!" click="clickHanlder(event)" />
<mx:Label id="messageDisplay" />
</mx:Application> 
And then compiled into a flash video file, and finally served to the client via the (flash) inclusion.  And we should check the timestamp of the SHP script against the compile flash video so we won't waste CPU cycle by compiling the same script over and over again.

The first thing to do is to ensure we can generate the correct MXML.  It is mostly straight forward:


(define (mx:app . widgets) 
  `(mx:Application ((xmlns:mx "http://www.adobe.com/2006/mxml"))
                   . ,widgets))

(define (mx:label (text "") #:id (id #f))
  `(mx:Label ((text ,text)
              ,@(if (not id) '() `((id ,id))))))

(define (mx:button #:label (label "") #:click (click #f))
  `(mx:Button ((label ,label) 
               ,@(if (not click) '() `((click ,click))))))




;; more mxml widget definitions  
With the above we now can generate the MXML, albeit in an incomplete form.  The next step would then be to get the generated mxml *saved* to a predefined location, instead of serving them.



***UPDATE*** 2009/9/15 - I decided to just allow xexpr for mxml in bzlib/flexer until I have better handle on exactly what mxml elements are in use, so I will rollback the support for mx:button, mx:script, etc, and just let xexpr take its place.

Saving The Generated Output To File

Redirectly the output from mx:app would basically require a function that transforms the xexpr into string and then save it to a file, so our job is to figure out where we want to save it.  By default we would save them into $htdocs/mxml (so make sure you have it created in your $htdocs directory).

(define mxml-base-path (make-parameter "/mxml"))

(define (mxml->string mxml)
  (xexpr->string mxml))

;; determine the path of the mxml-file 
(define (mxml-path path)
  (build-path* ($htdocs) (mxml-base-path) path))

(define (mxml path . widgets)
  (let ((app (apply mx:app widgets)))
    (call-with-output-file 
        (mxml-path path)
      (lambda (out)
        (display (mxml->string app) out))
      #:exists 'replace)
    app))
Compile the MXML with Flex SDK 

The FLEX SDK has a commandline compiler so we can incorporate it for compilation.  You just need to make sure the compiler is in the system path.

(define (compile! path) 
  (sys/call "mxmlc"
            (path->string (mxml-path path))
            "-output"
            (path->string (flash-path (string-append path ".swf")))))
And then we should call the compiler and then return the flashing embedding call (flash <path>) instead of returning the raw mxml:

(define (mxml path #:id (id #f) #:height (height 400) #:width (width 400) . widgets)
  (let ((app (apply mx:app widgets)))
    (call-with-output-file 
        (mxml-path path)
      (lambda (out)
        (display (mxml->string app) out))
      #:exists 'replace)
    (compile! path) 
    (flash (string-append path ".swf") #:id id #:height height #:width width)))
If you have the mxmlc setup correctly - you'll see the just-in-time compiled flash video being served up.  As of this moment, the integration concept is complete.

Of course, mxmlc is slow even for trivial flash object compilations and hence it shouldn't be a hit that you want to take constantly, even during development stage - what we need is some optimizations so the flash video is only compiled when necessary.  We will talk about the optimizations next time.  Stay tuned.

No comments:

Post a Comment