Flex Reader on Google App Engine

This entry is part of 3 in the series Flex on GAE

This is the second tutorial in a planned series of four, and is about my FlexReader application. It will introduce

After web search, Google Reader is the second Google application, that I use most often. Google Reader is a free online feeds aggregator. It had a Beta status for a long time, but is now officially released. Most of all I like the vi like scrolling with the j and k keys. Since I was evaluating FlexBuilder and had already made an application on Google App Engine, I continued the trend by making a scaled down imitation of Google Reader in Flex 3. FlexReader deviates from Google Reader by having a public shared repository of feeds, instead of a per user one. It also allows you to comment, or rate read items.

FlexReader demo video ( logo displayed from shareware product ).

In Flex Search on Google App Engine the basic concepts of Flex and Google App Engine were covered. As I promised I will go into the more the technical details this time, which means that I will assume some basic knowledge of Flex and Google App Engine. This is also necessary, because FlexReader is technically more challenging than FlexSearch.

Table of contents

  • 1 Flex front-end
    • 1.1 The main FlexReader application
    • 1.2 Requesting feed information
    • 1.3. FeedEntryCollection
    • 1.4 Flex unit testing
    • 1.5 Viewing comments and e4x
  • 2. Python back-end
    • 2.1 The main Python script handler main.py
    • 2.2 The Feed and EntryComment model
    • 2.3 Adding a feed
    • 2.4 Discovering or finding feeds
    • 2.5 Adding comments
  • 3. Resources
  • 4. Conclusion

1 Flex front-end

I have listed with the tree command, the folder structure of part of my Flex project below. Further, you can see on the right, which Python scripts on Google App Engine communicate to which Flex components.

 |-- FlexReader.as                           
 |-- FlexReader.mxml
 `-- net
     `-- ivanidris
         `-- flexreader
             |-- FeedEntry.as
             |-- FeedEntryCollection.as
             |-- FeedInfo.as
             |-- FeedRequest.as                 ----       main.py
             `-- windows
                 |-- AddComment.mxml            ----       addComment.py
                 |-- AddFeed.mxml               ----       addFeed.py
                 |-- CommentsWindow.mxml        ----       viewComments.py
                 |-- Find.as                    ----       findFeed.py, addSelectedFeeds.py
                 `-- FindFeed.mxml

FlexReader.mxml in Listing 1 is the main application GUI descriptor file. FlexReader.as contains most of the programming logic of the main application.The net.ivanidris.flexreader package contains various Actionscript classes. The MXML files in the net.ivanidris.flexreader.windows package describe various pop up dialogs.

The content element of the feed entries can contain HTML tags. Flex components have very poor HTML rendering capabilities, consequently I mixed a bit of Javascript as well, but otherwise I tried to use Flex as much as possible. I added a Javascript function in the web page that contains the Flex application.

1
2
3
function setContent(s) {
    document.getElementById('preview').innerHTML = s;
}

In case you are wondering, the function sets the content of the div container with id preview, to have the HTML string, that is passed in the parameter s. See the Resources section for details about the skins and art in this application.

1.1 The main FlexReader application

Let’s start with the main MXML file FlexReader.mxml ( Listing 1 ). The feed items are displayed in a Flex Grid. The buttons to the right enable the user to add feeds, discover feeds or view rated items. A set of keyboard commands have been defined as well: j moves down, k moves up, o opens page, b bookmarks the link this feed item is pointing to, r reloads and c opens a comment pop up.

Figure 1: The FlexReader user interface and UML class diagram.

flex readerflex reader class diagram

You can see in the class diagram of Figure 1 that the FlexReader, FeedEntryCollection, FeedEntry and Counter classes are involved. FeedEntryCollection is covered in 1.3.

Listing 1: FlexReader.mxml the main Flex application.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" 
   xmlns:iic="net.ivanidris.flex.components.*"
   applicationComplete="initApp();" viewSourceURL="srcview/index.html">
   <mx:Script source="FlexReader.as"/>
   <mx:Style source="main.css"/>
   <iic:NavigationHeader/>
   <mx:Panel id="mainPanel" width="800" height="385" title="Feed Reader. Right click to view source.">
      <mx:Spacer width="100%"/>
      <mx:HBox>
            <mx:Label fontWeight="bold" id="currentDisplay"/>
            <mx:Label fontWeight="bold" id="display"/>
      </mx:HBox>
 
       <mx:HBox>
         <mx:Grid id="readerGrid" width="500" >
            <mx:GridRow>
               <mx:GridItem backgroundColor="#FFFCFC">
                  <mx:Label id="label1"/>
               </mx:GridItem>
            </mx:GridRow>  
         </mx:Grid>
         <mx:Grid id="feedsPanel">
            <mx:GridRow>
               <mx:GridItem>
                  <iic:PushButton id="addFeedButton" label="Add Feed" click="addFeed();"/>
                  <iic:PushButton id="discover" label="Find Feed" click="findFeed();"/>
                     <iic:PushButton label="View Rated" id="ratedButton" click="viewRated();"/>
               </mx:GridItem>
            </mx:GridRow>
 
            <mx:GridRow>
                 <mx:GridItem>
                      <mx:Text text="j down, k up, o opens page"/>
                 </mx:GridItem>
            </mx:GridRow>
            <mx:GridRow>
                 <mx:GridItem>
                      <mx:Text text="b bookmark, r reload, c comment."/>   
                 </mx:GridItem>
            </mx:GridRow>
         </mx:Grid>
       </mx:HBox>
   </mx:Panel> 
</mx:Application>

The Script tag imports FlexReader.as of Listing 2, that contains most of the Actionscript code. When the application GUI is done loading the method initApp in FlexReader.as is called. The aforementioned function performs initialization and starts the actual application. The main container is a Flex Panel. HBox is a box, where components are layout horizontally. Grid corresponds to the table HTML tag. The GridRow tag is the equivalent of the tr HTML tag. The same goes for the GridItem tag and HTML td tag. Every button has a click property linked to a function in FlexReader.as – here below:

Listing 2: FlexReader.as the Actionscript code behind FlexReader.mxml.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import flash.events.KeyboardEvent;
 
import mx.managers.PopUpManager;
 
import net.ivanidris.flex.Counter;
import net.ivanidris.flexreader.FeedEntryCollection;
import net.ivanidris.flexreader.FeedRequest;
import net.ivanidris.flexreader.windows.*;
 
   private var entries:FeedEntryCollection = new FeedEntryCollection();
   private var counter:Counter = new Counter(0);
   private var feedLinks:Array = new Array();
 
    public function getFeed() :void {
        var request:FeedRequest = new FeedRequest(this, this.entries, this.counter, this.feedLinks);
        request.initGrid();
        request.getFeed();
    }
 
    public function viewRated(): void {
        var commentsWindow:CommentsWindow =
             CommentsWindow(PopUpManager.createPopUp(this, CommentsWindow, false));
    }
 
    public function addFeed():void {
        var addWindow:AddFeed =
             AddFeed(PopUpManager.createPopUp(this, AddFeed, false));
    }
 
    private function initApp():void {
      addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
        getFeed();
    }
 
    private function onKeyDown(e:KeyboardEvent):void {
        if(String.fromCharCode(e.charCode) == 'r' ) {
            var request:FeedRequest = new FeedRequest(this, this.entries, this.counter, this.feedLinks);
            request.getFeed();
        }
    }
 
    public function findFeed():void {
        var findWindow:FindFeed =
             FindFeed(PopUpManager.createPopUp(this, FindFeed, false));
    }

But first, the function initApp is called. It registers the function onKeyDown to be triggered whenever the ‘r’ key is pressed. This effectively refreshes the reader list. viewRated, addFeed and findFeed produce a window using a related class. PopUpManager’s createPopUp method is responsible for the actual window creation. createPopUp returns an instance of the Actionscript Object, which is similar to the Object class in Java, and is likewise at the root of the class hierarchy. This means, that a cast has to performed to the appropriate type. getFeed performs a feed request with the help of FeedRequest.

Series Navigation
By the author of NumPy Beginner's Guide, NumPy Cookbook and Instant Pygame. If you enjoyed this post, please consider leaving a comment or subscribing to the RSS feed to have future articles delivered to your feed reader.
Share
This entry was posted in programming and tagged , . Bookmark the permalink.