Perfect Gradients for Perfect Buttons

Posted in Justin Shacklette on May 10th, 2010 by admin

admin originally posted this on Saturnboy.

Photoshop does this annoying thing where purely vertical gradients have some horizontal variation. Yes, it’s usually only plus or minus one bit of color, but it offends! I’ve battled Photoshop for a while on this, but I just can’t seem to get exactly what I want out of it. So to make a perfect gradient, I decided to write some code. The requirements are simple: given a starting color and a set of deltas, output a perfect gradient.

Here are some quick examples:

1
gradient1
2
gradient2
3
gradient3
#000000
4, 1, 0.25
#eeeeff
-2.2, -1, -0.3
#ff0099
-1, 0, 1
 

If we zoom in on example #1, which starts with black (#000000) and has deltas of 4, 1, 0.25, we see the following:

zoomed gradient
 

The diagram shows the first ten rows of the gradient. The delta values are accumulated with each row, and only the whole part of the resulting color value is used (aka I take the floor of each color bit). So in this example, using the fractional delta of 0.25 results in exactly one additional blue bit every four rows. Ahhh, perfect!

The Code

No need to use some fancy new language, I wrote a simple PHP program to handle commandline input and output a perfect PNG gradient. The interesting part is the function that generates and saves the gradient:

function build_image($filename, $w, $h, $color, $delta) {
  $img = imagecreatetruecolor($w, $h);
 
  $c = imagecolorallocate($img, $color[0], $color[1], $color[2]);
  $d = $delta;
 
  for ($y = 0; $y < $h; $y++) {
    imagefilledrectangle($img, 0, $y, $w - 1, $y + 1, $c);
 
    $c = imagecolorallocate($img,
      clamp(floor($color[0] + $d[0])),
      clamp(floor($color[1] + $d[1])),
      clamp(floor($color[2] + $d[2]))
    );
 
    $d = array($d[0] + $delta[0], $d[1] + $delta[1], $d[2] + $delta[2]);
  }
 
  imagepng($img, $filename);
  imagedestroy($img);
}

The code is straight forward. First, create the image via imagecreatetruecolor(). Then, starting with the starting color, draw a one pixel tall rectangle for each row of the image. The next row’s color is computed in each iteration by adding the accumulated delta to the starting color. Finally, output the image as a PNG via imagepng() and free the memory. The complete php source can be downloaded here.

Button Time

Once we have our perfect gradient engine in place, it’s time to make some perfect buttons. To achieve the standard glass button look-and-feel, I typically fuse two gradients together: light on the top, dark on the bottom.

Here are the two halves of a pretty red button, along with their starting color and deltas:

TOP
BOTTOM
top
bottom
#ff8080
-3,-3,-3
#d23c3c
-3,-3,-3
 

And the two commandline invocations of gradient.php to create the gradients:

php gradient.php 100x16 ff8080 -3,-3,-3 top.png
php gradient.php 100x16 d23c3c -3,-3,-3 bottom.png

If I want my buttons to be sexy, rounded corners are a must. My favorite photoshop trick to create multiple rounded buttons is to use a rounded alpha-transparent button with each gradient as a clipping mask. Using a clipping mask is a simple way to guarantee button geometry remains fixed while colors are changed.

Here is the layers pane showing the two gradients fused together and used as a clipping mask for the rounded alpha-transparent button:

clip mask
 

The result is a horizontally stretchable gradient button, that doesn’t look half bad. See for yourself:

button
 

Custom UIButton

The final button asset can be used as desired, but here is a simple Objective-C example since I’ve been in iPhone world lately:

UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setFrame:CGRectMake(20, 20, 140, 32)];
[btn setBackgroundImage:[[UIImage imageNamed:@"btn-red.png"]
      stretchableImageWithLeftCapWidth:10.0
      topCapHeight:0.0] forState:UIControlStateNormal];
[btn setTitle:@"BUTTON" forState:UIControlStateNormal];
[btn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[btn.titleLabel setFont:[UIFont boldSystemFontOfSize:14]];

Create a new UIButton of type UIButtonTypeCustom and then set the button skin as the backgroundImage. The horizontal stretchability is due to the stretchableImageWithLeftCapWidth and topCapHeight.

Here is a screenshot from the iPhone simulator showing the button in action:

screenshot
 
Files
  • gradient.php – the perfect gradient engine
  • gradient-button.psd – the photoshop source for the red button image, including the rounded alpha-transparent button and fused red gradients
  • btn-red.png – the red button image

Schizophrenic Following

Posted in Justin Shacklette on May 5th, 2010 by admin

admin originally posted this on Saturnboy.

This post is a follow-up to my previous post, The Schizophrenic Programmer. I must have struck a chord, because I got a ton of fantastic feedback not just on this blog, but also on Reddit and DZone.

After further reflection, I still agree with what I said and my conclusions. Moving forward, I plan to learn fewer languages and less syntax. My daily pain is that too much of my knowledge is too tightly bound to the implementation details, and when I switch languages I can’t bring my solutions to bear. It sucks, and it makes me feel stupid. So the simple fix is to put my energy into learning more unbound concepts (like my soft skills of speaking and writing) and more easily transferable concepts (like APIs).

Analytics

google-analytics

I had a huge bump in traffic mostly due to Reddit and DZone that basically crushed my regular traffic down into the noise. My poor slice at Slicehost was taking a beating, so I quickly installed the Hyper Cache plugin. It’s dead simple and it really works. On Sunday morning, my load went from 0.2 to 0.02 instantly.

I also tracked my twitter analytics via bit.ly. As a side note, you can just put a plus “+” at the end of any bit.ly url to see it’s analytics. So, since the original post was shortened to http://bit.ly/cR6cnY, the analytics are available here: http://bit.ly/cR6cnY+.

Commentary

Lots of stuff was said, but I just wanted to touch on some of the key points.

  1. I feel your pain – thanks. I find it comforting to know I’m not alone.
  2. definition of schizophrenia – a couple of comments mentioned that my post had nothing to do with schizophrenia. First, I suggest you look it up: “contradictory or antagonistic qualities or attitudes”. My Erland knowledge is definitely in opposition with my Java, which is in opposition with my Actionscript, etc. If you don’t have this problem, good on you. Second, there’s thing called artistic license and if you are a writer you get to use it. I picked a sensational word for a reason. I welcome criticism of the choice, but it’s silly to take umbrage with its validity.
  3. short sighted – my plan was called out as short sighted a couple of times. I tend to think of life and plans like this… There are three choices when it comes to plans. First, you can have no plan, and just drift along. Sometimes you get smashed into the rocks, and other times you wash up on a tropical paradise with beer, virgins, and fiber broadband. Second, you can have a short term plan that gets revisited and revised as things change. Third, you can have a short term plan, but be so unbelievably naive that you think it is actually some awesome long term plan. Life is agile. All plans are short term.
  4. embrace and extend – a few people said don’t fight it and embrace the insanity. I really lust after new stuff, so it was easy to try to the embrace-the-insanity method first. Alas, it didn’t work out and I think it actually made matters worse. Thus, I decided to go with a new plan to fight against the insanity.
  5. python – a couple people mentioned I should switch to python. Yes I’ve written many thousands of lines of python, but none in 2010 so far.

Thanks for reading. As always, I love feedback.

The Schizophrenic Programmer

Posted in Justin Shacklette on April 21st, 2010 by admin

admin originally posted this on Saturnboy.

Hi, my name is Justin and I have a problem. I’m insane!1

Being insane really sucks. But I’ve got a plan to get back on track. First, I’ll dole out some blame. Then, I’ll give a little background. Next, I’ll really dig into my current insanity. And finally, I’ll layout my plan for the future.

The Blame

I like to curse. And when I’m mad, I find it definitely helps to calm the rage. So to begin, I’d like to give a big fuck you to Andy Hunt and Dave Thomas, the pragmatic programmers. I love you guys but you get at least some of the blame for making me insane. Next, I’d like to give some blame to my wonderful employer, Gorilla Logic, and their penchant for landing such a diverse set of inspiring projects. You suck for building a candy store and filling it with the sweetest treats.

Unfortunately for me, the lion’s share of the blame is mine and mine alone. I’m insane today because of my lust for the new coupled with my desire to be great. I want to be a great dad, a great husband, and a great employee. Who wants to be average?

It’s just not possible that I’m alone. Who doesn’t like cool new stuff? Who doesn’t want to be great? There must be thousands, if not tens of thousands, of fellow developers suffering from insanity that goes undiagnosed. So this tale goes out to you, my functionally insane brethren. Together we can heal ourselves.

Who Am I?

I’d like to give a little background before I get in too deep with my current insanity. I’m a developer. I get paid to write code (woo hoo!) to solve problems. Blah, blah, blah, if you just fill in the rest with a bunch of geek stereotypes you won’t be far off. Yes, I like to IM my coworkers instead of just walking over and talking with them. Yes, I read slashdot. Yes, I read sci-fi. Yes, I played lots of Dungeons & Dragons.2 Et cetera…

I consider myself a smart guy, just like every single developer I’ve ever met. I stay hungry and motivated to learn something new every day, every week, every year. Thanks to Andy and Dave, I learn one new language every year. And most importantly, I try very hard to listen or at least listen more than I argue. Sometimes it doesn’t work out so well, but that’s another tale…

How I Became Insane

I’m a big fan of “right tool for the job” in both life and development. In the realm of development, the concept of right tool for the job has metamorphosized into right language for the job, aka polyglot programming. On the face of it, polyglot programming has a lot of advantages (here’s a good talk by Dean Wampler), but it is one of the root causes of my insanity.

At Gorilla Logic, we are regular practitioners of polyglot programming. Our typical enterprise RIA project uses Java on the backend and Flex on the frontend. Of course, the mix of languages doesn’t stop there. I develop for the web, so when you talk about websites, WordPress and Drupal are immediately in the conversation, and thus PHP. And since a rich client-side experience is critically important, HTML, CSS, and Javascript are drawn into the mix. In the mobile world, Blackberry and Android are thankfully mostly Java, but anything Apple takes me into the non-GC’d world of Objective-C.

I’ve also been diligently learning my one language a year for a while now. And when you combine that with work, it has simply become too much. I’ve become scattered, my mind has become messy. Looking at the syntax level, the symptoms are acute. I can’t write a loop. I can’t match a pattern. I can’t build array operations. My context switching pain is severe, it can literally take hours before I get back up to speed with the language at hand.

But it’s not just the low-level stuff anymore. I’m finding that even at the highest levels of architecture, clean thought is hard to achieve. Different languages tend to espouse different patterns and paradigms that directly impact architecture level decisions. For example, if I wanted to write a server in PHP I might consider using OS support like fork and cron. In Java, I’m off in thread land. Erlang it’s processing and messaging. Scala is all about actors.

I find myself mixing paradigms and doing stupid stuff, like trying to fake Erlang’s message passing in PHP by multiple scripts communicating by repeatedly touching rows in a database. Dumb, but becoming harder to avoid as everything slowly bleeds together in my mind. I’m in the mud and I desperately what to be clean again.

My Current Insanity

Here’s a broad sample of what I’ve done lately (as in the last month or so) for work and personal projects:

Erlang – Work
Freshened my Erlang, wrote a sample custom module for ejabberd, played with Nitrogen.
Flex/AS3 – Work
I helped out a little bit with FlexMonkey 1.0, including the fuzzy pixel bitmap comparison support.
Flex/AS3 – Work
ScrumMonkey is another Gorilla Logic open-source project. I upgraded everything to work with Flex 4 and LCDS 3.0.
Java – Work
Fun with Spring, Hibernate, and Spring Actionscript. Can’t say any more about the project.
Javascript – Work
I helped develop FlexMonkium, a Selenium-to-FlexMonkey testing tool that enabled automated functional testing of hybrid web apps. I used Javascript and XUL.
Objective-C – Justin
Wrote and published a US states and capitals memorization helper app. Read more or get it from the app store.
Objective-C – Work
iPhone and iPad. Cool stuff. Can’t talk about it yet.
PHP – Justin
Released Viceroy, a one-column WordPress theme with a dash of pink.
PHP – Work
We needed to get our community feedback out of Google Groups and into our FlexMonkey Forum. I wrote a simple scrapper in PHP and blogged about it.
PHP – Justin
Various WordPress and Drupal side projects just for fun.
Ant, Maven, Bash, Rake – Work & Justin
Scripted a bunch of stuff…

And that’s just my current insanity: the languages that I’ve touched lately. But I’m not special! At least, not in this case. The list is similar for many co-workers, and many of my friends that are web developers.

The Sensible Plan

If I’m to blame, then I’ve got to be the one to fix it. So here’s my plan:

  • Forget one language every year – Forget the syntax, forget the weirdness, and forget the whole ecosystem (frameworks, tools, community). Say goodbye and don’t look back. But before you leave a language behind, pick one core concept, one of the things the language does right, and take that with you. For example, when I forget Erlang, I’ll take the concept of concurrent programming with me. So I’ll remember stuff like immutability, message passing, and processes. When I forget Ruby, I’ll take DSLs with me. Ruby does lots of stuff well, like blocks, mixins, terse syntax, and meta-programming, but I always enjoyed using all the great DSLs the best, so that’s what I’ll remember.
  • Don’t learn one programming language every year – Yep, I’m going against Andy & Dave. So no Haskel, Clojure, Duby, Go, or Reia for me this year. I’m obviously feeling a little full on languages right now, so I’ll take a break for a few years.
  • Learn one or more APIs every year – Since Web 3.0 is all about APIs, I might as well spend some time learning more of them. The big boys are obvious: Twitter, Facebook, Flickr, Google Maps, but there are plenty more that are really interesting. From the practical side, every single web project either integrates with other APIs, wants their own API, or has some set of requirements that force a good SOA architecture (aka some internal set of APIs).
  • Upgrade my soft skills – Slinging code is fun, but it can’t be the only thing I do if I want to be a great employee. So instead of picking up all these little bits of shiny tech, I’m going to going to focus my lust for the new on upgrading my soft skills. I write this blog to improve my writing, but I haven’t spoken at a big conference since CLEO/QELS in 1999 and I was really bad. So I hope to do some public speaking in 2010.

Basically, I’m hoping to elevate my kung-fu by going for more depth of knowledge. Then, I’ll do my best to control the relentless expansion of breadth. New and shiny is no longer sufficient. Talking things through with co-workers and writing this post is a great first step into the future. I’m already feeling optimistic about my path.

Footnotes
  1. I’m not really insane, at least I don’t think so. I’m just functionally insane. For a glimpse into the mind of a real schizophrenic, I highly recommend Is There No Place On Earth For Me? by Susan Sheehan. It won the non-fiction pulitzer prize in 1983, so it’s a little bit dated now, but an unimaginable story.
  2. Actually, my friends and I played a lot of Rolemaster (wikipedia) and Champions (wikipedia) which are superior role playing games. If you are a true fan, you’ll understand. If not, the image of boys with dice conjured by Dungeons & Dragons is a good takeaway.

UPDATE: I wrote a follow-up post to address all the great feedback: Schizophrenic Following

Scraping Google Groups

Posted in FlexMonkey, Justin Shacklette on March 28th, 2010 by admin

admin originally posted this on Saturnboy.

When we launched the new and improved Gorilla Logic website, we decided to bring all our open source projects together under one roof. In order to migrate all things FlexMonkey back to our website, we need to get our forum data migrated out of Google Groups. Alas, Google doesn’t provide any way to export data from Google Groups. The only way to preserve the amazing contributions from the FlexMonkey community was to scrape Google Groups. So that’s just what we did.

With a very minimal amount of PHP, I was able to walk the entire FlexMonkey Google Group, scrap all the topics (aka threads) and all the posts inside each thread. The first step was to build a generic scraper class that grabs an html page (using cURL) and parses out all unique outbound links.

Here’s the code for the Scraper class:

class Scraper {
    private $url = '';
    public $html = '';
    public $links = array();
 
    public function __construct($url) {
        $this->url = $url;
    }
 
    public function run() {
        $this->html = '';
        $this->links = array();
 
        //scrape url & store html
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->url);
        curl_setopt($ch, CURLOPT_HEADER, 0);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $this->html = curl_exec($ch);
        curl_close($ch);
 
         //parse html for all links
        $matches = array();
        preg_match_all('#<a.*?href\s*=\s*"(.*?)".*?>(.*?)</a>#i', $this->html, $matches);
 
        if ($matches !== false && count($matches) == 3) {
            for ($i = 0; $i < count($matches[1]); $i++) {
                $href = $matches[1][$i];
                $val = $matches[2][$i];
 
                //unique links
                if (!array_key_exists($href, $this->links)) {
                    $this->links[$href] = $val;
                }
            }
        }
    }
}

In the run() method, cURL is used to grab the html. Next, a regular expression is used to match all outbound links. The links are are stored in a hash, while making sure they point to unique urls.

Built on top of the generic Scraper class is a specialized Google Groups scraper class, aptly named GoogleGroupsScraper. For a given Google Group, the url of the main page (containing a list of most recent topics) is:

http://groups.google.com/group/[GROUP]/topics

And the url of a single topic (aka thread) is:

http://groups.google.com/group/[GROUP]/browse_thread/thread/[THEAD_ID]#

Where [GROUP] is the name of the Google Group, and [THREAD_ID] is some alphanumeric id. Most importantly, at the bottom of the main page is an Older » link that points to the next page of topics. The GoogleGroupsScraper exploits this to spider the entire group, recording topic title and topic url as it walks each page.

Next, each individual topic page is scraped by the GoogleGroupsTopicScraper class and parsed into a list of posts with author name, date, timestamp, etc. The topic scraper uses various regular expressions to extract and massage the html to extract the different parts of each post. In particular, the post body needs a lots of work to strip out any Google Groups specific links and code.

Lastly, the topics and their posts are assembled into an XML document with a nice big CDATA block around the post body to preserve the html content.

Here’s some sample output from the scraper:

<?xml version="1.0" encoding="UTF-8"?>
<scrape group="flexmonkey">
  <topic>
    <title>FlexMonkey User Group is now located at www.gorillalogic.com/flexmonkey!</title>
    <link>http://groups.google.com/group/flexmonkey/browse_thread/thread/fe9ed66bf56db88e#</link>
    <posts>
      <post idx="0">
        <author>Stu</author>
        <email>stu.st...@gorillalogic.com</email>
        <date>February 10, 2010 21:17:52 UTC</date>
        <timestamp>1265836672</timestamp>
        <body>
<![CDATA[
<p>People of FlexMonkey, <p>We have migrated the FlexMonkey discussion forum to <a href="http://www.gorillalogic.com/flexmonkey">http://www.gorillalogic.com/flexmonkey</a>. Please note that you will need to re-subscribe to the new forum to continue receiving FlexMonkey discussion messages. <p>-Stu <br>
]]>
        </body>
      </post>
    </posts>
  </topic>
  <topic>
    <title>Record button clicks based on Ids instead of names?</title>
    <link>http://groups.google.com/group/flexmonkey/browse_thread/thread/4f079b1959374f53#</link>
    <posts>
      <post idx="0">
        <author>Shilpa</author>
        <email>shilpa.g...@gmail.com</email>
        <date>February 9, 2010 23:44:44 UTC</date>
        <timestamp>1265759084</timestamp>
        <body>...</body>
      </post>
      <post idx="1">
        <author>Shilpa</author>
        <email>shilpa.g...@gmail.com</email>
        <date>February 10, 2010 00:05:44 UTC</date>
        <timestamp>1265760344</timestamp>
        <body>...</body>
      </post>
      <post idx="2">
        <author>Gokuldas K Pillai</author>
        <email>gokul...@gmail.com</email>
        <date>February 10, 2010 00:16:34 UTC</date>
        <timestamp>1265760994</timestamp>
        <body>...</body>
      </post>
      <post idx="3">
        <author>Shilpa</author>
        <email>shilpa.g...@gmail.com</email>
        <date>February 10, 2010 01:18:42 UTC</date>
        <timestamp>1265764722</timestamp>
        <body>...</body>
      </post>
...

Finally, there is a very simple PHP driver for the scraper that runs the scraping process:

require_once('GoogleGroupsScraper.class.php');
 
$scraper = new GoogleGroupsScraper('[GROUP]');
$scraper->run();
 
print $scraper->getXML();

And you run it as usual:

php scrape.php > output.xml

Just enter the name of the Google Group you wish to scrap, and away you go. Here are a couple of notes to help you along:

  1. [GROUP] is the group name as it appears in the url, so no spaces, etc.
  2. It’s not fast, so be patient, or modify the scraper code to generate some intermediate output.
  3. Via a browser, Google Group displays 30 topics per page, but via PHP & cURL you only get 10. Probably some Cookie or User Agent magic going on.
  4. Not much error handling. The error handling that exists isn’t very good. It will break.
  5. Good luck!

Please download the code and use it however you wish. Hopefully, putting the code online and writing this post will save someone else some time when migrating data off Google Groups.

Files

Managing Event Listeners in Flex 4

Posted in Justin Shacklette on March 22nd, 2010 by admin

admin originally posted this on Saturnboy.

Event-driven programming is at the heart of Flex. It also lies close to the heart of insanity for the developer. When an application becomes too loosely coupled, things get painful fast. The perfect storm arrives when you marry an event-driven UI with an event-driven asynchronous backend. Better async testing becomes the least of your problems, and you are constantly trying to figure out who is listening to what, when.

In these cases, I often find that some simple runtime management of event listeners can really help. To begin, I start with some component whose listeners I want to manage, then I override the addEventListener() and removeEventListener() functions in that component to always keep track of who is actively listening.

Here is the basic code that can be added to any component (actually any descendent of EventDispatcher which is basically everything in Flex):

private var _listeners:Object = {};
 
override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void {
    super.addEventListener(type, listener, useCapture, priority, useWeakReference);
 
    if (_listeners.hasOwnProperty(type)) {
        (_listeners[type] as Array).push(listener);
    } else {
        _listeners[type] = [listener];
    }
}
 
override public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void {
    super.removeEventListener(type, listener, useCapture);
 
    if (_listeners.hasOwnProperty(type)) {
        var listeners:Array = _listeners[type] as Array;
        if (listeners.length <= 1) {
            delete _listeners[type];
        } else {
            var idx:int = listeners.indexOf(listener);
            if (idx != -1) {
                listeners.splice(idx,1);
            }
        }
    }
}

We just store all listeners of a given event type in an array, and store the array as the values in a hash keyed by event type. When a new listener is added via addEventListener(), we check if any listeners of that event type exist. If yes, we push the new listener onto the array. If no, we create a new one-item array containing just the new listener. The tracking process for removeEventListener() is just the opposite.

Once we have all the listener bookkeeping in place, we can add any type of event listener management that we want. For example, he’s a removeAllEventListeners() function:

public function removeAllEventListeners(type:String = ''):void {
    if (type.length == 0) {
        for (var t:String in _listeners) {
            removeAllEventListeners(t);
        }
    } else if (_listeners.hasOwnProperty(type)) {
        if (hasEventListener(type)) {
            for each (var listener:Function in _listeners[type]) {
                removeEventListener(type, listener);
            }
        }
    }
}

Nothing cosmic, as my boss likes to say, just walk all the listeners of a given event type and remove them. If the event type is blank, just use some dirty recursion to walk all the types to remove everything.

Here is a sample application which is tracking all the listeners on a Panel component (view source enabled):

Flash is required. Get it here!

You can add/remove a custom listener for the foo custom event type using the buttons. Or hit remove all to clear everything (including the listeners attached by default by the Flex framework).

Files

Parsing Twitter with RegExp

Posted in Justin Shacklette on February 23rd, 2010 by admin

admin originally posted this on Saturnboy.

I needed a very simple Twitter cache for a project I’m working on. And I was very happy to trade off some realtime accuracy for reliability. In addition to caching the tweets, I also needed to pre-process them into css-able html with clickable links, usernames, and hashtags. The web had a few nice examples of how to use regular expressions to parse the raw tweet text, but I decided to take what I liked and do the rest myself.

Links

Here’s the PHP code for parsing links out of the raw tweet text:

$text = preg_replace(
    '@(https?://([-\w\.]+)+(/([\w/_\.]*(\?\S+)?(#\S+)?)?)?)@',
     '<a href="$1">$1</a>',
    $text);

I only wanted http and https links, with an optional query part (\?\S+)? and an option anchor part (#\S+)?. The conversion of a text link into an html link is done using back references, which in PHP is $1, $2, etc. In the expression above, I use $1 twice to put the matched link into both the href attribute and the link text.

Users

Here’s the PHP code for parsing Twitter usernames:

$text = preg_replace(
    '/@(\w+)/',
    '<a href="http://twitter.com/$1">@$1</a>',
    $text);

Nothing special, just take the @ and all following word characters (letters, digits, and underscores), and turn it into a user link.

Hashtags

Here’s the PHP code for parsing Twitter hashtags:

$text = preg_replace(
    '/\s+#(\w+)/',
    ' <a href="http://search.twitter.com/search?q=%23$1">#$1</a>',
    $text);

Getting the hashtags right was the most tricky of the three. I decided to only grab hashtags that were proceeded by one or more spaces. The real magic is the %23 in the query string, which forces a search on the complete hashtag, including the # part. For example, compare a search for #flex to a search for flex.

The Cache

The cache is just a simple cron job that periodically queries Twitter and retrieves the latest tweets. Most importantly, the cache fails gracefully if Twitter is inaccessible, which it does by doing exactly nothing if Twitter is down. This guarantees that my app always has valid data (when my server is up, the cache is up too), but with the possibility that the data is a little old.

Here’s the notable function in the cache:

function getTweets($user, $num = 3) {
    //first, get the user's timeline
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "http://twitter.com/statuses/user_timeline/$user.json?count=$num");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $json = curl_exec($ch);
    curl_close($ch);
 
    if ($json === false) { return false; } //abort on error
 
    //second, convert the resulting json into PHP
    $result = json_decode($json);
 
    //third, build up the html output
    $s = '';
    foreach ($result as $item) {
        //handle any special characters
        $text = htmlentities($item->text, ENT_QUOTES, 'utf-8');
 
        //build the metadata part
        $meta = date('g:ia M jS', strtotime($item->created_at)) . ' from ' . $item->source;
 
        //parse the tweet text into html
        $text = preg_replace('@(https?://([-\w\.]+)+(/([\w/_\.]*(\?\S+)?(#\S+)?)?)?)@', '<a href="$1">$1</a>', $text);
        $text = preg_replace('/@(\w+)/', '<a href="http://twitter.com/$1">@$1</a>', $text);
        $text = preg_replace('/\s#(\w+)/', ' <a href="http://search.twitter.com/search?q=%23$1">#$1</a>', $text);
 
        //assemble everything
        $s .= '<p class="tweet">' . $text . "<br />\n" . '<span class="tweet-meta">' . $meta . "</span></p>\n";
    }
 
    return $s;
}

First, we query the user’s JSON timeline using cURL. Second, we use PHP’s awesome json_decode function to convert the JSON into objects. And lastly, we iterate over the tweets and parse everything into our desired HTML output.

Here some sample output from my twitter feed:

<p class="tweet">Been reading Programming Goggle App Engine. Actually feeling dumber now than before I started. Too much to learn.<br /> 
<span class="tweet-meta">2:58pm Feb 14th from <a href="http://www.tweetdeck.com/" rel="nofollow">TweetDeck</a></span></p>
 
<p class="tweet">Blog Post :: Async Testing with FlexUnit 4 :: <a href="http://bit.ly/cGLnaI">http://bit.ly/cGLnaI</a><br /> 
<span class="tweet-meta">3:33pm Feb 11th from <a href="http://www.tweetdeck.com/" rel="nofollow">TweetDeck</a></span></p>
 
<p class="tweet">Blog Post :: A Better HTML Template for Flex 4 :: <a href="http://bit.ly/70DLsj">http://bit.ly/70DLsj</a><br /> 
<span class="tweet-meta">12:55pm Jan 25th from <a href="http://www.tweetdeck.com/" rel="nofollow">TweetDeck</a></span></p>

Once I have the output, I can do whatever I want with it: save to disk, stick it in the database, keep it in memory, cache it in memcache, etc. In my case, I wanted the simplest possible option, so I chose to write it out as a static html file.

The end. The rest of the app’s not ready yet…

Faking Transaction in LCDS 3

Posted in Justin Shacklette on February 19th, 2010 by admin

admin originally posted this on Saturnboy.

Alas, LCDS 3 doesn’t support the notion of transaction (in the sense of a Database Transaction). I assume that somewhere deep within the system transactions are used to ensure correctness of data in the db, but none of this is exposed to the user. Fortunately, it is possible to bundle up a bunch of updates on the client and push them all at once to the server. Fake transactions, as I like to call them, are quite useful. But they can also be dangerous because they are not really transactions (hence the fake part), and don’t always work as you would hope.

Fake It

Faking a tranaction is done using the autoCommit property on a generated service. According to the docs, setting autoCommit to false blocks Data Management from pushing any changes to the server until commit() is called manually.

Here’s a direct quote:

“…set a DataService component autoCommit property to false to allow only manual calls to the commit() method. It is important to set autoCommit to false when you are going to make more than one change…so that the DataService component can batch those changes and send them in one batch to the destination.”

So to make life easy, I made a simple static function that does the autoCommit gymnastics:

public static function fakeIt(service:DataService, func:Function):AsyncToken {
    if (!service.autoCommit) {
        throw new Error("ERROR: autoCommit is already off.");
    }
    if (service.commitRequired) {
        throw new Error("ERROR: another transaction is already open.");
    }
 
    service.autoCommit = false;
    func();
    var token:AsyncToken = service.commit();
    service.autoCommit = true;
    return token;
}

After some error handling, we toggle autoCommit to false, call our function, then commit any updates, toggle autoCommit back on, and return the token.

Usage

Using our fake transaction function is straight forward, and typically involves passing in and inline anonymous function.

Imagine our favorite example of teams and players, where each team has one-to-many players. We might choose to perform a sequence of operations to add a new player to an existing team, like this:

var token:AsyncToken = FakeTransaction.fakeIt(
    teamService.serviceControl,
    function ():void {
        //rename the team
        team.name = 'Denver Nuggetz';
 
        //create player
        var p:Player = new Player();
        p.name = 'Carmelo Anthony';
 
        //wire both side of relationship
        p.team = team;
        team.players.addItem(p);
 
        playerService.createPlayer(p);
    });
 
token.addResponder(new AsyncResponder(successHandler, faultHandler));

In this case, the fake transaction is used to ensure the team is renamed and the player is added. Both operations occur together, so it should not be possible to have the new player on the old team, or have the renamed team without the new player.

Warning! Danger!

Experience has shown me that fake transactions don’t always work as one would expect if they were real transactions. Sometimes, particularly when you are manipulating entities already under Data Management, it seems like operations are not really batched but instead executed one at a time. Thankfully, it appears that LCDS is order preserving, and by that I mean it doesn’t magically re-order things. LCDS always executes operations in the order they come in. I can only recommend you test your application vigorously to ensure what you expect to happen actually happens (FlexUnit4 is pretty awesome for testing async backends).

Files

Like Peas and Carrots: LCDS 3 and UTF-8

Posted in Justin Shacklette on February 17th, 2010 by admin

admin originally posted this on Saturnboy.

I just wrote about how to handle special characters in Flex 4 when written as HTML entities in MXML. Now I’ve moved my data with the special characters out of MXML and down into a MySQL database. Data access is provided by a vanilla LCDS 3 backend. I now have a very different problem than what I had before: How do I get UTF-8 data out of the database with LCDS and onto the display?

MySQL and UTF-8

In theory, LCDS is perfectly happy with special characters and foreign languages (here’s a link to supported characters sets in LiveCycle ES2). So this time around, our problem has nothing to do with Flex 4 or LCDS, instead it’s all about the database. For our example, we’ll skip the pure model driven development route and just start with a simple database with a single players table.

Create the database:

CREATE DATABASE ballerz DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE USER 'baller'@'localhost' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON ballerz.* TO baller@localhost;

It is possible to configure MySQL to default to UTF-8 friendly behavior but the CREATE DATABASE command guarantees that the newly created db will be happy.

Create the players table:

CREATE TABLE players (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(255),
  PRIMARY KEY (id)
);

Nothing special here, just use VARCHAR for the text fields. In this case, we only have the player’s name.

Insert some sample data:

INSERT INTO players (id,name) VALUES (1, "Carmelo Anthony");
INSERT INTO players (id,name) VALUES (2, "Chaunçey Billups");
INSERT INTO players (id,name) VALUES (3, "Nenê");
INSERT INTO players (id,name) VALUES (4, "Këñÿõn Martin");
INSERT INTO players (id,name) VALUES (5, "LeBrøn James");
INSERT INTO players (id,name) VALUES (6, "Mo Williams");
INSERT INTO players (id,name) VALUES (7, "Shaquille O†Neal");
INSERT INTO players (id,name) VALUES (8, "Ænderson Varejao");
INSERT INTO players (id,name) VALUES (9, "Zýdrunãs Ílgauskãs");

I put some extra special characters into the INSERT statements just for fun.

Digging Deeper: When connecting to MySQL from the commandline you can use the --default-character-set=utf8 option to force your terminal to show UTF-8 characters correctly.

LCDS and UTF-8

Now that the database is correctly setup to handle UTF-8, the rest of the LCDS setup is straight forward (see my getting started part 1 and part 2 posts). Create a new LCDS webapp via copy-and-paste from the template app, then fire up Flash Builder 4 and get to work. In the Modeler plugin, configure a new RDS connection and just drag-and-drop the players table into the model.

Here’s a screenshot of our LCDS model:

model

And here’s a screenshot of the running app (remember this is backed by LCDS, so no running demo):

screenshot

Lastly, the frontend code showing just the highlights:

<?xml version="1.0" encoding="utf-8"?>
<s:Application ...
        creationComplete="complete()">
 
    <fx:Script>
        <![CDATA[
           private function complete():void {
                getPlayers.token = playerService.getAll();
            }
        ]]>
    </fx:Script>
 
    <fx:Declarations>
        <s:CallResponder id="getPlayers" />
        <Ballerz:PlayerService id="playerService" />
    </fx:Declarations>
 
    <s:List 
            dataProvider="{getPlayers.lastResult}"
            labelField="name" />
</s:Application>

Again, no magic here, I use a simple getAll() query to retrieve the entire players table then feed it into a List via the CallResponder’s lastResult property.

Conclusion

So the moral of our story is: if you correctly configure your database to support UTF-8 and you correctly get UTF-8 data into your tables, then everything just works. LCDS will transparently get data out of the db and Flex will transparently get it onto the screen.

Files

Async Testing with FlexUnit 4

Posted in Justin Shacklette on February 11th, 2010 by admin

admin originally posted this on Saturnboy.

I tend to spend a large portion of my development time worrying about the various interfaces across the application. I like to worry about UX (aka the interface between the user and my app). I like to worry about the ORM (aka the interface between the database and my code). And I especially like to worry about the client-side service layer (aka the interface between the backend and the frontend). When I worry, I very quickly find myself writing tests.

The new hotness in Flex testing is, of course, FlexMonkey, developed and open-sourced by my company, Gorilla Logic. The next best Flex testing platform, and the new hotness in its own right, is FlexUnit 4, developed and open-sourced by our partners at Digital Primates. FlexUnit 4 is the Flex 4 unit testing framework. Because of its awesome async testing support, along with many other great features, it is ideally suited to test client-side service layers. In this post, I’m going to explore async testing with FlexUnit 4 to better understand how I can help mitigate the pain of asynchronous backend services that are ever-present in enterprise Flex applications.

Test: Create Team

Let’s imagine I have my favorite data model of teams and players, with a one-to-many relationship between the two entities. Next, let’s assume that I wrote a beautiful client-side service layer that has both basic CRUD operations like create team and delete team, and more complex operations like trade player. I’d like to cover everything with a set of tests so I can spend my time worrying about other things.

Here’s a simple async test case for the basic create team operation:

[Test(async)]
public function createTeam():void {
    var token:AsyncToken = service.createTeam('Los Angeles Lakers');
    token.addResponder(Async.asyncResponder(this, new TestResponder(createTeam2, fault), TIMEOUT));
}
public function createTeam2(event:ResultEvent, passThroughData:Object):void {
    var token:AsyncToken = service.getAllTeams();
    token.addResponder(Async.asyncResponder(this, new TestResponder(createTeam3, fault), TIMEOUT));
}
public function createTeam3(event:ResultEvent, passThroughData:Object):void {
    var teams:ArrayCollection = event.result as ArrayCollection;
    assertThat('Team not created', 'Los Angeles Lakers', inArray(teams.toArray()));
}

First, we create a new team, then we load all the teams, and lastly, we verify that the newly created team is in the list. There are two important things to note: async stuff is everywhere ([Test(async)] metadata, AsyncToken, AsyncResponder, etc.), and there is a chain of functions (createTeam() chains to createTeam2() which chains to createTeam3()). In particular, the chain pattern is characteristic of any async testing. Every single non-trivial async test involves a chain of function calls to do the work of testing an asynchronous backend.

Here’s a simple diagram of the chain for the create team test:

create-team

Each diagram box is just a logic operation in our test, and they also happen to correspond exactly to the functions that make up the test chain.

Test: Trade Player

When testing the more complex client-side service layer operations, or simply writing more complex tests, the chain pattern often develops branches and sub-chains as various pieces of state are verified asynchronously.

A good example is the trade player operation, which we might test using a chain with two branches: one to verify the player was removed from old team, and one to verify the player was added to the new team. Here’s the diagram:

trade-player

We don’t really care what goes on inside the client-side service layer to achieve this, or even what happens on the backend (it’s probably just as simple as changing the team_id column on the players table to the new team’s id). We only care that the test passes.

And the accompanying test code:

[Test(async)]
public function tradePlayer():void {
    var token:AsyncToken = service.tradePlayer('Carmelo Anthony', 'Denver Nuggets', 'Cleveland Cavaliers');
    token.addResponder(Async.asyncResponder(this, new TestResponder(tradePlayer2, fault), TIMEOUT));
}
public function tradePlayer2(event:ResultEvent, passThroughData:Object):void {
    var token:AsyncToken = service.getPlayersByTeam('Denver Nuggets');
    token.addResponder(Async.asyncResponder(this, new TestResponder(tradePlayer3, fault), TIMEOUT));
 
    var token2:AsyncToken = service.getPlayersByTeam('Cleveland Cavaliers');
    token2.addResponder(Async.asyncResponder(this, new TestResponder(tradePlayer4, fault), TIMEOUT));
}
public function tradePlayer3(event:ResultEvent, passThroughData:Object):void {
    var players:ArrayCollection = event.result as ArrayCollection;
    assertThat('Traded player not removed from old team', 'Carmelo Anthony', not(inArray(players.toArray())));
}
public function tradePlayer4(event:ResultEvent, passThroughData:Object):void {
    var players:ArrayCollection = event.result as ArrayCollection;
    assertThat('Traded player not added to new team', 'Carmelo Anthony', inArray(players.toArray()));
}

The interesting part occurs in the second step in the chain, tradePlayer2(). In this function, we use a pair of AsyncTokens, to fork the chain into two sub-chains. One sub-chain gets all the players on the old team and verifies that the traded player has been removed. And the other sub-chain gets all the players on the new team and verifies that the trade player has been added.

A Better Approach

Right now, the chained function approach is the only approach for testing an asynchronous client-side service layer. As another example, you can see the chained function approach appears again when I tested an LCDS-powered backend in my Getting Real with LCDS, Part 1 article at InsideRIA.com.

There has got to be something better, right? Chained functions work fine, but boy are they ugly looking in code. I’ve been having a discussion on the FlexUnit forums about better async testing. The general wisdom is that one could use the Sequence interfaces to build an async action and have the SequenceRunner manage the chain. Currently, the best documentation on Sequences is the old Fluint wiki doc. In enterprise Flex development, async backends tend to swarm like locusts, so I hope to have some code to show soon to streamline the testing process.

Files

A Better HTML Template for Flex 4

Posted in Justin Shacklette on January 25th, 2010 by admin

admin originally posted this on Saturnboy.

Adobe took a big step forward using swfobject.js to handle swf embedding in Flex 4’s index.template.html, but in my opinion they didn’t go quite far enough.

Here are my changes:

  1. Short and sweet, so I can understand what the hell is going on.
  2. Use Google AJAX Libraries API to include swfobject.js.
  3. No IE6 crap.
  4. No Browser History crap.

Here’s my current version:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>${application}</title>
 
<style type="text/css" media="screen">
html, body, #flashcontent { height:${height}; }
body { margin:0; padding:0; overflow:hidden; }
</style>
 
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
<script type="text/javascript">
swfobject.embedSWF(
    '${swf}.swf',
    'flashcontent',
    '${width}', '${height}',
    '${version_major}.${version_minor}.${version_revision}',
    '${expressInstallSwf}',
    false,
    { 'bgColor':'${bgcolor}' });
</script>
</head>
 
<body>
<div id="flashcontent">
Flash is required.  <a href="http://www.adobe.com/go/getflashplayer">Get it here!</a>
</div>
</body>
</html>
Flashvars

Using flashvars is easy with SWFObject (see the embedSWF() method in the docs), just put them in as a Javascript hash like this:

<script type="text/javascript">
swfobject.embedSWF(
    '${swf}.swf',
    'flashcontent',
    '${width}', '${height}',
    '${version_major}.${version_minor}.${version_revision}',
    '${expressInstallSwf}',
    { key1:'val1', key2:'val2', key3:'val3' },
    { 'bgColor':'${bgcolor}' });
</script>
View Source

Lastly, here’s a little trick that I use to embed flash into a post on this blog (or anywhere else) that enables view source to work with a relative url, so I can leave the viewSourceURL attribute alone (defaults to viewSourceURL="srcview/index.html").

<script type="text/javascript">
swfobject.embedSWF(
    '${swf}.swf',
    'flashcontent',
    '${width}', '${height}',
    '${version_major}.${version_minor}.${version_revision}',
    '${expressInstallSwf}',
    false,
    { 'bgColor':'${bgcolor}', base:'.' });
</script>

Adding the base parameter, and setting it to '.', tells the embedded SWF to lookup urls relative to itself. The net effect is that view source just works.

Files