Fernando Correia’s Weblog

July 30, 2008

Flex client using PureMVC

Filed under: Software Development — Fernando Correia @ 8:42 pm
Tags: ,

Continuing my quest to build a Flex client to a Python service on Google App Engine, I decided to try converting my previous example from the Cairngorm framework to PureMVC.

I liked the PureMVC approach better. Its notification mechanism is very simple and powerful. The view components are quite independent from the rest of the application. And, best of all, the framework’s architect, Cliff Hall, has a clear vision that the framework must be kept simple, with few features. He also is very active on writing documentation, examples and answering user questions on the forums.

After reading all documentation and examples I could find, I set out to convert one of those examples to deal with my business entitites: projects.

I must warn that what I am showing here is an exploratory project and a work in progress. It doesn’t represent, by any stretch, the best practices. By documenting my journey I intend to help others that may follow, and also to get some feedback and help on better approaches.

This is what the application looks like:

Using PureMVC the application is structured around a Façade object that has a reference to a collection of Models, a collection of Views and a collection of Controllers. Like this:

The model uses a “proxy” class that has the responsability of managing model objects. The view uses “mediator” classes that have the responsability of connecting the view to the application without too much coupling. And the controller has commands that are invoked by the framework when certain notifications are raised.

It is a good architecture with low coupling, high cohesion and clear separation of responsabilities. It is also lightweight and more concerned on providing guidance and structure than on restricting what the programmer can do. Currently I am considering this my framework of choice for my Flex experiments.

My post is not about explaining how PureMVC works. The project website does it quite well already, at least for those who are willing to invest a few hours learning it. But I will show some snippets of code that may at least raise some curiosity:

This is how the projects list forwards the “New project” command:

private function onNew(event:Event):void
{
    var project:ProjectVO = new ProjectVO();
    sendNotification(ApplicationFacade.NEW_PROJECT, project);
}

This is how the project form prepares itself for entering a new project:

case ApplicationFacade.NEW_PROJECT:
    projectForm.project = note.getBody() as ProjectVO;
    projectForm.mode = ProjectForm.MODE_ADD;
    projectForm.code.setFocus();
    break;

When the user submits the new project, this is how the project form handles this event:

private function onAdd(event:Event):void
{
    var project:ProjectVO = projectForm.project;
    projectProxy.addItem(project);
    sendNotification(ApplicationFacade.PROJECT_ADDED, project);
    clearForm();
}

So, the view mediator, that is a kind of Supervising Controller, causes the model to update itself with the new project’s data. This method in the model’s proxy class is implemented like this:

public function addItem(item:Object):void
{
    Gateway().call("ProjectService.save", new Responder(onAddItemResult), item);
}

So, it talks directly to the service using a remote object gateway.

There is also a Participants list, but it is not persisted to the server yet. It is only kept in the client’s memory and goes away when the application is closed. I am still trying to figure out the better way of dealing with it.

I hope this helps. The project is published on github. Feedback is welcome.

July 25, 2008

A few explanations about my test project

Filed under: Software Development — Fernando Correia @ 8:15 am
Tags: , ,

I thank Rien for giving valuable feedback on my test project. He had some questions that show I am not explaining my approach well enough. So I will try to clarify. Let see:

Love your idea of starting a good commented pytthon-flex-cairngorm project! Very much needed indeed!

Glad to hear you think that too. I think that as we share what we learn, all of us will benefit. One thing, though: this project is about a Flex client to Google App Engine. Not necessarily about Cairngorm. I will probably go the way of PureMVC. No problem for Cairngorm users, anyway, because the project is about the client-server integration, not about the framework for the Flex client.

I got your project running, there’s only one thing I do not understand just yet.

You have 2 folders “server” and “python-client”. They contain each pyamf in its whole? Is that necsessary?
It would help me and others much if you could explain why they are splitup in 2 seperate folders.

I agree that is a waste. Space is cheap, though. My reasoning is that I want my “server” directory to contain only what must be running at Google App Engine. No client tools at all. That is, I want a self-contained web service on GAE, and nothing else.

Since I also wanted a Python client to be able to test PyAMF in a way that is independent of Flex, that led me to create a “python-client” directory. I put a separate copy of PyAMF there so both subprojects (Python server and client) would be independent and self-contained, with no external dependencies.

And how the client.py knows of ProjectService.py and EchoService.py I don’t seem to get how they are linked up?

Well, first about the server. It does not know about any client. It only knows about requests that come through PyAMF.

About the clients, both (Python and Flex) are bound to the server in a naïve way that is suitable only for a test project. They use a fixed URL to the server (http://localhost:8080/) and they use the name of two services (EchoService and ProjectService) that are routed inside main.py in the server to the actual implementation classes.

Because I only start up “dev_appserver.py server”? And then openup the flex app.

You can start only one instance of the server on port 8080 and have both the Python and the Flex client talking to it.

I hope that clarifies a bit. Feel free to ask if you have more questions. Just keep in mind that I am still learning all this.

Good luck!

July 23, 2008

Cairngorm Flex client talking to Google App Engine

Filed under: Software Development — Fernando Correia @ 8:23 pm
Tags: ,

As I move forward in my project to learn about Flex and Google App Engine, I converted it to use the Cairngorm Flex framework.

My previous iteration was monolitic. Presentation, business and data access concerns where all mixed in a single source file.

This technique served my purpose to be able to see all my program in a single glance while I tried to learn how Flex worked. But this approach doesn’t scale for larger projects.

So I obviosly needed to learn how to get a better organization of a data-driven Flex application. Cairngorm was an obvious choice because it is supported and promoted by Adobe. They are even so kind as to send me periodic e-mails with tips about it!

So, I read Adobe’s introductory paper (Flex 3: Introducing Cairngorm, June 2008) and used the flex_ror_sdk_issue_tracker2 from the Ruby on Rails RIA SDK by Adobe project as an example.

In a few hours I had my application in a new structure. I even learned how to separate interface components in views, like this one:

User gestures are announced as events:


private function DeleteSelectedProjectClicked():void {
    var event:DeleteProjectEvent = new DeleteProjectEvent(dgProjects.selectedItem);
    event.dispatch();
    getProjects();
}

Such events are processed by commands like this:


public class DeleteProjectCommand implements ICommand
{
    private var __model:ModelLocator = ModelLocator.getInstance();
    private var __locator:ServiceLocator = ServiceLocator.getInstance();

    public function execute(event:CairngormEvent):void
    {
        var project:Object = (event as DeleteProjectEvent).project;
        var service:Services = __locator as Services;
        service.gateway.call("ProjectService.delete", new Responder(onResults), project);
    }

    public function onResults(result:Object):void {
    }
}

I can see that Cairngorm is a viable alternative to build Flex applications, specially larger ones. But there are some aspects of it that seem a bit odd to me. For instance, the idea of using a big bucket of global vars as your repository for model data. And also the way I have to repeat myself creating events and commands in paralel, and having to remember to include them in the controller.

My next step will be to experiment with the PureMVC framework, an alternative to Cairngorm.

My sample project is hosted on github. It is able to show, create, update and delete projects using a service written in Python and hosted on Google App Engine. The Flex client uses the Cairngorm framework and the Python service uses the PyAMF library for communication with the client.

Please be advised that this is a learning project and does not represent a production-quality application or an example of best practices.

Feedback is welcome.

Edit: This is an ongoing project that has advanced quite a lot since this article. Browse my blog if you’re interested in learning about the progress of this example.

July 20, 2008

CRUD operations on Google App Engine

Filed under: Software Development — Fernando Correia @ 6:22 pm
Tags: ,

Now my Flex and Python Test project has the basic CRUD operations. It can create, retrieve, update and delete objects.

This is how the operations are implemented on the server:


class Project(db.Model):
    code = db.IntegerProperty()
    name = db.StringProperty()
    created_at = db.DateTimeProperty(auto_now_add=True)
    modified_at = db.DateTimeProperty(auto_now=True)

class ProjectService:
    def get(self, code):
        logging.debug('get %s' % (code))
        project = Project.gql("WHERE code = :1", code).get()
        return project

    def insert(self, project):
        logging.debug('insert %s' % (project))
        new_project = Project()
        new_project.code = int(project.code)
        new_project.name = project.name
        new_project.put()
        return new_project

    def update(self, project):
        logging.debug('udpate %s' % (project))
        existing_project = Project.get(project._key)
        existing_project.name = project.name
        existing_project.put()
        return Project.get(project._key)

    def save(self, project):
        logging.debug('save %s' % (project))
        if hasattr(project, '_key') and project._key != None:
            return self.update(project)
        else:
            return self.insert(project)

    def delete(self, project):
        logging.debug('delete %s' % (project))
        existing_project = Project.get(project._key)
        existing_project.delete()

    def get_all(self):
        logging.debug('get_all')
        return Project.all().fetch(1000)

In my project, these operations are used in a Flex form and in a Python command-line application. All the source code is published in github. Feedback is welcome.

Edit: This is an ongoing project that has advanced quite a lot since this article. Browse my blog if you’re interested in learning about the progress of this example.

July 19, 2008

Flex client updating objects in Google App Engine

Filed under: Software Development — Fernando Correia @ 10:31 pm
Tags: , ,

I am continuing to learn how to integrate Flex to Google App Engine. Now my test project is able to update objects, in addition to inserting and retrieving them.

On the server side, this is done by the update service operation:

def update(self, project):
    logging.debug('udpate %s' % (project))
    existing_project = Project.get(project._key)
    existing_project.name = project.name
    existing_project.put()
    return Project.get(project._key)

There is also a new save operation that decides between insert and update depending on the state of the object. If the object was read before, it is updated. Otherwise, it is inserted:

def save(self, project):
    logging.debug('save %s' % (project))
    if hasattr(project, '_key') and project._key != None:
        return self.update(project)
    else:
        return self.insert(project)

The App Engine model is also saving the date and time each object was last modified:

class Project(db.Model):
    # ...
    modified_at = db.DateTimeProperty(auto_now=True)

On the client side, the save operation is called after an object is prepared. If the object was being edited, its key was saved and so the service will know it must be updated. If it is a new object, the key will be null so the service will insert a new object.

public function submitProject():void {
    var newProject:Object = new Object();
    newProject.code = project_code.text;
    newProject.name = project_name.text;
    newProject._key = project._key;
    gateway.call("ProjectService.save", new Responder(null, onFault), newProject);
    getProjects();
}

The _key property is a very important piece of information in this context. So I patched the PyAMF library to include it within every object it sends to the client. There is a ticket for that on the PyAMF project.

def writeObjectAMF(self, obj, args, kwargs, remove):
    """
    Writes an object that has already been prepared by writeObjectAMF0 or writeObjectAMF3.
    """
    try:
        obj._key = str(obj.key())
    except:
        obj._key = None
    self.writeObject(obj, *args, **kwargs)
    del obj._key
    if remove:
        self.context.class_aliases[obj.__class__] = None

All the source code for this project is hosted in github. Please be aware that I am sharing what I find out as I learn. I am sure there are better ways of using this tools. I hope to learn them. Comments are welcome.

July 18, 2008

Debugging Flex apps with Firefox

Filed under: Software Development — Fernando Correia @ 9:23 am
Tags:

While I was building my Flex client to a Google App Engine application, I was not able to make the Flex 3 debugger work.

I found out that this is a known bug related to Firefox 3. There is a workaround of setting the wmode parameter to “opaque” in index.template.html, but that didn’t work for me.

What did work, though, was to set Flex 3 to use another browser. This is done by opening the Window | Preferences dialog and adding a new browser in the General | Web Browser section.

I was able to debug using Internet Explorer. Later I installed Safari and it worked too. It seems to open faster than IE, so I’ll probably use it to debug my Flex apps until the issue with Firefox 3 is sorted out.

July 17, 2008

Flex client talking to GAE

Filed under: Software Development — Fernando Correia @ 10:29 pm
Tags: ,

In a new iteration of my Flex and Python Test project, I created a crude Flex client. It is able to talk to the GAE server using AMF and perform two operations: retrieve all projects, and insert a new project. Both the client and the server see the data as objects.

This is what the user interface looks like:

And this is the Flex source code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onInit()">
    <mx:Script>
       <!&#91;CDATA&#91;
        import flash.net.Responder;
        import mx.controls.Alert;

           &#91;Bindable&#93;
        public var dataProvider:Array;

        &#91;Bindable&#93;
        public var statusMsg:String;

        public var gateway:NetConnection;

        public function onInit():void
           {
            statusMsg = "Loading data...";
               gateway = new NetConnection();
               gateway.connect("http://localhost:8080/");
            getProjects();
          }

        public function getProjects():void {
            gateway.call("ProjectService.get_all", new Responder(getResult, onFault));
        }

        public function getResult(result:Array):void {
            dataProvider = result;
            statusMsg = "Data loaded.";
        }

        public function submitProject():void {
            var newProject:Object = new Object();
            newProject.code = project_code.text;
            newProject.name = project_name.text;
            gateway.call("ProjectService.insert", new Responder(null, onFault), newProject);
            getProjects();
        }

        public function onFault(info:Object):void {
            statusMsg = info.toString();
        }
       &#93;&#93;>
    </mx:Script>

    <mx:DataGrid id="dgProjects" x="10" y="10" dataProvider="{dataProvider}"
        width="356" height="183">
        <mx:columns>
            <mx:DataGridColumn headerText="Code" dataField="code"/>
            <mx:DataGridColumn headerText="Name" dataField="name"/>
        </mx:columns>
    </mx:DataGrid>
    <mx:Button x="374" y="11" label="Reload" click="onInit()"/>
    <mx:Label x="10" y="203" text="Created at"/>

    <mx:TextInput x="103" y="201" id="selected_created_at"
        text="{dgProjects.selectedItem.created_at}" width="263"/>
    <mx:Form x="10" y="240" width="356" borderStyle="solid" cornerRadius="2">
      <mx:FormHeading label="New Project"/>
      <mx:HBox>
         <mx:Label text="Code"/>
         <mx:TextInput id="project_code" width="159"/>
      </mx:HBox>
      <mx:HBox>
         <mx:Label text="Name"/>
         <mx:TextInput id="project_name"/>
      </mx:HBox>
      <mx:Button label="Insert" click="submitProject()"/>
    </mx:Form>
    <mx:Label x="10" y="384" text="{statusMsg}" width="356"/>
</mx:Application>

The project is hosted in github. Feedback is welcome.

July 16, 2008

Inserting and retrieving objects using PyAMF with GAE

Filed under: Software Development — Fernando Correia @ 9:50 pm
Tags: ,

I advanced another step on my Flex and Python Test project. Now it is able to insert new objects in the data store and to retrieve them.

The Python client has some new abilities:

client.py test

Runs an Echo test against the server. This is the result:

test
TEST
1

client.py insert code name

This command inserts new objects on the data store. For instance:

client.py insert 10 "Project Ten"
client.py insert 12 "Project Twelve"

client.py get code

This command gets an object by one of its fields (code). For instance:

client.py get 10

Results in:

Project code = 10, name = Project Ten, created at 2008-07-17 00:20:32

The data is returned as an object.

client.py all

Retrieves all objects as a list:

Project code = 10, name = Project Ten, created at 2008-07-17 00:20:32
Project code = 12, name = Project Twelve, created at 2008-07-17 00:20:55

Some code highlights:

in server/services/ProjectService.py:

class ProjectService:
    def get(self, code):
        project = Project.gql("WHERE code = :1", code).get()
        return project
        
    def insert(self, code, name):
        project = Project()
        project.code = code
        project.name = name
        project.put()

    def get_all(self):
        return Project.all().fetch(1000)

in python-client/client.py:

def insert(code, name):
    gw = RemotingService('http://localhost:8080/')
    service = gw.getService('ProjectService')
    service.insert(int(code), name)

def get(code):
    gw = RemotingService('http://localhost:8080/')
    service = gw.getService('ProjectService')
    project = service.get(int(code))
    if project == None:
        print "Project %s not found." % (code)
    else:
        print_project(project)

def all():
    gw = RemotingService('http://localhost:8080/')
    service = gw.getService('ProjectService')
    projects = service.get_all()
    for project in projects:
        print_project(project)

My next step will be to add these abilities to the Flex client as well.

The project is hosted in github. Comments are most welcome.

July 15, 2008

Flex and Python project

Filed under: Software Development — Fernando Correia @ 1:26 pm
Tags: , ,

I am working on a project to learn how to use Google App Engine. This is the architecture:

The client will use Adobe Flex to have a rich interface without the hassle of dealing with differences in the implementation of HTML, CSS and Javascript between browsers. The server will be a Python service on Google App Engine. The client will talk to the server using AMF encoded messages over HTTP. If possible, using a RESTful convention.

The goal of this experiment is to create a Web application with a rich user interface and a fast response time in a high-performance server environment without worrying too much about server administration.

The flex-and-python-test project is available in github.

Some highlights of the source code:

server/main.py:

import wsgiref.handlers
from pyamf.remoting.gateway.wsgi import WSGIGateway
from services import EchoService, ProjectService

services = {
    'EchoService': EchoService.EchoService,
    'ProjectService': ProjectService.ProjectService,
}

def main():
    application = WSGIGateway(services)
    wsgiref.handlers.CGIHandler().run(application)

if __name__ == '__main__':
    main()

server/services/EchoService.py:

class EchoService:
    def echo(self, data):
        return data;

    def echo_upper(self, data):
        return str(data).upper();

python-client/client.py:

from pyamf.remoting.client import RemotingService

gw = RemotingService('http://localhost:8080/')

print "Testing EchoService:"
service = gw.getService('EchoService')
print service.echo('test')
print service.echo_upper('test')
print service.echo_upper(1)

print "Testing ProjectService:"
service = gw.getService('ProjectService')
print service.get(1)
print service.get_all()

flex-client/src/main.mxml:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:RemoteObject id="remoteObj" endpoint="http://localhost:8080/"
  destination="EchoService"
  result="resultField.text = event.toString(); outputField.text = event.result.toString();"
  fault="resultField.text = event.toString()"/>
  <mx:Label x="10" y="10" text="Input:"/>
  <mx:TextInput x="70" y="10" id="inputField"/>
  <mx:Button x="238" y="10" label="Submit" click="remoteObj.echo_upper(inputField.text)"/>
  <mx:Label x="10" y="42" text="Output:"/>
  <mx:TextInput x="70" y="40" width="652" id="outputField" editable="false"/>
  <mx:Label x="10" y="71" text="Result:"/>
  <mx:TextArea x="70" y="70" width="652" height="332" id="resultField" editable="false"/>
</mx:Application>

The first version of this project is working. The next stop will be model objects.

Edit: This project is now finished. You can see the final result in this article.

Create a free website or blog at WordPress.com.