<?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>Gorges Blog &#187; Rasmus Schultz</title>
	<atom:link href="http://blog.GORGES.us/author/rschultz/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.GORGES.us</link>
	<description>Web Sites that Grow Your Business - our blog</description>
	<lastBuildDate>Fri, 17 May 2013 16:34:55 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>Customizing the TRAC ticket workflow</title>
		<link>http://blog.GORGES.us/2013/03/customizing-the-trac-ticket-workflow/</link>
		<comments>http://blog.GORGES.us/2013/03/customizing-the-trac-ticket-workflow/#comments</comments>
		<pubDate>Tue, 26 Mar 2013 12:55:40 +0000</pubDate>
		<dc:creator>Rasmus Schultz</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[ticket workflow]]></category>
		<category><![CDATA[TRAC]]></category>
		<category><![CDATA[version control]]></category>

		<guid isPermaLink="false">http://blog.GORGES.us/?p=800</guid>
		<description><![CDATA[At GORGES (among other tools) we use TRAC to manage bugs and feature requests (aka &#8220;tickets&#8221;) and tracking those against various version control systems. I did a quick investigation into TRAC configuration yesterday, and discovered how to customize the ticket workflow to better fit our process. Out of the box, the normal workflow for tickets [...]]]></description>
				<content:encoded><![CDATA[<p>At GORGES (among other tools) we use <a title="TRAC" href="http://trac.edgewall.org/" target="_blank">TRAC</a> to manage bugs and feature requests (aka &#8220;tickets&#8221;) and tracking those against various <a title="version control systems" href="http://en.wikipedia.org/wiki/Revision_control" target="_blank">version control systems</a>.</p>
<p>I did a quick investigation into TRAC configuration yesterday, and discovered how to customize the <a title="ticket workflow" href="http://trac.edgewall.org/wiki/TracWorkflow" target="_blank">ticket workflow</a> to better fit our process.</p>
<p>Out of the box, the normal workflow for tickets is pretty simple: a ticket start out as &#8220;new&#8221; &#8211; it is then assigned for someone to work on, eventually accepted as done, and then finally closed. There are couple of ways to deviate from this process, such as when the work gets rejected and needs more work, or when the ticket needs to be reassigned for somebody else to work on for some reason, but that&#8217;s generally how it goes.</p>
<p>Our process on larger projects is quite a bit more detailed than that &#8211; in fact, most places I&#8217;ve worked, the process is usually a bit more granular than that. Our process is something along the lines of this:</p>
<p>1. A new tickets is created<br />
2. The ticket is assigned for someone to work on.<br />
3. A solution in implemented<br />
4. The solution is deployed to a test (aka &#8220;staging&#8221;) site, for review<br />
5. The work is reviewed and accepted</p>
<p>Again, there are a couple of ways to deviate from that workflow, includes the ones mentioned before, and also in some cases closing a ticket (for example because it&#8217;s invalid) or reopening a ticket that had already previously been closed.</p>
<h3>The Wrong Approach</h3>
<p>Some people resort to simply adding a new property to tickets, which is very easy to do &#8211; here&#8217;s a snippet from a custom option added to the &#8220;trac.ini&#8221; file:</p>
<p><code style="font-size: 90%;">[ticket-custom]<br />
progress = select<br />
progress.label = Progress<br />
progress.options = new|assigned|implemented|reviewing|accepted|rejected|closed<br />
progress.value = new<br />
</code></p>
<p>This adds a new drop-down to tickets, labeled &#8220;Progress&#8221;, with the choices you can see defined by the &#8220;progress.options&#8221; setting above. You will need to make some adjustments to the <a title="reports" href="http://trac.edgewall.org/wiki/TracReports" target="_blank">reports</a>, because custom values don&#8217;t show up by default, but that&#8217;s doable.</p>
<p>Why is this wrong?</p>
<p>Ponder for a moment why we had words like &#8220;new&#8221; and &#8220;assigned&#8221; in our Progress field &#8211; words which also appear in the ticket status.</p>
<p>The problem is that Progress is basically synonymous with Status, and what we end up with is basically two different Status settings. This causes a number of problems, including the fact that our new Progress field doesn&#8217;t actually mean anything in TRAC as such &#8211; it&#8217;s just a label. And for another, the fact that you can create conflicting Status and Progress combinations &#8211; for example, your ticket could be &#8220;accepted&#8221; and &#8220;rejected&#8221;, at the same time!</p>
<p>So without a fair amount of discipline, and agreement and understanding of how to manage these values &#8220;correctly&#8221; amongst the team-members, this is not really going to work well.</p>
<h3>The Right Approach</h3>
<p>Fortunately, when you look into it, and experiment with it for a bit, you will discover that the ticket workflow in TRAC is in fact incredibly flexible!</p>
<p>It&#8217;s a bit hard to understand and configure than a simple custom field, but so much more valuable, as you can fully customize this to fit your team&#8217;s workflow.</p>
<p>Here is the workflow-configuration I came up with, implementing the workflow I described in the beginning of this article:</p>
<p><code>[ticket-workflow]<br />
leave = * -&gt; *<br />
leave.name = No status change - leave<br />
leave.operations = leave_status<br />
leave.default = 0</code></p>
<p>assign = new -&gt; assigned<br />
assign.permissions = TICKET_MODIFY<br />
assign.operations = set_owner<br />
assign.name = Assign<br />
assign.default = -1</p>
<p>implement = assigned,rejected -&gt; implemented<br />
implement.permissions = TICKET_MODIFY<br />
implement.name = Implemented<br />
implement.default = -2</p>
<p>deploy = assigned,implemented,rejected -&gt; reviewing<br />
deploy.permissions = TICKET_MODIFY<br />
deploy.name = Deployed for review<br />
deploy.default = -3</p>
<p>accept = reviewing -&gt; closed<br />
accept.permissions = TICKET_MODIFY<br />
accept.operations = set_resolution<br />
accept.set_resolution = fixed<br />
accept.name = Accept<br />
accept.default = -4</p>
<p>reject = reviewing -&gt; rejected<br />
reject.permissions = TICKET_MODIFY<br />
reject.name = Rejected (needs more work)<br />
reject.default = -5</p>
<p>close = new,assigned,implemented,reviewing,rejected -&gt; closed<br />
close.permissions = TICKET_MODIFY<br />
close.operations = set_resolution<br />
close.name = Close<br />
close.default = -6</p>
<p>reassign = assigned,implemented,reviewing,rejected -&gt; assigned<br />
reassign.permissions = TICKET_MODIFY<br />
reassign.operations = set_owner<br />
reassign.name = Re-assign<br />
reassign.default = -7</p>
<p>reopen = closed -&gt; assigned<br />
reopen.permissions = TICKET_CREATE<br />
reopen.operations = set_owner<br />
reopen.name = Re-open and Assign<br />
reopen.default = -8</p>
<p>Each section of this configuration defines a single possible action in the workflow &#8211; the activities you can perform: assign, implement, deploy, accept or reject, close, and reassign or reopen when needed.</p>
<p>The first setting for each defined action (with the &#8220;<code>-&gt;</code>&#8221; symbol in it) defines allowed status(es) and the new status after performing that action &#8211; for example, the &#8220;deploy&#8221; action is defined as &#8220;assigned,implemented,rejected -&gt; reviewing&#8221;, meaning you can change the ticket-status to &#8220;reviewing&#8221; only when the status is &#8220;assigned&#8221;, &#8220;implemented&#8221; or &#8220;rejected&#8221;. Note that the statuses themselves are not formally defined in the configuration file &#8211; the possible statuses are implicitly defined by configuring actions.</p>
<p>The &#8220;permissions&#8221; setting is optional, and defines what level of permission is required to perform the action. For the most part, the right to modify a ticket (TICKET_MODIFY) is enough &#8211; but in our case, we think that reopening a ticket should only be allowed if you&#8217;re allowed to create new tickets (TICKET_CREATE).</p>
<p>The &#8220;operations&#8221; setting defines one or more <a title="operations" href="http://trac.edgewall.org/wiki/TracWorkflow#BasicTicketWorkflowCustomization" target="_blank">operations</a> you want TRAC to do when this action is performed. This affects the user-interface &#8211; for example, the &#8220;set_resolution&#8221; operation (which we used for the &#8220;close&#8221; action above) displays with a drop-down to select the resolution. Note that the &#8220;set_resolution&#8221; <strong>operation</strong> has an optional &#8220;set_resolution&#8221; <strong>setting</strong>, which allows you to specify one or more resolution-types allowed for this action &#8211; for example, the &#8220;accept&#8221; action we defined, only allows one resolution: &#8220;fixed&#8221;.</p>
<p>The are two more simple, but very important (and underused) settings for actions. The &#8220;name&#8221; setting defines how the action is displayed &#8211; for example, &#8220;Re-open and Assign&#8221; is more descriptive than simply &#8220;reopen&#8221; &#8211; use this setting to clarify the meaning of the action and how or when to use it. And finally, the perhaps somewhat misleadingly named &#8220;default&#8221; setting, which actually defines the priority (sort order) of available actions in the user-interface. This is important, because it enables you to communicate which action is most likely next, by displaying it first. Oddly, the options are sorted backwards by this value, which is why I used a negative number in our configuration.</p>
<h4>Conclusion</h4>
<p>TRAC is a very powerful tool that comes with a lot of humble (sometimes underwhelming) configuration out of the box &#8211; but when you look under the surface, it really is an incredibly powerful tool with lots of optional features and an incredible degree of flexibility, allowing you to adapt it to pretty much any work-environment.</p>
<p>I hope you enjoyed this little tutorial and a glimpse of our process.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.GORGES.us/2013/03/customizing-the-trac-ticket-workflow/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&lt;b&gt; and &lt;i&gt; are still good</title>
		<link>http://blog.GORGES.us/2012/06/b-and-i-are-still-good/</link>
		<comments>http://blog.GORGES.us/2012/06/b-and-i-are-still-good/#comments</comments>
		<pubDate>Wed, 27 Jun 2012 11:41:31 +0000</pubDate>
		<dc:creator>Rasmus Schultz</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.GORGES.us/?p=815</guid>
		<description><![CDATA[I just had a small &#8220;a-hah&#8221;. The &#60;b&#62; and &#60;i&#62; tags have been generally dismissed, in favor of &#60;strong&#62; and &#60;em&#62; which have semantic meaning. But as they pointed out in the twitter bootstrap documentation, &#60;b&#62; and &#60;i&#62; are still valid! Most people seem to think they were deprecated because they have no semantic meaning [...]]]></description>
				<content:encoded><![CDATA[<p>I just had a small &#8220;a-hah&#8221;.</p>
<p>The <a href="http://dev.w3.org/html5/markup/b.html" target="_blank"><tt>&lt;b&gt;</tt></a> and <a href="http://dev.w3.org/html5/markup/i.html" target="_blank"><tt>&lt;i&gt;</tt></a> tags have been generally dismissed, in favor of <tt>&lt;strong&gt;</tt> and <tt>&lt;em&gt;</tt> which have semantic meaning.</p>
<p>But as they pointed out in the twitter bootstrap documentation, <tt>&lt;b&gt;</tt> and <tt>&lt;i&gt;</tt> are still valid!</p>
<p>Most people seem to think they were deprecated because they have no semantic meaning &#8211; that was certainly my impression.</p>
<p>But they were not deprecated &#8211; in fact they were kept around for the very same reason &#8211; because they have no semantic meaning.</p>
<p>In other words, if you need to highlight words of phrases without conveying additional importance, you should be using <tt>&lt;b&gt;</tt>, while <tt>&lt;i&gt;</tt> can be used for example to stress technical terms or voice, again, without communicating anything semantic, just for visual distinction.</p>
<p>This may seem like a tiny, irrelevant thing, but I find these details can be helpful when thinking about HTML as a &#8220;rich text&#8221; medium.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.GORGES.us/2012/06/b-and-i-are-still-good/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Client-side Data Binding</title>
		<link>http://blog.GORGES.us/2012/02/client-side-data-binding/</link>
		<comments>http://blog.GORGES.us/2012/02/client-side-data-binding/#comments</comments>
		<pubDate>Mon, 06 Feb 2012 14:23:43 +0000</pubDate>
		<dc:creator>Rasmus Schultz</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://blog.GORGES.us/?p=674</guid>
		<description><![CDATA[Anybody give much thought to client-side data-binding these days? OrientDB is nearing version 1.0, and it has a native HTTP/JSON interface &#8211; it supports classes, inheritance and all kinds of relationships, all the things we try to simulate with object-relational mappers when building a domain-model, which makes me wonder&#8230; why use an ORM at all? [...]]]></description>
				<content:encoded><![CDATA[<p>Anybody give much thought to client-side data-binding these days?</p>
<p>OrientDB is nearing version 1.0, and it has a native HTTP/JSON interface &#8211; it supports classes, inheritance and all kinds of relationships, all the things we try to simulate with object-relational mappers when building a domain-model, which makes me wonder&#8230; why use an ORM at all?</p>
<p>If you&#8217;re going to do all your data-binding on the client-side, and if a graph database can natively represent your domain-model without a line of code, why struggle to achieve the same thing with server-side code and object-relational mapping?</p>
<p>You&#8217;d only need controllers/actions for actual business-operations. You could probably implement a thin &#8220;proxy&#8221; for operations like updates and deletes, access control and user identity, etc&#8230; You would never need to render a template, generate tables or forms or parse a form-post or implement tedious CRUD operations on the server-side.</p>
<p>It all sounds dreamy, and I can see this working out great for applications.</p>
<p>But what about SEO? The internet lives and breathes HTML. So it seems it&#8217;s not an approach that will work for public-facing pages.</p>
<p>Any thoughts? <img src='http://blog.GORGES.us/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://blog.GORGES.us/2012/02/client-side-data-binding/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>jQuery replacement for window.console</title>
		<link>http://blog.GORGES.us/2011/12/jquery-replacement-for-window-console/</link>
		<comments>http://blog.GORGES.us/2011/12/jquery-replacement-for-window-console/#comments</comments>
		<pubDate>Fri, 09 Dec 2011 13:14:44 +0000</pubDate>
		<dc:creator>Rasmus Schultz</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.GORGES.us/?p=723</guid>
		<description><![CDATA[Can you honestly say you have never left a console.log() statement in production code? There are legitimate reasons to leave them in test/staging code, if you&#8217;re still working out some details. So I wrote this little library as a replacement. It doesn&#8217;t blow up when the console is not available, adds chainable log() and warn() [...]]]></description>
				<content:encoded><![CDATA[<p>Can you honestly say you have never left a console.log() statement in production code?</p>
<p>There are legitimate reasons to leave them in test/staging code, if you&#8217;re still working out some details.</p>
<p>So I wrote <a title="jQuery console.log() replacement" href="http://jsfiddle.net/mindplay/D6DPr/">this little library</a> as a replacement.</p>
<p>It doesn&#8217;t blow up when the console is not available, adds chainable log() and warn() methods to jQuery selectors, and improves a bit on the display in FF and IE9.</p>
<p>Tested and working nicely in FF, IE9 and Chrome.</p>
<p>I realize this isn&#8217;t a groundbreaking idea &#8211; but I looked at a couple of existing libraries that do something similar, and nothing really seemed to work well in IE.</p>
<p>Also, this kind of thing should be small &#8211; this script compresses to less than 0.5 KB.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.GORGES.us/2011/12/jquery-replacement-for-window-console/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jQuery DOM shortcuts</title>
		<link>http://blog.GORGES.us/2011/06/jquery-dom-shortcuts/</link>
		<comments>http://blog.GORGES.us/2011/06/jquery-dom-shortcuts/#comments</comments>
		<pubDate>Mon, 13 Jun 2011 13:29:16 +0000</pubDate>
		<dc:creator>Rasmus Schultz</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://blog.GORGES.us/?p=656</guid>
		<description><![CDATA[Working daily with jQuery, at GORGES I frequently need to construct little document snippets and forms on the fly. Today I came up with a quick little meta-programming &#8220;hack&#8221; that makes this very easy and fast. Most people simply do something along the lines of this: jQuery injects the HTML string into the DOM, and [...]]]></description>
				<content:encoded><![CDATA[<p>Working daily with <a href="http://jquery.com/">jQuery</a>, at GORGES I frequently need to construct little document snippets and forms on the fly. Today I came up with a quick little meta-programming &#8220;hack&#8221; that makes this very easy and fast.</p>
<p>Most people simply do something along the lines of this:</p>
<pre class="brush: plain; title: ; notranslate">
$('body').append('&lt;h1&gt;Hello, World&lt;/h1&gt;');
</pre>
<p>jQuery injects the HTML string into the DOM, and all is well.</p>
<p>But sometimes you need to inject more structured content, or even entire forms &#8211; what I typically do is something along the lines of this:</p>
<pre class="brush: plain; title: ; notranslate">
jQuery('body').append(
  $('&lt;div/&gt;').append(
    $('&lt;div/&gt;').text('Hi there'),
    $('&lt;p/&gt;').text('How do you do?')
  )
);
</pre>
<p>With all the parentheses, angle brackets and repetitive calls to append(), this doesn&#8217;t exactly look nice.</p>
<p>Recognizing the repetitive construction of elements, followed by calls to append, what if we had a set of tiny functions that would do both in one simple operation? I decided to give it a shot, and here&#8217;s what it looks like:</p>
<pre class="brush: plain; title: ; notranslate">
jQuery('body').append(
  $div(
    $h2('Do you love it?'),
    $form(
      $div('Sign here:', $text()),
      $div($label($checkbox('yes'), 'I love it!')),
      $ul(
        $li($radio('1'), 'Option One'),
        $li($radio('2'), 'Option Two'),
        $li($radio('3'), 'Option Three')
      ),
      $submit('I submit!')
    )
  )
);
</pre>
<p>The functions prefixed with the $-sign create the elements, and pass down any arguments to the append() method.</p>
<p>The input element methods, such as $radio and $submit are a bit different &#8211; they create input elements, set the type-attribute, and if you pass them an argument, they apply that as the value-attribute on the element. (input elements can&#8217;t have child-elements, so this doesn&#8217;t get in the way of anything.)</p>
<p>You can see a <a href="http://jsfiddle.net/mindplay/TufsA/">working example and source code here</a>.</p>
<p>Because JavaScript is a highly dynamic language, I didn&#8217;t need to write each of these $-functions by hand &#8211; with just 13 lines of JavaScript, applying a simple meta-programming technique, all of the functions are automatically generated.</p>
<p>Finally, it&#8217;s worth noting that something like <code>$('&lt;div/&gt;')</code> actually results in a string of HTML being parsed and then inserted into the browser. My handy little DOM-helpers avoid the parser &#8211; they access the DOM and create HTML elements directly, which means they execute around <a href="http://stackoverflow.com/questions/327047/what-is-the-most-efficient-way-to-create-html-elements-using-jquery/327065#327065">10 times faster</a> compared to the first example.</p>
<h4>Update</h4>
<p>One of my GORGES colleagues pointed out the fact that I had no convenient way to apply attributes to the created elements &#8211; if you don&#8217;t like the repetitive calls to attr(), <a href="http://jsfiddle.net/mindplay/N8cme/">try this updated version</a>, which provides a simplified way to apply attributes quickly. This takes a bit more code, but I suspect he&#8217;s now porting this to CoffeeScript</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.GORGES.us/2011/06/jquery-dom-shortcuts/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Transition operating systems</title>
		<link>http://blog.GORGES.us/2011/05/transition-operating-systems/</link>
		<comments>http://blog.GORGES.us/2011/05/transition-operating-systems/#comments</comments>
		<pubDate>Mon, 16 May 2011 12:24:15 +0000</pubDate>
		<dc:creator>Rasmus Schultz</dc:creator>
				<category><![CDATA[Mobile Development]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[User Interface UX]]></category>

		<guid isPermaLink="false">http://blog.GORGES.us/?p=501</guid>
		<description><![CDATA[Why modern mobile operating systems are "transition" operating systems.]]></description>
				<content:encoded><![CDATA[<p>Modern mobile operating systems are all the hype, and we&#8217;re feeling it here at GORGES &#8211; clients are becoming increasingly aware of the importance of mobile technology.</p>
<p>The major contenders when it comes to mobile operating systems are <a href="http://www.android.com/" target="_blank">Android</a>, <a href="http://en.wikipedia.org/wiki/IOS_(Apple)" target="_blank">iOS</a> (more commonly known as iPhone, iPad and iPod), and <a href="http://www.microsoft.com/windowsphone/en-us/default.aspx" target="_blank">Windows Phone 7</a> &#8211; all fantastic mobile operating systems, it is hard to even begin to compare them to their predecessors.</p>
<p>But why were we suddenly (these past few years) imbued with these brilliant new OS&#8217;es?</p>
<p>The answer of course is advances in hardware &#8211; lower cost, more computation power, high resolution displays and multi-touch, more storage, faster wireless networks, longer battery life&#8230; and on top of all that, advances in nanotechnology has made everything smaller and lighter, too.</p>
<p>My point with this little article, is to share with you an observation that has surfaced in a few of my conversations lately: that these new mobile operating systems are part of a transition towards &#8220;real&#8221; operating systems running on mobile devices. In fact, I&#8217;ve taken to referring to them as &#8220;transition operating systems&#8221; myself.</p>
<p>The evolution of mobile hardware does not cease. What made these new OS&#8217;es possible, is the fact that mobile devices are now almost as fast as &#8220;real&#8221; computers &#8211; and that same progress will put an end to them, too.</p>
<p>Soon, mobile devices will be fast enough to run &#8220;real&#8221; operating systems &#8211; and when that time comes, why would you want a dedicated mobile OS, or even a mobile device dedicated to wireless communications, such as your phone? If you could run a real Windows, OSX or Linux OS on your device, why wouldn&#8217;t you? At least two of the major players in mobile hardware, <a href="http://blogs.forbes.com/briancaulfield/2011/01/24/ard-core-nvidia-could-reveal-quad-core-mobile-processor-this-year/" target="_blank">NVidia</a> and <a href="http://gadgetsteria.com/2011/02/14/qualcomm-announces-2-5ghz-quad-core-snapdragon-processor-mwc/" target="_blank">QualComm</a>, are currently racing to bring <a href="http://en.wikipedia.org/wiki/Multi-core_processor" target="_blank">quad-core processors</a> to market this year, so it may be more imminent than you think.</p>
<p>I suspect certain companies, such as Apple, are already having the same realization &#8211; it was recently <a href="http://www.9to5mac.com/31203/mac-os-x-10-7-to-borrow-some-ios-ui-features-claim" target="_blank">rumored</a> that certain features are being migrated from iOS to OSX. My guess is, they&#8217;re getting ready to add support for iOS apps to OSX, with the intent of running OSX on a mobile device in the no-too-distant future.</p>
<p>This is all speculation, of course &#8211; and either way, it doesn&#8217;t diminish the value of the mobile &#8220;transition&#8221; operating systems, which paved the way for new advances in user interface / user experience development, and have set new standards for human-machine interfaces overall.</p>
<p>And it certainly doesn&#8217;t mean that you should hold out on your business ideas &#8211; waiting for real operating systems to hit your cell phones &#8211; the new mobile platforms are already embedded in our lives, and they are inevitably here to stay, in some form or another.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.GORGES.us/2011/05/transition-operating-systems/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Change tracking with phpMyAdmin</title>
		<link>http://blog.GORGES.us/2011/02/change-tracking-with-phpmyadmin/</link>
		<comments>http://blog.GORGES.us/2011/02/change-tracking-with-phpmyadmin/#comments</comments>
		<pubDate>Mon, 14 Feb 2011 14:56:28 +0000</pubDate>
		<dc:creator>Rasmus Schultz</dc:creator>
				<category><![CDATA[System Administration]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://blog.GORGES.us/?p=496</guid>
		<description><![CDATA[phpMyAdmin recently had a cool new addition: change tracking - read why this matters to PHP/MySQL developers!]]></description>
				<content:encoded><![CDATA[<p>A couple of years back, I posted a feature request in the phpMyAdmin tracker. I was planning to implement this feature myself and I wrote a fairly complete specification. I submitted that with my feature request.</p>
<p>Much to my excitement, I recently discovered that this feature is now available in phpMyAdmin, and pretty much to my exact specifications!</p>
<p>The feature is change tracking &#8211; essentially a simple automatic schema version control system.</p>
<p>You can think of this idea as auto-generated, forward-only migrations. In other words, it doesn&#8217;t enable you to easily revert to a previous schema version, except by re-initializing the schema to a previous version, destroying existing data &#8211; but it also doesn&#8217;t involve writing migrations by hand.</p>
<p>You just open your schema, turn on tracking, and then proceed to work with phpMyAdmin the way you normally would &#8211; making changes to the schema (and/or data) using the GUI interface; your choice of SQL statements are tracked and recorded, and at the end, a snapshot of the complete schema is taken, enabling you to upgrade from one schema version to another, or initialize the schema to a specific version.</p>
<p>If you&#8217;re not already using database migrations, this could be a big timesaver, and should lighten the work of keeping your local/test/production databases in sync &#8211; as well as maintaining a history of what was changed and when. Nice!</p>
<p>Thanks, phpMyAdmin Team!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.GORGES.us/2011/02/change-tracking-with-phpmyadmin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>What is a Framework?</title>
		<link>http://blog.GORGES.us/2010/06/what-is-a-framework/</link>
		<comments>http://blog.GORGES.us/2010/06/what-is-a-framework/#comments</comments>
		<pubDate>Tue, 15 Jun 2010 12:27:52 +0000</pubDate>
		<dc:creator>Rasmus Schultz</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[active record]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[full stack frameworks]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[yii]]></category>

		<guid isPermaLink="false">http://blog.GORGES.us/?p=257</guid>
		<description><![CDATA[Given the diversity of packages that call themselves frameworks, the broadest description we can give, is that frameworks for web development are collections of reusable software components for building websites (or web-based applications).]]></description>
				<content:encoded><![CDATA[<p>Given the diversity of packages that call themselves frameworks, the broadest description we can give, is that frameworks for web development are collections of reusable software components for building websites (or web-based applications).</p>
<p>Frameworks generally belong to one of two camps, &#8220;pure frameworks&#8221; and &#8220;full stack frameworks&#8221;.</p>
<p>Most frameworks don&#8217;t make a distinction between &#8220;framework&#8221; and &#8220;application&#8221; features, and as a result they become cluttered. They also become more daunting to newcomers, with their sheer volume of classes that attempt to solve every practical problem a web developer might ever encounter &#8211; user management, document and image handling, upload and downloading, etc.</p>
<p>My favorite PHP framework, <a href="http://www.yiiframework.com/">Yii</a>, is a pure framework &#8211; free from the burden that many frameworks drag around, namely a full &#8220;application stack&#8221;. Yii consists almost exclusively of components and features designed to support certain practices or paradigms.</p>
<p>One of my favorite things about Yii, as compared to &#8220;full stack&#8221; frameworks, such as <a href="http://framework.zend.com/">Zend</a>, is that Yii only comes with components and architecture that can be rightfully said to belong in the &#8220;framework&#8221; domain, and not the &#8220;application&#8221; domain.</p>
<p>Of course, it may be hard in some cases to draw an exact line between the two, and I think the first criteria for selecting features that belong in the framework, and not in an application stack, should be:</p>
<p>• Is it absolutely general-purpose?<br />
• If not, is it fully extensible?</p>
<p>Certain features, like the URL manager or Active Record, are not absolutely general purpose, in the sense that they may not satisfy every possible need anyone could ever have. But they are sufficiently general purpose in the sense that almost everybody is going to need at least the core functionality of those components. And because they are fully extensible, developers can build on top of them, rather than having to replace them, if they find that a component does not fully cater to their specific needs.</p>
<p>The reason why I do not want application features in a framework, is because I know from experience that these will not meet my strict requirements. I will eventually end up replacing many of these features and the existing features provided by the framework are reduced to baggage.</p>
<p>Everybody is different &#8211; we all have different goals, and even if we share some goals, we usually have different means for reaching those goals.</p>
<p>I believe the reason why we web developers converge around a framework, is because we agree on certain practices. The framework is designed to leverage those practices in a convenient and streamlined way.</p>
<p>The subtle art of the framework is to achieve accepted practice, without getting in the way of individuality &#8211; to enable us to adhere to the good practices that we agree upon, while still allowing us to be as different, as versatile and as colorful as we can!</p>
<p>In short, frameworks enable us to focus our efforts on &#8220;business logic&#8221;, the functionality that has real value to the website users. Thanks to the framework, we can focus on the practical requirements, without sacrificing the values that professional software developers care about: clean, maintainable and extensible code.</p>
<p>With a good framework, we can deliver value <strong>and</strong> high code quality, without charging extra for quality!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.GORGES.us/2010/06/what-is-a-framework/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Web Tools for Natural Language Processing</title>
		<link>http://blog.GORGES.us/2009/09/web-tools-for-natural-language-processing/</link>
		<comments>http://blog.GORGES.us/2009/09/web-tools-for-natural-language-processing/#comments</comments>
		<pubDate>Tue, 01 Sep 2009 13:00:24 +0000</pubDate>
		<dc:creator>Rasmus Schultz</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[natural language processing]]></category>
		<category><![CDATA[Web 3.0]]></category>

		<guid isPermaLink="false">http://blog.GORGES.us/?p=157</guid>
		<description><![CDATA[Natural language processing is an important part of the semantic web.  Here is a short survey of some tools that are available to make your web application smarter.]]></description>
				<content:encoded><![CDATA[<p>We have been researching Web 3.0, which is the moniker assigned to the next generation of web applications that really understands what you are trying to do.</p>
<p>Part of creating &#8220;smart&#8221; web applications is understanding the semantics of what people type in, which implies using natural language processing.  Natural language processing software examines unstructured documents, and generates structured metadata that computers can handle.</p>
<p>Our application needed to understand phrases that people enter into a web browser.  We found three different approaches to handling this unstructured text:</p>
<p><span style="text-decoration: underline;"><strong>SaaS APIs</strong></span></p>
<p>These are hosted applications. All offer limited services at no charge, commercial services are generally pretty expensive. The major players appear to be:</p>
<p><a href="http://www.zemanta.com/"><strong>Zemanta</strong></a>: offers an API with automatic tagging, among many other features.</p>
<p><a href="http://www.opencalais.com/"><strong>OpenCalais</strong></a>: while it is by no means &#8220;open&#8221;, this API is powered by Reuters &#8211; which means that their &#8220;corpus&#8221; (body of words understood by the system) was composed using one of the world&#8217;s largest and most accurate volumes of text.</p>
<p><a href="http://www.alchemyapi.com/"><strong>Alchemy API</strong></a>: offers automated categorization, tagging, keywords, etc.</p>
<p><span style="text-decoration: underline;"><strong>NLP Toolkits</strong></span></p>
<p>These are open-source toolkits (APIs that you can install on your own server) for analysis of unstructured text. Learning how to apply one of these might take a considerable effort &#8211; someone would have to learn at least the basics of NLP, to apply this software, or you might choose to hire a consultant with the the skills to develop this part of the application.</p>
<p><a href="http://www.nltk.org/"><strong>NLTK.org</strong></a>: a library written in Python, started in 2005, has been slowly creeping towards release 1.0 for the past year or so. While relatively young, it may be based on newer research than some of the more mature NLP libraries. Many corpora, grammar collections and trained models ready to use.</p>
<p><a href="http://gate.ac.uk/"><strong>GATE</strong></a>: General Architecture for Text Engineering. Stable and proven toolkit for Java &#8211; this project started in 1995. Countless subprojects leverage this toolkit for various purposes.</p>
<p><a href="http://garraf.epsevg.upc.es/freeling/"><strong>FreeLing</strong></a>: Widely used toolkit in C++, with APIs for Java, PERL and Python. Online demos of this library demonstrate graphically how a short sentence can be broken down to a kind of tree-structure (nested subject/object, verb/adverb, etc.)</p>
<p>These are just a few examples &#8211; there are so many toolkits, and applications using these toolkits, that it would be impossible to make a choice based on a superficial analysis. To make a qualified choice, we would need to study at least the basics, or we would need the help of someone who knows enough about it to make a recommendation based on our needs.</p>
<p><span style="text-decoration: underline;"><strong>Roll-your-own</strong></span></p>
<p>Using e.g. MySQL, the <a href="http://en.wikipedia.org/wiki/Stemming">Porter stemmer</a>, a stop-word list and various other techniques to roll a basic search engine. Perhaps throw in a Bayesian text similarity measurement, to help rank the results and create stronger/weaker links between tables of keywords and posts.</p>
<p>It&#8217;s not NLP, and it&#8217;s not &#8220;web 3.0&#8243;, or &#8220;the semantic web&#8221; that everyone is buzzing about these days &#8211; because it does not understand semantics, and this will not yield the same kind of results &#8211; NLP systems &#8220;understand&#8221; unstructured text, where words like &#8220;not&#8221; and &#8220;really&#8221; can reverse or amplify the meaning of a subject &#8211; whereas anything you can roll on your own would most likely just recognize and consider these words &#8220;stop words&#8221; (ignoring them).</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.GORGES.us/2009/09/web-tools-for-natural-language-processing/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Dynamic resource management in JavaScript</title>
		<link>http://blog.GORGES.us/2009/04/dynamic-resource-management-in-javascript/</link>
		<comments>http://blog.GORGES.us/2009/04/dynamic-resource-management-in-javascript/#comments</comments>
		<pubDate>Wed, 01 Apr 2009 14:00:21 +0000</pubDate>
		<dc:creator>Rasmus Schultz</dc:creator>
				<category><![CDATA[Technology]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[web 2.0]]></category>

		<guid isPermaLink="false">http://blog.GORGES.us/?p=8</guid>
		<description><![CDATA[One of the most important features of modern web sites overall, is their ability to respond quickly to user requests. One way that modern sites achieve this, is by loading on-demand, in smaller increments - in this article, we provide a solution for loading JavaScript and CSS resources dynamically.]]></description>
				<content:encoded><![CDATA[<p>One of the most important features of modern web sites overall, is their ability to respond quickly and come back with the information requested by the user. We live in an &#8220;attention economy&#8221; on the Web 2.0, and if your site does not respond pretty much instantly, the user will find a site that does.</p>
<p>One way that modern sites achieve this, is by loading in smaller increments &#8211; for example, loading the detailed content for an item on a list can be deferred using AJAX.</p>
<p>Another way to break down the loading of a web application into smaller chunks, is by loading various other resources, specifically JavaScript and CSS files, on demand.</p>
<p>Doing this in a compatible and clean way, can be tricky.</p>
<h2>The Challenge</h2>
<p>Let&#8217;s look at some of the requirements that a good solution to this problem must fulfill.</p>
<p>First of all, some of the JavaScript frameworks we work with, do provide some sort of resource management. But we work with many different frameworks, and sometimes with no framework at all, so our solution needs to be <em>stand-alone</em> as well as <em>compatible</em> with most major JavaScript frameworks.</p>
<p>Secondly, we target all modern browsers &#8211; and therefore, our solution must be <em>fully cross browser</em> , targeting Internet Explorer 6 through 8, Firefox, Opera and Chrome, and hopefully any other standards compliant future browsers.</p>
<p>JavaScript is (or can be) object-oriented, meaning that a class in one script could potentially extend a class in another script. Therefore, since we are going to load resource asynchronously, we must ensure that scripts are loaded and executed <em>in order</em> .</p>
<p>Another concern is notification of resource readiness. Since resources are loaded asynchronously, and sometimes have already been loaded once, our script needs to provide a callback-notification when the resource is ready and available.</p>
<p>And finally, we don&#8217;t want to write code for every project to keep track of what&#8217;s been loaded and what has not. In other words, we need a <em>load-once</em> method, so that classes, widgets and stylesheets can be automatically loaded the first time they are needed.</p>
<h3>Limitations</h3>
<p>We&#8217;re going to accept certain limitations of this script.</p>
<p>For one, we&#8217;re not going to attempt to do a lot of error handling &#8211; if a resource can&#8217;t be loaded, this is a problem that needs to be solved by the developer, and not really something you can provide a &#8220;pretty&#8221; solution for anyway.</p>
<p>And secondly, some resource management scripts attempt to inject scripts in a way that allows scripts from foreign domains to run without security limitations. This has certain other drawbacks that I won&#8217;t get into, but we&#8217;re going to assume that you&#8217;re loading scripts from your own domain.</p>
<h2>Our Solution</h2>
<p>Our script comes in the form of a classless singleton object, which provides two methods for loading resources on demand:</p>
<pre class="brush: jscript; title: ; notranslate">
Loader.load( url, [callback-function], [context-object], [driver-name] );
Loader.once( url, [callback-function], [context-object], [driver-name] );
</pre>
<p>The <strong>Loader.load()</strong> method will allow you to load the same resource repeatedly &#8211; for example, you will be able to execute the same JavaScript more than once. Just keep in mind that duplicate class declarations, and duplicate initialization, for example, could cause problems. Most likely, you will only want to use this method if you want to intentionally overwrite existing variables.</p>
<p>The <strong>Loader.once()</strong> method will ensure that the same resource is only loaded once. Note that the callback will be called when the resource is <em>ready</em> , not only the first time when the resource actually loads.</p>
<p>Arguments for the two functions are identical:</p>
<ul>
<li><strong>url</strong> : required &#8211; relative (to your page) or absolute URL to a JavaScript or CSS resource.</li>
<li><strong>callback-function</strong> : optional callback-function &#8211; called when the resource (and any resources requested before it) has loaded.</li>
<li><strong>context-object</strong> : optional object to use as the context (this) for the callback-function. Use null (or leave out) if calling context is unimportant.</li>
<li><strong>driver-name</strong> : optional driver-name, e.g. &#8220;css&#8221; or &#8220;js&#8221; &#8211; this determines how the loaded resource is handled. If unspecified, the loader will try to determine the driver by file-extension, but will default to &#8220;js&#8221; if the file extension at the end of the URL does not match &#8220;.css&#8221; or &#8220;.js&#8221;.</li>
</ul>
<h3>Conclusion</h3>
<p>When you need to load a collection of resources, remember that they will be loaded in the order you request them. For example, loading components for a widget-based framework such as ExtJS is possible &#8211; you can load a component class declaration, and then load another class that extends it, but you must request them in order.</p>
<p>Another thing to keep in mind, when you need to know when a collection of resources are ready, you don&#8217;t need to attach a callback to every request &#8211; just attach your callback-function to the last request, as this won&#8217;t execute until all previously requested resources are ready.</p>
<h3>Source Code</h3>
<p>Finally, here is the source code for you to cut and paste:</p>
<pre class="brush: jscript; title: ; notranslate">
/*

Version:   1.1
Developer: Rasmus Schultz
License:   GPL v3 &lt;http://www.gnu.org/licenses/gpl-3.0-standalone.html&gt;

Copyright 2009, Gorges Web Sites &lt;http://www.GORGES.us&gt;

Removing this notice from the source code would be bad karma.

*/

var Loader = {
q: [], // the Queue for pending items to be loaded
reg: {}, // a registry to ensure items are loaded only once
load: function(url, cb, context, driver, once) {
var dd = url.split('.').pop();
if (!Loader.drivers[dd]) dd = driver || 'js';
var p = new Loader.Proxy(
{ url: url, cb: cb || function(){}, reg: once, driver: dd, context: context }
);
Loader.q.push(p);
Loader.next();
return p;
},
once: function(url, cb, context, driver) {
this.load(url, cb, context, driver, 1);
},
next: function() {
for (i=0; i&lt;Loader.q.length; i++) {
var l = Loader.q[i];
if (l.state == 1) return; // already loading
if (l.state == 0) return l.load(); // not loading (and not yet loaded)
}
}
}

Loader.Proxy = function(opt) {
this.driver = opt.driver;
this.context = opt.context || this;
this.url = opt.url;
this.reg = opt.reg;
this.state = 0; // inactive
this.cb = opt.cb;
this.load = function() {
if (this.reg &amp;amp;amp;amp;amp;amp;&amp;amp;amp;amp;amp;amp; Loader.reg[this.url]) return this.loaded(); // already loaded once
this.state = 1; // loading
var hd = document.getElementsByTagName(&quot;head&quot;)[0];
var el = Loader.drivers[this.driver](this, this.url + (this.url.indexOf('?') == -1 ? '?' : '&amp;amp;amp;amp;amp;amp;') + new Date().getTime());
hd.appendChild(el);
}
this.loaded = function() {
this.state = 2; // loaded
if (this.reg) Loader.reg[this.url] = 1;
this.cb.call(this.context);
Loader.next();
}
}

Loader.drivers = {

js: function(proxy, url) {
var el = document.createElement('script');
el.type = 'text/javascript';
el.src = url;
var me = proxy;
if (el.attachEvent) { // IE
el.attachEvent('onreadystatechange', function() {
if (el.readyState == 'loaded' || el.readyState == 'complete') me.loaded();
});
} else { // DOM
el.onload = function() { me.loaded(); }
}
return el;
},

css: function(proxy, url) {
var el = document.createElement('link');
el.rel = 'stylesheet';
el.type = 'text/css';
el.href = url;
el.media = 'all';
new (function(link, proxy){
this.index = document.styleSheets.length;
this.link = link;
this.proxy = proxy;
var me = this;
this.check = function() {
try {
var s = document.styleSheets[me.index];
if ((s.rules || s.cssRules).length) { // DOM || FF
window.clearInterval(me.int);
me.proxy.loaded();
}
} catch (e) {};
}
this.int = window.setInterval(this.check, 100);
})(el, proxy);
return el;
}

}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.GORGES.us/2009/04/dynamic-resource-management-in-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
