<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>kuwamoto.org &#187; programming</title>
	<atom:link href="http://kuwamoto.org/category/programming/feed/" rel="self" type="application/rss+xml" />
	<link>http://kuwamoto.org</link>
	<description>various stuff, mostly boring</description>
	<lastBuildDate>Sun, 18 Mar 2012 00:16:17 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>JavaScript performance optimization, take 1</title>
		<link>http://kuwamoto.org/2009/10/09/javascript-performance-optimization-1/</link>
		<comments>http://kuwamoto.org/2009/10/09/javascript-performance-optimization-1/#comments</comments>
		<pubDate>Fri, 09 Oct 2009 23:32:45 +0000</pubDate>
		<dc:creator>sho</dc:creator>
				<category><![CDATA[ajax]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[mootools]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://kuwamoto.org/?p=249</guid>
		<description><![CDATA[For the last several months, Mike and I have been working on a new project, which is nearing closed beta. That means we need to start battening down the hatches, and today was the day to start tackling client-side JavaScript performance. I&#8217;ve actually done quite a bit of performance work in my life, but not [...]]]></description>
			<content:encoded><![CDATA[<p>For the last several months, Mike and I have been working on a new project, which is nearing closed beta. That means we need to start battening down the hatches, and today was the day to start tackling client-side JavaScript performance.</p>
<p>I&#8217;ve actually done quite a bit of performance work in my life, but not with JavaScript, so I though I&#8217;d take some notes along the way.</p>
<h3>Firebug is your friend</h3>
<p>In my mind, there are really three ways to a significant dent in performance:</p>
<ol>
<li>Find bad algorithms and replace them with fast ones</li>
<li>Find code that doesn&#8217;t actually have to be called and skip it.</li>
<li>Optimize the code that gets called most often</li>
</ol>
<p>And you really can&#8217;t do any of the three without a profiler. You might think you know what the problem is, but you won&#8217;t know until you profile it. In my case, I started out thinking that I had event listeners hanging around that weren&#8217;t letting go of their events, but the profiler (in this case, Firebug) told me I was completely wrong.</p>
<p>To get started profiling in Firebug, go to the console tab, press the &#8216;profile&#8217; button, do some stuff, and hit the &#8216;profile&#8217; button again. That&#8217;s it.</p>
<p>You&#8217;ll then be presented with data that looks like this:</p>
<p><a href="http://kuwamoto.org/wp-content/uploads/2009/10/Picture-1.png"><img src="http://kuwamoto.org/wp-content/uploads/2009/10/Picture-1-300x84.png" alt="Firebug" title="Firebug" width="300" height="84" class="aligncenter size-medium wp-image-255" /></a></p>
<p>For my money, the two most important columns are &#8216;own time&#8217; and &#8216;time&#8217;. &#8216;time&#8217; is the total time spent in a function including any functions that are called by that function, and &#8216;own time&#8217; is the same thing minus the time taken up by other functions.</p>
<h3>Problem: $$(&#8216;.class&#8217;) can be SLOW!</h3>
<p>I created a test where I did the same UI gesture 8 times, and this is what I discovered. Looking at &#8216;own time&#8217; told me that most of my time was going to DOM traversal via the $$ function.</p>
<p>Looking at &#8216;time&#8217; told me that the methods responsible for calling $$ were all central functions that were called in many places throughout my code, so it was worth making them as efficient as possible before figuring out whether there was a way to avoid calling some of them altogether. </p>
<h3>Phase 1 &#8212; replacing traversals of the entire DOM tree (via $$) with smaller traversals</h3>
<p>Roughly speaking, this corresponds to strategy (3).</p>
<table class="data">
<tr class="odd">
<th>What</th>
<th>total time</th>
<th>%delta<br />
from prev</th>
<th>%delta<br />
from base</th>
</tr>
<tr class="even">
<td>Baseline</td>
<td>2812ms</td>
<td>&#8211;</td>
<td>&#8211;</td>
</tr>
<tr class="odd">
<td>Replace $$(&#8216;.class&#8217;) by $(&#8216;section&#8217;).getElements(&#8216;.class&#8217;) in critical sections</td>
<td>2345ms</td>
<td>20%</td>
<td>20%</td>
</tr>
<tr class="even">
<td>Chage getElements(&#8216;.class&#8217;) to getElements(&#8216;div.class&#8217;) in critical sections</td>
<td>2094ms</td>
<td>12%</td>
<td>34%</td>
</tr>
<tr class="odd">
<td>Found more places to do the above optimizations</td>
<td>1723ms</td>
<td>22%</td>
<td>63%</td>
</tr>
<tr class="even">
<td>Replaced getElements() with getChildren() where possible</td>
<td>1641ms</td>
<td>5%</td>
<td>71%</td>
</tr>
</table>
<p>Along the way, I tried all sorts of other optimizations, but none of them yielded much benefit. Now that I was reaching the point of diminishing returns, it was time to see if there were chunks of code I could safely skip.</p>
<h3>Phase 2 &#8212; skipping handler functions when possible</h3>
<p>I knew that there was almost certainly code I was running that could be skipped (strategy 2). Why?</p>
<p>I find that when writing UI code, it is often easier to use brute force to make sure that everything is working consistently. For example, if an AJAX call updates a certain part of the screen, it is often easier to blow away all event handlers from everything and re-add them where needed, rather than just patching up event handlers for the portion of the screen that was updating.</p>
<p>My rationale is that you can always fix this at the end. And well, it was now time to pay the piper.</p>
<p>My test case involved doing the same UI gesture 8 times. And most of the time was going to the following functions:</p>
<p><code><br />
add_panel_handlers_if_needed(): 8 times<br />
add_content_handlers(): 16 times<br />
add_panel_handlers(): 8 times<br />
actually_do_drag_cleanup(): 8 times<br />
remove_content_handlers(): 24 times<br />
fix_detail_handlers(): 8 times<br />
handle_click(): 8 times<br />
fix_toggle_rte_handlers(): 8 times<br />
add_drag_handlers_and_start(): 8 times<br />
add_insert_handlers(): 8 times<br />
</code></p>
<p>You can see that some functions are being called 8 times and some were being called 24 times. As it turns out, this was just due to programmer laziness. By adding a few checks, some of those redundant calls could be safely avoided.</p>
<p>The other thing that was causing extra work is that only certain interactions caused screen updates that needed event handlers to be reattached. By writing some code to check for that, I was able to avoid many of these calls altogether.</p>
<table class="data">
<tr class="odd">
<th>What</th>
<th>total time</th>
<th>%delta<br />
from prev</th>
<th>%delta<br />
from base</th>
</tr>
<tr class="even">
<td>Baseline</td>
<td>2812ms</td>
<td>&#8211;</td>
<td>&#8211;</td>
</tr>
<tr class="odd">
<td>End of phase 1</td>
<td>1641ms</td>
<td>71%</td>
<td>71%</td>
</tr>
<tr class="even">
<td>Remove redundant calls to remove_content_handlers and add_content_handlers</td>
<td>1389ms</td>
<td>18%</td>
<td>102%</td>
</tr>
<tr class="odd">
<td>Skip certain fixup calls when content is determined not to have changed</td>
<td>1073ms</td>
<td>29%</td>
<td>162%</td>
</tr>
</table>
<p>(P.S. there is some small part of my brain that tells me that instead of manually worrying about these event handlers, I should just bite the bullet and switch to JQuery. But I&#8217;m not there yet.)</p>
<h3>Summary</h3>
<p>So, what&#8217;s the moral? First off, doing $$(&#8216;.class&#8217;) is slow. Second, large performance boosts usually come from a combination of skipping code that doesn&#8217;t have to run and optimizing the code that does. This was no exception. </p>
<p>One more thing. I just have to say that Firebug is amazing. I expected it to have trouble giving useful timings in the face of inconsistent UI gestures and garbage collection, but it did the &#8220;right thing&#8221;, which many desktop profilers don&#8217;t manage to do. If I had one wish, I wish I could get it to bundle up calls from specified library files and allocate the time spent in them to the calling function. </p>
<p>Ok. Back to more optimizing.</p>
]]></content:encoded>
			<wfw:commentRss>http://kuwamoto.org/2009/10/09/javascript-performance-optimization-1/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Improved pluralizing in PHP, ActionScript, and RoR</title>
		<link>http://kuwamoto.org/2007/12/17/improved-pluralizing-in-php-actionscript-and-ror/</link>
		<comments>http://kuwamoto.org/2007/12/17/improved-pluralizing-in-php-actionscript-and-ror/#comments</comments>
		<pubDate>Mon, 17 Dec 2007 19:55:36 +0000</pubDate>
		<dc:creator>sho</dc:creator>
				<category><![CDATA[actionscript]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[rails]]></category>

		<guid isPermaLink="false">http://kuwamoto.org/2007/12/17/improved-pluralizing-in-php-actionscript-and-ror/</guid>
		<description><![CDATA[I found a number of issues with the pluralizing code I posted the other day, and I felt compelled to fix it. While I was at it, I ported the code to ActionScript 3 and then back to Ruby, so you can use the pluralizer in your Flex or RoR apps. BTW, for those of [...]]]></description>
			<content:encoded><![CDATA[<p>I found a number of issues with the pluralizing code I posted the other day, and I felt compelled to fix it. While I was at it, I ported the code to ActionScript 3 and then back to Ruby, so you can use the pluralizer in your Flex or RoR apps. </p>
<p>BTW, for those of you who love language and know regular expressions (this means you, lori and nj!), please help me spot any problems with these rules.</p>
<p>The reason it was important for me to get these pluralization rules rights is that I am using these for human-readable strings. In my project, users get to name things however they want and it is nice to be able to pluralize (or singularize) these words when communicating with the user.</p>
<p>(As a side note, I think the Ruby on Rails idea of auto-pluralizing table names to be a bit bizarre. I like having magic frameworks that do all the work for me, but I want my frameworks to have predictable behavior!! I mean&#8230; how bad would it be if the table that holds the &#8220;person&#8221; object was called &#8220;person&#8221; instead of &#8220;people&#8221;?)</p>
<p>Thanks again to the <a href="http://dev.rubyonrails.org/browser/trunk/activesupport/lib/active_support/inflections.rb">Rails team</a> for getting this ball rolling, and to <a href="http://www.eval.ca/articles/php-pluralize">Paul Osman</a> for the original PHP version of this code.</p>
<p>On to the code. All code below is covered under the MIT license.</p>
<p>PHP:</p>
<pre>// Thanks to http://www.eval.ca/articles/php-pluralize (MIT license)
//           http://dev.rubyonrails.org/browser/trunk/activesupport/lib/active_support/inflections.rb (MIT license)
//           http://www.fortunecity.com/bally/durrus/153/gramch13.html
//           http://www2.gsu.edu/~wwwesl/egw/crump.htm
//
// Changes (12/17/07)
//   Major changes
//   --
//   Fixed irregular noun algorithm to use regular expressions just like the original Ruby source.
//       (this allows for things like fireman -> firemen
//   Fixed the order of the singular array, which was backwards.
//
//   Minor changes
//   --
//   Removed incorrect pluralization rule for /([^aeiouy]|qu)ies$/ => $1y
//   Expanded on the list of exceptions for *o -> *oes, and removed rule for buffalo -> buffaloes
//   Removed dangerous singularization rule for /([^f])ves$/ => $1fe
//   Added more specific rules for singularizing lives, wives, knives, sheaves, loaves, and leaves and thieves
//   Added exception to /(us)es$/ => $1 rule for houses => house and blouses => blouse
//   Added excpetions for feet, geese and teeth
//   Added rule for deer -> deer

// Changes:
//   Removed rule for virus -> viri
//   Added rule for potato -> potatoes
//   Added rule for *us -> *uses

class Inflect
{
    static $plural = array(
        '/(quiz)$/i'               => "$1zes",
        '/^(ox)$/i'                => "$1en",
        '/([m|l])ouse$/i'          => "$1ice",
        '/(matr|vert|ind)ix|ex$/i' => "$1ices",
        '/(x|ch|ss|sh)$/i'         => "$1es",
        '/([^aeiouy]|qu)y$/i'      => "$1ies",
        '/(hive)$/i'               => "$1s",
        '/(?:([^f])fe|([lr])f)$/i' => "$1$2ves",
        '/(shea|lea|loa|thie)f$/i' => "$1ves",
        '/sis$/i'                  => "ses",
        '/([ti])um$/i'             => "$1a",
        '/(tomat|potat|ech|her|vet)o$/i'=> "$1oes",
        '/(bu)s$/i'                => "$1ses",
        '/(alias)$/i'              => "$1es",
        '/(octop)us$/i'            => "$1i",
        '/(ax|test)is$/i'          => "$1es",
        '/(us)$/i'                 => "$1es",
        '/s$/i'                    => "s",
        '/$/'                      => "s"
    );

    static $singular = array(
        '/(quiz)zes$/i'             => "$1",
        '/(matr)ices$/i'            => "$1ix",
        '/(vert|ind)ices$/i'        => "$1ex",
        '/^(ox)en$/i'               => "$1",
        '/(alias)es$/i'             => "$1",
        '/(octop|vir)i$/i'          => "$1us",
        '/(cris|ax|test)es$/i'      => "$1is",
        '/(shoe)s$/i'               => "$1",
        '/(o)es$/i'                 => "$1",
        '/(bus)es$/i'               => "$1",
        '/([m|l])ice$/i'            => "$1ouse",
        '/(x|ch|ss|sh)es$/i'        => "$1",
        '/(m)ovies$/i'              => "$1ovie",
        '/(s)eries$/i'              => "$1eries",
        '/([^aeiouy]|qu)ies$/i'     => "$1y",
        '/([lr])ves$/i'             => "$1f",
        '/(tive)s$/i'               => "$1",
        '/(hive)s$/i'               => "$1",
        '/(li|wi|kni)ves$/i'        => "$1fe",
        '/(shea|loa|lea|thie)ves$/i'=> "$1f",
        '/(^analy)ses$/i'           => "$1sis",
        '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i'  => "$1$2sis",
        '/([ti])a$/i'               => "$1um",
        '/(n)ews$/i'                => "$1ews",
        '/(h|bl)ouses$/i'           => "$1ouse",
        '/(corpse)s$/i'             => "$1",
        '/(us)es$/i'                => "$1",
        '/s$/i'                     => ""
    );

    static $irregular = array(
        'move'   => 'moves',
        'foot'   => 'feet',
        'goose'  => 'geese',
        'sex'    => 'sexes',
        'child'  => 'children',
        'man'    => 'men',
        'tooth'  => 'teeth',
        'person' => 'people'
    );

    static $uncountable = array(
        'sheep',
        'fish',
        'deer',
        'series',
        'species',
        'money',
        'rice',
        'information',
        'equipment'
    );

    public static function pluralize( $string )
    {
        // save some time in the case that singular and plural are the same
        if ( in_array( strtolower( $string ), self::$uncountable ) )
            return $string;

        // check for irregular singular forms
        foreach ( self::$irregular as $pattern => $result )
        {
            $pattern = '/' . $pattern . '$/i';

            if ( preg_match( $pattern, $string ) )
                return preg_replace( $pattern, $result, $string);
        }

        // check for matches using regular expressions
        foreach ( self::$plural as $pattern => $result )
        {
            if ( preg_match( $pattern, $string ) )
                return preg_replace( $pattern, $result, $string );
        }

        return $string;
    }

    public static function singularize( $string )
    {
        // save some time in the case that singular and plural are the same
        if ( in_array( strtolower( $string ), self::$uncountable ) )
            return $string;

        // check for irregular plural forms
        foreach ( self::$irregular as $result => $pattern )
        {
            $pattern = '/' . $pattern . '$/i';

            if ( preg_match( $pattern, $string ) )
                return preg_replace( $pattern, $result, $string);
        }

        // check for matches using regular expressions
        foreach ( self::$singular as $pattern => $result )
        {
            if ( preg_match( $pattern, $string ) )
                return preg_replace( $pattern, $result, $string );
        }

        return $string;
    }

    public static function pluralize_if($count, $string)
    {
        if ($count == 1)
            return "1 $string";
        else
            return $count . " " . self::pluralize($string);
    }
}</pre>
<p>ActionScript:</p>
<pre>package
{
public class Inflect
{
    private static var plural : Array = [
        [/(quiz)$/i,                     "$1zes"],
        [/^(ox)$/i,                      "$1en"],
        [/([m|l])ouse$/i,                "$1ice"],
        [/(matr|vert|ind)ix|ex$/i,       "$1ices"],
        [/(x|ch|ss|sh)$/i,               "$1es"],
        [/([^aeiouy]|qu)y$/i,            "$1ies"],
        [/(hive)$/i,                     "$1s"],
        [/(?:([^f])fe|([lr])f)$/i,       "$1$2ves"],
        [/(shea|lea|loa|thie)f$/i,       "$1ves"],
        [/sis$/i,                        "ses"],
        [/([ti])um$/i,                   "$1a"],
        [/(tomat|potat|ech|her|vet)o$/i, "$1oes"],
        [/(bu)s$/i,                      "$1ses"],
        [/(alias|status)$/i,             "$1es"],
        [/(octop)us$/i,                  "$1i"],
        [/(ax|test)is$/i,                "$1es"],
        [/(us)$/i,                       "$1es"],
        [/s$/i,                          "s"],
        [/$/i,                           "s"]
    ];

    private static var singular : Array = [
        [/(quiz)zes$/i,             "$1"],
        [/(matr)ices$/i,            "$1ix"],
        [/(vert|ind)ices$/i,        "$1ex"],
        [/^(ox)en$/i,               "$1"],
        [/(alias|status)es$/i,      "$1"],
        [/(octop|vir)i$/i,          "$1us"],
        [/(cris|ax|test)es$/i,      "$1is"],
        [/(shoe)s$/i,               "$1"],
        [/(o)es$/i,                 "$1"],
        [/(bus)es$/i,               "$1"],
        [/([m|l])ice$/i,            "$1ouse"],
        [/(x|ch|ss|sh)es$/i,        "$1"],
        [/(m)ovies$/i,              "$1ovie"],
        [/(s)eries$/i,              "$1eries"],
        [/([^aeiouy]|qu)ies$/i,     "$1y"],
        [/([lr])ves$/i,             "$1f"],
        [/(tive)s$/i,               "$1"],
        [/(hive)s$/i,               "$1"],
        [/(li|wi|kni)ves$/i,        "$1fe"],
        [/(shea|loa|lea|thie)ves$/i,"$1f"],
        [/(^analy)ses$/i,           "$1sis"],
        [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i,  "$1$2sis"],
        [/([ti])a$/i,               "$1um"],
        [/(n)ews$/i,                "$1ews"],
        [/(h|bl)ouses$/i,           "$1ouse"],
        [/(corpse)s$/i,             "$1"],
        [/(us)es$/i,                "$1"],
        [/s$/i,                     ""]
    ];

    private static var irregular : Array = [
        ['move'   , 'moves'],
        ['foot'   , 'feet'],
        ['goose'  , 'geese'],
        ['sex'    , 'sexes'],
        ['child'  , 'children'],
        ['man'    , 'men'],
        ['tooth'  , 'teeth'],
        ['person' , 'people']
    ];

    private static var uncountable : Array = [
        'sheep',
        'fish',
        'deer',
        'series',
        'species',
        'money',
        'rice',
        'information',
        'equipment'
    ];

    public static function pluralize( string : String ) : String
    {
        var pattern : RegExp;
        var result : String;

        // save some time in the case that singular and plural are the same
        if (uncountable.indexOf(string.toLowerCase()) != -1)
          return string;

        // check for irregular singular forms
        var item : Array;
        for each ( item in irregular )
        {
            pattern = new RegExp(item[0] + "$", "i");
            result = item[1];

            if (pattern.test(string))
            {
                return string.replace(pattern, result);
            }
        }

        // check for matches using regular expressions
        for each ( item in plural)
        {
            pattern = item[0];
            result = item[1];

            if (pattern.test(string))
            {
                return string.replace(pattern, result);
            }
        }

        return string;
    }

    public static function singularize( string : String ) : String
    {
        var pattern : RegExp;
        var result : String

        // save some time in the case that singular and plural are the same
        if (uncountable.indexOf(string.toLowerCase()) != -1)
            return string;

        // check for irregular singular forms
        var item : Array;
        for each ( item in irregular )
        {
            pattern = new RegExp(item[1] + "$", "i");
            result = item[0];

            if (pattern.test(string))
            {
                return string.replace(pattern, result);
            }
       }

       // check for matches using regular expressions
       for each ( item in singular)
       {
            pattern = item[0];
            result = item[1];

            if (pattern.test(string))
            {
                return string.replace(pattern, result);
            }
       }

       return string;

    }

    public static function pluralizeIf(count : int, string : String) : String
    {
        if (count == 1)
            return "1 " + string;
        else
            return count.toString() + " " + pluralize(string);
    }
}
}</pre>
<p>Ruby on Rails (use this to replace your Inflect.rb):</p>
<pre>Inflector.inflections do |inflect|
    inflect.plural(/$/, 's')
    inflect.plural(/s$/i, 's')
    inflect.plural(/(us)$/i, '\\1es')
    inflect.plural(/(ax|test)is$/i, '\\1es')
    inflect.plural(/(octop)us$/i, '\\1i')
    inflect.plural(/(alias)$/i, '\\1es')
    inflect.plural(/(bu)s$/i, '\\1ses')
    inflect.plural(/(tomat|potat|ech|her|vet)o$/i, '\\1oes')
    inflect.plural(/([ti])um$/i, '\\1a')
    inflect.plural(/sis$/i, 'ses')
    inflect.plural(/(shea|lea|loa|thie)f$/i, '\\1ves')
    inflect.plural(/(?:([^f])fe|([lr])f)$/i, '\\1\\2ves')
    inflect.plural(/(hive)$/i, '\\1s')
    inflect.plural(/([^aeiouy]|qu)y$/i, '\\1ies')
    inflect.plural(/(x|ch|ss|sh)$/i, '\\1es')
    inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\\1ices')
    inflect.plural(/([m|l])ouse$/i, '\\1ice')
    inflect.plural(/^(ox)$/i, '\\1en')
    inflect.plural(/(quiz)$/i, '\\1zes')

    inflect.singular(/s$/i, '')
    inflect.singular(/(us)es$/i, '\\1')
    inflect.singular(/(corpse)s$/i, '\\1')
    inflect.singular(/(h|bl)ouses$/i, '\\1ouse')
    inflect.singular(/(n)ews$/i, '\\1ews')
    inflect.singular(/([ti])a$/i, '\\1um')
    inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\\1\\2sis')
    inflect.singular(/(^analy)ses$/i, '\\1sis')
    inflect.singular(/(shea|loa|lea|thie)ves$/i, '\\1f')
    inflect.singular(/(li|wi|kni)ves$/i, '\\1fe')
    inflect.singular(/(hive)s$/i, '\\1')
    inflect.singular(/(tive)s$/i, '\\1')
    inflect.singular(/([lr])ves$/i, '\\1f')
    inflect.singular(/([^aeiouy]|qu)ies$/i, '\\1y')
    inflect.singular(/(s)eries$/i, '\\1eries')
    inflect.singular(/(m)ovies$/i, '\\1ovie')
    inflect.singular(/(x|ch|ss|sh)es$/i, '\\1')
    inflect.singular(/([m|l])ice$/i, '\\1ouse')
    inflect.singular(/(bus)es$/i, '\\1')
    inflect.singular(/(o)es$/i, '\\1')
    inflect.singular(/(shoe)s$/i, '\\1')
    inflect.singular(/(cris|ax|test)es$/i, '\\1is')
    inflect.singular(/(octop|vir)i$/i, '\\1us')
    inflect.singular(/(alias)es$/i, '\\1')
    inflect.singular(/^(ox)en/i, '\\1')
    inflect.singular(/(vert|ind)ices$/i, '\\1ex')
    inflect.singular(/(matr)ices$/i, '\\1ix')
    inflect.singular(/(quiz)zes$/i, '\\1')

    inflect.irregular('person', 'people')
    inflect.irregular('tooth', 'teeth')
    inflect.irregular('man', 'men')
    inflect.irregular('child', 'children')
    inflect.irregular('sex', 'sexes')
    inflect.irregular('goose', 'geese')
    inflect.irregular('foot', 'feet')
    inflect.irregular('move', 'moves')

    inflect.uncountable(%w(equipment information rice money species series deer fish sheep))
end</pre>
]]></content:encoded>
			<wfw:commentRss>http://kuwamoto.org/2007/12/17/improved-pluralizing-in-php-actionscript-and-ror/feed/</wfw:commentRss>
		<slash:comments>34</slash:comments>
		</item>
		<item>
		<title>AS3 &#8212; on the lack of private and protected constructors</title>
		<link>http://kuwamoto.org/2006/04/05/as3-on-the-lack-of-private-and-protected-constructors/</link>
		<comments>http://kuwamoto.org/2006/04/05/as3-on-the-lack-of-private-and-protected-constructors/#comments</comments>
		<pubDate>Wed, 05 Apr 2006 16:03:49 +0000</pubDate>
		<dc:creator>sho</dc:creator>
				<category><![CDATA[actionscript]]></category>
		<category><![CDATA[flex]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://kuwamoto.org/?p=54</guid>
		<description><![CDATA[As I was talking about using objects as enums, someone made a comment about the lack of private and protected constructors. I know that the this is a sore spot, so I thought I&#8217;d explain my view of how we got here, and give my two cents. The problem is that unlike what we did [...]]]></description>
			<content:encoded><![CDATA[<p>As I was talking about using objects as enums, someone made a comment about the lack of private and protected constructors. I know that the this is a sore spot, so I thought I&#8217;d explain my view of how we got here, and give my two cents. </p>
<p>The problem is that unlike what we did with ActionScript 2, we are working tightly with people from Mozilla and others to standardize on ECMAScript edition 4. The ECMAScript standard is not final, but we are adhering as closely as we can to the spec as it develops.<br />
<span id="more-54"></span></p>
<h2 class="separator">~</h2>
<p>Private and protected constructors is not something that the group has been able to tackle yet, and it is a nontrivial change to the language that requires lots of careful thought.</p>
<p>Ideally, we would have been able to think through all the ramifications of private and protected constructors and work with other ECMA members to make sure the design was sound, but that just wasn&#8217;t possible in the amount of time we had.</p>
<p>Given this, there were three choices:</p>
<p>1) Delay ActionScipt 3 (and Flex, etc.) significantly.<br />
2) Just &#8220;go with&#8221; a quick and dirty implementation of private and protected constructors for AS3 and risk compatibility problems down the road once the ECMA spec becomes final.<br />
3) Don&#8217;t allow private and protected constructors for now.</p>
<p>We went with option (3). </p>
<p>The main uses of private and protected constructors are singletons and abstract base classes. In both of these cases, I agree that the lack of real private and protected constructors is a pain. There are hackarounds which will work for now, but I am eagerly awaiting the day when we don&#8217;t have to use them anymore.</p>
<p>As for the specific use case described in the previous post (using objects as enumerations), the main thing that private/protected constructors allows you to do is to ensure that the set of &#8220;enum&#8221; values cannot be extended later by someone else.</p>
<p>I personally like having this level of control, but I am willing to live without it. It &#8220;feels better&#8221; to create a system where no one can add any more values to the enumeration, but in practice, </p>
<p>a) If people create new values for the enumeration and try to use them with code that wasn&#8217;t expecting those values, it will obviously break. So who is going to do this?</p>
<p>b) If people create new values for the enumeration and use them only within their own code&#8230; well&#8230; it kind of breaks the spirit of what an enumeration is, but it might be just fine.</p>
<p>Here&#8217;s a concrete example of scenario (b). Imagine that I create a text control with three values for alignment: LEFT, RIGHT, and CENTER. Someone subclasses this control, and wants to create a fourth enumeration value: JUSTIFY. This new value would obviously only be useful for this person&#8217;s subclass, but who am I to say that he/she shouldn&#8217;t create it?  </p>
<p>Like I said, I would ideally like the option of controlling what can and can&#8217;t get extended, but in the case of enumerations, I&#8217;m ok with not having that control for now.</p>
]]></content:encoded>
			<wfw:commentRss>http://kuwamoto.org/2006/04/05/as3-on-the-lack-of-private-and-protected-constructors/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>FAB &#8211; Flex / AJAX bridge</title>
		<link>http://kuwamoto.org/2006/03/08/fab-flex-ajax-bridge/</link>
		<comments>http://kuwamoto.org/2006/03/08/fab-flex-ajax-bridge/#comments</comments>
		<pubDate>Wed, 08 Mar 2006 16:07:59 +0000</pubDate>
		<dc:creator>sho</dc:creator>
				<category><![CDATA[ajax]]></category>
		<category><![CDATA[flex]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://kuwamoto.org/?p=47</guid>
		<description><![CDATA[FAB &#8211; Flex/AJAX Bridge &#8211; is a library created by Ely Greenfield, who is a good friend and Flex Architect. FAB lets you control Flex applications using JavaScript. Method calls just work, and getters and setters are converted to method calls (because browsers don&#8217;t support getters and setters yet). You can even attach event listeners [...]]]></description>
			<content:encoded><![CDATA[<p>FAB &#8211; Flex/AJAX Bridge &#8211; is a library created by <a href="http://www.quietlyscheming.com/blog/">Ely Greenfield</a>, who is a good friend and Flex Architect.</p>
<p>FAB lets you control Flex applications using JavaScript. Method calls just work, and getters and setters are converted to method calls (because browsers don&#8217;t support getters and setters yet).</p>
<p>You can even attach event listeners from JavaScript and pass function objects back and forth. For example, you can create an MXML file with a button in it and drive all of the logic from within your HTML/JavaScript.</p>
<p>MXML:<br />
&#8211;<br />
&lt;mx:Application xmlns:mx=&#8221;http://www.macromedia.com/2005/mxml&#8221;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;fab:FABridge xmlns:fab=&#8221;bridge.*&#8221; /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:Button id=&#8221;okButton&#8221; label=&#8221;OK&#8221; /&gt;<br />
&lt;/mx:Application&gt;</p>
<p>JavaScript:<br />
&#8211;<br />
function init()<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;var flexApp = FABridge.flash.root();<br />
&nbsp;&nbsp;&nbsp;&nbsp;flexApp.okButton().addEventListener(&#8220;click&#8221;, handleClick);<br />
}</p>
<p>function handleClick(event)<br />
{<br />
&nbsp;&nbsp;&nbsp;&nbsp;// handle the click event here.<br />
}</p>
<p>You can read more about it on Ely&#8217;s blog, which I predict will be worth reading.</p>
<p><a href="http://www.quietlyscheming.com/blog/2006/03/06/flex-and-ajax/">http://www.quietlyscheming.com/blog/2006/03/06/flex-and-ajax/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://kuwamoto.org/2006/03/08/fab-flex-ajax-bridge/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Slides from Flashforward 2006</title>
		<link>http://kuwamoto.org/2006/03/06/slides-from-flashforward-2006/</link>
		<comments>http://kuwamoto.org/2006/03/06/slides-from-flashforward-2006/#comments</comments>
		<pubDate>Mon, 06 Mar 2006 20:51:51 +0000</pubDate>
		<dc:creator>sho</dc:creator>
				<category><![CDATA[flashforward]]></category>
		<category><![CDATA[flex]]></category>
		<category><![CDATA[programming]]></category>
		<category><![CDATA[ria]]></category>

		<guid isPermaLink="false">http://kuwamoto.org/?p=46</guid>
		<description><![CDATA[As promised, here are my slides and notes from Flashforward. Some things you will need to know: The layout syntax uses the new beta 2 syntax, which is not out yet. The notes cover a bit less material than the slides. That&#8217;s because the notes were finalized before the slides were, in order to get [...]]]></description>
			<content:encoded><![CDATA[<p>As promised, here are my slides and notes from Flashforward. Some things you will need to know:</p>
<ol>
<li>The layout syntax uses the new beta 2 syntax, which is not out yet.</li>
<li>The notes cover a bit less material than the slides. That&#8217;s because the notes were finalized before the slides were, in order to get them printed on paper for conference attendees. If I have time, I may extend the notes but with so much going on, I am not sure I will have time to do this.</li>
</ol>
<p class="innerquote">
<a href="http://examples.kuwamoto.org/FF_2006_Seattle/Flex_For_Flash_Developers_FF_2006_final.ppt">[Slides - ppt]</a> <a href="http://examples.kuwamoto.org/FF_2006_Seattle/Flex_For_Flash_Developers_FF_2006_final.pdf">[Slides - pdf]</a><br />
<a href="http://examples.kuwamoto.org/FF_2006_Seattle/Flash_forward_notes_Sho_Kuwamoto.doc">[Speaker notes - doc]</a> <a href="http://examples.kuwamoto.org/FF_2006_Seattle/Flash_forward_notes_Sho_Kuwamoto.pdf">[Speaker notes - pdf]</a></p>
]]></content:encoded>
			<wfw:commentRss>http://kuwamoto.org/2006/03/06/slides-from-flashforward-2006/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Evolution of a feature &#8211; layout constraints</title>
		<link>http://kuwamoto.org/2006/02/24/evolution-of-a-feature-layout-constraints/</link>
		<comments>http://kuwamoto.org/2006/02/24/evolution-of-a-feature-layout-constraints/#comments</comments>
		<pubDate>Sat, 25 Feb 2006 04:45:39 +0000</pubDate>
		<dc:creator>sho</dc:creator>
				<category><![CDATA[flex]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://kuwamoto.org/?p=44</guid>
		<description><![CDATA[We spend a lot of time here on the Flex / Flex Builder team arguing over exactly how each piece of the framework should work, and how the syntax should look. I thought it might be interesting to have a glimpse into how some of these features evolve, and why. I&#8217;m picking a particularly controversial [...]]]></description>
			<content:encoded><![CDATA[<p>We spend a lot of time here on the Flex / Flex Builder team arguing over exactly how each piece of the framework should work, and how the syntax should look.</p>
<p>I thought it might be interesting to have a glimpse into how some of these features evolve, and why. I&#8217;m picking a particularly controversial example: layout constraints.<br />
<span id="more-44"></span></p>
<h2 class="separator">~</h2>
<p>Layout constraints came about because designers in particular weren&#8217;t happy using containers for layout. Actually, let me say it a different way. When you drag things around, they generally expect things to stay put, although they are fine with things moving around when it makes sense (like when you drag something into a control bar). And they want pixel level control over the position of every element.</p>
<p>Among coders, feedback on container based layout was mixed. Most of them &#8220;got&#8221; it, and some of them loved it. But when you step back, it wasn&#8217;t good for all layouts. Boxy layouts (think forms) were just fine. More freeform layouts (think Flash) were hard to achieve.</p>
<p>There was also a performance issue. Nesting of containers meant nesting of movie clips. Removing the nesting, however, meant that fluid resizing layouts were hard to achieve.</p>
<h2 class="separator">~</h2>
<p>For all these reasons, we decided to add a constraint system to Flex. We originally talked about all sorts of more complex ideas &#8212; sibling constraints and other more exotic constraints &#8212; but we settled the more conservative approach of only supporting constraints relative to the parent container.</p>
<p>Let&#8217;s say that we want to put a button in the top right corner of our app. Our original syntax looked like this:</p>
<h3>Attempt 1:</h3>
<p>&lt;mx:Panel width=&#8221;100%&#8221; height=&#8221;100%&#8221;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:Canvas width=&#8221;100%&#8221; height=&#8221;100%&#8221;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:layoutConstraints &gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:EdgeAnchor target=&#8221;myButton&#8221; right=&#8221;30&#8243; /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/mx:layoutConstraints&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:Button id=&#8221;myButton&#8221; y=&#8221;30&#8243; /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/mx:Canvas&gt;<br />
&lt;/mx:Panel&gt;</p>
<p>This would allow us to add new types of constraints in the future, such as sibling constraints. The downside, however, lay in the tooling. Every time you add a constraint to a control, you have to add an ID. If the user changes the ID, you have to change it in both places. It&#8217;s not rocket science, but it&#8217;s error prone, and it can lead to situations in which the tool is trying to be &#8220;too smart&#8221;.</p>
<p>We moved the layout constraints to be inside the control itself. This fixed the problem of tracking IDs. </p>
<h3>Attempt 2:</h3>
<p>&lt;mx:Panel width=&#8221;100%&#8221; height=&#8221;100%&#8221;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:Canvas width=&#8221;100%&#8221; height=&#8221;100%&#8221;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:Button y=&#8221;30&#8243;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:layoutConstraints&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:EdgeAnchor right=&#8221;30&#8243; /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/mx:layoutConstraints&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/mx:Button&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/mx:Canvas&gt;<br />
&lt;/mx:Panel&gt;</p>
<p>The next issue was that it was confusing to have the canvas inside the panel. People kept accidentally resizing the canvas instead of the panel, or pulling the panel out of the canvas. We also decided that the name &#8220;EdgeAnchor&#8221; was too verbose.</p>
<p>After much arguing about making things general vs. dealing with the specific needs in the 80% case, we decided to make Panel and Application understand how to do multiple different kinds of layout: absolute, vertical and horizontal.</p>
<h3>Attempt 3:</h3>
<p>&lt;mx:Panel width=&#8221;100%&#8221; height=&#8221;100%&#8221; layout=&#8221;absolute&#8221;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:Button y=&#8221;30&#8243;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:layoutConstraints&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:Anchor right=&#8221;30&#8243; /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/mx:layoutConstraints&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/mx:Button&gt;<br />
&lt;/mx:Panel&gt;</p>
<p>Right around this time, people were dragging panels into the view, centering them, and expecting them to stay centered no matter what size the browser was. It was especially confusing because Flex Builder has snapping heuristics and the panel would snap into place when it was centered. We dealt with this by adding a centering constraint.</p>
<h3>Attempt 4:</h3>
<p>&lt;mx:Panel width=&#8221;100%&#8221; height=&#8221;100%&#8221; layout=&#8221;absolute&#8221;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:Button y=&#8221;30&#8243;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:layoutConstraints&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:Anchor horizontalCenter=&#8221;0&#8243; /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/mx:layoutConstraints&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;/mx:Button&gt;<br />
&lt;/mx:Panel&gt;</p>
<p>Soon after we made this change, the team was busy doing lots of what we call scenario testing: building applications in the same way our customers would. One thing about layout constraints that stuck out like a sore thumb was its verbosity. Where buttons used to be on a single line, they now occupied 5. We argued about this for a while, and decided to make layout constraints styles, partially because this was consistent with CSS.</p>
<p>We also decided to promote the use of &#8220;left&#8221; and &#8220;top&#8221; as opposed to &#8220;x&#8221; and &#8220;y&#8221;. One might wonder how these are different, but they are. The &#8220;left&#8221; property is a constraint that specifies the desired position for the left coordinate. The &#8220;x&#8221; property is the coordinate that an object happens to be at. Thus, if you were to use a move effect to move an object programmtically, its &#8220;x&#8221; property would change, but its &#8220;left&#8221; would not.</p>
<p>In any case, the resulting syntax looks like this (not in beta 1, but will be part of beta 2):</p>
<h3>Attempt 5:</h3>
<p>&lt;mx:Panel width=&#8221;100%&#8221; height=&#8221;100%&#8221; layout=&#8221;absolute&#8221;&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;mx:Button top=&#8221;30&#8243; right=&#8221;30&#8243; /&gt;<br />
&lt;/mx:Panel&gt;</p>
<h3>The moral</h3>
<p>Why did it take so long to arrive at what appears to be an obvious syntax? Perhaps it was too obvious. When we started, we were trying to solve for many different things: forward compatibility, generalizability, and the possibility of many different kinds of constraints. Through trial and error, we arrived at the simplest way to express the basic things we need for this release. </p>
<p>In this case, I am confident we ended up with the right answer, but it took a while. In fact, there are people out there in the Flash community who aren&#8217;t convinced that layout constraints are a good idea who I think we will turn around based on the new syntax. </p>
<p>Sometimes, these things take time to sort out. The answers always seem obvious in retrospect, but you can&#8217;t expect to get to the right answer straight away.</p>
]]></content:encoded>
			<wfw:commentRss>http://kuwamoto.org/2006/02/24/evolution-of-a-feature-layout-constraints/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>

